From 62b4f1ea498924f4fd9b49ae2f7645b70aac0e17 Mon Sep 17 00:00:00 2001
From: joaoluisam
Date: Tue, 7 Jan 2025 10:56:38 +0000
Subject: [PATCH 01/91] Add mantle config (#15844)
* add mantle, soneium and sonic core configs
* add l1 orcale config
* update develop
---
.../config/toml/defaults/Mantle_Mainnet.toml | 37 +++
.../config/toml/defaults/Mantle_Sepolia.toml | 38 +++
docs/CONFIG.md | 220 ++++++++++++++++++
3 files changed, 295 insertions(+)
create mode 100644 core/chains/evm/config/toml/defaults/Mantle_Mainnet.toml
create mode 100644 core/chains/evm/config/toml/defaults/Mantle_Sepolia.toml
diff --git a/core/chains/evm/config/toml/defaults/Mantle_Mainnet.toml b/core/chains/evm/config/toml/defaults/Mantle_Mainnet.toml
new file mode 100644
index 00000000000..33a34184f0b
--- /dev/null
+++ b/core/chains/evm/config/toml/defaults/Mantle_Mainnet.toml
@@ -0,0 +1,37 @@
+ChainID = '5000'
+FinalityTagEnabled = true
+FinalityDepth = 1200
+ChainType = 'optimismBedrock'
+LogPollInterval = '2s'
+MinIncomingConfirmations = 1
+NoNewFinalizedHeadsThreshold = '40m0s'
+
+[HeadTracker]
+HistoryDepth = 1250
+
+[GasEstimator]
+PriceMax = '120 gwei'
+# Limit values are high as Mantle's GasPrice is in native token (MNT) instead of ETH. Their proprietary TokenRatio parameter is used to adjust fees
+LimitDefault = 80_000_000_000
+LimitMax = 100_000_000_000
+BumpMin = '100 wei'
+BumpThreshold = 60
+EIP1559DynamicFees = true
+FeeCapDefault = '120 gwei'
+# Mantle recommends setting Priority Fee to 0 in their docs linked here: https://docs-v2.mantle.xyz/devs/concepts/tx-fee/eip-1559#application-of-eip-1559-in-mantle-v2-tectonic
+TipCapDefault = '0 wei'
+TipCapMin = '0 wei'
+
+[GasEstimator.BlockHistory]
+# Default is 24, which leads to bumpy gas prices. In CCIP
+# we want to smooth out the gas prices, so we increase the sample size.
+BlockHistorySize = 200
+# The formula for FeeCap is (current block base fee * (1.125 ^ EIP1559FeeCapBufferBlocks) + tipcap)
+# where tipcap is managed by the block history estimators. In the context of CCIP,
+# the gas price is relayed to other changes for quotes so we want accurate/avg not pessimistic values.
+# So we set this to zero so FeeCap = baseFee + tipcap.
+EIP1559FeeCapBufferBlocks = 0
+
+[GasEstimator.DAOracle]
+OracleType = 'opstack'
+OracleAddress = '0x420000000000000000000000000000000000000F'
diff --git a/core/chains/evm/config/toml/defaults/Mantle_Sepolia.toml b/core/chains/evm/config/toml/defaults/Mantle_Sepolia.toml
new file mode 100644
index 00000000000..bdfa4a204f8
--- /dev/null
+++ b/core/chains/evm/config/toml/defaults/Mantle_Sepolia.toml
@@ -0,0 +1,38 @@
+ChainID = '5003'
+FinalityTagEnabled = true
+FinalityDepth = 1200
+ChainType = 'optimismBedrock'
+LogPollInterval = '2s'
+MinIncomingConfirmations = 1
+NoNewFinalizedHeadsThreshold = '60m0s'
+
+[HeadTracker]
+HistoryDepth = 1250
+
+[GasEstimator]
+PriceMax = '120 gwei'
+# Limit values are high as Mantle's GasPrice is in native token (MNT) instead of ETH. Their proprietary TokenRatio parameter is used to adjust fees
+LimitDefault = 80000000000
+LimitMax = 100000000000
+BumpMin = '100 wei'
+BumpPercent = 20
+BumpThreshold = 60
+EIP1559DynamicFees = true
+FeeCapDefault = '120 gwei'
+# Mantle recommends setting Priority Fee to 0 in their docs linked here: https://docs-v2.mantle.xyz/devs/concepts/tx-fee/eip-1559#application-of-eip-1559-in-mantle-v2-tectonic
+TipCapDefault = '0 wei'
+TipCapMin = '0 wei'
+
+[GasEstimator.BlockHistory]
+# Default is 24, which leads to bumpy gas prices. In CCIP
+# we want to smooth out the gas prices, so we increase the sample size.
+BlockHistorySize = 200
+# The formula for FeeCap is (current block base fee * (1.125 ^ EIP1559FeeCapBufferBlocks) + tipcap)
+# where tipcap is managed by the block history estimators. In the context of CCIP,
+# the gas price is relayed to other changes for quotes so we want accurate/avg not pessimistic values.
+# So we set this to zero so FeeCap = baseFee + tipcap.
+EIP1559FeeCapBufferBlocks = 0
+
+[GasEstimator.DAOracle]
+OracleType = 'opstack'
+OracleAddress = '0x420000000000000000000000000000000000000F'
diff --git a/docs/CONFIG.md b/docs/CONFIG.md
index 13ab1548e00..f3a30838366 100644
--- a/docs/CONFIG.md
+++ b/docs/CONFIG.md
@@ -6682,6 +6682,226 @@ GasLimitDefault = 400000
+BOB Testnet (808813)
```toml
From 8729cb73812ae4896808cb8e718cc1b710848289 Mon Sep 17 00:00:00 2001
From: Sam
Date: Tue, 7 Jan 2025 10:56:55 -0500
Subject: [PATCH 04/91] Fix racey test (failed to close client) (#15853)
---
.../relay/evm/mercury/wsrpc/client_test.go | 33 ++++++++++---------
1 file changed, 17 insertions(+), 16 deletions(-)
diff --git a/core/services/relay/evm/mercury/wsrpc/client_test.go b/core/services/relay/evm/mercury/wsrpc/client_test.go
index f5b5be82a33..b2bbf074e91 100644
--- a/core/services/relay/evm/mercury/wsrpc/client_test.go
+++ b/core/services/relay/evm/mercury/wsrpc/client_test.go
@@ -14,7 +14,6 @@ import (
"github.com/smartcontractkit/wsrpc"
"github.com/smartcontractkit/chainlink-common/pkg/services/servicetest"
- "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/services/keystore/keys/csakey"
@@ -131,26 +130,26 @@ func Test_Client_Transmit(t *testing.T) {
})
t.Run("recovers panics in underlying client and attempts redial", func(t *testing.T) {
- conn := &mocks.MockConn{
- Ready: true,
- State: grpc_connectivity.Ready,
- InvokeF: func(ctx context.Context, method string, args interface{}, reply interface{}) error {
- panic("TESTING CONN INVOKE PANIC")
- },
+ makeConn := func() *mocks.MockConn {
+ return &mocks.MockConn{
+ Ready: true,
+ State: grpc_connectivity.Ready,
+ InvokeF: func(ctx context.Context, method string, args interface{}, reply interface{}) error {
+ panic("TESTING CONN INVOKE PANIC")
+ },
+ }
}
- ch := make(chan struct{}, 100)
+ ch := make(chan *mocks.MockConn, 100)
cnt := 0
f := func(ctxCaller context.Context, target string, opts ...wsrpc.DialOption) (Conn, error) {
cnt++
switch cnt {
- case 1:
- ch <- struct{}{}
+ case 1, 2:
+ conn := makeConn()
+ ch <- conn
return conn, nil
- case 2:
- ch <- struct{}{}
- return nil, nil
default:
t.Fatalf("too many dials, got: %d", cnt)
return nil, nil
@@ -169,11 +168,12 @@ func Test_Client_Transmit(t *testing.T) {
}
c := newClient(opts)
- require.NoError(t, c.Start(tests.Context(t)))
+ servicetest.Run(t, c)
// drain the channel
+ var conn *mocks.MockConn
select {
- case <-ch:
+ case conn = <-ch:
assert.Equal(t, 1, cnt)
default:
t.Fatalf("expected dial to be called")
@@ -183,10 +183,11 @@ func Test_Client_Transmit(t *testing.T) {
require.EqualError(t, err, "Transmit: caught panic: TESTING CONN INVOKE PANIC")
// expect conn to be closed and re-dialed
- <-ch
+ conn2 := <-ch
assert.Equal(t, 2, cnt)
assert.True(t, conn.Closed)
+ assert.False(t, conn2.Closed)
})
}
From aabd54df8c4169edf2fd97e18571ca7651e822a4 Mon Sep 17 00:00:00 2001
From: Justin Kaseman
Date: Tue, 7 Jan 2025 08:32:02 -0800
Subject: [PATCH 05/91] Checkout Standard Capabilities (#15671)
* During build, add standard capability binaries to -plugins image
* Shift down token step
* Remove set-git-config
---
.github/workflows/build-publish-develop-pr.yml | 17 ++++++++++++++++-
tools/bin/goreleaser_utils | 10 ++++++++++
2 files changed, 26 insertions(+), 1 deletion(-)
diff --git a/.github/workflows/build-publish-develop-pr.yml b/.github/workflows/build-publish-develop-pr.yml
index 85f29a15cb0..4aed811daa6 100644
--- a/.github/workflows/build-publish-develop-pr.yml
+++ b/.github/workflows/build-publish-develop-pr.yml
@@ -89,13 +89,28 @@ jobs:
goarch: arm64
dist_name: linux_arm64_v8.0
steps:
- - name: Checkout repository
+ - name: Checkout chainlink repository
uses: actions/checkout@v4.2.1
with:
persist-credentials: false
ref: ${{ env.CHECKOUT_REF }}
fetch-depth: 0
+ - name: Setup Github Token
+ id: token
+ uses: smartcontractkit/.github/actions/setup-github-token@ef78fa97bf3c77de6563db1175422703e9e6674f # setup-github-token@0.2.1
+ with:
+ aws-role-arn: ${{ secrets.AWS_OIDC_GLOBAL_READ_ONLY_TOKEN_ISSUER_ROLE_ARN }}
+ aws-lambda-url: ${{ secrets.AWS_INFRA_RELENG_TOKEN_ISSUER_LAMBDA_URL }}
+ aws-region: ${{ secrets.AWS_REGION }}
+
+ - name: Checkout capabilities repository
+ uses: actions/checkout@v4.2.1
+ with:
+ repository: smartcontractkit/capabilities
+ token: ${{ steps.token.outputs.access-token }}
+ path: capabilities
+
- name: Configure aws credentials
uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502 # v4.0.2
with:
diff --git a/tools/bin/goreleaser_utils b/tools/bin/goreleaser_utils
index 0bf745d5a58..e38acf6494b 100755
--- a/tools/bin/goreleaser_utils
+++ b/tools/bin/goreleaser_utils
@@ -13,6 +13,7 @@ before_hook() {
install_local_plugins
install_remote_plugins
mkdir -p "$lib_path/plugins"
+ build_standard_capabilities
# Retrieve GOPATH
GOPATH=$(go env GOPATH)
@@ -71,6 +72,15 @@ get_remote_plugin_paths() {
done
}
+build_standard_capabilities() {
+ cd ./capabilities
+ npx nx@19.8.2 init
+ ./nx run-many -t build
+ # binaries get put into /bin under /bin/amd64/ and /bin/arm64/
+ cp "./bin/$(go env GOARCH)"/* "../$lib_path/plugins"
+ cd ../
+}
+
install_remote_plugins() {
ldflags=(-ldflags "$(./tools/bin/ldflags)")
From 484124ae569227acaf0d33a8e4599e90fbef23de Mon Sep 17 00:00:00 2001
From: Jordan Krage
Date: Tue, 7 Jan 2025 10:45:42 -0600
Subject: [PATCH 06/91] bump mockery to v2.50.0 (#15789)
---
GNUmakefile | 2 +-
common/client/mock_head_test.go | 8 +-
common/client/mock_node_selector_test.go | 16 +-
common/client/mock_node_test.go | 64 +++----
.../mock_pool_chain_info_provider_test.go | 6 +-
common/client/mock_rpc_client_test.go | 14 +-
common/client/mock_send_only_client_test.go | 10 +-
common/client/mock_send_only_node_test.go | 42 +++--
common/headtracker/mocks/head_broadcaster.go | 16 +-
common/headtracker/mocks/head_trackable.go | 4 +-
common/headtracker/mocks/head_tracker.go | 24 ++-
common/txmgr/mocks/tx_manager.go | 24 +--
common/txmgr/types/mocks/forwarder_manager.go | 18 +-
common/txmgr/types/mocks/key_store.go | 2 +-
.../txmgr/types/mocks/tx_attempt_builder.go | 24 ++-
common/txmgr/types/mocks/tx_store.go | 10 +-
common/txmgr/types/mocks/tx_strategy.go | 4 +-
common/types/mocks/head.go | 34 ++--
common/types/mocks/monitoring_endpoint.go | 4 +-
common/types/mocks/subscription.go | 8 +-
contracts/GNUmakefile | 2 +-
core/bridges/mocks/orm.go | 2 +-
.../ccip/types/mocks/ccip_oracle.go | 6 +-
.../ccip/types/mocks/oracle_creator.go | 4 +-
.../remote/types/mocks/dispatcher.go | 26 +--
.../remote/types/mocks/receiver.go | 4 +-
.../targets/mocks/contract_value_getter.go | 16 +-
.../targets/mocks/contract_writer.go | 22 +--
core/chains/evm/client/mocks/client.go | 12 +-
.../evm/config/mocks/chain_scoped_config.go | 4 +-
core/chains/evm/config/mocks/gas_estimator.go | 44 ++---
core/chains/evm/forwarders/mocks/orm.go | 2 +-
core/chains/evm/gas/mocks/evm_estimator.go | 14 +-
.../chains/evm/gas/mocks/evm_fee_estimator.go | 14 +-
.../evm/gas/mocks/fee_estimator_client.go | 2 +-
.../gas/mocks/fee_history_estimator_client.go | 2 +-
.../chains/evm/gas/rollups/mocks/l1_oracle.go | 10 +-
.../evm/gas/rollups/mocks/l1_oracle_client.go | 2 +-
core/chains/evm/keystore/mocks/eth.go | 2 +-
core/chains/evm/log/mocks/abigen_contract.go | 4 +-
core/chains/evm/log/mocks/broadcast.go | 22 +--
core/chains/evm/log/mocks/broadcaster.go | 24 +--
core/chains/evm/logpoller/mocks/log_poller.go | 16 +-
core/chains/evm/mocks/balance_monitor.go | 12 +-
core/chains/evm/txmgr/mocks/config.go | 12 +-
core/chains/evm/txmgr/mocks/evm_tx_store.go | 162 +++++++++---------
core/chains/legacyevm/mocks/chain.go | 76 ++++----
.../legacyevm/mocks/legacy_chain_container.go | 8 +-
core/cmd/mocks/prompter.go | 4 +-
core/config/mocks/telemetry_ingress.go | 18 +-
.../mocks/telemetry_ingress_endpoint.go | 10 +-
.../ccip/mocks/commit_store_interface.go | 4 +-
.../ccip/mocks/evm2_evm_off_ramp_interface.go | 4 +-
.../ccip/mocks/evm2_evm_on_ramp_interface.go | 4 +-
.../ccip/mocks/fee_quoter_interface.go | 4 +-
.../ccip/mocks/link_token_interface.go | 4 +-
.../v1_2_0/evm2_evm_off_ramp_interface.go | 4 +-
.../arbitrum_gateway_router_interface.go | 4 +-
.../arbitrum_inbox_interface.go | 4 +-
.../arbitrum_l1_bridge_adapter_interface.go | 4 +-
.../arbitrum_l2_bridge_adapter_interface.go | 4 +-
.../arb_rollup_core_interface.go | 4 +-
.../mocks/mock_arbsys/arb_sys_interface.go | 4 +-
.../l2_arbitrum_gateway_interface.go | 4 +-
.../l2_arbitrum_messenger_interface.go | 4 +-
.../node_interface_interface.go | 4 +-
...optimism_dispute_game_factory_interface.go | 4 +-
.../optimism_l2_output_oracle_interface.go | 4 +-
.../optimism_portal_interface.go | 4 +-
.../optimism_portal2_interface.go | 4 +-
core/internal/mocks/application.go | 52 +++---
core/internal/mocks/flags.go | 4 +-
core/internal/mocks/flux_aggregator.go | 4 +-
core/logger/logger_mocks.go | 58 +++----
core/services/blockhashstore/mocks/bhs.go | 4 +-
core/services/blockhashstore/mocks/timer.go | 2 +-
core/services/ccip/mocks/orm.go | 2 +-
.../chainlink/mocks/general_config.go | 90 +++++-----
.../feeds/mocks/connections_manager.go | 8 +-
.../feeds/mocks/feeds_manager_client.go | 2 +-
core/services/feeds/mocks/orm.go | 2 +-
core/services/feeds/mocks/service.go | 6 +-
.../fluxmonitorv2/mocks/contract_submitter.go | 2 +-
core/services/fluxmonitorv2/mocks/flags.go | 6 +-
.../mocks/key_store_interface.go | 2 +-
core/services/fluxmonitorv2/mocks/orm.go | 2 +-
.../functions/mocks/bridge_accessor.go | 2 +-
.../mocks/external_adapter_client.go | 2 +-
.../functions/mocks/functions_listener.go | 4 +-
.../functions/mocks/offchain_transmitter.go | 4 +-
core/services/functions/mocks/orm.go | 2 +-
.../connector/mocks/gateway_connector.go | 14 +-
.../mocks/gateway_connector_handler.go | 6 +-
.../gateway/connector/mocks/signer.go | 2 +-
.../allowlist/mocks/onchain_allowlist.go | 4 +-
.../handlers/functions/allowlist/mocks/orm.go | 2 +-
.../mocks/onchain_subscriptions.go | 4 +-
.../functions/subscriptions/mocks/orm.go | 2 +-
core/services/gateway/handlers/mocks/don.go | 2 +-
.../gateway/handlers/mocks/handler.go | 4 +-
.../network/mocks/connection_acceptor.go | 4 +-
.../network/mocks/connection_initiator.go | 2 +-
.../gateway/network/mocks/http_client.go | 2 +-
.../network/mocks/http_request_handler.go | 2 +-
.../gateway/network/mocks/http_server.go | 8 +-
.../network/mocks/web_socket_server.go | 6 +-
.../headreporter/head_reporter_mock.go | 2 +-
.../headreporter/prometheus_backend_mock.go | 12 +-
core/services/job/mocks/kv_store.go | 2 +-
core/services/job/mocks/orm.go | 8 +-
core/services/job/mocks/service_ctx.go | 4 +-
core/services/job/mocks/spawner.go | 12 +-
core/services/keystore/mocks/aptos.go | 4 +-
core/services/keystore/mocks/cosmos.go | 4 +-
core/services/keystore/mocks/csa.go | 4 +-
core/services/keystore/mocks/eth.go | 6 +-
core/services/keystore/mocks/master.go | 24 +--
core/services/keystore/mocks/ocr.go | 4 +-
core/services/keystore/mocks/ocr2.go | 4 +-
core/services/keystore/mocks/p2p.go | 4 +-
core/services/keystore/mocks/solana.go | 4 +-
core/services/keystore/mocks/starknet.go | 4 +-
core/services/keystore/mocks/vrf.go | 4 +-
core/services/keystore/mocks/workflow.go | 4 +-
core/services/mocks/checker.go | 10 +-
.../ocr/mocks/ocr_contract_tracker_db.go | 2 +-
.../internal/cache/mocks/chain_health_mock.go | 4 +-
.../mocks/token_pool_batched_reader_mock.go | 4 +-
.../mocks/price_registry_mock.go | 2 +-
.../mocks/commit_store_reader_mock.go | 4 +-
.../ccipdata/mocks/offramp_reader_mock.go | 4 +-
.../ccipdata/mocks/onramp_reader_mock.go | 4 +-
.../mocks/price_registry_reader_mock.go | 4 +-
.../ccipdata/mocks/token_pool_reader_mock.go | 6 +-
.../ccipdata/mocks/usdc_reader_mock.go | 2 +-
.../ccipdb/mocks/price_service_mock.go | 4 +-
.../pricegetter/all_price_getter_mock.go | 4 +-
.../plugins/ccip/internal/pricegetter/mock.go | 4 +-
.../internal/rpclib/rpclibmocks/evm_mock.go | 2 +-
.../prices/gas_price_estimator_commit_mock.go | 2 +-
.../prices/gas_price_estimator_exec_mock.go | 2 +-
.../ccip/prices/gas_price_estimator_mock.go | 2 +-
.../plugins/ccip/tokendata/reader_mock.go | 4 +-
.../evmregistry/v20/mocks/registry.go | 2 +-
.../v21/core/mocks/upkeep_state_reader.go | 2 +-
.../evmregistry/v21/mocks/http_client.go | 2 +-
.../evmregistry/v21/mocks/registry.go | 17 +-
.../promwrapper/mocks/prometheus_backend.go | 22 +--
.../ocr2/plugins/threshold/mocks/decryptor.go | 2 +-
core/services/p2p/types/mocks/peer.go | 54 +++---
core/services/p2p/types/mocks/peer_wrapper.go | 12 +-
core/services/p2p/types/mocks/signer.go | 2 +-
core/services/pipeline/mocks/config.go | 14 +-
core/services/pipeline/mocks/orm.go | 12 +-
.../mocks/pipeline_param_unmarshaler.go | 2 +-
core/services/pipeline/mocks/runner.go | 12 +-
core/services/registrysyncer/mocks/orm.go | 2 +-
.../relay/evm/mercury/mocks/async_deleter.go | 4 +-
core/services/relay/evm/mocks/codec.go | 30 ++--
.../relay/evm/mocks/request_round_db.go | 2 +-
.../relay/evm/read/mocks/batch_caller.go | 2 +-
core/services/relay/evm/read/mocks/reader.go | 56 +++---
.../relay/evm/read/mocks/registrar.go | 2 +-
.../mocks/ccip_transaction_status_checker.go | 2 +-
.../evm/types/mocks/log_poller_wrapper.go | 12 +-
core/services/s4/mocks/orm.go | 2 +-
core/services/s4/mocks/storage.go | 4 +-
.../synchronization/mocks/telem_client.go | 2 +-
.../mocks/telemetry_service.go | 12 +-
.../monitoring_endpoint_generator_mock.go | 2 +-
.../vrf/mocks/aggregator_v3_interface.go | 4 +-
core/services/vrf/mocks/config.go | 6 +-
core/services/vrf/mocks/fee_config.go | 6 +-
core/services/vrf/mocks/vrf_coordinator_v2.go | 4 +-
.../mocks/external_initiator_manager.go | 2 +-
core/services/webhook/mocks/http_client.go | 2 +-
core/services/workflows/syncer/mocks/orm.go | 2 +-
core/sessions/ldapauth/mocks/ldap_client.go | 4 +-
core/sessions/ldapauth/mocks/ldap_conn.go | 4 +-
.../sessions/mocks/authentication_provider.go | 2 +-
core/sessions/mocks/basic_admin_users_orm.go | 2 +-
deployment/mocks/offchain_client_mock.go | 2 +-
182 files changed, 923 insertions(+), 878 deletions(-)
diff --git a/GNUmakefile b/GNUmakefile
index bb200cc2cb9..f877f01decb 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -146,7 +146,7 @@ gomodslocalupdate: gomods ## Run gomod-local-update
.PHONY: mockery
mockery: $(mockery) ## Install mockery.
- go install github.com/vektra/mockery/v2@v2.46.3
+ go install github.com/vektra/mockery/v2@v2.50.0
.PHONY: codecgen
codecgen: $(codecgen) ## Install codecgen
diff --git a/common/client/mock_head_test.go b/common/client/mock_head_test.go
index f75bb340305..01884d3fcfc 100644
--- a/common/client/mock_head_test.go
+++ b/common/client/mock_head_test.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package client
@@ -21,7 +21,7 @@ func (_m *mockHead) EXPECT() *mockHead_Expecter {
return &mockHead_Expecter{mock: &_m.Mock}
}
-// BlockDifficulty provides a mock function with given fields:
+// BlockDifficulty provides a mock function with no fields
func (_m *mockHead) BlockDifficulty() *big.Int {
ret := _m.Called()
@@ -68,7 +68,7 @@ func (_c *mockHead_BlockDifficulty_Call) RunAndReturn(run func() *big.Int) *mock
return _c
}
-// BlockNumber provides a mock function with given fields:
+// BlockNumber provides a mock function with no fields
func (_m *mockHead) BlockNumber() int64 {
ret := _m.Called()
@@ -113,7 +113,7 @@ func (_c *mockHead_BlockNumber_Call) RunAndReturn(run func() int64) *mockHead_Bl
return _c
}
-// IsValid provides a mock function with given fields:
+// IsValid provides a mock function with no fields
func (_m *mockHead) IsValid() bool {
ret := _m.Called()
diff --git a/common/client/mock_node_selector_test.go b/common/client/mock_node_selector_test.go
index c01895a0ee7..c4201664b4a 100644
--- a/common/client/mock_node_selector_test.go
+++ b/common/client/mock_node_selector_test.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package client
@@ -8,11 +8,11 @@ import (
)
// mockNodeSelector is an autogenerated mock type for the NodeSelector type
-type mockNodeSelector[CHAIN_ID types.ID, RPC any] struct {
+type mockNodeSelector[CHAIN_ID types.ID, RPC interface{}] struct {
mock.Mock
}
-type mockNodeSelector_Expecter[CHAIN_ID types.ID, RPC any] struct {
+type mockNodeSelector_Expecter[CHAIN_ID types.ID, RPC interface{}] struct {
mock *mock.Mock
}
@@ -20,7 +20,7 @@ func (_m *mockNodeSelector[CHAIN_ID, RPC]) EXPECT() *mockNodeSelector_Expecter[C
return &mockNodeSelector_Expecter[CHAIN_ID, RPC]{mock: &_m.Mock}
}
-// Name provides a mock function with given fields:
+// Name provides a mock function with no fields
func (_m *mockNodeSelector[CHAIN_ID, RPC]) Name() string {
ret := _m.Called()
@@ -39,7 +39,7 @@ func (_m *mockNodeSelector[CHAIN_ID, RPC]) Name() string {
}
// mockNodeSelector_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Name'
-type mockNodeSelector_Name_Call[CHAIN_ID types.ID, RPC any] struct {
+type mockNodeSelector_Name_Call[CHAIN_ID types.ID, RPC interface{}] struct {
*mock.Call
}
@@ -65,7 +65,7 @@ func (_c *mockNodeSelector_Name_Call[CHAIN_ID, RPC]) RunAndReturn(run func() str
return _c
}
-// Select provides a mock function with given fields:
+// Select provides a mock function with no fields
func (_m *mockNodeSelector[CHAIN_ID, RPC]) Select() Node[CHAIN_ID, RPC] {
ret := _m.Called()
@@ -86,7 +86,7 @@ func (_m *mockNodeSelector[CHAIN_ID, RPC]) Select() Node[CHAIN_ID, RPC] {
}
// mockNodeSelector_Select_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Select'
-type mockNodeSelector_Select_Call[CHAIN_ID types.ID, RPC any] struct {
+type mockNodeSelector_Select_Call[CHAIN_ID types.ID, RPC interface{}] struct {
*mock.Call
}
@@ -114,7 +114,7 @@ func (_c *mockNodeSelector_Select_Call[CHAIN_ID, RPC]) RunAndReturn(run func() N
// newMockNodeSelector creates a new instance of mockNodeSelector. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
-func newMockNodeSelector[CHAIN_ID types.ID, RPC any](t interface {
+func newMockNodeSelector[CHAIN_ID types.ID, RPC interface{}](t interface {
mock.TestingT
Cleanup(func())
}) *mockNodeSelector[CHAIN_ID, RPC] {
diff --git a/common/client/mock_node_test.go b/common/client/mock_node_test.go
index e5b090ab641..f0fc6a4cb58 100644
--- a/common/client/mock_node_test.go
+++ b/common/client/mock_node_test.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package client
@@ -10,11 +10,11 @@ import (
)
// mockNode is an autogenerated mock type for the Node type
-type mockNode[CHAIN_ID types.ID, RPC any] struct {
+type mockNode[CHAIN_ID types.ID, RPC interface{}] struct {
mock.Mock
}
-type mockNode_Expecter[CHAIN_ID types.ID, RPC any] struct {
+type mockNode_Expecter[CHAIN_ID types.ID, RPC interface{}] struct {
mock *mock.Mock
}
@@ -22,7 +22,7 @@ func (_m *mockNode[CHAIN_ID, RPC]) EXPECT() *mockNode_Expecter[CHAIN_ID, RPC] {
return &mockNode_Expecter[CHAIN_ID, RPC]{mock: &_m.Mock}
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *mockNode[CHAIN_ID, RPC]) Close() error {
ret := _m.Called()
@@ -41,7 +41,7 @@ func (_m *mockNode[CHAIN_ID, RPC]) Close() error {
}
// mockNode_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close'
-type mockNode_Close_Call[CHAIN_ID types.ID, RPC any] struct {
+type mockNode_Close_Call[CHAIN_ID types.ID, RPC interface{}] struct {
*mock.Call
}
@@ -67,7 +67,7 @@ func (_c *mockNode_Close_Call[CHAIN_ID, RPC]) RunAndReturn(run func() error) *mo
return _c
}
-// ConfiguredChainID provides a mock function with given fields:
+// ConfiguredChainID provides a mock function with no fields
func (_m *mockNode[CHAIN_ID, RPC]) ConfiguredChainID() CHAIN_ID {
ret := _m.Called()
@@ -79,14 +79,16 @@ func (_m *mockNode[CHAIN_ID, RPC]) ConfiguredChainID() CHAIN_ID {
if rf, ok := ret.Get(0).(func() CHAIN_ID); ok {
r0 = rf()
} else {
- r0 = ret.Get(0).(CHAIN_ID)
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(CHAIN_ID)
+ }
}
return r0
}
// mockNode_ConfiguredChainID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ConfiguredChainID'
-type mockNode_ConfiguredChainID_Call[CHAIN_ID types.ID, RPC any] struct {
+type mockNode_ConfiguredChainID_Call[CHAIN_ID types.ID, RPC interface{}] struct {
*mock.Call
}
@@ -112,7 +114,7 @@ func (_c *mockNode_ConfiguredChainID_Call[CHAIN_ID, RPC]) RunAndReturn(run func(
return _c
}
-// HighestUserObservations provides a mock function with given fields:
+// HighestUserObservations provides a mock function with no fields
func (_m *mockNode[CHAIN_ID, RPC]) HighestUserObservations() ChainInfo {
ret := _m.Called()
@@ -131,7 +133,7 @@ func (_m *mockNode[CHAIN_ID, RPC]) HighestUserObservations() ChainInfo {
}
// mockNode_HighestUserObservations_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'HighestUserObservations'
-type mockNode_HighestUserObservations_Call[CHAIN_ID types.ID, RPC any] struct {
+type mockNode_HighestUserObservations_Call[CHAIN_ID types.ID, RPC interface{}] struct {
*mock.Call
}
@@ -157,7 +159,7 @@ func (_c *mockNode_HighestUserObservations_Call[CHAIN_ID, RPC]) RunAndReturn(run
return _c
}
-// Name provides a mock function with given fields:
+// Name provides a mock function with no fields
func (_m *mockNode[CHAIN_ID, RPC]) Name() string {
ret := _m.Called()
@@ -176,7 +178,7 @@ func (_m *mockNode[CHAIN_ID, RPC]) Name() string {
}
// mockNode_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Name'
-type mockNode_Name_Call[CHAIN_ID types.ID, RPC any] struct {
+type mockNode_Name_Call[CHAIN_ID types.ID, RPC interface{}] struct {
*mock.Call
}
@@ -202,7 +204,7 @@ func (_c *mockNode_Name_Call[CHAIN_ID, RPC]) RunAndReturn(run func() string) *mo
return _c
}
-// Order provides a mock function with given fields:
+// Order provides a mock function with no fields
func (_m *mockNode[CHAIN_ID, RPC]) Order() int32 {
ret := _m.Called()
@@ -221,7 +223,7 @@ func (_m *mockNode[CHAIN_ID, RPC]) Order() int32 {
}
// mockNode_Order_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Order'
-type mockNode_Order_Call[CHAIN_ID types.ID, RPC any] struct {
+type mockNode_Order_Call[CHAIN_ID types.ID, RPC interface{}] struct {
*mock.Call
}
@@ -247,7 +249,7 @@ func (_c *mockNode_Order_Call[CHAIN_ID, RPC]) RunAndReturn(run func() int32) *mo
return _c
}
-// RPC provides a mock function with given fields:
+// RPC provides a mock function with no fields
func (_m *mockNode[CHAIN_ID, RPC]) RPC() RPC {
ret := _m.Called()
@@ -259,14 +261,16 @@ func (_m *mockNode[CHAIN_ID, RPC]) RPC() RPC {
if rf, ok := ret.Get(0).(func() RPC); ok {
r0 = rf()
} else {
- r0 = ret.Get(0).(RPC)
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(RPC)
+ }
}
return r0
}
// mockNode_RPC_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RPC'
-type mockNode_RPC_Call[CHAIN_ID types.ID, RPC any] struct {
+type mockNode_RPC_Call[CHAIN_ID types.ID, RPC interface{}] struct {
*mock.Call
}
@@ -298,7 +302,7 @@ func (_m *mockNode[CHAIN_ID, RPC]) SetPoolChainInfoProvider(_a0 PoolChainInfoPro
}
// mockNode_SetPoolChainInfoProvider_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetPoolChainInfoProvider'
-type mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID types.ID, RPC any] struct {
+type mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID types.ID, RPC interface{}] struct {
*mock.Call
}
@@ -321,7 +325,7 @@ func (_c *mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID, RPC]) Return() *mockN
}
func (_c *mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID, RPC]) RunAndReturn(run func(PoolChainInfoProvider)) *mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID, RPC] {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
@@ -344,7 +348,7 @@ func (_m *mockNode[CHAIN_ID, RPC]) Start(_a0 context.Context) error {
}
// mockNode_Start_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Start'
-type mockNode_Start_Call[CHAIN_ID types.ID, RPC any] struct {
+type mockNode_Start_Call[CHAIN_ID types.ID, RPC interface{}] struct {
*mock.Call
}
@@ -371,7 +375,7 @@ func (_c *mockNode_Start_Call[CHAIN_ID, RPC]) RunAndReturn(run func(context.Cont
return _c
}
-// State provides a mock function with given fields:
+// State provides a mock function with no fields
func (_m *mockNode[CHAIN_ID, RPC]) State() nodeState {
ret := _m.Called()
@@ -390,7 +394,7 @@ func (_m *mockNode[CHAIN_ID, RPC]) State() nodeState {
}
// mockNode_State_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'State'
-type mockNode_State_Call[CHAIN_ID types.ID, RPC any] struct {
+type mockNode_State_Call[CHAIN_ID types.ID, RPC interface{}] struct {
*mock.Call
}
@@ -416,7 +420,7 @@ func (_c *mockNode_State_Call[CHAIN_ID, RPC]) RunAndReturn(run func() nodeState)
return _c
}
-// StateAndLatest provides a mock function with given fields:
+// StateAndLatest provides a mock function with no fields
func (_m *mockNode[CHAIN_ID, RPC]) StateAndLatest() (nodeState, ChainInfo) {
ret := _m.Called()
@@ -445,7 +449,7 @@ func (_m *mockNode[CHAIN_ID, RPC]) StateAndLatest() (nodeState, ChainInfo) {
}
// mockNode_StateAndLatest_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'StateAndLatest'
-type mockNode_StateAndLatest_Call[CHAIN_ID types.ID, RPC any] struct {
+type mockNode_StateAndLatest_Call[CHAIN_ID types.ID, RPC interface{}] struct {
*mock.Call
}
@@ -471,7 +475,7 @@ func (_c *mockNode_StateAndLatest_Call[CHAIN_ID, RPC]) RunAndReturn(run func() (
return _c
}
-// String provides a mock function with given fields:
+// String provides a mock function with no fields
func (_m *mockNode[CHAIN_ID, RPC]) String() string {
ret := _m.Called()
@@ -490,7 +494,7 @@ func (_m *mockNode[CHAIN_ID, RPC]) String() string {
}
// mockNode_String_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'String'
-type mockNode_String_Call[CHAIN_ID types.ID, RPC any] struct {
+type mockNode_String_Call[CHAIN_ID types.ID, RPC interface{}] struct {
*mock.Call
}
@@ -516,13 +520,13 @@ func (_c *mockNode_String_Call[CHAIN_ID, RPC]) RunAndReturn(run func() string) *
return _c
}
-// UnsubscribeAllExceptAliveLoop provides a mock function with given fields:
+// UnsubscribeAllExceptAliveLoop provides a mock function with no fields
func (_m *mockNode[CHAIN_ID, RPC]) UnsubscribeAllExceptAliveLoop() {
_m.Called()
}
// mockNode_UnsubscribeAllExceptAliveLoop_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UnsubscribeAllExceptAliveLoop'
-type mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID types.ID, RPC any] struct {
+type mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID types.ID, RPC interface{}] struct {
*mock.Call
}
@@ -544,13 +548,13 @@ func (_c *mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, RPC]) Return() *
}
func (_c *mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, RPC]) RunAndReturn(run func()) *mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, RPC] {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
// newMockNode creates a new instance of mockNode. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
-func newMockNode[CHAIN_ID types.ID, RPC any](t interface {
+func newMockNode[CHAIN_ID types.ID, RPC interface{}](t interface {
mock.TestingT
Cleanup(func())
}) *mockNode[CHAIN_ID, RPC] {
diff --git a/common/client/mock_pool_chain_info_provider_test.go b/common/client/mock_pool_chain_info_provider_test.go
index c44f10b3f2f..7523a060329 100644
--- a/common/client/mock_pool_chain_info_provider_test.go
+++ b/common/client/mock_pool_chain_info_provider_test.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package client
@@ -17,7 +17,7 @@ func (_m *mockPoolChainInfoProvider) EXPECT() *mockPoolChainInfoProvider_Expecte
return &mockPoolChainInfoProvider_Expecter{mock: &_m.Mock}
}
-// HighestUserObservations provides a mock function with given fields:
+// HighestUserObservations provides a mock function with no fields
func (_m *mockPoolChainInfoProvider) HighestUserObservations() ChainInfo {
ret := _m.Called()
@@ -62,7 +62,7 @@ func (_c *mockPoolChainInfoProvider_HighestUserObservations_Call) RunAndReturn(r
return _c
}
-// LatestChainInfo provides a mock function with given fields:
+// LatestChainInfo provides a mock function with no fields
func (_m *mockPoolChainInfoProvider) LatestChainInfo() (int, ChainInfo) {
ret := _m.Called()
diff --git a/common/client/mock_rpc_client_test.go b/common/client/mock_rpc_client_test.go
index 396914f320b..9ad71c646e4 100644
--- a/common/client/mock_rpc_client_test.go
+++ b/common/client/mock_rpc_client_test.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package client
@@ -38,7 +38,9 @@ func (_m *mockRPCClient[CHAIN_ID, HEAD]) ChainID(ctx context.Context) (CHAIN_ID,
if rf, ok := ret.Get(0).(func(context.Context) CHAIN_ID); ok {
r0 = rf(ctx)
} else {
- r0 = ret.Get(0).(CHAIN_ID)
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(CHAIN_ID)
+ }
}
if rf, ok := ret.Get(1).(func(context.Context) error); ok {
@@ -78,7 +80,7 @@ func (_c *mockRPCClient_ChainID_Call[CHAIN_ID, HEAD]) RunAndReturn(run func(cont
return _c
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *mockRPCClient[CHAIN_ID, HEAD]) Close() {
_m.Called()
}
@@ -106,7 +108,7 @@ func (_c *mockRPCClient_Close_Call[CHAIN_ID, HEAD]) Return() *mockRPCClient_Clos
}
func (_c *mockRPCClient_Close_Call[CHAIN_ID, HEAD]) RunAndReturn(run func()) *mockRPCClient_Close_Call[CHAIN_ID, HEAD] {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
@@ -156,7 +158,7 @@ func (_c *mockRPCClient_Dial_Call[CHAIN_ID, HEAD]) RunAndReturn(run func(context
return _c
}
-// GetInterceptedChainInfo provides a mock function with given fields:
+// GetInterceptedChainInfo provides a mock function with no fields
func (_m *mockRPCClient[CHAIN_ID, HEAD]) GetInterceptedChainInfo() (ChainInfo, ChainInfo) {
ret := _m.Called()
@@ -489,7 +491,7 @@ func (_c *mockRPCClient_UnsubscribeAllExcept_Call[CHAIN_ID, HEAD]) Return() *moc
}
func (_c *mockRPCClient_UnsubscribeAllExcept_Call[CHAIN_ID, HEAD]) RunAndReturn(run func(...types.Subscription)) *mockRPCClient_UnsubscribeAllExcept_Call[CHAIN_ID, HEAD] {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
diff --git a/common/client/mock_send_only_client_test.go b/common/client/mock_send_only_client_test.go
index b47b56a8304..0def3c58a2e 100644
--- a/common/client/mock_send_only_client_test.go
+++ b/common/client/mock_send_only_client_test.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package client
@@ -38,7 +38,9 @@ func (_m *mockSendOnlyClient[CHAIN_ID]) ChainID(_a0 context.Context) (CHAIN_ID,
if rf, ok := ret.Get(0).(func(context.Context) CHAIN_ID); ok {
r0 = rf(_a0)
} else {
- r0 = ret.Get(0).(CHAIN_ID)
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(CHAIN_ID)
+ }
}
if rf, ok := ret.Get(1).(func(context.Context) error); ok {
@@ -78,7 +80,7 @@ func (_c *mockSendOnlyClient_ChainID_Call[CHAIN_ID]) RunAndReturn(run func(conte
return _c
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *mockSendOnlyClient[CHAIN_ID]) Close() {
_m.Called()
}
@@ -106,7 +108,7 @@ func (_c *mockSendOnlyClient_Close_Call[CHAIN_ID]) Return() *mockSendOnlyClient_
}
func (_c *mockSendOnlyClient_Close_Call[CHAIN_ID]) RunAndReturn(run func()) *mockSendOnlyClient_Close_Call[CHAIN_ID] {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
diff --git a/common/client/mock_send_only_node_test.go b/common/client/mock_send_only_node_test.go
index 98a6f0ba2fb..16d463df3de 100644
--- a/common/client/mock_send_only_node_test.go
+++ b/common/client/mock_send_only_node_test.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package client
@@ -10,11 +10,11 @@ import (
)
// mockSendOnlyNode is an autogenerated mock type for the SendOnlyNode type
-type mockSendOnlyNode[CHAIN_ID types.ID, RPC any] struct {
+type mockSendOnlyNode[CHAIN_ID types.ID, RPC interface{}] struct {
mock.Mock
}
-type mockSendOnlyNode_Expecter[CHAIN_ID types.ID, RPC any] struct {
+type mockSendOnlyNode_Expecter[CHAIN_ID types.ID, RPC interface{}] struct {
mock *mock.Mock
}
@@ -22,7 +22,7 @@ func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) EXPECT() *mockSendOnlyNode_Expecter[C
return &mockSendOnlyNode_Expecter[CHAIN_ID, RPC]{mock: &_m.Mock}
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) Close() error {
ret := _m.Called()
@@ -41,7 +41,7 @@ func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) Close() error {
}
// mockSendOnlyNode_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close'
-type mockSendOnlyNode_Close_Call[CHAIN_ID types.ID, RPC any] struct {
+type mockSendOnlyNode_Close_Call[CHAIN_ID types.ID, RPC interface{}] struct {
*mock.Call
}
@@ -67,7 +67,7 @@ func (_c *mockSendOnlyNode_Close_Call[CHAIN_ID, RPC]) RunAndReturn(run func() er
return _c
}
-// ConfiguredChainID provides a mock function with given fields:
+// ConfiguredChainID provides a mock function with no fields
func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) ConfiguredChainID() CHAIN_ID {
ret := _m.Called()
@@ -79,14 +79,16 @@ func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) ConfiguredChainID() CHAIN_ID {
if rf, ok := ret.Get(0).(func() CHAIN_ID); ok {
r0 = rf()
} else {
- r0 = ret.Get(0).(CHAIN_ID)
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(CHAIN_ID)
+ }
}
return r0
}
// mockSendOnlyNode_ConfiguredChainID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ConfiguredChainID'
-type mockSendOnlyNode_ConfiguredChainID_Call[CHAIN_ID types.ID, RPC any] struct {
+type mockSendOnlyNode_ConfiguredChainID_Call[CHAIN_ID types.ID, RPC interface{}] struct {
*mock.Call
}
@@ -112,7 +114,7 @@ func (_c *mockSendOnlyNode_ConfiguredChainID_Call[CHAIN_ID, RPC]) RunAndReturn(r
return _c
}
-// Name provides a mock function with given fields:
+// Name provides a mock function with no fields
func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) Name() string {
ret := _m.Called()
@@ -131,7 +133,7 @@ func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) Name() string {
}
// mockSendOnlyNode_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Name'
-type mockSendOnlyNode_Name_Call[CHAIN_ID types.ID, RPC any] struct {
+type mockSendOnlyNode_Name_Call[CHAIN_ID types.ID, RPC interface{}] struct {
*mock.Call
}
@@ -157,7 +159,7 @@ func (_c *mockSendOnlyNode_Name_Call[CHAIN_ID, RPC]) RunAndReturn(run func() str
return _c
}
-// RPC provides a mock function with given fields:
+// RPC provides a mock function with no fields
func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) RPC() RPC {
ret := _m.Called()
@@ -169,14 +171,16 @@ func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) RPC() RPC {
if rf, ok := ret.Get(0).(func() RPC); ok {
r0 = rf()
} else {
- r0 = ret.Get(0).(RPC)
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(RPC)
+ }
}
return r0
}
// mockSendOnlyNode_RPC_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RPC'
-type mockSendOnlyNode_RPC_Call[CHAIN_ID types.ID, RPC any] struct {
+type mockSendOnlyNode_RPC_Call[CHAIN_ID types.ID, RPC interface{}] struct {
*mock.Call
}
@@ -221,7 +225,7 @@ func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) Start(_a0 context.Context) error {
}
// mockSendOnlyNode_Start_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Start'
-type mockSendOnlyNode_Start_Call[CHAIN_ID types.ID, RPC any] struct {
+type mockSendOnlyNode_Start_Call[CHAIN_ID types.ID, RPC interface{}] struct {
*mock.Call
}
@@ -248,7 +252,7 @@ func (_c *mockSendOnlyNode_Start_Call[CHAIN_ID, RPC]) RunAndReturn(run func(cont
return _c
}
-// State provides a mock function with given fields:
+// State provides a mock function with no fields
func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) State() nodeState {
ret := _m.Called()
@@ -267,7 +271,7 @@ func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) State() nodeState {
}
// mockSendOnlyNode_State_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'State'
-type mockSendOnlyNode_State_Call[CHAIN_ID types.ID, RPC any] struct {
+type mockSendOnlyNode_State_Call[CHAIN_ID types.ID, RPC interface{}] struct {
*mock.Call
}
@@ -293,7 +297,7 @@ func (_c *mockSendOnlyNode_State_Call[CHAIN_ID, RPC]) RunAndReturn(run func() no
return _c
}
-// String provides a mock function with given fields:
+// String provides a mock function with no fields
func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) String() string {
ret := _m.Called()
@@ -312,7 +316,7 @@ func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) String() string {
}
// mockSendOnlyNode_String_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'String'
-type mockSendOnlyNode_String_Call[CHAIN_ID types.ID, RPC any] struct {
+type mockSendOnlyNode_String_Call[CHAIN_ID types.ID, RPC interface{}] struct {
*mock.Call
}
@@ -340,7 +344,7 @@ func (_c *mockSendOnlyNode_String_Call[CHAIN_ID, RPC]) RunAndReturn(run func() s
// newMockSendOnlyNode creates a new instance of mockSendOnlyNode. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
-func newMockSendOnlyNode[CHAIN_ID types.ID, RPC any](t interface {
+func newMockSendOnlyNode[CHAIN_ID types.ID, RPC interface{}](t interface {
mock.TestingT
Cleanup(func())
}) *mockSendOnlyNode[CHAIN_ID, RPC] {
diff --git a/common/headtracker/mocks/head_broadcaster.go b/common/headtracker/mocks/head_broadcaster.go
index 87ac609e605..97ef4c4395d 100644
--- a/common/headtracker/mocks/head_broadcaster.go
+++ b/common/headtracker/mocks/head_broadcaster.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -53,11 +53,11 @@ func (_c *HeadBroadcaster_BroadcastNewLongestChain_Call[H, BLOCK_HASH]) Return()
}
func (_c *HeadBroadcaster_BroadcastNewLongestChain_Call[H, BLOCK_HASH]) RunAndReturn(run func(H)) *HeadBroadcaster_BroadcastNewLongestChain_Call[H, BLOCK_HASH] {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *HeadBroadcaster[H, BLOCK_HASH]) Close() error {
ret := _m.Called()
@@ -102,7 +102,7 @@ func (_c *HeadBroadcaster_Close_Call[H, BLOCK_HASH]) RunAndReturn(run func() err
return _c
}
-// HealthReport provides a mock function with given fields:
+// HealthReport provides a mock function with no fields
func (_m *HeadBroadcaster[H, BLOCK_HASH]) HealthReport() map[string]error {
ret := _m.Called()
@@ -149,7 +149,7 @@ func (_c *HeadBroadcaster_HealthReport_Call[H, BLOCK_HASH]) RunAndReturn(run fun
return _c
}
-// Name provides a mock function with given fields:
+// Name provides a mock function with no fields
func (_m *HeadBroadcaster[H, BLOCK_HASH]) Name() string {
ret := _m.Called()
@@ -194,7 +194,7 @@ func (_c *HeadBroadcaster_Name_Call[H, BLOCK_HASH]) RunAndReturn(run func() stri
return _c
}
-// Ready provides a mock function with given fields:
+// Ready provides a mock function with no fields
func (_m *HeadBroadcaster[H, BLOCK_HASH]) Ready() error {
ret := _m.Called()
@@ -301,7 +301,9 @@ func (_m *HeadBroadcaster[H, BLOCK_HASH]) Subscribe(callback headtracker.HeadTra
if rf, ok := ret.Get(0).(func(headtracker.HeadTrackable[H, BLOCK_HASH]) H); ok {
r0 = rf(callback)
} else {
- r0 = ret.Get(0).(H)
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(H)
+ }
}
if rf, ok := ret.Get(1).(func(headtracker.HeadTrackable[H, BLOCK_HASH]) func()); ok {
diff --git a/common/headtracker/mocks/head_trackable.go b/common/headtracker/mocks/head_trackable.go
index 50ea4665280..5475aeec254 100644
--- a/common/headtracker/mocks/head_trackable.go
+++ b/common/headtracker/mocks/head_trackable.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -53,7 +53,7 @@ func (_c *HeadTrackable_OnNewLongestChain_Call[H, BLOCK_HASH]) Return() *HeadTra
}
func (_c *HeadTrackable_OnNewLongestChain_Call[H, BLOCK_HASH]) RunAndReturn(run func(context.Context, H)) *HeadTrackable_OnNewLongestChain_Call[H, BLOCK_HASH] {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
diff --git a/common/headtracker/mocks/head_tracker.go b/common/headtracker/mocks/head_tracker.go
index e0541bdd786..97e3233fbcb 100644
--- a/common/headtracker/mocks/head_tracker.go
+++ b/common/headtracker/mocks/head_tracker.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -70,7 +70,7 @@ func (_c *HeadTracker_Backfill_Call[H, BLOCK_HASH]) RunAndReturn(run func(contex
return _c
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *HeadTracker[H, BLOCK_HASH]) Close() error {
ret := _m.Called()
@@ -115,7 +115,7 @@ func (_c *HeadTracker_Close_Call[H, BLOCK_HASH]) RunAndReturn(run func() error)
return _c
}
-// HealthReport provides a mock function with given fields:
+// HealthReport provides a mock function with no fields
func (_m *HeadTracker[H, BLOCK_HASH]) HealthReport() map[string]error {
ret := _m.Called()
@@ -179,13 +179,17 @@ func (_m *HeadTracker[H, BLOCK_HASH]) LatestAndFinalizedBlock(ctx context.Contex
if rf, ok := ret.Get(0).(func(context.Context) H); ok {
r0 = rf(ctx)
} else {
- r0 = ret.Get(0).(H)
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(H)
+ }
}
if rf, ok := ret.Get(1).(func(context.Context) H); ok {
r1 = rf(ctx)
} else {
- r1 = ret.Get(1).(H)
+ if ret.Get(1) != nil {
+ r1 = ret.Get(1).(H)
+ }
}
if rf, ok := ret.Get(2).(func(context.Context) error); ok {
@@ -225,7 +229,7 @@ func (_c *HeadTracker_LatestAndFinalizedBlock_Call[H, BLOCK_HASH]) RunAndReturn(
return _c
}
-// LatestChain provides a mock function with given fields:
+// LatestChain provides a mock function with no fields
func (_m *HeadTracker[H, BLOCK_HASH]) LatestChain() H {
ret := _m.Called()
@@ -237,7 +241,9 @@ func (_m *HeadTracker[H, BLOCK_HASH]) LatestChain() H {
if rf, ok := ret.Get(0).(func() H); ok {
r0 = rf()
} else {
- r0 = ret.Get(0).(H)
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(H)
+ }
}
return r0
@@ -270,7 +276,7 @@ func (_c *HeadTracker_LatestChain_Call[H, BLOCK_HASH]) RunAndReturn(run func() H
return _c
}
-// Name provides a mock function with given fields:
+// Name provides a mock function with no fields
func (_m *HeadTracker[H, BLOCK_HASH]) Name() string {
ret := _m.Called()
@@ -315,7 +321,7 @@ func (_c *HeadTracker_Name_Call[H, BLOCK_HASH]) RunAndReturn(run func() string)
return _c
}
-// Ready provides a mock function with given fields:
+// Ready provides a mock function with no fields
func (_m *HeadTracker[H, BLOCK_HASH]) Ready() error {
ret := _m.Called()
diff --git a/common/txmgr/mocks/tx_manager.go b/common/txmgr/mocks/tx_manager.go
index a296158a005..4e4c2f17bec 100644
--- a/common/txmgr/mocks/tx_manager.go
+++ b/common/txmgr/mocks/tx_manager.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -33,7 +33,7 @@ func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) EXPECT
return &TxManager_Expecter[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]{mock: &_m.Mock}
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) Close() error {
ret := _m.Called()
@@ -565,7 +565,9 @@ func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) GetFor
if rf, ok := ret.Get(0).(func(context.Context, ADDR) ADDR); ok {
r0 = rf(ctx, eoa)
} else {
- r0 = ret.Get(0).(ADDR)
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(ADDR)
+ }
}
if rf, ok := ret.Get(1).(func(context.Context, ADDR) error); ok {
@@ -622,7 +624,9 @@ func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) GetFor
if rf, ok := ret.Get(0).(func(context.Context, ADDR, ADDR) ADDR); ok {
r0 = rf(ctx, eoa, ocr2AggregatorID)
} else {
- r0 = ret.Get(0).(ADDR)
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(ADDR)
+ }
}
if rf, ok := ret.Get(1).(func(context.Context, ADDR, ADDR) error); ok {
@@ -721,7 +725,7 @@ func (_c *TxManager_GetTransactionStatus_Call[CHAIN_ID, HEAD, ADDR, TX_HASH, BLO
return _c
}
-// HealthReport provides a mock function with given fields:
+// HealthReport provides a mock function with no fields
func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) HealthReport() map[string]error {
ret := _m.Called()
@@ -768,7 +772,7 @@ func (_c *TxManager_HealthReport_Call[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH,
return _c
}
-// Name provides a mock function with given fields:
+// Name provides a mock function with no fields
func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) Name() string {
ret := _m.Called()
@@ -843,11 +847,11 @@ func (_c *TxManager_OnNewLongestChain_Call[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_
}
func (_c *TxManager_OnNewLongestChain_Call[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) RunAndReturn(run func(context.Context, HEAD)) *TxManager_OnNewLongestChain_Call[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
-// Ready provides a mock function with given fields:
+// Ready provides a mock function with no fields
func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) Ready() error {
ret := _m.Called()
@@ -921,7 +925,7 @@ func (_c *TxManager_RegisterResumeCallback_Call[CHAIN_ID, HEAD, ADDR, TX_HASH, B
}
func (_c *TxManager_RegisterResumeCallback_Call[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) RunAndReturn(run func(txmgr.ResumeCallback)) *TxManager_RegisterResumeCallback_Call[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
@@ -1108,7 +1112,7 @@ func (_c *TxManager_Trigger_Call[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ,
}
func (_c *TxManager_Trigger_Call[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) RunAndReturn(run func(ADDR)) *TxManager_Trigger_Call[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
diff --git a/common/txmgr/types/mocks/forwarder_manager.go b/common/txmgr/types/mocks/forwarder_manager.go
index 4582e4bac07..8ee0be73822 100644
--- a/common/txmgr/types/mocks/forwarder_manager.go
+++ b/common/txmgr/types/mocks/forwarder_manager.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -23,7 +23,7 @@ func (_m *ForwarderManager[ADDR]) EXPECT() *ForwarderManager_Expecter[ADDR] {
return &ForwarderManager_Expecter[ADDR]{mock: &_m.Mock}
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *ForwarderManager[ADDR]) Close() error {
ret := _m.Called()
@@ -143,7 +143,9 @@ func (_m *ForwarderManager[ADDR]) ForwarderFor(ctx context.Context, addr ADDR) (
if rf, ok := ret.Get(0).(func(context.Context, ADDR) ADDR); ok {
r0 = rf(ctx, addr)
} else {
- r0 = ret.Get(0).(ADDR)
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(ADDR)
+ }
}
if rf, ok := ret.Get(1).(func(context.Context, ADDR) error); ok {
@@ -200,7 +202,9 @@ func (_m *ForwarderManager[ADDR]) ForwarderForOCR2Feeds(ctx context.Context, eoa
if rf, ok := ret.Get(0).(func(context.Context, ADDR, ADDR) ADDR); ok {
r0 = rf(ctx, eoa, ocr2Aggregator)
} else {
- r0 = ret.Get(0).(ADDR)
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(ADDR)
+ }
}
if rf, ok := ret.Get(1).(func(context.Context, ADDR, ADDR) error); ok {
@@ -242,7 +246,7 @@ func (_c *ForwarderManager_ForwarderForOCR2Feeds_Call[ADDR]) RunAndReturn(run fu
return _c
}
-// HealthReport provides a mock function with given fields:
+// HealthReport provides a mock function with no fields
func (_m *ForwarderManager[ADDR]) HealthReport() map[string]error {
ret := _m.Called()
@@ -289,7 +293,7 @@ func (_c *ForwarderManager_HealthReport_Call[ADDR]) RunAndReturn(run func() map[
return _c
}
-// Name provides a mock function with given fields:
+// Name provides a mock function with no fields
func (_m *ForwarderManager[ADDR]) Name() string {
ret := _m.Called()
@@ -334,7 +338,7 @@ func (_c *ForwarderManager_Name_Call[ADDR]) RunAndReturn(run func() string) *For
return _c
}
-// Ready provides a mock function with given fields:
+// Ready provides a mock function with no fields
func (_m *ForwarderManager[ADDR]) Ready() error {
ret := _m.Called()
diff --git a/common/txmgr/types/mocks/key_store.go b/common/txmgr/types/mocks/key_store.go
index 18161301b22..942d2773d9b 100644
--- a/common/txmgr/types/mocks/key_store.go
+++ b/common/txmgr/types/mocks/key_store.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
diff --git a/common/txmgr/types/mocks/tx_attempt_builder.go b/common/txmgr/types/mocks/tx_attempt_builder.go
index 746e580413c..d20b8249050 100644
--- a/common/txmgr/types/mocks/tx_attempt_builder.go
+++ b/common/txmgr/types/mocks/tx_attempt_builder.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -28,7 +28,7 @@ func (_m *TxAttemptBuilder[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE])
return &TxAttemptBuilder_Expecter[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]{mock: &_m.Mock}
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *TxAttemptBuilder[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) Close() error {
ret := _m.Called()
@@ -73,7 +73,7 @@ func (_c *TxAttemptBuilder_Close_Call[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH,
return _c
}
-// HealthReport provides a mock function with given fields:
+// HealthReport provides a mock function with no fields
func (_m *TxAttemptBuilder[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) HealthReport() map[string]error {
ret := _m.Called()
@@ -120,7 +120,7 @@ func (_c *TxAttemptBuilder_HealthReport_Call[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOC
return _c
}
-// Name provides a mock function with given fields:
+// Name provides a mock function with no fields
func (_m *TxAttemptBuilder[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) Name() string {
ret := _m.Called()
@@ -190,7 +190,9 @@ func (_m *TxAttemptBuilder[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE])
if rf, ok := ret.Get(1).(func(context.Context, txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], []txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], logger.Logger) FEE); ok {
r1 = rf(ctx, tx, previousAttempt, priorAttempts, lggr)
} else {
- r1 = ret.Get(1).(FEE)
+ if ret.Get(1) != nil {
+ r1 = ret.Get(1).(FEE)
+ }
}
if rf, ok := ret.Get(2).(func(context.Context, txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], []txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], logger.Logger) uint64); ok {
@@ -464,7 +466,9 @@ func (_m *TxAttemptBuilder[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE])
if rf, ok := ret.Get(1).(func(context.Context, txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], logger.Logger, ...feetypes.Opt) FEE); ok {
r1 = rf(ctx, tx, lggr, opts...)
} else {
- r1 = ret.Get(1).(FEE)
+ if ret.Get(1) != nil {
+ r1 = ret.Get(1).(FEE)
+ }
}
if rf, ok := ret.Get(2).(func(context.Context, txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], logger.Logger, ...feetypes.Opt) uint64); ok {
@@ -558,7 +562,9 @@ func (_m *TxAttemptBuilder[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE])
if rf, ok := ret.Get(1).(func(context.Context, txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], logger.Logger, int, ...feetypes.Opt) FEE); ok {
r1 = rf(ctx, tx, lggr, txType, opts...)
} else {
- r1 = ret.Get(1).(FEE)
+ if ret.Get(1) != nil {
+ r1 = ret.Get(1).(FEE)
+ }
}
if rf, ok := ret.Get(2).(func(context.Context, txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], logger.Logger, int, ...feetypes.Opt) uint64); ok {
@@ -651,11 +657,11 @@ func (_c *TxAttemptBuilder_OnNewLongestChain_Call[CHAIN_ID, HEAD, ADDR, TX_HASH,
}
func (_c *TxAttemptBuilder_OnNewLongestChain_Call[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) RunAndReturn(run func(context.Context, HEAD)) *TxAttemptBuilder_OnNewLongestChain_Call[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
-// Ready provides a mock function with given fields:
+// Ready provides a mock function with no fields
func (_m *TxAttemptBuilder[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) Ready() error {
ret := _m.Called()
diff --git a/common/txmgr/types/mocks/tx_store.go b/common/txmgr/types/mocks/tx_store.go
index b75ee69302a..0c3b4d93258 100644
--- a/common/txmgr/types/mocks/tx_store.go
+++ b/common/txmgr/types/mocks/tx_store.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -130,7 +130,7 @@ func (_c *TxStore_CheckTxQueueCapacity_Call[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH,
return _c
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) Close() {
_m.Called()
}
@@ -158,7 +158,7 @@ func (_c *TxStore_Close_Call[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE])
}
func (_c *TxStore_Close_Call[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) RunAndReturn(run func()) *TxStore_Close_Call[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE] {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
@@ -571,7 +571,9 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindLatestS
if rf, ok := ret.Get(0).(func(context.Context, ADDR, CHAIN_ID) SEQ); ok {
r0 = rf(ctx, fromAddress, chainID)
} else {
- r0 = ret.Get(0).(SEQ)
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(SEQ)
+ }
}
if rf, ok := ret.Get(1).(func(context.Context, ADDR, CHAIN_ID) error); ok {
diff --git a/common/txmgr/types/mocks/tx_strategy.go b/common/txmgr/types/mocks/tx_strategy.go
index b4f344d4424..98b6343ca9a 100644
--- a/common/txmgr/types/mocks/tx_strategy.go
+++ b/common/txmgr/types/mocks/tx_strategy.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -83,7 +83,7 @@ func (_c *TxStrategy_PruneQueue_Call) RunAndReturn(run func(context.Context, typ
return _c
}
-// Subject provides a mock function with given fields:
+// Subject provides a mock function with no fields
func (_m *TxStrategy) Subject() uuid.NullUUID {
ret := _m.Called()
diff --git a/common/types/mocks/head.go b/common/types/mocks/head.go
index 85bef18a0be..b00c155ccc7 100644
--- a/common/types/mocks/head.go
+++ b/common/types/mocks/head.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -24,7 +24,7 @@ func (_m *Head[BLOCK_HASH]) EXPECT() *Head_Expecter[BLOCK_HASH] {
return &Head_Expecter[BLOCK_HASH]{mock: &_m.Mock}
}
-// BlockDifficulty provides a mock function with given fields:
+// BlockDifficulty provides a mock function with no fields
func (_m *Head[BLOCK_HASH]) BlockDifficulty() *big.Int {
ret := _m.Called()
@@ -71,7 +71,7 @@ func (_c *Head_BlockDifficulty_Call[BLOCK_HASH]) RunAndReturn(run func() *big.In
return _c
}
-// BlockHash provides a mock function with given fields:
+// BlockHash provides a mock function with no fields
func (_m *Head[BLOCK_HASH]) BlockHash() BLOCK_HASH {
ret := _m.Called()
@@ -83,7 +83,9 @@ func (_m *Head[BLOCK_HASH]) BlockHash() BLOCK_HASH {
if rf, ok := ret.Get(0).(func() BLOCK_HASH); ok {
r0 = rf()
} else {
- r0 = ret.Get(0).(BLOCK_HASH)
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(BLOCK_HASH)
+ }
}
return r0
@@ -116,7 +118,7 @@ func (_c *Head_BlockHash_Call[BLOCK_HASH]) RunAndReturn(run func() BLOCK_HASH) *
return _c
}
-// BlockNumber provides a mock function with given fields:
+// BlockNumber provides a mock function with no fields
func (_m *Head[BLOCK_HASH]) BlockNumber() int64 {
ret := _m.Called()
@@ -161,7 +163,7 @@ func (_c *Head_BlockNumber_Call[BLOCK_HASH]) RunAndReturn(run func() int64) *Hea
return _c
}
-// ChainLength provides a mock function with given fields:
+// ChainLength provides a mock function with no fields
func (_m *Head[BLOCK_HASH]) ChainLength() uint32 {
ret := _m.Called()
@@ -206,7 +208,7 @@ func (_c *Head_ChainLength_Call[BLOCK_HASH]) RunAndReturn(run func() uint32) *He
return _c
}
-// EarliestHeadInChain provides a mock function with given fields:
+// EarliestHeadInChain provides a mock function with no fields
func (_m *Head[BLOCK_HASH]) EarliestHeadInChain() types.Head[BLOCK_HASH] {
ret := _m.Called()
@@ -253,7 +255,7 @@ func (_c *Head_EarliestHeadInChain_Call[BLOCK_HASH]) RunAndReturn(run func() typ
return _c
}
-// GetParent provides a mock function with given fields:
+// GetParent provides a mock function with no fields
func (_m *Head[BLOCK_HASH]) GetParent() types.Head[BLOCK_HASH] {
ret := _m.Called()
@@ -300,7 +302,7 @@ func (_c *Head_GetParent_Call[BLOCK_HASH]) RunAndReturn(run func() types.Head[BL
return _c
}
-// GetParentHash provides a mock function with given fields:
+// GetParentHash provides a mock function with no fields
func (_m *Head[BLOCK_HASH]) GetParentHash() BLOCK_HASH {
ret := _m.Called()
@@ -312,7 +314,9 @@ func (_m *Head[BLOCK_HASH]) GetParentHash() BLOCK_HASH {
if rf, ok := ret.Get(0).(func() BLOCK_HASH); ok {
r0 = rf()
} else {
- r0 = ret.Get(0).(BLOCK_HASH)
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(BLOCK_HASH)
+ }
}
return r0
@@ -345,7 +349,7 @@ func (_c *Head_GetParentHash_Call[BLOCK_HASH]) RunAndReturn(run func() BLOCK_HAS
return _c
}
-// GetTimestamp provides a mock function with given fields:
+// GetTimestamp provides a mock function with no fields
func (_m *Head[BLOCK_HASH]) GetTimestamp() time.Time {
ret := _m.Called()
@@ -402,7 +406,9 @@ func (_m *Head[BLOCK_HASH]) HashAtHeight(blockNum int64) BLOCK_HASH {
if rf, ok := ret.Get(0).(func(int64) BLOCK_HASH); ok {
r0 = rf(blockNum)
} else {
- r0 = ret.Get(0).(BLOCK_HASH)
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(BLOCK_HASH)
+ }
}
return r0
@@ -494,7 +500,7 @@ func (_c *Head_HeadAtHeight_Call[BLOCK_HASH]) RunAndReturn(run func(int64) (type
return _c
}
-// IsValid provides a mock function with given fields:
+// IsValid provides a mock function with no fields
func (_m *Head[BLOCK_HASH]) IsValid() bool {
ret := _m.Called()
@@ -539,7 +545,7 @@ func (_c *Head_IsValid_Call[BLOCK_HASH]) RunAndReturn(run func() bool) *Head_IsV
return _c
}
-// LatestFinalizedHead provides a mock function with given fields:
+// LatestFinalizedHead provides a mock function with no fields
func (_m *Head[BLOCK_HASH]) LatestFinalizedHead() types.Head[BLOCK_HASH] {
ret := _m.Called()
diff --git a/common/types/mocks/monitoring_endpoint.go b/common/types/mocks/monitoring_endpoint.go
index d88be7cbf3f..892391d79a3 100644
--- a/common/types/mocks/monitoring_endpoint.go
+++ b/common/types/mocks/monitoring_endpoint.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -46,7 +46,7 @@ func (_c *MonitoringEndpoint_SendLog_Call) Return() *MonitoringEndpoint_SendLog_
}
func (_c *MonitoringEndpoint_SendLog_Call) RunAndReturn(run func([]byte)) *MonitoringEndpoint_SendLog_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
diff --git a/common/types/mocks/subscription.go b/common/types/mocks/subscription.go
index b0b87c7287a..6c0764683b0 100644
--- a/common/types/mocks/subscription.go
+++ b/common/types/mocks/subscription.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -17,7 +17,7 @@ func (_m *Subscription) EXPECT() *Subscription_Expecter {
return &Subscription_Expecter{mock: &_m.Mock}
}
-// Err provides a mock function with given fields:
+// Err provides a mock function with no fields
func (_m *Subscription) Err() <-chan error {
ret := _m.Called()
@@ -64,7 +64,7 @@ func (_c *Subscription_Err_Call) RunAndReturn(run func() <-chan error) *Subscrip
return _c
}
-// Unsubscribe provides a mock function with given fields:
+// Unsubscribe provides a mock function with no fields
func (_m *Subscription) Unsubscribe() {
_m.Called()
}
@@ -92,7 +92,7 @@ func (_c *Subscription_Unsubscribe_Call) Return() *Subscription_Unsubscribe_Call
}
func (_c *Subscription_Unsubscribe_Call) RunAndReturn(run func()) *Subscription_Unsubscribe_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
diff --git a/contracts/GNUmakefile b/contracts/GNUmakefile
index 7361754ee0b..e9d87385b52 100644
--- a/contracts/GNUmakefile
+++ b/contracts/GNUmakefile
@@ -39,7 +39,7 @@ abigen: ## Build & install abigen.
.PHONY: mockery
mockery: $(mockery) ## Install mockery.
- go install github.com/vektra/mockery/v2@v2.43.2
+ go install github.com/vektra/mockery/v2@v2.50.0
.PHONY: foundry
foundry: ## Install foundry.
diff --git a/core/bridges/mocks/orm.go b/core/bridges/mocks/orm.go
index 54dd6ef4d3c..d1ecd7c6adc 100644
--- a/core/bridges/mocks/orm.go
+++ b/core/bridges/mocks/orm.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
diff --git a/core/capabilities/ccip/types/mocks/ccip_oracle.go b/core/capabilities/ccip/types/mocks/ccip_oracle.go
index 5ab860cc62e..5f1b7202e52 100644
--- a/core/capabilities/ccip/types/mocks/ccip_oracle.go
+++ b/core/capabilities/ccip/types/mocks/ccip_oracle.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -17,7 +17,7 @@ func (_m *CCIPOracle) EXPECT() *CCIPOracle_Expecter {
return &CCIPOracle_Expecter{mock: &_m.Mock}
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *CCIPOracle) Close() error {
ret := _m.Called()
@@ -62,7 +62,7 @@ func (_c *CCIPOracle_Close_Call) RunAndReturn(run func() error) *CCIPOracle_Clos
return _c
}
-// Start provides a mock function with given fields:
+// Start provides a mock function with no fields
func (_m *CCIPOracle) Start() error {
ret := _m.Called()
diff --git a/core/capabilities/ccip/types/mocks/oracle_creator.go b/core/capabilities/ccip/types/mocks/oracle_creator.go
index 1906df7e063..3618e70e7b7 100644
--- a/core/capabilities/ccip/types/mocks/oracle_creator.go
+++ b/core/capabilities/ccip/types/mocks/oracle_creator.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -82,7 +82,7 @@ func (_c *OracleCreator_Create_Call) RunAndReturn(run func(context.Context, uint
return _c
}
-// Type provides a mock function with given fields:
+// Type provides a mock function with no fields
func (_m *OracleCreator) Type() types.OracleType {
ret := _m.Called()
diff --git a/core/capabilities/remote/types/mocks/dispatcher.go b/core/capabilities/remote/types/mocks/dispatcher.go
index d7f2ab45bac..161cec75b2f 100644
--- a/core/capabilities/remote/types/mocks/dispatcher.go
+++ b/core/capabilities/remote/types/mocks/dispatcher.go
@@ -1,11 +1,11 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
import (
context "context"
- p2ptypes "github.com/smartcontractkit/chainlink/v2/core/services/p2p/types"
+ ragep2ptypes "github.com/smartcontractkit/libocr/ragep2p/types"
mock "github.com/stretchr/testify/mock"
types "github.com/smartcontractkit/chainlink/v2/core/capabilities/remote/types"
@@ -24,7 +24,7 @@ func (_m *Dispatcher) EXPECT() *Dispatcher_Expecter {
return &Dispatcher_Expecter{mock: &_m.Mock}
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *Dispatcher) Close() error {
ret := _m.Called()
@@ -69,7 +69,7 @@ func (_c *Dispatcher_Close_Call) RunAndReturn(run func() error) *Dispatcher_Clos
return _c
}
-// HealthReport provides a mock function with given fields:
+// HealthReport provides a mock function with no fields
func (_m *Dispatcher) HealthReport() map[string]error {
ret := _m.Called()
@@ -116,7 +116,7 @@ func (_c *Dispatcher_HealthReport_Call) RunAndReturn(run func() map[string]error
return _c
}
-// Name provides a mock function with given fields:
+// Name provides a mock function with no fields
func (_m *Dispatcher) Name() string {
ret := _m.Called()
@@ -161,7 +161,7 @@ func (_c *Dispatcher_Name_Call) RunAndReturn(run func() string) *Dispatcher_Name
return _c
}
-// Ready provides a mock function with given fields:
+// Ready provides a mock function with no fields
func (_m *Dispatcher) Ready() error {
ret := _m.Called()
@@ -236,12 +236,12 @@ func (_c *Dispatcher_RemoveReceiver_Call) Return() *Dispatcher_RemoveReceiver_Ca
}
func (_c *Dispatcher_RemoveReceiver_Call) RunAndReturn(run func(string, uint32)) *Dispatcher_RemoveReceiver_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
// Send provides a mock function with given fields: peerID, msgBody
-func (_m *Dispatcher) Send(peerID p2ptypes.PeerID, msgBody *types.MessageBody) error {
+func (_m *Dispatcher) Send(peerID ragep2ptypes.PeerID, msgBody *types.MessageBody) error {
ret := _m.Called(peerID, msgBody)
if len(ret) == 0 {
@@ -249,7 +249,7 @@ func (_m *Dispatcher) Send(peerID p2ptypes.PeerID, msgBody *types.MessageBody) e
}
var r0 error
- if rf, ok := ret.Get(0).(func(p2ptypes.PeerID, *types.MessageBody) error); ok {
+ if rf, ok := ret.Get(0).(func(ragep2ptypes.PeerID, *types.MessageBody) error); ok {
r0 = rf(peerID, msgBody)
} else {
r0 = ret.Error(0)
@@ -264,15 +264,15 @@ type Dispatcher_Send_Call struct {
}
// Send is a helper method to define mock.On call
-// - peerID p2ptypes.PeerID
+// - peerID ragep2ptypes.PeerID
// - msgBody *types.MessageBody
func (_e *Dispatcher_Expecter) Send(peerID interface{}, msgBody interface{}) *Dispatcher_Send_Call {
return &Dispatcher_Send_Call{Call: _e.mock.On("Send", peerID, msgBody)}
}
-func (_c *Dispatcher_Send_Call) Run(run func(peerID p2ptypes.PeerID, msgBody *types.MessageBody)) *Dispatcher_Send_Call {
+func (_c *Dispatcher_Send_Call) Run(run func(peerID ragep2ptypes.PeerID, msgBody *types.MessageBody)) *Dispatcher_Send_Call {
_c.Call.Run(func(args mock.Arguments) {
- run(args[0].(p2ptypes.PeerID), args[1].(*types.MessageBody))
+ run(args[0].(ragep2ptypes.PeerID), args[1].(*types.MessageBody))
})
return _c
}
@@ -282,7 +282,7 @@ func (_c *Dispatcher_Send_Call) Return(_a0 error) *Dispatcher_Send_Call {
return _c
}
-func (_c *Dispatcher_Send_Call) RunAndReturn(run func(p2ptypes.PeerID, *types.MessageBody) error) *Dispatcher_Send_Call {
+func (_c *Dispatcher_Send_Call) RunAndReturn(run func(ragep2ptypes.PeerID, *types.MessageBody) error) *Dispatcher_Send_Call {
_c.Call.Return(run)
return _c
}
diff --git a/core/capabilities/remote/types/mocks/receiver.go b/core/capabilities/remote/types/mocks/receiver.go
index e14e4d0e98f..143c57ab079 100644
--- a/core/capabilities/remote/types/mocks/receiver.go
+++ b/core/capabilities/remote/types/mocks/receiver.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -52,7 +52,7 @@ func (_c *Receiver_Receive_Call) Return() *Receiver_Receive_Call {
}
func (_c *Receiver_Receive_Call) RunAndReturn(run func(context.Context, *types.MessageBody)) *Receiver_Receive_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
diff --git a/core/capabilities/targets/mocks/contract_value_getter.go b/core/capabilities/targets/mocks/contract_value_getter.go
index b662562ba44..e342442e3a5 100644
--- a/core/capabilities/targets/mocks/contract_value_getter.go
+++ b/core/capabilities/targets/mocks/contract_value_getter.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -72,7 +72,7 @@ func (_c *ContractValueGetter_Bind_Call) RunAndReturn(run func(context.Context,
}
// GetLatestValue provides a mock function with given fields: _a0, _a1, _a2, _a3, _a4
-func (_m *ContractValueGetter) GetLatestValue(_a0 context.Context, _a1 string, _a2 primitives.ConfidenceLevel, _a3 any, _a4 any) error {
+func (_m *ContractValueGetter) GetLatestValue(_a0 context.Context, _a1 string, _a2 primitives.ConfidenceLevel, _a3 interface{}, _a4 interface{}) error {
ret := _m.Called(_a0, _a1, _a2, _a3, _a4)
if len(ret) == 0 {
@@ -80,7 +80,7 @@ func (_m *ContractValueGetter) GetLatestValue(_a0 context.Context, _a1 string, _
}
var r0 error
- if rf, ok := ret.Get(0).(func(context.Context, string, primitives.ConfidenceLevel, any, any) error); ok {
+ if rf, ok := ret.Get(0).(func(context.Context, string, primitives.ConfidenceLevel, interface{}, interface{}) error); ok {
r0 = rf(_a0, _a1, _a2, _a3, _a4)
} else {
r0 = ret.Error(0)
@@ -98,15 +98,15 @@ type ContractValueGetter_GetLatestValue_Call struct {
// - _a0 context.Context
// - _a1 string
// - _a2 primitives.ConfidenceLevel
-// - _a3 any
-// - _a4 any
+// - _a3 interface{}
+// - _a4 interface{}
func (_e *ContractValueGetter_Expecter) GetLatestValue(_a0 interface{}, _a1 interface{}, _a2 interface{}, _a3 interface{}, _a4 interface{}) *ContractValueGetter_GetLatestValue_Call {
return &ContractValueGetter_GetLatestValue_Call{Call: _e.mock.On("GetLatestValue", _a0, _a1, _a2, _a3, _a4)}
}
-func (_c *ContractValueGetter_GetLatestValue_Call) Run(run func(_a0 context.Context, _a1 string, _a2 primitives.ConfidenceLevel, _a3 any, _a4 any)) *ContractValueGetter_GetLatestValue_Call {
+func (_c *ContractValueGetter_GetLatestValue_Call) Run(run func(_a0 context.Context, _a1 string, _a2 primitives.ConfidenceLevel, _a3 interface{}, _a4 interface{})) *ContractValueGetter_GetLatestValue_Call {
_c.Call.Run(func(args mock.Arguments) {
- run(args[0].(context.Context), args[1].(string), args[2].(primitives.ConfidenceLevel), args[3].(any), args[4].(any))
+ run(args[0].(context.Context), args[1].(string), args[2].(primitives.ConfidenceLevel), args[3].(interface{}), args[4].(interface{}))
})
return _c
}
@@ -116,7 +116,7 @@ func (_c *ContractValueGetter_GetLatestValue_Call) Return(_a0 error) *ContractVa
return _c
}
-func (_c *ContractValueGetter_GetLatestValue_Call) RunAndReturn(run func(context.Context, string, primitives.ConfidenceLevel, any, any) error) *ContractValueGetter_GetLatestValue_Call {
+func (_c *ContractValueGetter_GetLatestValue_Call) RunAndReturn(run func(context.Context, string, primitives.ConfidenceLevel, interface{}, interface{}) error) *ContractValueGetter_GetLatestValue_Call {
_c.Call.Return(run)
return _c
}
diff --git a/core/capabilities/targets/mocks/contract_writer.go b/core/capabilities/targets/mocks/contract_writer.go
index c6128e68fc7..55eb88b9ce2 100644
--- a/core/capabilities/targets/mocks/contract_writer.go
+++ b/core/capabilities/targets/mocks/contract_writer.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -24,7 +24,7 @@ func (_m *ContractWriter) EXPECT() *ContractWriter_Expecter {
return &ContractWriter_Expecter{mock: &_m.Mock}
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *ContractWriter) Close() error {
ret := _m.Called()
@@ -184,7 +184,7 @@ func (_c *ContractWriter_GetTransactionStatus_Call) RunAndReturn(run func(contex
return _c
}
-// HealthReport provides a mock function with given fields:
+// HealthReport provides a mock function with no fields
func (_m *ContractWriter) HealthReport() map[string]error {
ret := _m.Called()
@@ -231,7 +231,7 @@ func (_c *ContractWriter_HealthReport_Call) RunAndReturn(run func() map[string]e
return _c
}
-// Name provides a mock function with given fields:
+// Name provides a mock function with no fields
func (_m *ContractWriter) Name() string {
ret := _m.Called()
@@ -276,7 +276,7 @@ func (_c *ContractWriter_Name_Call) RunAndReturn(run func() string) *ContractWri
return _c
}
-// Ready provides a mock function with given fields:
+// Ready provides a mock function with no fields
func (_m *ContractWriter) Ready() error {
ret := _m.Called()
@@ -368,7 +368,7 @@ func (_c *ContractWriter_Start_Call) RunAndReturn(run func(context.Context) erro
}
// SubmitTransaction provides a mock function with given fields: ctx, contractName, method, args, transactionID, toAddress, meta, value
-func (_m *ContractWriter) SubmitTransaction(ctx context.Context, contractName string, method string, args any, transactionID string, toAddress string, meta *types.TxMeta, value *big.Int) error {
+func (_m *ContractWriter) SubmitTransaction(ctx context.Context, contractName string, method string, args interface{}, transactionID string, toAddress string, meta *types.TxMeta, value *big.Int) error {
ret := _m.Called(ctx, contractName, method, args, transactionID, toAddress, meta, value)
if len(ret) == 0 {
@@ -376,7 +376,7 @@ func (_m *ContractWriter) SubmitTransaction(ctx context.Context, contractName st
}
var r0 error
- if rf, ok := ret.Get(0).(func(context.Context, string, string, any, string, string, *types.TxMeta, *big.Int) error); ok {
+ if rf, ok := ret.Get(0).(func(context.Context, string, string, interface{}, string, string, *types.TxMeta, *big.Int) error); ok {
r0 = rf(ctx, contractName, method, args, transactionID, toAddress, meta, value)
} else {
r0 = ret.Error(0)
@@ -394,7 +394,7 @@ type ContractWriter_SubmitTransaction_Call struct {
// - ctx context.Context
// - contractName string
// - method string
-// - args any
+// - args interface{}
// - transactionID string
// - toAddress string
// - meta *types.TxMeta
@@ -403,9 +403,9 @@ func (_e *ContractWriter_Expecter) SubmitTransaction(ctx interface{}, contractNa
return &ContractWriter_SubmitTransaction_Call{Call: _e.mock.On("SubmitTransaction", ctx, contractName, method, args, transactionID, toAddress, meta, value)}
}
-func (_c *ContractWriter_SubmitTransaction_Call) Run(run func(ctx context.Context, contractName string, method string, args any, transactionID string, toAddress string, meta *types.TxMeta, value *big.Int)) *ContractWriter_SubmitTransaction_Call {
+func (_c *ContractWriter_SubmitTransaction_Call) Run(run func(ctx context.Context, contractName string, method string, args interface{}, transactionID string, toAddress string, meta *types.TxMeta, value *big.Int)) *ContractWriter_SubmitTransaction_Call {
_c.Call.Run(func(args mock.Arguments) {
- run(args[0].(context.Context), args[1].(string), args[2].(string), args[3].(any), args[4].(string), args[5].(string), args[6].(*types.TxMeta), args[7].(*big.Int))
+ run(args[0].(context.Context), args[1].(string), args[2].(string), args[3].(interface{}), args[4].(string), args[5].(string), args[6].(*types.TxMeta), args[7].(*big.Int))
})
return _c
}
@@ -415,7 +415,7 @@ func (_c *ContractWriter_SubmitTransaction_Call) Return(_a0 error) *ContractWrit
return _c
}
-func (_c *ContractWriter_SubmitTransaction_Call) RunAndReturn(run func(context.Context, string, string, any, string, string, *types.TxMeta, *big.Int) error) *ContractWriter_SubmitTransaction_Call {
+func (_c *ContractWriter_SubmitTransaction_Call) RunAndReturn(run func(context.Context, string, string, interface{}, string, string, *types.TxMeta, *big.Int) error) *ContractWriter_SubmitTransaction_Call {
_c.Call.Return(run)
return _c
}
diff --git a/core/chains/evm/client/mocks/client.go b/core/chains/evm/client/mocks/client.go
index 25ed3698fe9..b55c608a590 100644
--- a/core/chains/evm/client/mocks/client.go
+++ b/core/chains/evm/client/mocks/client.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -481,7 +481,7 @@ func (_c *Client_CheckTxValidity_Call) RunAndReturn(run func(context.Context, co
return _c
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *Client) Close() {
_m.Called()
}
@@ -509,7 +509,7 @@ func (_c *Client_Close_Call) Return() *Client_Close_Call {
}
func (_c *Client_Close_Call) RunAndReturn(run func()) *Client_Close_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
@@ -573,7 +573,7 @@ func (_c *Client_CodeAt_Call) RunAndReturn(run func(context.Context, common.Addr
return _c
}
-// ConfiguredChainID provides a mock function with given fields:
+// ConfiguredChainID provides a mock function with no fields
func (_m *Client) ConfiguredChainID() *big.Int {
ret := _m.Called()
@@ -1079,7 +1079,7 @@ func (_c *Client_HeaderByNumber_Call) RunAndReturn(run func(context.Context, *bi
return _c
}
-// IsL2 provides a mock function with given fields:
+// IsL2 provides a mock function with no fields
func (_m *Client) IsL2() bool {
ret := _m.Called()
@@ -1300,7 +1300,7 @@ func (_c *Client_LatestFinalizedBlock_Call) RunAndReturn(run func(context.Contex
return _c
}
-// NodeStates provides a mock function with given fields:
+// NodeStates provides a mock function with no fields
func (_m *Client) NodeStates() map[string]string {
ret := _m.Called()
diff --git a/core/chains/evm/config/mocks/chain_scoped_config.go b/core/chains/evm/config/mocks/chain_scoped_config.go
index a2fe703f98f..000d2a7287c 100644
--- a/core/chains/evm/config/mocks/chain_scoped_config.go
+++ b/core/chains/evm/config/mocks/chain_scoped_config.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -20,7 +20,7 @@ func (_m *ChainScopedConfig) EXPECT() *ChainScopedConfig_Expecter {
return &ChainScopedConfig_Expecter{mock: &_m.Mock}
}
-// EVM provides a mock function with given fields:
+// EVM provides a mock function with no fields
func (_m *ChainScopedConfig) EVM() config.EVM {
ret := _m.Called()
diff --git a/core/chains/evm/config/mocks/gas_estimator.go b/core/chains/evm/config/mocks/gas_estimator.go
index 1ead5e68917..2f36086f947 100644
--- a/core/chains/evm/config/mocks/gas_estimator.go
+++ b/core/chains/evm/config/mocks/gas_estimator.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -24,7 +24,7 @@ func (_m *GasEstimator) EXPECT() *GasEstimator_Expecter {
return &GasEstimator_Expecter{mock: &_m.Mock}
}
-// BlockHistory provides a mock function with given fields:
+// BlockHistory provides a mock function with no fields
func (_m *GasEstimator) BlockHistory() config.BlockHistory {
ret := _m.Called()
@@ -71,7 +71,7 @@ func (_c *GasEstimator_BlockHistory_Call) RunAndReturn(run func() config.BlockHi
return _c
}
-// BumpMin provides a mock function with given fields:
+// BumpMin provides a mock function with no fields
func (_m *GasEstimator) BumpMin() *assets.Wei {
ret := _m.Called()
@@ -118,7 +118,7 @@ func (_c *GasEstimator_BumpMin_Call) RunAndReturn(run func() *assets.Wei) *GasEs
return _c
}
-// BumpPercent provides a mock function with given fields:
+// BumpPercent provides a mock function with no fields
func (_m *GasEstimator) BumpPercent() uint16 {
ret := _m.Called()
@@ -163,7 +163,7 @@ func (_c *GasEstimator_BumpPercent_Call) RunAndReturn(run func() uint16) *GasEst
return _c
}
-// BumpThreshold provides a mock function with given fields:
+// BumpThreshold provides a mock function with no fields
func (_m *GasEstimator) BumpThreshold() uint64 {
ret := _m.Called()
@@ -208,7 +208,7 @@ func (_c *GasEstimator_BumpThreshold_Call) RunAndReturn(run func() uint64) *GasE
return _c
}
-// BumpTxDepth provides a mock function with given fields:
+// BumpTxDepth provides a mock function with no fields
func (_m *GasEstimator) BumpTxDepth() uint32 {
ret := _m.Called()
@@ -253,7 +253,7 @@ func (_c *GasEstimator_BumpTxDepth_Call) RunAndReturn(run func() uint32) *GasEst
return _c
}
-// DAOracle provides a mock function with given fields:
+// DAOracle provides a mock function with no fields
func (_m *GasEstimator) DAOracle() config.DAOracle {
ret := _m.Called()
@@ -300,7 +300,7 @@ func (_c *GasEstimator_DAOracle_Call) RunAndReturn(run func() config.DAOracle) *
return _c
}
-// EIP1559DynamicFees provides a mock function with given fields:
+// EIP1559DynamicFees provides a mock function with no fields
func (_m *GasEstimator) EIP1559DynamicFees() bool {
ret := _m.Called()
@@ -345,7 +345,7 @@ func (_c *GasEstimator_EIP1559DynamicFees_Call) RunAndReturn(run func() bool) *G
return _c
}
-// EstimateLimit provides a mock function with given fields:
+// EstimateLimit provides a mock function with no fields
func (_m *GasEstimator) EstimateLimit() bool {
ret := _m.Called()
@@ -390,7 +390,7 @@ func (_c *GasEstimator_EstimateLimit_Call) RunAndReturn(run func() bool) *GasEst
return _c
}
-// FeeCapDefault provides a mock function with given fields:
+// FeeCapDefault provides a mock function with no fields
func (_m *GasEstimator) FeeCapDefault() *assets.Wei {
ret := _m.Called()
@@ -437,7 +437,7 @@ func (_c *GasEstimator_FeeCapDefault_Call) RunAndReturn(run func() *assets.Wei)
return _c
}
-// FeeHistory provides a mock function with given fields:
+// FeeHistory provides a mock function with no fields
func (_m *GasEstimator) FeeHistory() config.FeeHistory {
ret := _m.Called()
@@ -484,7 +484,7 @@ func (_c *GasEstimator_FeeHistory_Call) RunAndReturn(run func() config.FeeHistor
return _c
}
-// LimitDefault provides a mock function with given fields:
+// LimitDefault provides a mock function with no fields
func (_m *GasEstimator) LimitDefault() uint64 {
ret := _m.Called()
@@ -529,7 +529,7 @@ func (_c *GasEstimator_LimitDefault_Call) RunAndReturn(run func() uint64) *GasEs
return _c
}
-// LimitJobType provides a mock function with given fields:
+// LimitJobType provides a mock function with no fields
func (_m *GasEstimator) LimitJobType() config.LimitJobType {
ret := _m.Called()
@@ -576,7 +576,7 @@ func (_c *GasEstimator_LimitJobType_Call) RunAndReturn(run func() config.LimitJo
return _c
}
-// LimitMax provides a mock function with given fields:
+// LimitMax provides a mock function with no fields
func (_m *GasEstimator) LimitMax() uint64 {
ret := _m.Called()
@@ -621,7 +621,7 @@ func (_c *GasEstimator_LimitMax_Call) RunAndReturn(run func() uint64) *GasEstima
return _c
}
-// LimitMultiplier provides a mock function with given fields:
+// LimitMultiplier provides a mock function with no fields
func (_m *GasEstimator) LimitMultiplier() float32 {
ret := _m.Called()
@@ -666,7 +666,7 @@ func (_c *GasEstimator_LimitMultiplier_Call) RunAndReturn(run func() float32) *G
return _c
}
-// LimitTransfer provides a mock function with given fields:
+// LimitTransfer provides a mock function with no fields
func (_m *GasEstimator) LimitTransfer() uint64 {
ret := _m.Called()
@@ -711,7 +711,7 @@ func (_c *GasEstimator_LimitTransfer_Call) RunAndReturn(run func() uint64) *GasE
return _c
}
-// Mode provides a mock function with given fields:
+// Mode provides a mock function with no fields
func (_m *GasEstimator) Mode() string {
ret := _m.Called()
@@ -756,7 +756,7 @@ func (_c *GasEstimator_Mode_Call) RunAndReturn(run func() string) *GasEstimator_
return _c
}
-// PriceDefault provides a mock function with given fields:
+// PriceDefault provides a mock function with no fields
func (_m *GasEstimator) PriceDefault() *assets.Wei {
ret := _m.Called()
@@ -803,7 +803,7 @@ func (_c *GasEstimator_PriceDefault_Call) RunAndReturn(run func() *assets.Wei) *
return _c
}
-// PriceMax provides a mock function with given fields:
+// PriceMax provides a mock function with no fields
func (_m *GasEstimator) PriceMax() *assets.Wei {
ret := _m.Called()
@@ -898,7 +898,7 @@ func (_c *GasEstimator_PriceMaxKey_Call) RunAndReturn(run func(common.Address) *
return _c
}
-// PriceMin provides a mock function with given fields:
+// PriceMin provides a mock function with no fields
func (_m *GasEstimator) PriceMin() *assets.Wei {
ret := _m.Called()
@@ -945,7 +945,7 @@ func (_c *GasEstimator_PriceMin_Call) RunAndReturn(run func() *assets.Wei) *GasE
return _c
}
-// TipCapDefault provides a mock function with given fields:
+// TipCapDefault provides a mock function with no fields
func (_m *GasEstimator) TipCapDefault() *assets.Wei {
ret := _m.Called()
@@ -992,7 +992,7 @@ func (_c *GasEstimator_TipCapDefault_Call) RunAndReturn(run func() *assets.Wei)
return _c
}
-// TipCapMin provides a mock function with given fields:
+// TipCapMin provides a mock function with no fields
func (_m *GasEstimator) TipCapMin() *assets.Wei {
ret := _m.Called()
diff --git a/core/chains/evm/forwarders/mocks/orm.go b/core/chains/evm/forwarders/mocks/orm.go
index 860ddef855b..a98769ee8cc 100644
--- a/core/chains/evm/forwarders/mocks/orm.go
+++ b/core/chains/evm/forwarders/mocks/orm.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
diff --git a/core/chains/evm/gas/mocks/evm_estimator.go b/core/chains/evm/gas/mocks/evm_estimator.go
index 159adb1a037..13f8d210e64 100644
--- a/core/chains/evm/gas/mocks/evm_estimator.go
+++ b/core/chains/evm/gas/mocks/evm_estimator.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -159,7 +159,7 @@ func (_c *EvmEstimator_BumpLegacyGas_Call) RunAndReturn(run func(context.Context
return _c
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *EvmEstimator) Close() error {
ret := _m.Called()
@@ -344,7 +344,7 @@ func (_c *EvmEstimator_GetLegacyGas_Call) RunAndReturn(run func(context.Context,
return _c
}
-// HealthReport provides a mock function with given fields:
+// HealthReport provides a mock function with no fields
func (_m *EvmEstimator) HealthReport() map[string]error {
ret := _m.Called()
@@ -391,7 +391,7 @@ func (_c *EvmEstimator_HealthReport_Call) RunAndReturn(run func() map[string]err
return _c
}
-// L1Oracle provides a mock function with given fields:
+// L1Oracle provides a mock function with no fields
func (_m *EvmEstimator) L1Oracle() rollups.L1Oracle {
ret := _m.Called()
@@ -438,7 +438,7 @@ func (_c *EvmEstimator_L1Oracle_Call) RunAndReturn(run func() rollups.L1Oracle)
return _c
}
-// Name provides a mock function with given fields:
+// Name provides a mock function with no fields
func (_m *EvmEstimator) Name() string {
ret := _m.Called()
@@ -513,11 +513,11 @@ func (_c *EvmEstimator_OnNewLongestChain_Call) Return() *EvmEstimator_OnNewLonge
}
func (_c *EvmEstimator_OnNewLongestChain_Call) RunAndReturn(run func(context.Context, *evmtypes.Head)) *EvmEstimator_OnNewLongestChain_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
-// Ready provides a mock function with given fields:
+// Ready provides a mock function with no fields
func (_m *EvmEstimator) Ready() error {
ret := _m.Called()
diff --git a/core/chains/evm/gas/mocks/evm_fee_estimator.go b/core/chains/evm/gas/mocks/evm_fee_estimator.go
index d0802f81ebd..9a8a9e456a7 100644
--- a/core/chains/evm/gas/mocks/evm_fee_estimator.go
+++ b/core/chains/evm/gas/mocks/evm_fee_estimator.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -102,7 +102,7 @@ func (_c *EvmFeeEstimator_BumpFee_Call) RunAndReturn(run func(context.Context, g
return _c
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *EvmFeeEstimator) Close() error {
ret := _m.Called()
@@ -309,7 +309,7 @@ func (_c *EvmFeeEstimator_GetMaxCost_Call) RunAndReturn(run func(context.Context
return _c
}
-// HealthReport provides a mock function with given fields:
+// HealthReport provides a mock function with no fields
func (_m *EvmFeeEstimator) HealthReport() map[string]error {
ret := _m.Called()
@@ -356,7 +356,7 @@ func (_c *EvmFeeEstimator_HealthReport_Call) RunAndReturn(run func() map[string]
return _c
}
-// L1Oracle provides a mock function with given fields:
+// L1Oracle provides a mock function with no fields
func (_m *EvmFeeEstimator) L1Oracle() rollups.L1Oracle {
ret := _m.Called()
@@ -403,7 +403,7 @@ func (_c *EvmFeeEstimator_L1Oracle_Call) RunAndReturn(run func() rollups.L1Oracl
return _c
}
-// Name provides a mock function with given fields:
+// Name provides a mock function with no fields
func (_m *EvmFeeEstimator) Name() string {
ret := _m.Called()
@@ -478,11 +478,11 @@ func (_c *EvmFeeEstimator_OnNewLongestChain_Call) Return() *EvmFeeEstimator_OnNe
}
func (_c *EvmFeeEstimator_OnNewLongestChain_Call) RunAndReturn(run func(context.Context, *evmtypes.Head)) *EvmFeeEstimator_OnNewLongestChain_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
-// Ready provides a mock function with given fields:
+// Ready provides a mock function with no fields
func (_m *EvmFeeEstimator) Ready() error {
ret := _m.Called()
diff --git a/core/chains/evm/gas/mocks/fee_estimator_client.go b/core/chains/evm/gas/mocks/fee_estimator_client.go
index 782c897923b..e44c4f9d029 100644
--- a/core/chains/evm/gas/mocks/fee_estimator_client.go
+++ b/core/chains/evm/gas/mocks/fee_estimator_client.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
diff --git a/core/chains/evm/gas/mocks/fee_history_estimator_client.go b/core/chains/evm/gas/mocks/fee_history_estimator_client.go
index 24ad7e23ad9..19cadd700e8 100644
--- a/core/chains/evm/gas/mocks/fee_history_estimator_client.go
+++ b/core/chains/evm/gas/mocks/fee_history_estimator_client.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
diff --git a/core/chains/evm/gas/rollups/mocks/l1_oracle.go b/core/chains/evm/gas/rollups/mocks/l1_oracle.go
index 4ed067663e2..24246a9b30d 100644
--- a/core/chains/evm/gas/rollups/mocks/l1_oracle.go
+++ b/core/chains/evm/gas/rollups/mocks/l1_oracle.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -23,7 +23,7 @@ func (_m *L1Oracle) EXPECT() *L1Oracle_Expecter {
return &L1Oracle_Expecter{mock: &_m.Mock}
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *L1Oracle) Close() error {
ret := _m.Called()
@@ -126,7 +126,7 @@ func (_c *L1Oracle_GasPrice_Call) RunAndReturn(run func(context.Context) (*asset
return _c
}
-// HealthReport provides a mock function with given fields:
+// HealthReport provides a mock function with no fields
func (_m *L1Oracle) HealthReport() map[string]error {
ret := _m.Called()
@@ -173,7 +173,7 @@ func (_c *L1Oracle_HealthReport_Call) RunAndReturn(run func() map[string]error)
return _c
}
-// Name provides a mock function with given fields:
+// Name provides a mock function with no fields
func (_m *L1Oracle) Name() string {
ret := _m.Called()
@@ -218,7 +218,7 @@ func (_c *L1Oracle_Name_Call) RunAndReturn(run func() string) *L1Oracle_Name_Cal
return _c
}
-// Ready provides a mock function with given fields:
+// Ready provides a mock function with no fields
func (_m *L1Oracle) Ready() error {
ret := _m.Called()
diff --git a/core/chains/evm/gas/rollups/mocks/l1_oracle_client.go b/core/chains/evm/gas/rollups/mocks/l1_oracle_client.go
index 8ff0bfa0908..73fac2ae846 100644
--- a/core/chains/evm/gas/rollups/mocks/l1_oracle_client.go
+++ b/core/chains/evm/gas/rollups/mocks/l1_oracle_client.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
diff --git a/core/chains/evm/keystore/mocks/eth.go b/core/chains/evm/keystore/mocks/eth.go
index b481be1b5c8..aefe6ff7548 100644
--- a/core/chains/evm/keystore/mocks/eth.go
+++ b/core/chains/evm/keystore/mocks/eth.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
diff --git a/core/chains/evm/log/mocks/abigen_contract.go b/core/chains/evm/log/mocks/abigen_contract.go
index 7db05b6e097..7fd1479810f 100644
--- a/core/chains/evm/log/mocks/abigen_contract.go
+++ b/core/chains/evm/log/mocks/abigen_contract.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -24,7 +24,7 @@ func (_m *AbigenContract) EXPECT() *AbigenContract_Expecter {
return &AbigenContract_Expecter{mock: &_m.Mock}
}
-// Address provides a mock function with given fields:
+// Address provides a mock function with no fields
func (_m *AbigenContract) Address() common.Address {
ret := _m.Called()
diff --git a/core/chains/evm/log/mocks/broadcast.go b/core/chains/evm/log/mocks/broadcast.go
index 6aadbc50b6d..4d7367819a2 100644
--- a/core/chains/evm/log/mocks/broadcast.go
+++ b/core/chains/evm/log/mocks/broadcast.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -25,7 +25,7 @@ func (_m *Broadcast) EXPECT() *Broadcast_Expecter {
return &Broadcast_Expecter{mock: &_m.Mock}
}
-// DecodedLog provides a mock function with given fields:
+// DecodedLog provides a mock function with no fields
func (_m *Broadcast) DecodedLog() interface{} {
ret := _m.Called()
@@ -72,7 +72,7 @@ func (_c *Broadcast_DecodedLog_Call) RunAndReturn(run func() interface{}) *Broad
return _c
}
-// EVMChainID provides a mock function with given fields:
+// EVMChainID provides a mock function with no fields
func (_m *Broadcast) EVMChainID() big.Int {
ret := _m.Called()
@@ -117,7 +117,7 @@ func (_c *Broadcast_EVMChainID_Call) RunAndReturn(run func() big.Int) *Broadcast
return _c
}
-// JobID provides a mock function with given fields:
+// JobID provides a mock function with no fields
func (_m *Broadcast) JobID() int32 {
ret := _m.Called()
@@ -162,7 +162,7 @@ func (_c *Broadcast_JobID_Call) RunAndReturn(run func() int32) *Broadcast_JobID_
return _c
}
-// LatestBlockHash provides a mock function with given fields:
+// LatestBlockHash provides a mock function with no fields
func (_m *Broadcast) LatestBlockHash() common.Hash {
ret := _m.Called()
@@ -209,7 +209,7 @@ func (_c *Broadcast_LatestBlockHash_Call) RunAndReturn(run func() common.Hash) *
return _c
}
-// LatestBlockNumber provides a mock function with given fields:
+// LatestBlockNumber provides a mock function with no fields
func (_m *Broadcast) LatestBlockNumber() uint64 {
ret := _m.Called()
@@ -254,7 +254,7 @@ func (_c *Broadcast_LatestBlockNumber_Call) RunAndReturn(run func() uint64) *Bro
return _c
}
-// RawLog provides a mock function with given fields:
+// RawLog provides a mock function with no fields
func (_m *Broadcast) RawLog() types.Log {
ret := _m.Called()
@@ -299,7 +299,7 @@ func (_c *Broadcast_RawLog_Call) RunAndReturn(run func() types.Log) *Broadcast_R
return _c
}
-// ReceiptsRoot provides a mock function with given fields:
+// ReceiptsRoot provides a mock function with no fields
func (_m *Broadcast) ReceiptsRoot() common.Hash {
ret := _m.Called()
@@ -346,7 +346,7 @@ func (_c *Broadcast_ReceiptsRoot_Call) RunAndReturn(run func() common.Hash) *Bro
return _c
}
-// StateRoot provides a mock function with given fields:
+// StateRoot provides a mock function with no fields
func (_m *Broadcast) StateRoot() common.Hash {
ret := _m.Called()
@@ -393,7 +393,7 @@ func (_c *Broadcast_StateRoot_Call) RunAndReturn(run func() common.Hash) *Broadc
return _c
}
-// String provides a mock function with given fields:
+// String provides a mock function with no fields
func (_m *Broadcast) String() string {
ret := _m.Called()
@@ -438,7 +438,7 @@ func (_c *Broadcast_String_Call) RunAndReturn(run func() string) *Broadcast_Stri
return _c
}
-// TransactionsRoot provides a mock function with given fields:
+// TransactionsRoot provides a mock function with no fields
func (_m *Broadcast) TransactionsRoot() common.Hash {
ret := _m.Called()
diff --git a/core/chains/evm/log/mocks/broadcaster.go b/core/chains/evm/log/mocks/broadcaster.go
index 1545624a83d..8e88fd7e30d 100644
--- a/core/chains/evm/log/mocks/broadcaster.go
+++ b/core/chains/evm/log/mocks/broadcaster.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -55,11 +55,11 @@ func (_c *Broadcaster_AddDependents_Call) Return() *Broadcaster_AddDependents_Ca
}
func (_c *Broadcaster_AddDependents_Call) RunAndReturn(run func(int)) *Broadcaster_AddDependents_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
-// AwaitDependents provides a mock function with given fields:
+// AwaitDependents provides a mock function with no fields
func (_m *Broadcaster) AwaitDependents() <-chan struct{} {
ret := _m.Called()
@@ -106,7 +106,7 @@ func (_c *Broadcaster_AwaitDependents_Call) RunAndReturn(run func() <-chan struc
return _c
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *Broadcaster) Close() error {
ret := _m.Called()
@@ -151,7 +151,7 @@ func (_c *Broadcaster_Close_Call) RunAndReturn(run func() error) *Broadcaster_Cl
return _c
}
-// DependentReady provides a mock function with given fields:
+// DependentReady provides a mock function with no fields
func (_m *Broadcaster) DependentReady() {
_m.Called()
}
@@ -179,11 +179,11 @@ func (_c *Broadcaster_DependentReady_Call) Return() *Broadcaster_DependentReady_
}
func (_c *Broadcaster_DependentReady_Call) RunAndReturn(run func()) *Broadcaster_DependentReady_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
-// HealthReport provides a mock function with given fields:
+// HealthReport provides a mock function with no fields
func (_m *Broadcaster) HealthReport() map[string]error {
ret := _m.Called()
@@ -230,7 +230,7 @@ func (_c *Broadcaster_HealthReport_Call) RunAndReturn(run func() map[string]erro
return _c
}
-// IsConnected provides a mock function with given fields:
+// IsConnected provides a mock function with no fields
func (_m *Broadcaster) IsConnected() bool {
ret := _m.Called()
@@ -323,7 +323,7 @@ func (_c *Broadcaster_MarkConsumed_Call) RunAndReturn(run func(context.Context,
return _c
}
-// Name provides a mock function with given fields:
+// Name provides a mock function with no fields
func (_m *Broadcaster) Name() string {
ret := _m.Called()
@@ -398,11 +398,11 @@ func (_c *Broadcaster_OnNewLongestChain_Call) Return() *Broadcaster_OnNewLongest
}
func (_c *Broadcaster_OnNewLongestChain_Call) RunAndReturn(run func(context.Context, *types.Head)) *Broadcaster_OnNewLongestChain_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
-// Ready provides a mock function with given fields:
+// Ready provides a mock function with no fields
func (_m *Broadcaster) Ready() error {
ret := _m.Called()
@@ -526,7 +526,7 @@ func (_c *Broadcaster_ReplayFromBlock_Call) Return() *Broadcaster_ReplayFromBloc
}
func (_c *Broadcaster_ReplayFromBlock_Call) RunAndReturn(run func(int64, bool)) *Broadcaster_ReplayFromBlock_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
diff --git a/core/chains/evm/logpoller/mocks/log_poller.go b/core/chains/evm/logpoller/mocks/log_poller.go
index 592157efb1c..444ee7d7e69 100644
--- a/core/chains/evm/logpoller/mocks/log_poller.go
+++ b/core/chains/evm/logpoller/mocks/log_poller.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -31,7 +31,7 @@ func (_m *LogPoller) EXPECT() *LogPoller_Expecter {
return &LogPoller_Expecter{mock: &_m.Mock}
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *LogPoller) Close() error {
ret := _m.Called()
@@ -301,7 +301,7 @@ func (_c *LogPoller_GetBlocksRange_Call) RunAndReturn(run func(context.Context,
return _c
}
-// GetFilters provides a mock function with given fields:
+// GetFilters provides a mock function with no fields
func (_m *LogPoller) GetFilters() map[string]logpoller.Filter {
ret := _m.Called()
@@ -394,7 +394,7 @@ func (_c *LogPoller_HasFilter_Call) RunAndReturn(run func(string) bool) *LogPoll
return _c
}
-// HealthReport provides a mock function with given fields:
+// HealthReport provides a mock function with no fields
func (_m *LogPoller) HealthReport() map[string]error {
ret := _m.Called()
@@ -441,7 +441,7 @@ func (_c *LogPoller_HealthReport_Call) RunAndReturn(run func() map[string]error)
return _c
}
-// Healthy provides a mock function with given fields:
+// Healthy provides a mock function with no fields
func (_m *LogPoller) Healthy() error {
ret := _m.Called()
@@ -1546,7 +1546,7 @@ func (_c *LogPoller_LogsWithSigs_Call) RunAndReturn(run func(context.Context, in
return _c
}
-// Name provides a mock function with given fields:
+// Name provides a mock function with no fields
func (_m *LogPoller) Name() string {
ret := _m.Called()
@@ -1591,7 +1591,7 @@ func (_c *LogPoller_Name_Call) RunAndReturn(run func() string) *LogPoller_Name_C
return _c
}
-// Ready provides a mock function with given fields:
+// Ready provides a mock function with no fields
func (_m *LogPoller) Ready() error {
ret := _m.Called()
@@ -1759,7 +1759,7 @@ func (_c *LogPoller_ReplayAsync_Call) Return() *LogPoller_ReplayAsync_Call {
}
func (_c *LogPoller_ReplayAsync_Call) RunAndReturn(run func(int64)) *LogPoller_ReplayAsync_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
diff --git a/core/chains/evm/mocks/balance_monitor.go b/core/chains/evm/mocks/balance_monitor.go
index 95382541179..abb86909046 100644
--- a/core/chains/evm/mocks/balance_monitor.go
+++ b/core/chains/evm/mocks/balance_monitor.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -26,7 +26,7 @@ func (_m *BalanceMonitor) EXPECT() *BalanceMonitor_Expecter {
return &BalanceMonitor_Expecter{mock: &_m.Mock}
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *BalanceMonitor) Close() error {
ret := _m.Called()
@@ -119,7 +119,7 @@ func (_c *BalanceMonitor_GetEthBalance_Call) RunAndReturn(run func(common.Addres
return _c
}
-// HealthReport provides a mock function with given fields:
+// HealthReport provides a mock function with no fields
func (_m *BalanceMonitor) HealthReport() map[string]error {
ret := _m.Called()
@@ -166,7 +166,7 @@ func (_c *BalanceMonitor_HealthReport_Call) RunAndReturn(run func() map[string]e
return _c
}
-// Name provides a mock function with given fields:
+// Name provides a mock function with no fields
func (_m *BalanceMonitor) Name() string {
ret := _m.Called()
@@ -241,11 +241,11 @@ func (_c *BalanceMonitor_OnNewLongestChain_Call) Return() *BalanceMonitor_OnNewL
}
func (_c *BalanceMonitor_OnNewLongestChain_Call) RunAndReturn(run func(context.Context, *types.Head)) *BalanceMonitor_OnNewLongestChain_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
-// Ready provides a mock function with given fields:
+// Ready provides a mock function with no fields
func (_m *BalanceMonitor) Ready() error {
ret := _m.Called()
diff --git a/core/chains/evm/txmgr/mocks/config.go b/core/chains/evm/txmgr/mocks/config.go
index 011ba19a958..91fed44dc7c 100644
--- a/core/chains/evm/txmgr/mocks/config.go
+++ b/core/chains/evm/txmgr/mocks/config.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -20,7 +20,7 @@ func (_m *Config) EXPECT() *Config_Expecter {
return &Config_Expecter{mock: &_m.Mock}
}
-// ChainType provides a mock function with given fields:
+// ChainType provides a mock function with no fields
func (_m *Config) ChainType() chaintype.ChainType {
ret := _m.Called()
@@ -65,7 +65,7 @@ func (_c *Config_ChainType_Call) RunAndReturn(run func() chaintype.ChainType) *C
return _c
}
-// FinalityDepth provides a mock function with given fields:
+// FinalityDepth provides a mock function with no fields
func (_m *Config) FinalityDepth() uint32 {
ret := _m.Called()
@@ -110,7 +110,7 @@ func (_c *Config_FinalityDepth_Call) RunAndReturn(run func() uint32) *Config_Fin
return _c
}
-// FinalityTagEnabled provides a mock function with given fields:
+// FinalityTagEnabled provides a mock function with no fields
func (_m *Config) FinalityTagEnabled() bool {
ret := _m.Called()
@@ -155,7 +155,7 @@ func (_c *Config_FinalityTagEnabled_Call) RunAndReturn(run func() bool) *Config_
return _c
}
-// NonceAutoSync provides a mock function with given fields:
+// NonceAutoSync provides a mock function with no fields
func (_m *Config) NonceAutoSync() bool {
ret := _m.Called()
@@ -200,7 +200,7 @@ func (_c *Config_NonceAutoSync_Call) RunAndReturn(run func() bool) *Config_Nonce
return _c
}
-// RPCDefaultBatchSize provides a mock function with given fields:
+// RPCDefaultBatchSize provides a mock function with no fields
func (_m *Config) RPCDefaultBatchSize() uint32 {
ret := _m.Called()
diff --git a/core/chains/evm/txmgr/mocks/evm_tx_store.go b/core/chains/evm/txmgr/mocks/evm_tx_store.go
index ca98ad6ceb8..a00e280d752 100644
--- a/core/chains/evm/txmgr/mocks/evm_tx_store.go
+++ b/core/chains/evm/txmgr/mocks/evm_tx_store.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -18,8 +18,6 @@ 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"
@@ -135,7 +133,7 @@ func (_c *EvmTxStore_CheckTxQueueCapacity_Call) RunAndReturn(run func(context.Co
return _c
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *EvmTxStore) Close() {
_m.Called()
}
@@ -163,7 +161,7 @@ func (_c *EvmTxStore_Close_Call) Return() *EvmTxStore_Close_Call {
}
func (_c *EvmTxStore_Close_Call) RunAndReturn(run func()) *EvmTxStore_Close_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
@@ -494,23 +492,23 @@ func (_c *EvmTxStore_DeleteReceiptByTxHash_Call) RunAndReturn(run func(context.C
}
// FindAttemptsRequiringReceiptFetch provides a mock function with given fields: ctx, chainID
-func (_m *EvmTxStore) FindAttemptsRequiringReceiptFetch(ctx context.Context, chainID *big.Int) ([]txmgr.TxAttempt, error) {
+func (_m *EvmTxStore) FindAttemptsRequiringReceiptFetch(ctx context.Context, chainID *big.Int) ([]types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error) {
ret := _m.Called(ctx, chainID)
if len(ret) == 0 {
panic("no return value specified for FindAttemptsRequiringReceiptFetch")
}
- var r0 []txmgr.TxAttempt
+ var r0 []types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]
var r1 error
- if rf, ok := ret.Get(0).(func(context.Context, *big.Int) ([]txmgr.TxAttempt, error)); ok {
+ if rf, ok := ret.Get(0).(func(context.Context, *big.Int) ([]types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error)); ok {
return rf(ctx, chainID)
}
- if rf, ok := ret.Get(0).(func(context.Context, *big.Int) []txmgr.TxAttempt); ok {
+ if rf, ok := ret.Get(0).(func(context.Context, *big.Int) []types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]); ok {
r0 = rf(ctx, chainID)
} else {
if ret.Get(0) != nil {
- r0 = ret.Get(0).([]txmgr.TxAttempt)
+ r0 = ret.Get(0).([]types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee])
}
}
@@ -542,12 +540,12 @@ func (_c *EvmTxStore_FindAttemptsRequiringReceiptFetch_Call) Run(run func(ctx co
return _c
}
-func (_c *EvmTxStore_FindAttemptsRequiringReceiptFetch_Call) Return(hashes []txmgr.TxAttempt, err error) *EvmTxStore_FindAttemptsRequiringReceiptFetch_Call {
+func (_c *EvmTxStore_FindAttemptsRequiringReceiptFetch_Call) Return(hashes []types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], err error) *EvmTxStore_FindAttemptsRequiringReceiptFetch_Call {
_c.Call.Return(hashes, err)
return _c
}
-func (_c *EvmTxStore_FindAttemptsRequiringReceiptFetch_Call) RunAndReturn(run func(context.Context, *big.Int) ([]txmgr.TxAttempt, error)) *EvmTxStore_FindAttemptsRequiringReceiptFetch_Call {
+func (_c *EvmTxStore_FindAttemptsRequiringReceiptFetch_Call) RunAndReturn(run func(context.Context, *big.Int) ([]types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error)) *EvmTxStore_FindAttemptsRequiringReceiptFetch_Call {
_c.Call.Return(run)
return _c
}
@@ -915,23 +913,23 @@ func (_c *EvmTxStore_FindReorgOrIncludedTxs_Call) RunAndReturn(run func(context.
}
// FindTxAttempt provides a mock function with given fields: ctx, hash
-func (_m *EvmTxStore) FindTxAttempt(ctx context.Context, hash common.Hash) (*txmgr.TxAttempt, error) {
+func (_m *EvmTxStore) FindTxAttempt(ctx context.Context, hash common.Hash) (*types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error) {
ret := _m.Called(ctx, hash)
if len(ret) == 0 {
panic("no return value specified for FindTxAttempt")
}
- var r0 *txmgr.TxAttempt
+ var r0 *types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]
var r1 error
- if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (*txmgr.TxAttempt, error)); ok {
+ if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (*types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error)); ok {
return rf(ctx, hash)
}
- if rf, ok := ret.Get(0).(func(context.Context, common.Hash) *txmgr.TxAttempt); ok {
+ if rf, ok := ret.Get(0).(func(context.Context, common.Hash) *types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]); ok {
r0 = rf(ctx, hash)
} else {
if ret.Get(0) != nil {
- r0 = ret.Get(0).(*txmgr.TxAttempt)
+ r0 = ret.Get(0).(*types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee])
}
}
@@ -963,34 +961,34 @@ func (_c *EvmTxStore_FindTxAttempt_Call) Run(run func(ctx context.Context, hash
return _c
}
-func (_c *EvmTxStore_FindTxAttempt_Call) Return(_a0 *txmgr.TxAttempt, _a1 error) *EvmTxStore_FindTxAttempt_Call {
+func (_c *EvmTxStore_FindTxAttempt_Call) Return(_a0 *types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], _a1 error) *EvmTxStore_FindTxAttempt_Call {
_c.Call.Return(_a0, _a1)
return _c
}
-func (_c *EvmTxStore_FindTxAttempt_Call) RunAndReturn(run func(context.Context, common.Hash) (*txmgr.TxAttempt, error)) *EvmTxStore_FindTxAttempt_Call {
+func (_c *EvmTxStore_FindTxAttempt_Call) RunAndReturn(run func(context.Context, common.Hash) (*types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error)) *EvmTxStore_FindTxAttempt_Call {
_c.Call.Return(run)
return _c
}
// FindTxAttemptConfirmedByTxIDs provides a mock function with given fields: ctx, ids
-func (_m *EvmTxStore) FindTxAttemptConfirmedByTxIDs(ctx context.Context, ids []int64) ([]txmgr.TxAttempt, error) {
+func (_m *EvmTxStore) FindTxAttemptConfirmedByTxIDs(ctx context.Context, ids []int64) ([]types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error) {
ret := _m.Called(ctx, ids)
if len(ret) == 0 {
panic("no return value specified for FindTxAttemptConfirmedByTxIDs")
}
- var r0 []txmgr.TxAttempt
+ var r0 []types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]
var r1 error
- if rf, ok := ret.Get(0).(func(context.Context, []int64) ([]txmgr.TxAttempt, error)); ok {
+ if rf, ok := ret.Get(0).(func(context.Context, []int64) ([]types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error)); ok {
return rf(ctx, ids)
}
- if rf, ok := ret.Get(0).(func(context.Context, []int64) []txmgr.TxAttempt); ok {
+ if rf, ok := ret.Get(0).(func(context.Context, []int64) []types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]); ok {
r0 = rf(ctx, ids)
} else {
if ret.Get(0) != nil {
- r0 = ret.Get(0).([]txmgr.TxAttempt)
+ r0 = ret.Get(0).([]types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee])
}
}
@@ -1022,12 +1020,12 @@ func (_c *EvmTxStore_FindTxAttemptConfirmedByTxIDs_Call) Run(run func(ctx contex
return _c
}
-func (_c *EvmTxStore_FindTxAttemptConfirmedByTxIDs_Call) Return(_a0 []txmgr.TxAttempt, _a1 error) *EvmTxStore_FindTxAttemptConfirmedByTxIDs_Call {
+func (_c *EvmTxStore_FindTxAttemptConfirmedByTxIDs_Call) Return(_a0 []types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], _a1 error) *EvmTxStore_FindTxAttemptConfirmedByTxIDs_Call {
_c.Call.Return(_a0, _a1)
return _c
}
-func (_c *EvmTxStore_FindTxAttemptConfirmedByTxIDs_Call) RunAndReturn(run func(context.Context, []int64) ([]txmgr.TxAttempt, error)) *EvmTxStore_FindTxAttemptConfirmedByTxIDs_Call {
+func (_c *EvmTxStore_FindTxAttemptConfirmedByTxIDs_Call) RunAndReturn(run func(context.Context, []int64) ([]types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error)) *EvmTxStore_FindTxAttemptConfirmedByTxIDs_Call {
_c.Call.Return(run)
return _c
}
@@ -1154,23 +1152,23 @@ func (_c *EvmTxStore_FindTxAttemptsRequiringResend_Call) RunAndReturn(run func(c
}
// FindTxByHash provides a mock function with given fields: ctx, hash
-func (_m *EvmTxStore) FindTxByHash(ctx context.Context, hash common.Hash) (*txmgr.Tx, error) {
+func (_m *EvmTxStore) FindTxByHash(ctx context.Context, hash common.Hash) (*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error) {
ret := _m.Called(ctx, hash)
if len(ret) == 0 {
panic("no return value specified for FindTxByHash")
}
- var r0 *txmgr.Tx
+ var r0 *types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]
var r1 error
- if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (*txmgr.Tx, error)); ok {
+ if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error)); ok {
return rf(ctx, hash)
}
- if rf, ok := ret.Get(0).(func(context.Context, common.Hash) *txmgr.Tx); ok {
+ if rf, ok := ret.Get(0).(func(context.Context, common.Hash) *types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]); ok {
r0 = rf(ctx, hash)
} else {
if ret.Get(0) != nil {
- r0 = ret.Get(0).(*txmgr.Tx)
+ r0 = ret.Get(0).(*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee])
}
}
@@ -1202,33 +1200,33 @@ func (_c *EvmTxStore_FindTxByHash_Call) Run(run func(ctx context.Context, hash c
return _c
}
-func (_c *EvmTxStore_FindTxByHash_Call) Return(_a0 *txmgr.Tx, _a1 error) *EvmTxStore_FindTxByHash_Call {
+func (_c *EvmTxStore_FindTxByHash_Call) Return(_a0 *types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], _a1 error) *EvmTxStore_FindTxByHash_Call {
_c.Call.Return(_a0, _a1)
return _c
}
-func (_c *EvmTxStore_FindTxByHash_Call) RunAndReturn(run func(context.Context, common.Hash) (*txmgr.Tx, error)) *EvmTxStore_FindTxByHash_Call {
+func (_c *EvmTxStore_FindTxByHash_Call) RunAndReturn(run func(context.Context, common.Hash) (*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error)) *EvmTxStore_FindTxByHash_Call {
_c.Call.Return(run)
return _c
}
// FindTxWithAttempts provides a mock function with given fields: ctx, etxID
-func (_m *EvmTxStore) FindTxWithAttempts(ctx context.Context, etxID int64) (txmgr.Tx, error) {
+func (_m *EvmTxStore) FindTxWithAttempts(ctx context.Context, etxID int64) (types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error) {
ret := _m.Called(ctx, etxID)
if len(ret) == 0 {
panic("no return value specified for FindTxWithAttempts")
}
- var r0 txmgr.Tx
+ var r0 types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]
var r1 error
- if rf, ok := ret.Get(0).(func(context.Context, int64) (txmgr.Tx, error)); ok {
+ if rf, ok := ret.Get(0).(func(context.Context, int64) (types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error)); ok {
return rf(ctx, etxID)
}
- if rf, ok := ret.Get(0).(func(context.Context, int64) txmgr.Tx); ok {
+ if rf, ok := ret.Get(0).(func(context.Context, int64) types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]); ok {
r0 = rf(ctx, etxID)
} else {
- r0 = ret.Get(0).(txmgr.Tx)
+ r0 = ret.Get(0).(types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee])
}
if rf, ok := ret.Get(1).(func(context.Context, int64) error); ok {
@@ -1259,12 +1257,12 @@ func (_c *EvmTxStore_FindTxWithAttempts_Call) Run(run func(ctx context.Context,
return _c
}
-func (_c *EvmTxStore_FindTxWithAttempts_Call) Return(etx txmgr.Tx, err error) *EvmTxStore_FindTxWithAttempts_Call {
+func (_c *EvmTxStore_FindTxWithAttempts_Call) Return(etx types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], err error) *EvmTxStore_FindTxWithAttempts_Call {
_c.Call.Return(etx, err)
return _c
}
-func (_c *EvmTxStore_FindTxWithAttempts_Call) RunAndReturn(run func(context.Context, int64) (txmgr.Tx, error)) *EvmTxStore_FindTxWithAttempts_Call {
+func (_c *EvmTxStore_FindTxWithAttempts_Call) RunAndReturn(run func(context.Context, int64) (types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error)) *EvmTxStore_FindTxWithAttempts_Call {
_c.Call.Return(run)
return _c
}
@@ -1390,23 +1388,23 @@ func (_c *EvmTxStore_FindTxWithSequence_Call) RunAndReturn(run func(context.Cont
}
// FindTxesByIDs provides a mock function with given fields: ctx, etxIDs, chainID
-func (_m *EvmTxStore) FindTxesByIDs(ctx context.Context, etxIDs []int64, chainID *big.Int) ([]*txmgr.Tx, error) {
+func (_m *EvmTxStore) FindTxesByIDs(ctx context.Context, etxIDs []int64, chainID *big.Int) ([]*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error) {
ret := _m.Called(ctx, etxIDs, chainID)
if len(ret) == 0 {
panic("no return value specified for FindTxesByIDs")
}
- var r0 []*txmgr.Tx
+ var r0 []*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]
var r1 error
- if rf, ok := ret.Get(0).(func(context.Context, []int64, *big.Int) ([]*txmgr.Tx, error)); ok {
+ if rf, ok := ret.Get(0).(func(context.Context, []int64, *big.Int) ([]*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error)); ok {
return rf(ctx, etxIDs, chainID)
}
- if rf, ok := ret.Get(0).(func(context.Context, []int64, *big.Int) []*txmgr.Tx); ok {
+ if rf, ok := ret.Get(0).(func(context.Context, []int64, *big.Int) []*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]); ok {
r0 = rf(ctx, etxIDs, chainID)
} else {
if ret.Get(0) != nil {
- r0 = ret.Get(0).([]*txmgr.Tx)
+ r0 = ret.Get(0).([]*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee])
}
}
@@ -1439,12 +1437,12 @@ func (_c *EvmTxStore_FindTxesByIDs_Call) Run(run func(ctx context.Context, etxID
return _c
}
-func (_c *EvmTxStore_FindTxesByIDs_Call) Return(etxs []*txmgr.Tx, err error) *EvmTxStore_FindTxesByIDs_Call {
+func (_c *EvmTxStore_FindTxesByIDs_Call) Return(etxs []*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], err error) *EvmTxStore_FindTxesByIDs_Call {
_c.Call.Return(etxs, err)
return _c
}
-func (_c *EvmTxStore_FindTxesByIDs_Call) RunAndReturn(run func(context.Context, []int64, *big.Int) ([]*txmgr.Tx, error)) *EvmTxStore_FindTxesByIDs_Call {
+func (_c *EvmTxStore_FindTxesByIDs_Call) RunAndReturn(run func(context.Context, []int64, *big.Int) ([]*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error)) *EvmTxStore_FindTxesByIDs_Call {
_c.Call.Return(run)
return _c
}
@@ -1512,23 +1510,23 @@ func (_c *EvmTxStore_FindTxesByMetaFieldAndStates_Call) RunAndReturn(run func(co
}
// FindTxesPendingCallback provides a mock function with given fields: ctx, latest, finalized, chainID
-func (_m *EvmTxStore) FindTxesPendingCallback(ctx context.Context, latest int64, finalized int64, chainID *big.Int) ([]txmgr.ReceiptPlus, error) {
+func (_m *EvmTxStore) FindTxesPendingCallback(ctx context.Context, latest int64, finalized int64, chainID *big.Int) ([]types.ReceiptPlus[*evmtypes.Receipt], error) {
ret := _m.Called(ctx, latest, finalized, chainID)
if len(ret) == 0 {
panic("no return value specified for FindTxesPendingCallback")
}
- var r0 []txmgr.ReceiptPlus
+ var r0 []types.ReceiptPlus[*evmtypes.Receipt]
var r1 error
- if rf, ok := ret.Get(0).(func(context.Context, int64, int64, *big.Int) ([]txmgr.ReceiptPlus, error)); ok {
+ if rf, ok := ret.Get(0).(func(context.Context, int64, int64, *big.Int) ([]types.ReceiptPlus[*evmtypes.Receipt], error)); ok {
return rf(ctx, latest, finalized, chainID)
}
- if rf, ok := ret.Get(0).(func(context.Context, int64, int64, *big.Int) []txmgr.ReceiptPlus); ok {
+ if rf, ok := ret.Get(0).(func(context.Context, int64, int64, *big.Int) []types.ReceiptPlus[*evmtypes.Receipt]); ok {
r0 = rf(ctx, latest, finalized, chainID)
} else {
if ret.Get(0) != nil {
- r0 = ret.Get(0).([]txmgr.ReceiptPlus)
+ r0 = ret.Get(0).([]types.ReceiptPlus[*evmtypes.Receipt])
}
}
@@ -1562,12 +1560,12 @@ func (_c *EvmTxStore_FindTxesPendingCallback_Call) Run(run func(ctx context.Cont
return _c
}
-func (_c *EvmTxStore_FindTxesPendingCallback_Call) Return(receiptsPlus []txmgr.ReceiptPlus, err error) *EvmTxStore_FindTxesPendingCallback_Call {
+func (_c *EvmTxStore_FindTxesPendingCallback_Call) Return(receiptsPlus []types.ReceiptPlus[*evmtypes.Receipt], err error) *EvmTxStore_FindTxesPendingCallback_Call {
_c.Call.Return(receiptsPlus, err)
return _c
}
-func (_c *EvmTxStore_FindTxesPendingCallback_Call) RunAndReturn(run func(context.Context, int64, int64, *big.Int) ([]txmgr.ReceiptPlus, error)) *EvmTxStore_FindTxesPendingCallback_Call {
+func (_c *EvmTxStore_FindTxesPendingCallback_Call) RunAndReturn(run func(context.Context, int64, int64, *big.Int) ([]types.ReceiptPlus[*evmtypes.Receipt], error)) *EvmTxStore_FindTxesPendingCallback_Call {
_c.Call.Return(run)
return _c
}
@@ -1756,23 +1754,23 @@ func (_c *EvmTxStore_FindTxesWithMetaFieldByStates_Call) RunAndReturn(run func(c
}
// FindTxsByStateAndFromAddresses provides a mock function with given fields: ctx, addresses, state, chainID
-func (_m *EvmTxStore) FindTxsByStateAndFromAddresses(ctx context.Context, addresses []common.Address, state types.TxState, chainID *big.Int) ([]*txmgr.Tx, error) {
+func (_m *EvmTxStore) FindTxsByStateAndFromAddresses(ctx context.Context, addresses []common.Address, state types.TxState, chainID *big.Int) ([]*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error) {
ret := _m.Called(ctx, addresses, state, chainID)
if len(ret) == 0 {
panic("no return value specified for FindTxsByStateAndFromAddresses")
}
- var r0 []*txmgr.Tx
+ var r0 []*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]
var r1 error
- if rf, ok := ret.Get(0).(func(context.Context, []common.Address, types.TxState, *big.Int) ([]*txmgr.Tx, error)); ok {
+ if rf, ok := ret.Get(0).(func(context.Context, []common.Address, types.TxState, *big.Int) ([]*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error)); ok {
return rf(ctx, addresses, state, chainID)
}
- if rf, ok := ret.Get(0).(func(context.Context, []common.Address, types.TxState, *big.Int) []*txmgr.Tx); ok {
+ if rf, ok := ret.Get(0).(func(context.Context, []common.Address, types.TxState, *big.Int) []*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]); ok {
r0 = rf(ctx, addresses, state, chainID)
} else {
if ret.Get(0) != nil {
- r0 = ret.Get(0).([]*txmgr.Tx)
+ r0 = ret.Get(0).([]*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee])
}
}
@@ -1806,12 +1804,12 @@ func (_c *EvmTxStore_FindTxsByStateAndFromAddresses_Call) Run(run func(ctx conte
return _c
}
-func (_c *EvmTxStore_FindTxsByStateAndFromAddresses_Call) Return(txs []*txmgr.Tx, err error) *EvmTxStore_FindTxsByStateAndFromAddresses_Call {
+func (_c *EvmTxStore_FindTxsByStateAndFromAddresses_Call) Return(txs []*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], err error) *EvmTxStore_FindTxsByStateAndFromAddresses_Call {
_c.Call.Return(txs, err)
return _c
}
-func (_c *EvmTxStore_FindTxsByStateAndFromAddresses_Call) RunAndReturn(run func(context.Context, []common.Address, types.TxState, *big.Int) ([]*txmgr.Tx, error)) *EvmTxStore_FindTxsByStateAndFromAddresses_Call {
+func (_c *EvmTxStore_FindTxsByStateAndFromAddresses_Call) RunAndReturn(run func(context.Context, []common.Address, types.TxState, *big.Int) ([]*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error)) *EvmTxStore_FindTxsByStateAndFromAddresses_Call {
_c.Call.Return(run)
return _c
}
@@ -2777,24 +2775,24 @@ func (_c *EvmTxStore_SetBroadcastBeforeBlockNum_Call) RunAndReturn(run func(cont
}
// Transactions provides a mock function with given fields: ctx, offset, limit
-func (_m *EvmTxStore) Transactions(ctx context.Context, offset int, limit int) ([]txmgr.Tx, int, error) {
+func (_m *EvmTxStore) Transactions(ctx context.Context, offset int, limit int) ([]types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], int, error) {
ret := _m.Called(ctx, offset, limit)
if len(ret) == 0 {
panic("no return value specified for Transactions")
}
- var r0 []txmgr.Tx
+ var r0 []types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]
var r1 int
var r2 error
- if rf, ok := ret.Get(0).(func(context.Context, int, int) ([]txmgr.Tx, int, error)); ok {
+ if rf, ok := ret.Get(0).(func(context.Context, int, int) ([]types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], int, error)); ok {
return rf(ctx, offset, limit)
}
- if rf, ok := ret.Get(0).(func(context.Context, int, int) []txmgr.Tx); ok {
+ if rf, ok := ret.Get(0).(func(context.Context, int, int) []types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]); ok {
r0 = rf(ctx, offset, limit)
} else {
if ret.Get(0) != nil {
- r0 = ret.Get(0).([]txmgr.Tx)
+ r0 = ret.Get(0).([]types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee])
}
}
@@ -2833,35 +2831,35 @@ func (_c *EvmTxStore_Transactions_Call) Run(run func(ctx context.Context, offset
return _c
}
-func (_c *EvmTxStore_Transactions_Call) Return(_a0 []txmgr.Tx, _a1 int, _a2 error) *EvmTxStore_Transactions_Call {
+func (_c *EvmTxStore_Transactions_Call) Return(_a0 []types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], _a1 int, _a2 error) *EvmTxStore_Transactions_Call {
_c.Call.Return(_a0, _a1, _a2)
return _c
}
-func (_c *EvmTxStore_Transactions_Call) RunAndReturn(run func(context.Context, int, int) ([]txmgr.Tx, int, error)) *EvmTxStore_Transactions_Call {
+func (_c *EvmTxStore_Transactions_Call) RunAndReturn(run func(context.Context, int, int) ([]types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], int, error)) *EvmTxStore_Transactions_Call {
_c.Call.Return(run)
return _c
}
// TransactionsWithAttempts provides a mock function with given fields: ctx, offset, limit
-func (_m *EvmTxStore) TransactionsWithAttempts(ctx context.Context, offset int, limit int) ([]txmgr.Tx, int, error) {
+func (_m *EvmTxStore) TransactionsWithAttempts(ctx context.Context, offset int, limit int) ([]types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], int, error) {
ret := _m.Called(ctx, offset, limit)
if len(ret) == 0 {
panic("no return value specified for TransactionsWithAttempts")
}
- var r0 []txmgr.Tx
+ var r0 []types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]
var r1 int
var r2 error
- if rf, ok := ret.Get(0).(func(context.Context, int, int) ([]txmgr.Tx, int, error)); ok {
+ if rf, ok := ret.Get(0).(func(context.Context, int, int) ([]types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], int, error)); ok {
return rf(ctx, offset, limit)
}
- if rf, ok := ret.Get(0).(func(context.Context, int, int) []txmgr.Tx); ok {
+ if rf, ok := ret.Get(0).(func(context.Context, int, int) []types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]); ok {
r0 = rf(ctx, offset, limit)
} else {
if ret.Get(0) != nil {
- r0 = ret.Get(0).([]txmgr.Tx)
+ r0 = ret.Get(0).([]types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee])
}
}
@@ -2900,35 +2898,35 @@ func (_c *EvmTxStore_TransactionsWithAttempts_Call) Run(run func(ctx context.Con
return _c
}
-func (_c *EvmTxStore_TransactionsWithAttempts_Call) Return(_a0 []txmgr.Tx, _a1 int, _a2 error) *EvmTxStore_TransactionsWithAttempts_Call {
+func (_c *EvmTxStore_TransactionsWithAttempts_Call) Return(_a0 []types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], _a1 int, _a2 error) *EvmTxStore_TransactionsWithAttempts_Call {
_c.Call.Return(_a0, _a1, _a2)
return _c
}
-func (_c *EvmTxStore_TransactionsWithAttempts_Call) RunAndReturn(run func(context.Context, int, int) ([]txmgr.Tx, int, error)) *EvmTxStore_TransactionsWithAttempts_Call {
+func (_c *EvmTxStore_TransactionsWithAttempts_Call) RunAndReturn(run func(context.Context, int, int) ([]types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], int, error)) *EvmTxStore_TransactionsWithAttempts_Call {
_c.Call.Return(run)
return _c
}
// TxAttempts provides a mock function with given fields: ctx, offset, limit
-func (_m *EvmTxStore) TxAttempts(ctx context.Context, offset int, limit int) ([]txmgr.TxAttempt, int, error) {
+func (_m *EvmTxStore) TxAttempts(ctx context.Context, offset int, limit int) ([]types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], int, error) {
ret := _m.Called(ctx, offset, limit)
if len(ret) == 0 {
panic("no return value specified for TxAttempts")
}
- var r0 []txmgr.TxAttempt
+ var r0 []types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]
var r1 int
var r2 error
- if rf, ok := ret.Get(0).(func(context.Context, int, int) ([]txmgr.TxAttempt, int, error)); ok {
+ if rf, ok := ret.Get(0).(func(context.Context, int, int) ([]types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], int, error)); ok {
return rf(ctx, offset, limit)
}
- if rf, ok := ret.Get(0).(func(context.Context, int, int) []txmgr.TxAttempt); ok {
+ if rf, ok := ret.Get(0).(func(context.Context, int, int) []types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]); ok {
r0 = rf(ctx, offset, limit)
} else {
if ret.Get(0) != nil {
- r0 = ret.Get(0).([]txmgr.TxAttempt)
+ r0 = ret.Get(0).([]types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee])
}
}
@@ -2967,12 +2965,12 @@ func (_c *EvmTxStore_TxAttempts_Call) Run(run func(ctx context.Context, offset i
return _c
}
-func (_c *EvmTxStore_TxAttempts_Call) Return(_a0 []txmgr.TxAttempt, _a1 int, _a2 error) *EvmTxStore_TxAttempts_Call {
+func (_c *EvmTxStore_TxAttempts_Call) Return(_a0 []types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], _a1 int, _a2 error) *EvmTxStore_TxAttempts_Call {
_c.Call.Return(_a0, _a1, _a2)
return _c
}
-func (_c *EvmTxStore_TxAttempts_Call) RunAndReturn(run func(context.Context, int, int) ([]txmgr.TxAttempt, int, error)) *EvmTxStore_TxAttempts_Call {
+func (_c *EvmTxStore_TxAttempts_Call) RunAndReturn(run func(context.Context, int, int) ([]types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], int, error)) *EvmTxStore_TxAttempts_Call {
_c.Call.Return(run)
return _c
}
diff --git a/core/chains/legacyevm/mocks/chain.go b/core/chains/legacyevm/mocks/chain.go
index 14a89027323..e642ca2c608 100644
--- a/core/chains/legacyevm/mocks/chain.go
+++ b/core/chains/legacyevm/mocks/chain.go
@@ -1,18 +1,22 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
import (
big "math/big"
+ common "github.com/ethereum/go-ethereum/common"
client "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client"
+
config "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config"
context "context"
+ evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types"
+
gas "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas"
- headtrackertypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types"
+ headtracker "github.com/smartcontractkit/chainlink/v2/common/headtracker"
log "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log"
@@ -24,7 +28,7 @@ import (
monitor "github.com/smartcontractkit/chainlink/v2/core/chains/evm/monitor"
- txmgr "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr"
+ txmgr "github.com/smartcontractkit/chainlink/v2/common/txmgr"
types "github.com/smartcontractkit/chainlink-common/pkg/types"
)
@@ -42,7 +46,7 @@ func (_m *Chain) EXPECT() *Chain_Expecter {
return &Chain_Expecter{mock: &_m.Mock}
}
-// BalanceMonitor provides a mock function with given fields:
+// BalanceMonitor provides a mock function with no fields
func (_m *Chain) BalanceMonitor() monitor.BalanceMonitor {
ret := _m.Called()
@@ -89,7 +93,7 @@ func (_c *Chain_BalanceMonitor_Call) RunAndReturn(run func() monitor.BalanceMoni
return _c
}
-// Client provides a mock function with given fields:
+// Client provides a mock function with no fields
func (_m *Chain) Client() client.Client {
ret := _m.Called()
@@ -136,7 +140,7 @@ func (_c *Chain_Client_Call) RunAndReturn(run func() client.Client) *Chain_Clien
return _c
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *Chain) Close() error {
ret := _m.Called()
@@ -181,7 +185,7 @@ func (_c *Chain_Close_Call) RunAndReturn(run func() error) *Chain_Close_Call {
return _c
}
-// Config provides a mock function with given fields:
+// Config provides a mock function with no fields
func (_m *Chain) Config() config.ChainScopedConfig {
ret := _m.Called()
@@ -228,7 +232,7 @@ func (_c *Chain_Config_Call) RunAndReturn(run func() config.ChainScopedConfig) *
return _c
}
-// GasEstimator provides a mock function with given fields:
+// GasEstimator provides a mock function with no fields
func (_m *Chain) GasEstimator() gas.EvmFeeEstimator {
ret := _m.Called()
@@ -331,20 +335,20 @@ func (_c *Chain_GetChainStatus_Call) RunAndReturn(run func(context.Context) (typ
return _c
}
-// HeadBroadcaster provides a mock function with given fields:
-func (_m *Chain) HeadBroadcaster() headtrackertypes.HeadBroadcaster {
+// HeadBroadcaster provides a mock function with no fields
+func (_m *Chain) HeadBroadcaster() headtracker.HeadBroadcaster[*evmtypes.Head, common.Hash] {
ret := _m.Called()
if len(ret) == 0 {
panic("no return value specified for HeadBroadcaster")
}
- var r0 headtrackertypes.HeadBroadcaster
- if rf, ok := ret.Get(0).(func() headtrackertypes.HeadBroadcaster); ok {
+ var r0 headtracker.HeadBroadcaster[*evmtypes.Head, common.Hash]
+ if rf, ok := ret.Get(0).(func() headtracker.HeadBroadcaster[*evmtypes.Head, common.Hash]); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
- r0 = ret.Get(0).(headtrackertypes.HeadBroadcaster)
+ r0 = ret.Get(0).(headtracker.HeadBroadcaster[*evmtypes.Head, common.Hash])
}
}
@@ -368,30 +372,30 @@ func (_c *Chain_HeadBroadcaster_Call) Run(run func()) *Chain_HeadBroadcaster_Cal
return _c
}
-func (_c *Chain_HeadBroadcaster_Call) Return(_a0 headtrackertypes.HeadBroadcaster) *Chain_HeadBroadcaster_Call {
+func (_c *Chain_HeadBroadcaster_Call) Return(_a0 headtracker.HeadBroadcaster[*evmtypes.Head, common.Hash]) *Chain_HeadBroadcaster_Call {
_c.Call.Return(_a0)
return _c
}
-func (_c *Chain_HeadBroadcaster_Call) RunAndReturn(run func() headtrackertypes.HeadBroadcaster) *Chain_HeadBroadcaster_Call {
+func (_c *Chain_HeadBroadcaster_Call) RunAndReturn(run func() headtracker.HeadBroadcaster[*evmtypes.Head, common.Hash]) *Chain_HeadBroadcaster_Call {
_c.Call.Return(run)
return _c
}
-// HeadTracker provides a mock function with given fields:
-func (_m *Chain) HeadTracker() headtrackertypes.HeadTracker {
+// HeadTracker provides a mock function with no fields
+func (_m *Chain) HeadTracker() headtracker.HeadTracker[*evmtypes.Head, common.Hash] {
ret := _m.Called()
if len(ret) == 0 {
panic("no return value specified for HeadTracker")
}
- var r0 headtrackertypes.HeadTracker
- if rf, ok := ret.Get(0).(func() headtrackertypes.HeadTracker); ok {
+ var r0 headtracker.HeadTracker[*evmtypes.Head, common.Hash]
+ if rf, ok := ret.Get(0).(func() headtracker.HeadTracker[*evmtypes.Head, common.Hash]); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
- r0 = ret.Get(0).(headtrackertypes.HeadTracker)
+ r0 = ret.Get(0).(headtracker.HeadTracker[*evmtypes.Head, common.Hash])
}
}
@@ -415,17 +419,17 @@ func (_c *Chain_HeadTracker_Call) Run(run func()) *Chain_HeadTracker_Call {
return _c
}
-func (_c *Chain_HeadTracker_Call) Return(_a0 headtrackertypes.HeadTracker) *Chain_HeadTracker_Call {
+func (_c *Chain_HeadTracker_Call) Return(_a0 headtracker.HeadTracker[*evmtypes.Head, common.Hash]) *Chain_HeadTracker_Call {
_c.Call.Return(_a0)
return _c
}
-func (_c *Chain_HeadTracker_Call) RunAndReturn(run func() headtrackertypes.HeadTracker) *Chain_HeadTracker_Call {
+func (_c *Chain_HeadTracker_Call) RunAndReturn(run func() headtracker.HeadTracker[*evmtypes.Head, common.Hash]) *Chain_HeadTracker_Call {
_c.Call.Return(run)
return _c
}
-// HealthReport provides a mock function with given fields:
+// HealthReport provides a mock function with no fields
func (_m *Chain) HealthReport() map[string]error {
ret := _m.Called()
@@ -472,7 +476,7 @@ func (_c *Chain_HealthReport_Call) RunAndReturn(run func() map[string]error) *Ch
return _c
}
-// ID provides a mock function with given fields:
+// ID provides a mock function with no fields
func (_m *Chain) ID() *big.Int {
ret := _m.Called()
@@ -649,7 +653,7 @@ func (_c *Chain_ListNodeStatuses_Call) RunAndReturn(run func(context.Context, in
return _c
}
-// LogBroadcaster provides a mock function with given fields:
+// LogBroadcaster provides a mock function with no fields
func (_m *Chain) LogBroadcaster() log.Broadcaster {
ret := _m.Called()
@@ -696,7 +700,7 @@ func (_c *Chain_LogBroadcaster_Call) RunAndReturn(run func() log.Broadcaster) *C
return _c
}
-// LogPoller provides a mock function with given fields:
+// LogPoller provides a mock function with no fields
func (_m *Chain) LogPoller() logpoller.LogPoller {
ret := _m.Called()
@@ -743,7 +747,7 @@ func (_c *Chain_LogPoller_Call) RunAndReturn(run func() logpoller.LogPoller) *Ch
return _c
}
-// Logger provides a mock function with given fields:
+// Logger provides a mock function with no fields
func (_m *Chain) Logger() logger.Logger {
ret := _m.Called()
@@ -790,7 +794,7 @@ func (_c *Chain_Logger_Call) RunAndReturn(run func() logger.Logger) *Chain_Logge
return _c
}
-// Name provides a mock function with given fields:
+// Name provides a mock function with no fields
func (_m *Chain) Name() string {
ret := _m.Called()
@@ -835,7 +839,7 @@ func (_c *Chain_Name_Call) RunAndReturn(run func() string) *Chain_Name_Call {
return _c
}
-// Ready provides a mock function with given fields:
+// Ready provides a mock function with no fields
func (_m *Chain) Ready() error {
ret := _m.Called()
@@ -976,20 +980,20 @@ func (_c *Chain_Transact_Call) RunAndReturn(run func(context.Context, string, st
return _c
}
-// TxManager provides a mock function with given fields:
-func (_m *Chain) TxManager() txmgr.TxManager {
+// TxManager provides a mock function with no fields
+func (_m *Chain) TxManager() txmgr.TxManager[*big.Int, *evmtypes.Head, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee] {
ret := _m.Called()
if len(ret) == 0 {
panic("no return value specified for TxManager")
}
- var r0 txmgr.TxManager
- if rf, ok := ret.Get(0).(func() txmgr.TxManager); ok {
+ var r0 txmgr.TxManager[*big.Int, *evmtypes.Head, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]
+ if rf, ok := ret.Get(0).(func() txmgr.TxManager[*big.Int, *evmtypes.Head, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
- r0 = ret.Get(0).(txmgr.TxManager)
+ r0 = ret.Get(0).(txmgr.TxManager[*big.Int, *evmtypes.Head, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee])
}
}
@@ -1013,12 +1017,12 @@ func (_c *Chain_TxManager_Call) Run(run func()) *Chain_TxManager_Call {
return _c
}
-func (_c *Chain_TxManager_Call) Return(_a0 txmgr.TxManager) *Chain_TxManager_Call {
+func (_c *Chain_TxManager_Call) Return(_a0 txmgr.TxManager[*big.Int, *evmtypes.Head, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]) *Chain_TxManager_Call {
_c.Call.Return(_a0)
return _c
}
-func (_c *Chain_TxManager_Call) RunAndReturn(run func() txmgr.TxManager) *Chain_TxManager_Call {
+func (_c *Chain_TxManager_Call) RunAndReturn(run func() txmgr.TxManager[*big.Int, *evmtypes.Head, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]) *Chain_TxManager_Call {
_c.Call.Return(run)
return _c
}
diff --git a/core/chains/legacyevm/mocks/legacy_chain_container.go b/core/chains/legacyevm/mocks/legacy_chain_container.go
index 0425a016014..b5ddfe4891f 100644
--- a/core/chains/legacyevm/mocks/legacy_chain_container.go
+++ b/core/chains/legacyevm/mocks/legacy_chain_container.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -22,7 +22,7 @@ func (_m *LegacyChainContainer) EXPECT() *LegacyChainContainer_Expecter {
return &LegacyChainContainer_Expecter{mock: &_m.Mock}
}
-// ChainNodeConfigs provides a mock function with given fields:
+// ChainNodeConfigs provides a mock function with no fields
func (_m *LegacyChainContainer) ChainNodeConfigs() types.Configs {
ret := _m.Called()
@@ -127,7 +127,7 @@ func (_c *LegacyChainContainer_Get_Call) RunAndReturn(run func(string) (legacyev
return _c
}
-// Len provides a mock function with given fields:
+// Len provides a mock function with no fields
func (_m *LegacyChainContainer) Len() int {
ret := _m.Called()
@@ -243,7 +243,7 @@ func (_c *LegacyChainContainer_List_Call) RunAndReturn(run func(...string) ([]le
return _c
}
-// Slice provides a mock function with given fields:
+// Slice provides a mock function with no fields
func (_m *LegacyChainContainer) Slice() []legacyevm.Chain {
ret := _m.Called()
diff --git a/core/cmd/mocks/prompter.go b/core/cmd/mocks/prompter.go
index 03b6dae04cb..aca41d1ed4a 100644
--- a/core/cmd/mocks/prompter.go
+++ b/core/cmd/mocks/prompter.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -17,7 +17,7 @@ func (_m *Prompter) EXPECT() *Prompter_Expecter {
return &Prompter_Expecter{mock: &_m.Mock}
}
-// IsTerminal provides a mock function with given fields:
+// IsTerminal provides a mock function with no fields
func (_m *Prompter) IsTerminal() bool {
ret := _m.Called()
diff --git a/core/config/mocks/telemetry_ingress.go b/core/config/mocks/telemetry_ingress.go
index 3e85cf7da09..e77a559cd7c 100644
--- a/core/config/mocks/telemetry_ingress.go
+++ b/core/config/mocks/telemetry_ingress.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -22,7 +22,7 @@ func (_m *TelemetryIngress) EXPECT() *TelemetryIngress_Expecter {
return &TelemetryIngress_Expecter{mock: &_m.Mock}
}
-// BufferSize provides a mock function with given fields:
+// BufferSize provides a mock function with no fields
func (_m *TelemetryIngress) BufferSize() uint {
ret := _m.Called()
@@ -67,7 +67,7 @@ func (_c *TelemetryIngress_BufferSize_Call) RunAndReturn(run func() uint) *Telem
return _c
}
-// Endpoints provides a mock function with given fields:
+// Endpoints provides a mock function with no fields
func (_m *TelemetryIngress) Endpoints() []config.TelemetryIngressEndpoint {
ret := _m.Called()
@@ -114,7 +114,7 @@ func (_c *TelemetryIngress_Endpoints_Call) RunAndReturn(run func() []config.Tele
return _c
}
-// Logging provides a mock function with given fields:
+// Logging provides a mock function with no fields
func (_m *TelemetryIngress) Logging() bool {
ret := _m.Called()
@@ -159,7 +159,7 @@ func (_c *TelemetryIngress_Logging_Call) RunAndReturn(run func() bool) *Telemetr
return _c
}
-// MaxBatchSize provides a mock function with given fields:
+// MaxBatchSize provides a mock function with no fields
func (_m *TelemetryIngress) MaxBatchSize() uint {
ret := _m.Called()
@@ -204,7 +204,7 @@ func (_c *TelemetryIngress_MaxBatchSize_Call) RunAndReturn(run func() uint) *Tel
return _c
}
-// SendInterval provides a mock function with given fields:
+// SendInterval provides a mock function with no fields
func (_m *TelemetryIngress) SendInterval() time.Duration {
ret := _m.Called()
@@ -249,7 +249,7 @@ func (_c *TelemetryIngress_SendInterval_Call) RunAndReturn(run func() time.Durat
return _c
}
-// SendTimeout provides a mock function with given fields:
+// SendTimeout provides a mock function with no fields
func (_m *TelemetryIngress) SendTimeout() time.Duration {
ret := _m.Called()
@@ -294,7 +294,7 @@ func (_c *TelemetryIngress_SendTimeout_Call) RunAndReturn(run func() time.Durati
return _c
}
-// UniConn provides a mock function with given fields:
+// UniConn provides a mock function with no fields
func (_m *TelemetryIngress) UniConn() bool {
ret := _m.Called()
@@ -339,7 +339,7 @@ func (_c *TelemetryIngress_UniConn_Call) RunAndReturn(run func() bool) *Telemetr
return _c
}
-// UseBatchSend provides a mock function with given fields:
+// UseBatchSend provides a mock function with no fields
func (_m *TelemetryIngress) UseBatchSend() bool {
ret := _m.Called()
diff --git a/core/config/mocks/telemetry_ingress_endpoint.go b/core/config/mocks/telemetry_ingress_endpoint.go
index 18b3b8a046b..b8fa4dfe485 100644
--- a/core/config/mocks/telemetry_ingress_endpoint.go
+++ b/core/config/mocks/telemetry_ingress_endpoint.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -21,7 +21,7 @@ func (_m *TelemetryIngressEndpoint) EXPECT() *TelemetryIngressEndpoint_Expecter
return &TelemetryIngressEndpoint_Expecter{mock: &_m.Mock}
}
-// ChainID provides a mock function with given fields:
+// ChainID provides a mock function with no fields
func (_m *TelemetryIngressEndpoint) ChainID() string {
ret := _m.Called()
@@ -66,7 +66,7 @@ func (_c *TelemetryIngressEndpoint_ChainID_Call) RunAndReturn(run func() string)
return _c
}
-// Network provides a mock function with given fields:
+// Network provides a mock function with no fields
func (_m *TelemetryIngressEndpoint) Network() string {
ret := _m.Called()
@@ -111,7 +111,7 @@ func (_c *TelemetryIngressEndpoint_Network_Call) RunAndReturn(run func() string)
return _c
}
-// ServerPubKey provides a mock function with given fields:
+// ServerPubKey provides a mock function with no fields
func (_m *TelemetryIngressEndpoint) ServerPubKey() string {
ret := _m.Called()
@@ -156,7 +156,7 @@ func (_c *TelemetryIngressEndpoint_ServerPubKey_Call) RunAndReturn(run func() st
return _c
}
-// URL provides a mock function with given fields:
+// URL provides a mock function with no fields
func (_m *TelemetryIngressEndpoint) URL() *url.URL {
ret := _m.Called()
diff --git a/core/gethwrappers/ccip/mocks/commit_store_interface.go b/core/gethwrappers/ccip/mocks/commit_store_interface.go
index 0cadfd036a8..f1ff6c610a1 100644
--- a/core/gethwrappers/ccip/mocks/commit_store_interface.go
+++ b/core/gethwrappers/ccip/mocks/commit_store_interface.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mock_contracts
@@ -89,7 +89,7 @@ func (_c *CommitStoreInterface_AcceptOwnership_Call) RunAndReturn(run func(*bind
return _c
}
-// Address provides a mock function with given fields:
+// Address provides a mock function with no fields
func (_m *CommitStoreInterface) Address() common.Address {
ret := _m.Called()
diff --git a/core/gethwrappers/ccip/mocks/evm2_evm_off_ramp_interface.go b/core/gethwrappers/ccip/mocks/evm2_evm_off_ramp_interface.go
index e9e635d8711..73ee46418c9 100644
--- a/core/gethwrappers/ccip/mocks/evm2_evm_off_ramp_interface.go
+++ b/core/gethwrappers/ccip/mocks/evm2_evm_off_ramp_interface.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mock_contracts
@@ -88,7 +88,7 @@ func (_c *EVM2EVMOffRampInterface_AcceptOwnership_Call) RunAndReturn(run func(*b
return _c
}
-// Address provides a mock function with given fields:
+// Address provides a mock function with no fields
func (_m *EVM2EVMOffRampInterface) Address() common.Address {
ret := _m.Called()
diff --git a/core/gethwrappers/ccip/mocks/evm2_evm_on_ramp_interface.go b/core/gethwrappers/ccip/mocks/evm2_evm_on_ramp_interface.go
index 3c65e2c5627..428c71ba393 100644
--- a/core/gethwrappers/ccip/mocks/evm2_evm_on_ramp_interface.go
+++ b/core/gethwrappers/ccip/mocks/evm2_evm_on_ramp_interface.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mock_contracts
@@ -90,7 +90,7 @@ func (_c *EVM2EVMOnRampInterface_AcceptOwnership_Call) RunAndReturn(run func(*bi
return _c
}
-// Address provides a mock function with given fields:
+// Address provides a mock function with no fields
func (_m *EVM2EVMOnRampInterface) Address() common.Address {
ret := _m.Called()
diff --git a/core/gethwrappers/ccip/mocks/fee_quoter_interface.go b/core/gethwrappers/ccip/mocks/fee_quoter_interface.go
index 8fadeb40e08..b254eae565b 100644
--- a/core/gethwrappers/ccip/mocks/fee_quoter_interface.go
+++ b/core/gethwrappers/ccip/mocks/fee_quoter_interface.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mock_contracts
@@ -90,7 +90,7 @@ func (_c *FeeQuoterInterface_AcceptOwnership_Call) RunAndReturn(run func(*bind.T
return _c
}
-// Address provides a mock function with given fields:
+// Address provides a mock function with no fields
func (_m *FeeQuoterInterface) Address() common.Address {
ret := _m.Called()
diff --git a/core/gethwrappers/ccip/mocks/link_token_interface.go b/core/gethwrappers/ccip/mocks/link_token_interface.go
index 59e9d7c8543..dcf0c1cadbc 100644
--- a/core/gethwrappers/ccip/mocks/link_token_interface.go
+++ b/core/gethwrappers/ccip/mocks/link_token_interface.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mock_contracts
@@ -32,7 +32,7 @@ func (_m *LinkTokenInterface) EXPECT() *LinkTokenInterface_Expecter {
return &LinkTokenInterface_Expecter{mock: &_m.Mock}
}
-// Address provides a mock function with given fields:
+// Address provides a mock function with no fields
func (_m *LinkTokenInterface) Address() common.Address {
ret := _m.Called()
diff --git a/core/gethwrappers/ccip/mocks/v1_2_0/evm2_evm_off_ramp_interface.go b/core/gethwrappers/ccip/mocks/v1_2_0/evm2_evm_off_ramp_interface.go
index a7de44f93ea..7a20a3bd068 100644
--- a/core/gethwrappers/ccip/mocks/v1_2_0/evm2_evm_off_ramp_interface.go
+++ b/core/gethwrappers/ccip/mocks/v1_2_0/evm2_evm_off_ramp_interface.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mock_contracts
@@ -90,7 +90,7 @@ func (_c *EVM2EVMOffRampInterface_AcceptOwnership_Call) RunAndReturn(run func(*b
return _c
}
-// Address provides a mock function with given fields:
+// Address provides a mock function with no fields
func (_m *EVM2EVMOffRampInterface) Address() common.Address {
ret := _m.Called()
diff --git a/core/gethwrappers/liquiditymanager/mocks/mock_arbitrum_gateway_router/arbitrum_gateway_router_interface.go b/core/gethwrappers/liquiditymanager/mocks/mock_arbitrum_gateway_router/arbitrum_gateway_router_interface.go
index eac55ff03a3..d7b88a8f27b 100644
--- a/core/gethwrappers/liquiditymanager/mocks/mock_arbitrum_gateway_router/arbitrum_gateway_router_interface.go
+++ b/core/gethwrappers/liquiditymanager/mocks/mock_arbitrum_gateway_router/arbitrum_gateway_router_interface.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mock_arbitrum_gateway_router
@@ -33,7 +33,7 @@ func (_m *ArbitrumGatewayRouterInterface) EXPECT() *ArbitrumGatewayRouterInterfa
return &ArbitrumGatewayRouterInterface_Expecter{mock: &_m.Mock}
}
-// Address provides a mock function with given fields:
+// Address provides a mock function with no fields
func (_m *ArbitrumGatewayRouterInterface) Address() common.Address {
ret := _m.Called()
diff --git a/core/gethwrappers/liquiditymanager/mocks/mock_arbitrum_inbox/arbitrum_inbox_interface.go b/core/gethwrappers/liquiditymanager/mocks/mock_arbitrum_inbox/arbitrum_inbox_interface.go
index 7e9939b8023..934613e32fc 100644
--- a/core/gethwrappers/liquiditymanager/mocks/mock_arbitrum_inbox/arbitrum_inbox_interface.go
+++ b/core/gethwrappers/liquiditymanager/mocks/mock_arbitrum_inbox/arbitrum_inbox_interface.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mock_arbitrum_inbox
@@ -33,7 +33,7 @@ func (_m *ArbitrumInboxInterface) EXPECT() *ArbitrumInboxInterface_Expecter {
return &ArbitrumInboxInterface_Expecter{mock: &_m.Mock}
}
-// Address provides a mock function with given fields:
+// Address provides a mock function with no fields
func (_m *ArbitrumInboxInterface) Address() common.Address {
ret := _m.Called()
diff --git a/core/gethwrappers/liquiditymanager/mocks/mock_arbitrum_l1_bridge_adapter/arbitrum_l1_bridge_adapter_interface.go b/core/gethwrappers/liquiditymanager/mocks/mock_arbitrum_l1_bridge_adapter/arbitrum_l1_bridge_adapter_interface.go
index 65ddb6e398d..d1728d94e92 100644
--- a/core/gethwrappers/liquiditymanager/mocks/mock_arbitrum_l1_bridge_adapter/arbitrum_l1_bridge_adapter_interface.go
+++ b/core/gethwrappers/liquiditymanager/mocks/mock_arbitrum_l1_bridge_adapter/arbitrum_l1_bridge_adapter_interface.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mock_arbitrum_l1_bridge_adapter
@@ -29,7 +29,7 @@ func (_m *ArbitrumL1BridgeAdapterInterface) EXPECT() *ArbitrumL1BridgeAdapterInt
return &ArbitrumL1BridgeAdapterInterface_Expecter{mock: &_m.Mock}
}
-// Address provides a mock function with given fields:
+// Address provides a mock function with no fields
func (_m *ArbitrumL1BridgeAdapterInterface) Address() common.Address {
ret := _m.Called()
diff --git a/core/gethwrappers/liquiditymanager/mocks/mock_arbitrum_l2_bridge_adapter/arbitrum_l2_bridge_adapter_interface.go b/core/gethwrappers/liquiditymanager/mocks/mock_arbitrum_l2_bridge_adapter/arbitrum_l2_bridge_adapter_interface.go
index 873879f7d06..dc176879aa4 100644
--- a/core/gethwrappers/liquiditymanager/mocks/mock_arbitrum_l2_bridge_adapter/arbitrum_l2_bridge_adapter_interface.go
+++ b/core/gethwrappers/liquiditymanager/mocks/mock_arbitrum_l2_bridge_adapter/arbitrum_l2_bridge_adapter_interface.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mock_arbitrum_l2_bridge_adapter
@@ -26,7 +26,7 @@ func (_m *ArbitrumL2BridgeAdapterInterface) EXPECT() *ArbitrumL2BridgeAdapterInt
return &ArbitrumL2BridgeAdapterInterface_Expecter{mock: &_m.Mock}
}
-// Address provides a mock function with given fields:
+// Address provides a mock function with no fields
func (_m *ArbitrumL2BridgeAdapterInterface) Address() common.Address {
ret := _m.Called()
diff --git a/core/gethwrappers/liquiditymanager/mocks/mock_arbitrum_rollup_core/arb_rollup_core_interface.go b/core/gethwrappers/liquiditymanager/mocks/mock_arbitrum_rollup_core/arb_rollup_core_interface.go
index a41ee15a254..8dead9cf8b1 100644
--- a/core/gethwrappers/liquiditymanager/mocks/mock_arbitrum_rollup_core/arb_rollup_core_interface.go
+++ b/core/gethwrappers/liquiditymanager/mocks/mock_arbitrum_rollup_core/arb_rollup_core_interface.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mock_arbitrum_rollup_core
@@ -33,7 +33,7 @@ func (_m *ArbRollupCoreInterface) EXPECT() *ArbRollupCoreInterface_Expecter {
return &ArbRollupCoreInterface_Expecter{mock: &_m.Mock}
}
-// Address provides a mock function with given fields:
+// Address provides a mock function with no fields
func (_m *ArbRollupCoreInterface) Address() common.Address {
ret := _m.Called()
diff --git a/core/gethwrappers/liquiditymanager/mocks/mock_arbsys/arb_sys_interface.go b/core/gethwrappers/liquiditymanager/mocks/mock_arbsys/arb_sys_interface.go
index d07eef5b579..6e1af68b03d 100644
--- a/core/gethwrappers/liquiditymanager/mocks/mock_arbsys/arb_sys_interface.go
+++ b/core/gethwrappers/liquiditymanager/mocks/mock_arbsys/arb_sys_interface.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mock_arbsys
@@ -33,7 +33,7 @@ func (_m *ArbSysInterface) EXPECT() *ArbSysInterface_Expecter {
return &ArbSysInterface_Expecter{mock: &_m.Mock}
}
-// Address provides a mock function with given fields:
+// Address provides a mock function with no fields
func (_m *ArbSysInterface) Address() common.Address {
ret := _m.Called()
diff --git a/core/gethwrappers/liquiditymanager/mocks/mock_l2_arbitrum_gateway/l2_arbitrum_gateway_interface.go b/core/gethwrappers/liquiditymanager/mocks/mock_l2_arbitrum_gateway/l2_arbitrum_gateway_interface.go
index 5e8f4037eaf..d1b85f5a26b 100644
--- a/core/gethwrappers/liquiditymanager/mocks/mock_l2_arbitrum_gateway/l2_arbitrum_gateway_interface.go
+++ b/core/gethwrappers/liquiditymanager/mocks/mock_l2_arbitrum_gateway/l2_arbitrum_gateway_interface.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mock_l2_arbitrum_gateway
@@ -32,7 +32,7 @@ func (_m *L2ArbitrumGatewayInterface) EXPECT() *L2ArbitrumGatewayInterface_Expec
return &L2ArbitrumGatewayInterface_Expecter{mock: &_m.Mock}
}
-// Address provides a mock function with given fields:
+// Address provides a mock function with no fields
func (_m *L2ArbitrumGatewayInterface) Address() common.Address {
ret := _m.Called()
diff --git a/core/gethwrappers/liquiditymanager/mocks/mock_l2_arbitrum_messenger/l2_arbitrum_messenger_interface.go b/core/gethwrappers/liquiditymanager/mocks/mock_l2_arbitrum_messenger/l2_arbitrum_messenger_interface.go
index 1ee3fb075c4..576c15dbeef 100644
--- a/core/gethwrappers/liquiditymanager/mocks/mock_l2_arbitrum_messenger/l2_arbitrum_messenger_interface.go
+++ b/core/gethwrappers/liquiditymanager/mocks/mock_l2_arbitrum_messenger/l2_arbitrum_messenger_interface.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mock_l2_arbitrum_messenger
@@ -32,7 +32,7 @@ func (_m *L2ArbitrumMessengerInterface) EXPECT() *L2ArbitrumMessengerInterface_E
return &L2ArbitrumMessengerInterface_Expecter{mock: &_m.Mock}
}
-// Address provides a mock function with given fields:
+// Address provides a mock function with no fields
func (_m *L2ArbitrumMessengerInterface) Address() common.Address {
ret := _m.Called()
diff --git a/core/gethwrappers/liquiditymanager/mocks/mock_node_interface/node_interface_interface.go b/core/gethwrappers/liquiditymanager/mocks/mock_node_interface/node_interface_interface.go
index 6950201b8c8..0f85d52cb23 100644
--- a/core/gethwrappers/liquiditymanager/mocks/mock_node_interface/node_interface_interface.go
+++ b/core/gethwrappers/liquiditymanager/mocks/mock_node_interface/node_interface_interface.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mock_node_interface
@@ -29,7 +29,7 @@ func (_m *NodeInterfaceInterface) EXPECT() *NodeInterfaceInterface_Expecter {
return &NodeInterfaceInterface_Expecter{mock: &_m.Mock}
}
-// Address provides a mock function with given fields:
+// Address provides a mock function with no fields
func (_m *NodeInterfaceInterface) Address() common.Address {
ret := _m.Called()
diff --git a/core/gethwrappers/liquiditymanager/mocks/mock_optimism_dispute_game_factory/optimism_dispute_game_factory_interface.go b/core/gethwrappers/liquiditymanager/mocks/mock_optimism_dispute_game_factory/optimism_dispute_game_factory_interface.go
index 1ffa9426638..0e47f57a1f3 100644
--- a/core/gethwrappers/liquiditymanager/mocks/mock_optimism_dispute_game_factory/optimism_dispute_game_factory_interface.go
+++ b/core/gethwrappers/liquiditymanager/mocks/mock_optimism_dispute_game_factory/optimism_dispute_game_factory_interface.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mock_optimism_dispute_game_factory
@@ -26,7 +26,7 @@ func (_m *OptimismDisputeGameFactoryInterface) EXPECT() *OptimismDisputeGameFact
return &OptimismDisputeGameFactoryInterface_Expecter{mock: &_m.Mock}
}
-// Address provides a mock function with given fields:
+// Address provides a mock function with no fields
func (_m *OptimismDisputeGameFactoryInterface) Address() common.Address {
ret := _m.Called()
diff --git a/core/gethwrappers/liquiditymanager/mocks/mock_optimism_l2_output_oracle/optimism_l2_output_oracle_interface.go b/core/gethwrappers/liquiditymanager/mocks/mock_optimism_l2_output_oracle/optimism_l2_output_oracle_interface.go
index 2736d2a5ed3..bffece06092 100644
--- a/core/gethwrappers/liquiditymanager/mocks/mock_optimism_l2_output_oracle/optimism_l2_output_oracle_interface.go
+++ b/core/gethwrappers/liquiditymanager/mocks/mock_optimism_l2_output_oracle/optimism_l2_output_oracle_interface.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mock_optimism_l2_output_oracle
@@ -26,7 +26,7 @@ func (_m *OptimismL2OutputOracleInterface) EXPECT() *OptimismL2OutputOracleInter
return &OptimismL2OutputOracleInterface_Expecter{mock: &_m.Mock}
}
-// Address provides a mock function with given fields:
+// Address provides a mock function with no fields
func (_m *OptimismL2OutputOracleInterface) Address() common.Address {
ret := _m.Called()
diff --git a/core/gethwrappers/liquiditymanager/mocks/mock_optimism_portal/optimism_portal_interface.go b/core/gethwrappers/liquiditymanager/mocks/mock_optimism_portal/optimism_portal_interface.go
index dcfc2758406..cdfd585df4d 100644
--- a/core/gethwrappers/liquiditymanager/mocks/mock_optimism_portal/optimism_portal_interface.go
+++ b/core/gethwrappers/liquiditymanager/mocks/mock_optimism_portal/optimism_portal_interface.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mock_optimism_portal
@@ -28,7 +28,7 @@ func (_m *OptimismPortalInterface) EXPECT() *OptimismPortalInterface_Expecter {
return &OptimismPortalInterface_Expecter{mock: &_m.Mock}
}
-// Address provides a mock function with given fields:
+// Address provides a mock function with no fields
func (_m *OptimismPortalInterface) Address() common.Address {
ret := _m.Called()
diff --git a/core/gethwrappers/liquiditymanager/mocks/mock_optimism_portal_2/optimism_portal2_interface.go b/core/gethwrappers/liquiditymanager/mocks/mock_optimism_portal_2/optimism_portal2_interface.go
index d693ff317bc..bb820a7f415 100644
--- a/core/gethwrappers/liquiditymanager/mocks/mock_optimism_portal_2/optimism_portal2_interface.go
+++ b/core/gethwrappers/liquiditymanager/mocks/mock_optimism_portal_2/optimism_portal2_interface.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mock_optimism_portal_2
@@ -22,7 +22,7 @@ func (_m *OptimismPortal2Interface) EXPECT() *OptimismPortal2Interface_Expecter
return &OptimismPortal2Interface_Expecter{mock: &_m.Mock}
}
-// Address provides a mock function with given fields:
+// Address provides a mock function with no fields
func (_m *OptimismPortal2Interface) Address() common.Address {
ret := _m.Called()
diff --git a/core/internal/mocks/application.go b/core/internal/mocks/application.go
index 69a8a60ef45..65dfbde7559 100644
--- a/core/internal/mocks/application.go
+++ b/core/internal/mocks/application.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -108,7 +108,7 @@ func (_c *Application_AddJobV2_Call) RunAndReturn(run func(context.Context, *job
return _c
}
-// AuthenticationProvider provides a mock function with given fields:
+// AuthenticationProvider provides a mock function with no fields
func (_m *Application) AuthenticationProvider() sessions.AuthenticationProvider {
ret := _m.Called()
@@ -155,7 +155,7 @@ func (_c *Application_AuthenticationProvider_Call) RunAndReturn(run func() sessi
return _c
}
-// BasicAdminUsersORM provides a mock function with given fields:
+// BasicAdminUsersORM provides a mock function with no fields
func (_m *Application) BasicAdminUsersORM() sessions.BasicAdminUsersORM {
ret := _m.Called()
@@ -202,7 +202,7 @@ func (_c *Application_BasicAdminUsersORM_Call) RunAndReturn(run func() sessions.
return _c
}
-// BridgeORM provides a mock function with given fields:
+// BridgeORM provides a mock function with no fields
func (_m *Application) BridgeORM() bridges.ORM {
ret := _m.Called()
@@ -344,7 +344,7 @@ func (_c *Application_DeleteLogPollerDataAfter_Call) RunAndReturn(run func(conte
return _c
}
-// EVMORM provides a mock function with given fields:
+// EVMORM provides a mock function with no fields
func (_m *Application) EVMORM() types.Configs {
ret := _m.Called()
@@ -450,7 +450,7 @@ func (_c *Application_FindLCA_Call) RunAndReturn(run func(context.Context, *big.
return _c
}
-// GetAuditLogger provides a mock function with given fields:
+// GetAuditLogger provides a mock function with no fields
func (_m *Application) GetAuditLogger() audit.AuditLogger {
ret := _m.Called()
@@ -497,7 +497,7 @@ func (_c *Application_GetAuditLogger_Call) RunAndReturn(run func() audit.AuditLo
return _c
}
-// GetConfig provides a mock function with given fields:
+// GetConfig provides a mock function with no fields
func (_m *Application) GetConfig() chainlink.GeneralConfig {
ret := _m.Called()
@@ -544,7 +544,7 @@ func (_c *Application_GetConfig_Call) RunAndReturn(run func() chainlink.GeneralC
return _c
}
-// GetDB provides a mock function with given fields:
+// GetDB provides a mock function with no fields
func (_m *Application) GetDB() sqlutil.DataSource {
ret := _m.Called()
@@ -591,7 +591,7 @@ func (_c *Application_GetDB_Call) RunAndReturn(run func() sqlutil.DataSource) *A
return _c
}
-// GetExternalInitiatorManager provides a mock function with given fields:
+// GetExternalInitiatorManager provides a mock function with no fields
func (_m *Application) GetExternalInitiatorManager() webhook.ExternalInitiatorManager {
ret := _m.Called()
@@ -638,7 +638,7 @@ func (_c *Application_GetExternalInitiatorManager_Call) RunAndReturn(run func()
return _c
}
-// GetFeedsService provides a mock function with given fields:
+// GetFeedsService provides a mock function with no fields
func (_m *Application) GetFeedsService() feeds.Service {
ret := _m.Called()
@@ -685,7 +685,7 @@ func (_c *Application_GetFeedsService_Call) RunAndReturn(run func() feeds.Servic
return _c
}
-// GetHealthChecker provides a mock function with given fields:
+// GetHealthChecker provides a mock function with no fields
func (_m *Application) GetHealthChecker() services.Checker {
ret := _m.Called()
@@ -732,7 +732,7 @@ func (_c *Application_GetHealthChecker_Call) RunAndReturn(run func() services.Ch
return _c
}
-// GetKeyStore provides a mock function with given fields:
+// GetKeyStore provides a mock function with no fields
func (_m *Application) GetKeyStore() keystore.Master {
ret := _m.Called()
@@ -779,7 +779,7 @@ func (_c *Application_GetKeyStore_Call) RunAndReturn(run func() keystore.Master)
return _c
}
-// GetLogger provides a mock function with given fields:
+// GetLogger provides a mock function with no fields
func (_m *Application) GetLogger() logger.SugaredLogger {
ret := _m.Called()
@@ -826,7 +826,7 @@ func (_c *Application_GetLogger_Call) RunAndReturn(run func() logger.SugaredLogg
return _c
}
-// GetLoopRegistrarConfig provides a mock function with given fields:
+// GetLoopRegistrarConfig provides a mock function with no fields
func (_m *Application) GetLoopRegistrarConfig() plugins.RegistrarConfig {
ret := _m.Called()
@@ -873,7 +873,7 @@ func (_c *Application_GetLoopRegistrarConfig_Call) RunAndReturn(run func() plugi
return _c
}
-// GetLoopRegistry provides a mock function with given fields:
+// GetLoopRegistry provides a mock function with no fields
func (_m *Application) GetLoopRegistry() *plugins.LoopRegistry {
ret := _m.Called()
@@ -920,7 +920,7 @@ func (_c *Application_GetLoopRegistry_Call) RunAndReturn(run func() *plugins.Loo
return _c
}
-// GetRelayers provides a mock function with given fields:
+// GetRelayers provides a mock function with no fields
func (_m *Application) GetRelayers() chainlink.RelayerChainInteroperators {
ret := _m.Called()
@@ -967,7 +967,7 @@ func (_c *Application_GetRelayers_Call) RunAndReturn(run func() chainlink.Relaye
return _c
}
-// GetWebAuthnConfiguration provides a mock function with given fields:
+// GetWebAuthnConfiguration provides a mock function with no fields
func (_m *Application) GetWebAuthnConfiguration() sessions.WebAuthnConfiguration {
ret := _m.Called()
@@ -1012,7 +1012,7 @@ func (_c *Application_GetWebAuthnConfiguration_Call) RunAndReturn(run func() ses
return _c
}
-// ID provides a mock function with given fields:
+// ID provides a mock function with no fields
func (_m *Application) ID() uuid.UUID {
ret := _m.Called()
@@ -1059,7 +1059,7 @@ func (_c *Application_ID_Call) RunAndReturn(run func() uuid.UUID) *Application_I
return _c
}
-// JobORM provides a mock function with given fields:
+// JobORM provides a mock function with no fields
func (_m *Application) JobORM() job.ORM {
ret := _m.Called()
@@ -1106,7 +1106,7 @@ func (_c *Application_JobORM_Call) RunAndReturn(run func() job.ORM) *Application
return _c
}
-// JobSpawner provides a mock function with given fields:
+// JobSpawner provides a mock function with no fields
func (_m *Application) JobSpawner() job.Spawner {
ret := _m.Called()
@@ -1153,7 +1153,7 @@ func (_c *Application_JobSpawner_Call) RunAndReturn(run func() job.Spawner) *App
return _c
}
-// PipelineORM provides a mock function with given fields:
+// PipelineORM provides a mock function with no fields
func (_m *Application) PipelineORM() pipeline.ORM {
ret := _m.Called()
@@ -1413,7 +1413,7 @@ func (_c *Application_RunWebhookJobV2_Call) RunAndReturn(run func(context.Contex
return _c
}
-// SecretGenerator provides a mock function with given fields:
+// SecretGenerator provides a mock function with no fields
func (_m *Application) SecretGenerator() chainlink.SecretGenerator {
ret := _m.Called()
@@ -1552,7 +1552,7 @@ func (_c *Application_Start_Call) RunAndReturn(run func(context.Context) error)
return _c
}
-// Stop provides a mock function with given fields:
+// Stop provides a mock function with no fields
func (_m *Application) Stop() error {
ret := _m.Called()
@@ -1597,7 +1597,7 @@ func (_c *Application_Stop_Call) RunAndReturn(run func() error) *Application_Sto
return _c
}
-// TxmStorageService provides a mock function with given fields:
+// TxmStorageService provides a mock function with no fields
func (_m *Application) TxmStorageService() txmgr.EvmTxStore {
ret := _m.Called()
@@ -1644,7 +1644,7 @@ func (_c *Application_TxmStorageService_Call) RunAndReturn(run func() txmgr.EvmT
return _c
}
-// WakeSessionReaper provides a mock function with given fields:
+// WakeSessionReaper provides a mock function with no fields
func (_m *Application) WakeSessionReaper() {
_m.Called()
}
@@ -1672,7 +1672,7 @@ func (_c *Application_WakeSessionReaper_Call) Return() *Application_WakeSessionR
}
func (_c *Application_WakeSessionReaper_Call) RunAndReturn(run func()) *Application_WakeSessionReaper_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
diff --git a/core/internal/mocks/flags.go b/core/internal/mocks/flags.go
index 538af708893..53e340b0262 100644
--- a/core/internal/mocks/flags.go
+++ b/core/internal/mocks/flags.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -147,7 +147,7 @@ func (_c *Flags_AddAccess_Call) RunAndReturn(run func(*bind.TransactOpts, common
return _c
}
-// Address provides a mock function with given fields:
+// Address provides a mock function with no fields
func (_m *Flags) Address() common.Address {
ret := _m.Called()
diff --git a/core/internal/mocks/flux_aggregator.go b/core/internal/mocks/flux_aggregator.go
index 9dd63173fb1..9360a22fba5 100644
--- a/core/internal/mocks/flux_aggregator.go
+++ b/core/internal/mocks/flux_aggregator.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -149,7 +149,7 @@ func (_c *FluxAggregator_AcceptOwnership_Call) RunAndReturn(run func(*bind.Trans
return _c
}
-// Address provides a mock function with given fields:
+// Address provides a mock function with no fields
func (_m *FluxAggregator) Address() common.Address {
ret := _m.Called()
diff --git a/core/logger/logger_mocks.go b/core/logger/logger_mocks.go
index 643f5ff141f..09b303e02d0 100644
--- a/core/logger/logger_mocks.go
+++ b/core/logger/logger_mocks.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package logger
@@ -58,7 +58,7 @@ func (_c *MockLogger_Critical_Call) Return() *MockLogger_Critical_Call {
}
func (_c *MockLogger_Critical_Call) RunAndReturn(run func(...interface{})) *MockLogger_Critical_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
@@ -102,7 +102,7 @@ func (_c *MockLogger_Criticalf_Call) Return() *MockLogger_Criticalf_Call {
}
func (_c *MockLogger_Criticalf_Call) RunAndReturn(run func(string, ...interface{})) *MockLogger_Criticalf_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
@@ -146,7 +146,7 @@ func (_c *MockLogger_Criticalw_Call) Return() *MockLogger_Criticalw_Call {
}
func (_c *MockLogger_Criticalw_Call) RunAndReturn(run func(string, ...interface{})) *MockLogger_Criticalw_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
@@ -188,7 +188,7 @@ func (_c *MockLogger_Debug_Call) Return() *MockLogger_Debug_Call {
}
func (_c *MockLogger_Debug_Call) RunAndReturn(run func(...interface{})) *MockLogger_Debug_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
@@ -232,7 +232,7 @@ func (_c *MockLogger_Debugf_Call) Return() *MockLogger_Debugf_Call {
}
func (_c *MockLogger_Debugf_Call) RunAndReturn(run func(string, ...interface{})) *MockLogger_Debugf_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
@@ -276,7 +276,7 @@ func (_c *MockLogger_Debugw_Call) Return() *MockLogger_Debugw_Call {
}
func (_c *MockLogger_Debugw_Call) RunAndReturn(run func(string, ...interface{})) *MockLogger_Debugw_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
@@ -318,7 +318,7 @@ func (_c *MockLogger_Error_Call) Return() *MockLogger_Error_Call {
}
func (_c *MockLogger_Error_Call) RunAndReturn(run func(...interface{})) *MockLogger_Error_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
@@ -362,7 +362,7 @@ func (_c *MockLogger_Errorf_Call) Return() *MockLogger_Errorf_Call {
}
func (_c *MockLogger_Errorf_Call) RunAndReturn(run func(string, ...interface{})) *MockLogger_Errorf_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
@@ -406,7 +406,7 @@ func (_c *MockLogger_Errorw_Call) Return() *MockLogger_Errorw_Call {
}
func (_c *MockLogger_Errorw_Call) RunAndReturn(run func(string, ...interface{})) *MockLogger_Errorw_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
@@ -448,7 +448,7 @@ func (_c *MockLogger_Fatal_Call) Return() *MockLogger_Fatal_Call {
}
func (_c *MockLogger_Fatal_Call) RunAndReturn(run func(...interface{})) *MockLogger_Fatal_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
@@ -492,7 +492,7 @@ func (_c *MockLogger_Fatalf_Call) Return() *MockLogger_Fatalf_Call {
}
func (_c *MockLogger_Fatalf_Call) RunAndReturn(run func(string, ...interface{})) *MockLogger_Fatalf_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
@@ -536,7 +536,7 @@ func (_c *MockLogger_Fatalw_Call) Return() *MockLogger_Fatalw_Call {
}
func (_c *MockLogger_Fatalw_Call) RunAndReturn(run func(string, ...interface{})) *MockLogger_Fatalw_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
@@ -626,7 +626,7 @@ func (_c *MockLogger_Info_Call) Return() *MockLogger_Info_Call {
}
func (_c *MockLogger_Info_Call) RunAndReturn(run func(...interface{})) *MockLogger_Info_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
@@ -670,7 +670,7 @@ func (_c *MockLogger_Infof_Call) Return() *MockLogger_Infof_Call {
}
func (_c *MockLogger_Infof_Call) RunAndReturn(run func(string, ...interface{})) *MockLogger_Infof_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
@@ -714,11 +714,11 @@ func (_c *MockLogger_Infow_Call) Return() *MockLogger_Infow_Call {
}
func (_c *MockLogger_Infow_Call) RunAndReturn(run func(string, ...interface{})) *MockLogger_Infow_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
-// Name provides a mock function with given fields:
+// Name provides a mock function with no fields
func (_m *MockLogger) Name() string {
ret := _m.Called()
@@ -849,7 +849,7 @@ func (_c *MockLogger_Panic_Call) Return() *MockLogger_Panic_Call {
}
func (_c *MockLogger_Panic_Call) RunAndReturn(run func(...interface{})) *MockLogger_Panic_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
@@ -893,7 +893,7 @@ func (_c *MockLogger_Panicf_Call) Return() *MockLogger_Panicf_Call {
}
func (_c *MockLogger_Panicf_Call) RunAndReturn(run func(string, ...interface{})) *MockLogger_Panicf_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
@@ -937,7 +937,7 @@ func (_c *MockLogger_Panicw_Call) Return() *MockLogger_Panicw_Call {
}
func (_c *MockLogger_Panicw_Call) RunAndReturn(run func(string, ...interface{})) *MockLogger_Panicw_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
@@ -970,7 +970,7 @@ func (_c *MockLogger_Recover_Call) Return() *MockLogger_Recover_Call {
}
func (_c *MockLogger_Recover_Call) RunAndReturn(run func(interface{})) *MockLogger_Recover_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
@@ -1003,11 +1003,11 @@ func (_c *MockLogger_SetLogLevel_Call) Return() *MockLogger_SetLogLevel_Call {
}
func (_c *MockLogger_SetLogLevel_Call) RunAndReturn(run func(zapcore.Level)) *MockLogger_SetLogLevel_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
-// Sync provides a mock function with given fields:
+// Sync provides a mock function with no fields
func (_m *MockLogger) Sync() error {
ret := _m.Called()
@@ -1090,7 +1090,7 @@ func (_c *MockLogger_Trace_Call) Return() *MockLogger_Trace_Call {
}
func (_c *MockLogger_Trace_Call) RunAndReturn(run func(...interface{})) *MockLogger_Trace_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
@@ -1134,7 +1134,7 @@ func (_c *MockLogger_Tracef_Call) Return() *MockLogger_Tracef_Call {
}
func (_c *MockLogger_Tracef_Call) RunAndReturn(run func(string, ...interface{})) *MockLogger_Tracef_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
@@ -1178,7 +1178,7 @@ func (_c *MockLogger_Tracew_Call) Return() *MockLogger_Tracew_Call {
}
func (_c *MockLogger_Tracew_Call) RunAndReturn(run func(string, ...interface{})) *MockLogger_Tracew_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
@@ -1220,7 +1220,7 @@ func (_c *MockLogger_Warn_Call) Return() *MockLogger_Warn_Call {
}
func (_c *MockLogger_Warn_Call) RunAndReturn(run func(...interface{})) *MockLogger_Warn_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
@@ -1264,7 +1264,7 @@ func (_c *MockLogger_Warnf_Call) Return() *MockLogger_Warnf_Call {
}
func (_c *MockLogger_Warnf_Call) RunAndReturn(run func(string, ...interface{})) *MockLogger_Warnf_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
@@ -1308,7 +1308,7 @@ func (_c *MockLogger_Warnw_Call) Return() *MockLogger_Warnw_Call {
}
func (_c *MockLogger_Warnw_Call) RunAndReturn(run func(string, ...interface{})) *MockLogger_Warnw_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
diff --git a/core/services/blockhashstore/mocks/bhs.go b/core/services/blockhashstore/mocks/bhs.go
index 782b22e05f8..d3032c4bb0d 100644
--- a/core/services/blockhashstore/mocks/bhs.go
+++ b/core/services/blockhashstore/mocks/bhs.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -80,7 +80,7 @@ func (_c *BHS_IsStored_Call) RunAndReturn(run func(context.Context, uint64) (boo
return _c
}
-// IsTrusted provides a mock function with given fields:
+// IsTrusted provides a mock function with no fields
func (_m *BHS) IsTrusted() bool {
ret := _m.Called()
diff --git a/core/services/blockhashstore/mocks/timer.go b/core/services/blockhashstore/mocks/timer.go
index 1fe06d98092..e6fa10fbd07 100644
--- a/core/services/blockhashstore/mocks/timer.go
+++ b/core/services/blockhashstore/mocks/timer.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
diff --git a/core/services/ccip/mocks/orm.go b/core/services/ccip/mocks/orm.go
index 8353cc0f28c..30c365b24f1 100644
--- a/core/services/ccip/mocks/orm.go
+++ b/core/services/ccip/mocks/orm.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
diff --git a/core/services/chainlink/mocks/general_config.go b/core/services/chainlink/mocks/general_config.go
index db1efb3d86f..738909538f3 100644
--- a/core/services/chainlink/mocks/general_config.go
+++ b/core/services/chainlink/mocks/general_config.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -36,7 +36,7 @@ func (_m *GeneralConfig) EXPECT() *GeneralConfig_Expecter {
return &GeneralConfig_Expecter{mock: &_m.Mock}
}
-// AppID provides a mock function with given fields:
+// AppID provides a mock function with no fields
func (_m *GeneralConfig) AppID() uuid.UUID {
ret := _m.Called()
@@ -83,7 +83,7 @@ func (_c *GeneralConfig_AppID_Call) RunAndReturn(run func() uuid.UUID) *GeneralC
return _c
}
-// AptosConfigs provides a mock function with given fields:
+// AptosConfigs provides a mock function with no fields
func (_m *GeneralConfig) AptosConfigs() chainlink.RawConfigs {
ret := _m.Called()
@@ -130,7 +130,7 @@ func (_c *GeneralConfig_AptosConfigs_Call) RunAndReturn(run func() chainlink.Raw
return _c
}
-// AptosEnabled provides a mock function with given fields:
+// AptosEnabled provides a mock function with no fields
func (_m *GeneralConfig) AptosEnabled() bool {
ret := _m.Called()
@@ -175,7 +175,7 @@ func (_c *GeneralConfig_AptosEnabled_Call) RunAndReturn(run func() bool) *Genera
return _c
}
-// AuditLogger provides a mock function with given fields:
+// AuditLogger provides a mock function with no fields
func (_m *GeneralConfig) AuditLogger() config.AuditLogger {
ret := _m.Called()
@@ -222,7 +222,7 @@ func (_c *GeneralConfig_AuditLogger_Call) RunAndReturn(run func() config.AuditLo
return _c
}
-// AutoPprof provides a mock function with given fields:
+// AutoPprof provides a mock function with no fields
func (_m *GeneralConfig) AutoPprof() config.AutoPprof {
ret := _m.Called()
@@ -269,7 +269,7 @@ func (_c *GeneralConfig_AutoPprof_Call) RunAndReturn(run func() config.AutoPprof
return _c
}
-// Capabilities provides a mock function with given fields:
+// Capabilities provides a mock function with no fields
func (_m *GeneralConfig) Capabilities() config.Capabilities {
ret := _m.Called()
@@ -316,7 +316,7 @@ func (_c *GeneralConfig_Capabilities_Call) RunAndReturn(run func() config.Capabi
return _c
}
-// ConfigTOML provides a mock function with given fields:
+// ConfigTOML provides a mock function with no fields
func (_m *GeneralConfig) ConfigTOML() (string, string) {
ret := _m.Called()
@@ -371,7 +371,7 @@ func (_c *GeneralConfig_ConfigTOML_Call) RunAndReturn(run func() (string, string
return _c
}
-// CosmosConfigs provides a mock function with given fields:
+// CosmosConfigs provides a mock function with no fields
func (_m *GeneralConfig) CosmosConfigs() cosmosconfig.TOMLConfigs {
ret := _m.Called()
@@ -418,7 +418,7 @@ func (_c *GeneralConfig_CosmosConfigs_Call) RunAndReturn(run func() cosmosconfig
return _c
}
-// CosmosEnabled provides a mock function with given fields:
+// CosmosEnabled provides a mock function with no fields
func (_m *GeneralConfig) CosmosEnabled() bool {
ret := _m.Called()
@@ -463,7 +463,7 @@ func (_c *GeneralConfig_CosmosEnabled_Call) RunAndReturn(run func() bool) *Gener
return _c
}
-// Database provides a mock function with given fields:
+// Database provides a mock function with no fields
func (_m *GeneralConfig) Database() config.Database {
ret := _m.Called()
@@ -510,7 +510,7 @@ func (_c *GeneralConfig_Database_Call) RunAndReturn(run func() config.Database)
return _c
}
-// EVMConfigs provides a mock function with given fields:
+// EVMConfigs provides a mock function with no fields
func (_m *GeneralConfig) EVMConfigs() toml.EVMConfigs {
ret := _m.Called()
@@ -557,7 +557,7 @@ func (_c *GeneralConfig_EVMConfigs_Call) RunAndReturn(run func() toml.EVMConfigs
return _c
}
-// EVMEnabled provides a mock function with given fields:
+// EVMEnabled provides a mock function with no fields
func (_m *GeneralConfig) EVMEnabled() bool {
ret := _m.Called()
@@ -602,7 +602,7 @@ func (_c *GeneralConfig_EVMEnabled_Call) RunAndReturn(run func() bool) *GeneralC
return _c
}
-// EVMRPCEnabled provides a mock function with given fields:
+// EVMRPCEnabled provides a mock function with no fields
func (_m *GeneralConfig) EVMRPCEnabled() bool {
ret := _m.Called()
@@ -647,7 +647,7 @@ func (_c *GeneralConfig_EVMRPCEnabled_Call) RunAndReturn(run func() bool) *Gener
return _c
}
-// Feature provides a mock function with given fields:
+// Feature provides a mock function with no fields
func (_m *GeneralConfig) Feature() config.Feature {
ret := _m.Called()
@@ -694,7 +694,7 @@ func (_c *GeneralConfig_Feature_Call) RunAndReturn(run func() config.Feature) *G
return _c
}
-// FluxMonitor provides a mock function with given fields:
+// FluxMonitor provides a mock function with no fields
func (_m *GeneralConfig) FluxMonitor() config.FluxMonitor {
ret := _m.Called()
@@ -741,7 +741,7 @@ func (_c *GeneralConfig_FluxMonitor_Call) RunAndReturn(run func() config.FluxMon
return _c
}
-// Insecure provides a mock function with given fields:
+// Insecure provides a mock function with no fields
func (_m *GeneralConfig) Insecure() config.Insecure {
ret := _m.Called()
@@ -788,7 +788,7 @@ func (_c *GeneralConfig_Insecure_Call) RunAndReturn(run func() config.Insecure)
return _c
}
-// InsecureFastScrypt provides a mock function with given fields:
+// InsecureFastScrypt provides a mock function with no fields
func (_m *GeneralConfig) InsecureFastScrypt() bool {
ret := _m.Called()
@@ -833,7 +833,7 @@ func (_c *GeneralConfig_InsecureFastScrypt_Call) RunAndReturn(run func() bool) *
return _c
}
-// JobPipeline provides a mock function with given fields:
+// JobPipeline provides a mock function with no fields
func (_m *GeneralConfig) JobPipeline() config.JobPipeline {
ret := _m.Called()
@@ -880,7 +880,7 @@ func (_c *GeneralConfig_JobPipeline_Call) RunAndReturn(run func() config.JobPipe
return _c
}
-// Keeper provides a mock function with given fields:
+// Keeper provides a mock function with no fields
func (_m *GeneralConfig) Keeper() config.Keeper {
ret := _m.Called()
@@ -927,7 +927,7 @@ func (_c *GeneralConfig_Keeper_Call) RunAndReturn(run func() config.Keeper) *Gen
return _c
}
-// Log provides a mock function with given fields:
+// Log provides a mock function with no fields
func (_m *GeneralConfig) Log() config.Log {
ret := _m.Called()
@@ -1004,11 +1004,11 @@ func (_c *GeneralConfig_LogConfiguration_Call) Return() *GeneralConfig_LogConfig
}
func (_c *GeneralConfig_LogConfiguration_Call) RunAndReturn(run func(config.LogfFn, config.LogfFn)) *GeneralConfig_LogConfiguration_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
-// Mercury provides a mock function with given fields:
+// Mercury provides a mock function with no fields
func (_m *GeneralConfig) Mercury() config.Mercury {
ret := _m.Called()
@@ -1055,7 +1055,7 @@ func (_c *GeneralConfig_Mercury_Call) RunAndReturn(run func() config.Mercury) *G
return _c
}
-// OCR provides a mock function with given fields:
+// OCR provides a mock function with no fields
func (_m *GeneralConfig) OCR() config.OCR {
ret := _m.Called()
@@ -1102,7 +1102,7 @@ func (_c *GeneralConfig_OCR_Call) RunAndReturn(run func() config.OCR) *GeneralCo
return _c
}
-// OCR2 provides a mock function with given fields:
+// OCR2 provides a mock function with no fields
func (_m *GeneralConfig) OCR2() config.OCR2 {
ret := _m.Called()
@@ -1149,7 +1149,7 @@ func (_c *GeneralConfig_OCR2_Call) RunAndReturn(run func() config.OCR2) *General
return _c
}
-// P2P provides a mock function with given fields:
+// P2P provides a mock function with no fields
func (_m *GeneralConfig) P2P() config.P2P {
ret := _m.Called()
@@ -1196,7 +1196,7 @@ func (_c *GeneralConfig_P2P_Call) RunAndReturn(run func() config.P2P) *GeneralCo
return _c
}
-// Password provides a mock function with given fields:
+// Password provides a mock function with no fields
func (_m *GeneralConfig) Password() config.Password {
ret := _m.Called()
@@ -1243,7 +1243,7 @@ func (_c *GeneralConfig_Password_Call) RunAndReturn(run func() config.Password)
return _c
}
-// Prometheus provides a mock function with given fields:
+// Prometheus provides a mock function with no fields
func (_m *GeneralConfig) Prometheus() config.Prometheus {
ret := _m.Called()
@@ -1290,7 +1290,7 @@ func (_c *GeneralConfig_Prometheus_Call) RunAndReturn(run func() config.Promethe
return _c
}
-// Pyroscope provides a mock function with given fields:
+// Pyroscope provides a mock function with no fields
func (_m *GeneralConfig) Pyroscope() config.Pyroscope {
ret := _m.Called()
@@ -1337,7 +1337,7 @@ func (_c *GeneralConfig_Pyroscope_Call) RunAndReturn(run func() config.Pyroscope
return _c
}
-// RootDir provides a mock function with given fields:
+// RootDir provides a mock function with no fields
func (_m *GeneralConfig) RootDir() string {
ret := _m.Called()
@@ -1382,7 +1382,7 @@ func (_c *GeneralConfig_RootDir_Call) RunAndReturn(run func() string) *GeneralCo
return _c
}
-// Sentry provides a mock function with given fields:
+// Sentry provides a mock function with no fields
func (_m *GeneralConfig) Sentry() config.Sentry {
ret := _m.Called()
@@ -1504,7 +1504,7 @@ func (_c *GeneralConfig_SetLogSQL_Call) Return() *GeneralConfig_SetLogSQL_Call {
}
func (_c *GeneralConfig_SetLogSQL_Call) RunAndReturn(run func(bool)) *GeneralConfig_SetLogSQL_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
@@ -1538,11 +1538,11 @@ func (_c *GeneralConfig_SetPasswords_Call) Return() *GeneralConfig_SetPasswords_
}
func (_c *GeneralConfig_SetPasswords_Call) RunAndReturn(run func(*string, *string)) *GeneralConfig_SetPasswords_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
-// ShutdownGracePeriod provides a mock function with given fields:
+// ShutdownGracePeriod provides a mock function with no fields
func (_m *GeneralConfig) ShutdownGracePeriod() time.Duration {
ret := _m.Called()
@@ -1587,7 +1587,7 @@ func (_c *GeneralConfig_ShutdownGracePeriod_Call) RunAndReturn(run func() time.D
return _c
}
-// SolanaConfigs provides a mock function with given fields:
+// SolanaConfigs provides a mock function with no fields
func (_m *GeneralConfig) SolanaConfigs() solanaconfig.TOMLConfigs {
ret := _m.Called()
@@ -1634,7 +1634,7 @@ func (_c *GeneralConfig_SolanaConfigs_Call) RunAndReturn(run func() solanaconfig
return _c
}
-// SolanaEnabled provides a mock function with given fields:
+// SolanaEnabled provides a mock function with no fields
func (_m *GeneralConfig) SolanaEnabled() bool {
ret := _m.Called()
@@ -1679,7 +1679,7 @@ func (_c *GeneralConfig_SolanaEnabled_Call) RunAndReturn(run func() bool) *Gener
return _c
}
-// StarkNetEnabled provides a mock function with given fields:
+// StarkNetEnabled provides a mock function with no fields
func (_m *GeneralConfig) StarkNetEnabled() bool {
ret := _m.Called()
@@ -1724,7 +1724,7 @@ func (_c *GeneralConfig_StarkNetEnabled_Call) RunAndReturn(run func() bool) *Gen
return _c
}
-// StarknetConfigs provides a mock function with given fields:
+// StarknetConfigs provides a mock function with no fields
func (_m *GeneralConfig) StarknetConfigs() chainlinkconfig.TOMLConfigs {
ret := _m.Called()
@@ -1771,7 +1771,7 @@ func (_c *GeneralConfig_StarknetConfigs_Call) RunAndReturn(run func() chainlinkc
return _c
}
-// Telemetry provides a mock function with given fields:
+// Telemetry provides a mock function with no fields
func (_m *GeneralConfig) Telemetry() config.Telemetry {
ret := _m.Called()
@@ -1818,7 +1818,7 @@ func (_c *GeneralConfig_Telemetry_Call) RunAndReturn(run func() config.Telemetry
return _c
}
-// TelemetryIngress provides a mock function with given fields:
+// TelemetryIngress provides a mock function with no fields
func (_m *GeneralConfig) TelemetryIngress() config.TelemetryIngress {
ret := _m.Called()
@@ -1865,7 +1865,7 @@ func (_c *GeneralConfig_TelemetryIngress_Call) RunAndReturn(run func() config.Te
return _c
}
-// Threshold provides a mock function with given fields:
+// Threshold provides a mock function with no fields
func (_m *GeneralConfig) Threshold() config.Threshold {
ret := _m.Called()
@@ -1912,7 +1912,7 @@ func (_c *GeneralConfig_Threshold_Call) RunAndReturn(run func() config.Threshold
return _c
}
-// Tracing provides a mock function with given fields:
+// Tracing provides a mock function with no fields
func (_m *GeneralConfig) Tracing() config.Tracing {
ret := _m.Called()
@@ -1959,7 +1959,7 @@ func (_c *GeneralConfig_Tracing_Call) RunAndReturn(run func() config.Tracing) *G
return _c
}
-// Validate provides a mock function with given fields:
+// Validate provides a mock function with no fields
func (_m *GeneralConfig) Validate() error {
ret := _m.Called()
@@ -2004,7 +2004,7 @@ func (_c *GeneralConfig_Validate_Call) RunAndReturn(run func() error) *GeneralCo
return _c
}
-// ValidateDB provides a mock function with given fields:
+// ValidateDB provides a mock function with no fields
func (_m *GeneralConfig) ValidateDB() error {
ret := _m.Called()
@@ -2049,7 +2049,7 @@ func (_c *GeneralConfig_ValidateDB_Call) RunAndReturn(run func() error) *General
return _c
}
-// WebServer provides a mock function with given fields:
+// WebServer provides a mock function with no fields
func (_m *GeneralConfig) WebServer() config.WebServer {
ret := _m.Called()
diff --git a/core/services/feeds/mocks/connections_manager.go b/core/services/feeds/mocks/connections_manager.go
index 2c7b71bd05c..20d68315297 100644
--- a/core/services/feeds/mocks/connections_manager.go
+++ b/core/services/feeds/mocks/connections_manager.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -22,7 +22,7 @@ func (_m *ConnectionsManager) EXPECT() *ConnectionsManager_Expecter {
return &ConnectionsManager_Expecter{mock: &_m.Mock}
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *ConnectionsManager) Close() {
_m.Called()
}
@@ -50,7 +50,7 @@ func (_c *ConnectionsManager_Close_Call) Return() *ConnectionsManager_Close_Call
}
func (_c *ConnectionsManager_Close_Call) RunAndReturn(run func()) *ConnectionsManager_Close_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
@@ -83,7 +83,7 @@ func (_c *ConnectionsManager_Connect_Call) Return() *ConnectionsManager_Connect_
}
func (_c *ConnectionsManager_Connect_Call) RunAndReturn(run func(feeds.ConnectOpts)) *ConnectionsManager_Connect_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
diff --git a/core/services/feeds/mocks/feeds_manager_client.go b/core/services/feeds/mocks/feeds_manager_client.go
index b07362b7787..13ef5ed4c76 100644
--- a/core/services/feeds/mocks/feeds_manager_client.go
+++ b/core/services/feeds/mocks/feeds_manager_client.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
diff --git a/core/services/feeds/mocks/orm.go b/core/services/feeds/mocks/orm.go
index b4f5c47e51f..b6e00148682 100644
--- a/core/services/feeds/mocks/orm.go
+++ b/core/services/feeds/mocks/orm.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
diff --git a/core/services/feeds/mocks/service.go b/core/services/feeds/mocks/service.go
index 8ae42534f58..9d63e706c29 100644
--- a/core/services/feeds/mocks/service.go
+++ b/core/services/feeds/mocks/service.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -117,7 +117,7 @@ func (_c *Service_CancelSpec_Call) RunAndReturn(run func(context.Context, int64)
return _c
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *Service) Close() error {
ret := _m.Called()
@@ -1436,7 +1436,7 @@ func (_c *Service_Unsafe_SetConnectionsManager_Call) Return() *Service_Unsafe_Se
}
func (_c *Service_Unsafe_SetConnectionsManager_Call) RunAndReturn(run func(feeds.ConnectionsManager)) *Service_Unsafe_SetConnectionsManager_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
diff --git a/core/services/fluxmonitorv2/mocks/contract_submitter.go b/core/services/fluxmonitorv2/mocks/contract_submitter.go
index 79143fdbfdd..79fda08d29e 100644
--- a/core/services/fluxmonitorv2/mocks/contract_submitter.go
+++ b/core/services/fluxmonitorv2/mocks/contract_submitter.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
diff --git a/core/services/fluxmonitorv2/mocks/flags.go b/core/services/fluxmonitorv2/mocks/flags.go
index f37acea31c8..f1b098f7de6 100644
--- a/core/services/fluxmonitorv2/mocks/flags.go
+++ b/core/services/fluxmonitorv2/mocks/flags.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -25,7 +25,7 @@ func (_m *Flags) EXPECT() *Flags_Expecter {
return &Flags_Expecter{mock: &_m.Mock}
}
-// Address provides a mock function with given fields:
+// Address provides a mock function with no fields
func (_m *Flags) Address() common.Address {
ret := _m.Called()
@@ -72,7 +72,7 @@ func (_c *Flags_Address_Call) RunAndReturn(run func() common.Address) *Flags_Add
return _c
}
-// ContractExists provides a mock function with given fields:
+// ContractExists provides a mock function with no fields
func (_m *Flags) ContractExists() bool {
ret := _m.Called()
diff --git a/core/services/fluxmonitorv2/mocks/key_store_interface.go b/core/services/fluxmonitorv2/mocks/key_store_interface.go
index c147c7b619e..67a28f97e9b 100644
--- a/core/services/fluxmonitorv2/mocks/key_store_interface.go
+++ b/core/services/fluxmonitorv2/mocks/key_store_interface.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
diff --git a/core/services/fluxmonitorv2/mocks/orm.go b/core/services/fluxmonitorv2/mocks/orm.go
index 6ccd5f40ebe..24f516dcdc2 100644
--- a/core/services/fluxmonitorv2/mocks/orm.go
+++ b/core/services/fluxmonitorv2/mocks/orm.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
diff --git a/core/services/functions/mocks/bridge_accessor.go b/core/services/functions/mocks/bridge_accessor.go
index 59f6f8f3db9..6672c55bee9 100644
--- a/core/services/functions/mocks/bridge_accessor.go
+++ b/core/services/functions/mocks/bridge_accessor.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
diff --git a/core/services/functions/mocks/external_adapter_client.go b/core/services/functions/mocks/external_adapter_client.go
index a7d76c44a35..661ed076e4a 100644
--- a/core/services/functions/mocks/external_adapter_client.go
+++ b/core/services/functions/mocks/external_adapter_client.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
diff --git a/core/services/functions/mocks/functions_listener.go b/core/services/functions/mocks/functions_listener.go
index 58acb7981e3..52011a70167 100644
--- a/core/services/functions/mocks/functions_listener.go
+++ b/core/services/functions/mocks/functions_listener.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -22,7 +22,7 @@ func (_m *FunctionsListener) EXPECT() *FunctionsListener_Expecter {
return &FunctionsListener_Expecter{mock: &_m.Mock}
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *FunctionsListener) Close() error {
ret := _m.Called()
diff --git a/core/services/functions/mocks/offchain_transmitter.go b/core/services/functions/mocks/offchain_transmitter.go
index 9b61f9072e5..91ca230be64 100644
--- a/core/services/functions/mocks/offchain_transmitter.go
+++ b/core/services/functions/mocks/offchain_transmitter.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -22,7 +22,7 @@ func (_m *OffchainTransmitter) EXPECT() *OffchainTransmitter_Expecter {
return &OffchainTransmitter_Expecter{mock: &_m.Mock}
}
-// ReportChannel provides a mock function with given fields:
+// ReportChannel provides a mock function with no fields
func (_m *OffchainTransmitter) ReportChannel() chan *functions.OffchainResponse {
ret := _m.Called()
diff --git a/core/services/functions/mocks/orm.go b/core/services/functions/mocks/orm.go
index 22e6368ac1f..4e7f9d393cb 100644
--- a/core/services/functions/mocks/orm.go
+++ b/core/services/functions/mocks/orm.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
diff --git a/core/services/gateway/connector/mocks/gateway_connector.go b/core/services/gateway/connector/mocks/gateway_connector.go
index ba5c2213b5f..51dcafe7091 100644
--- a/core/services/gateway/connector/mocks/gateway_connector.go
+++ b/core/services/gateway/connector/mocks/gateway_connector.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -179,7 +179,7 @@ func (_c *GatewayConnector_ChallengeResponse_Call) RunAndReturn(run func(*url.UR
return _c
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *GatewayConnector) Close() error {
ret := _m.Called()
@@ -224,7 +224,7 @@ func (_c *GatewayConnector_Close_Call) RunAndReturn(run func() error) *GatewayCo
return _c
}
-// DonID provides a mock function with given fields:
+// DonID provides a mock function with no fields
func (_m *GatewayConnector) DonID() string {
ret := _m.Called()
@@ -269,7 +269,7 @@ func (_c *GatewayConnector_DonID_Call) RunAndReturn(run func() string) *GatewayC
return _c
}
-// GatewayIDs provides a mock function with given fields:
+// GatewayIDs provides a mock function with no fields
func (_m *GatewayConnector) GatewayIDs() []string {
ret := _m.Called()
@@ -316,7 +316,7 @@ func (_c *GatewayConnector_GatewayIDs_Call) RunAndReturn(run func() []string) *G
return _c
}
-// HealthReport provides a mock function with given fields:
+// HealthReport provides a mock function with no fields
func (_m *GatewayConnector) HealthReport() map[string]error {
ret := _m.Called()
@@ -363,7 +363,7 @@ func (_c *GatewayConnector_HealthReport_Call) RunAndReturn(run func() map[string
return _c
}
-// Name provides a mock function with given fields:
+// Name provides a mock function with no fields
func (_m *GatewayConnector) Name() string {
ret := _m.Called()
@@ -466,7 +466,7 @@ func (_c *GatewayConnector_NewAuthHeader_Call) RunAndReturn(run func(*url.URL) (
return _c
}
-// Ready provides a mock function with given fields:
+// Ready provides a mock function with no fields
func (_m *GatewayConnector) Ready() error {
ret := _m.Called()
diff --git a/core/services/gateway/connector/mocks/gateway_connector_handler.go b/core/services/gateway/connector/mocks/gateway_connector_handler.go
index 6f21cb9dd2f..424f608a9b5 100644
--- a/core/services/gateway/connector/mocks/gateway_connector_handler.go
+++ b/core/services/gateway/connector/mocks/gateway_connector_handler.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -23,7 +23,7 @@ func (_m *GatewayConnectorHandler) EXPECT() *GatewayConnectorHandler_Expecter {
return &GatewayConnectorHandler_Expecter{mock: &_m.Mock}
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *GatewayConnectorHandler) Close() error {
ret := _m.Called()
@@ -99,7 +99,7 @@ func (_c *GatewayConnectorHandler_HandleGatewayMessage_Call) Return() *GatewayCo
}
func (_c *GatewayConnectorHandler_HandleGatewayMessage_Call) RunAndReturn(run func(context.Context, string, *api.Message)) *GatewayConnectorHandler_HandleGatewayMessage_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
diff --git a/core/services/gateway/connector/mocks/signer.go b/core/services/gateway/connector/mocks/signer.go
index 0f240b87e33..9b2aa0fd97a 100644
--- a/core/services/gateway/connector/mocks/signer.go
+++ b/core/services/gateway/connector/mocks/signer.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
diff --git a/core/services/gateway/handlers/functions/allowlist/mocks/onchain_allowlist.go b/core/services/gateway/handlers/functions/allowlist/mocks/onchain_allowlist.go
index 78fb7d4ec4f..8340413718e 100644
--- a/core/services/gateway/handlers/functions/allowlist/mocks/onchain_allowlist.go
+++ b/core/services/gateway/handlers/functions/allowlist/mocks/onchain_allowlist.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -69,7 +69,7 @@ func (_c *OnchainAllowlist_Allow_Call) RunAndReturn(run func(common.Address) boo
return _c
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *OnchainAllowlist) Close() error {
ret := _m.Called()
diff --git a/core/services/gateway/handlers/functions/allowlist/mocks/orm.go b/core/services/gateway/handlers/functions/allowlist/mocks/orm.go
index 8a3bddfda78..261e967c820 100644
--- a/core/services/gateway/handlers/functions/allowlist/mocks/orm.go
+++ b/core/services/gateway/handlers/functions/allowlist/mocks/orm.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
diff --git a/core/services/gateway/handlers/functions/subscriptions/mocks/onchain_subscriptions.go b/core/services/gateway/handlers/functions/subscriptions/mocks/onchain_subscriptions.go
index 48f0c6cdadb..7312f2a6743 100644
--- a/core/services/gateway/handlers/functions/subscriptions/mocks/onchain_subscriptions.go
+++ b/core/services/gateway/handlers/functions/subscriptions/mocks/onchain_subscriptions.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -24,7 +24,7 @@ func (_m *OnchainSubscriptions) EXPECT() *OnchainSubscriptions_Expecter {
return &OnchainSubscriptions_Expecter{mock: &_m.Mock}
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *OnchainSubscriptions) Close() error {
ret := _m.Called()
diff --git a/core/services/gateway/handlers/functions/subscriptions/mocks/orm.go b/core/services/gateway/handlers/functions/subscriptions/mocks/orm.go
index 118c8d317a1..c093bd1523e 100644
--- a/core/services/gateway/handlers/functions/subscriptions/mocks/orm.go
+++ b/core/services/gateway/handlers/functions/subscriptions/mocks/orm.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
diff --git a/core/services/gateway/handlers/mocks/don.go b/core/services/gateway/handlers/mocks/don.go
index 7e28a1c5e97..93ab52c2473 100644
--- a/core/services/gateway/handlers/mocks/don.go
+++ b/core/services/gateway/handlers/mocks/don.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
diff --git a/core/services/gateway/handlers/mocks/handler.go b/core/services/gateway/handlers/mocks/handler.go
index 3f129a9ca83..96f3faad15c 100644
--- a/core/services/gateway/handlers/mocks/handler.go
+++ b/core/services/gateway/handlers/mocks/handler.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -25,7 +25,7 @@ func (_m *Handler) EXPECT() *Handler_Expecter {
return &Handler_Expecter{mock: &_m.Mock}
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *Handler) Close() error {
ret := _m.Called()
diff --git a/core/services/gateway/network/mocks/connection_acceptor.go b/core/services/gateway/network/mocks/connection_acceptor.go
index 4c2aaaabb18..48bcf9884a6 100644
--- a/core/services/gateway/network/mocks/connection_acceptor.go
+++ b/core/services/gateway/network/mocks/connection_acceptor.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -50,7 +50,7 @@ func (_c *ConnectionAcceptor_AbortHandshake_Call) Return() *ConnectionAcceptor_A
}
func (_c *ConnectionAcceptor_AbortHandshake_Call) RunAndReturn(run func(string)) *ConnectionAcceptor_AbortHandshake_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
diff --git a/core/services/gateway/network/mocks/connection_initiator.go b/core/services/gateway/network/mocks/connection_initiator.go
index 2c18ad59cb5..5bf10d01aa9 100644
--- a/core/services/gateway/network/mocks/connection_initiator.go
+++ b/core/services/gateway/network/mocks/connection_initiator.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
diff --git a/core/services/gateway/network/mocks/http_client.go b/core/services/gateway/network/mocks/http_client.go
index 2702cbd83fd..29bf4348036 100644
--- a/core/services/gateway/network/mocks/http_client.go
+++ b/core/services/gateway/network/mocks/http_client.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
diff --git a/core/services/gateway/network/mocks/http_request_handler.go b/core/services/gateway/network/mocks/http_request_handler.go
index 39b3c71be52..2756bddb3db 100644
--- a/core/services/gateway/network/mocks/http_request_handler.go
+++ b/core/services/gateway/network/mocks/http_request_handler.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
diff --git a/core/services/gateway/network/mocks/http_server.go b/core/services/gateway/network/mocks/http_server.go
index 0e29525bfc3..ae51ffaa512 100644
--- a/core/services/gateway/network/mocks/http_server.go
+++ b/core/services/gateway/network/mocks/http_server.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -22,7 +22,7 @@ func (_m *HttpServer) EXPECT() *HttpServer_Expecter {
return &HttpServer_Expecter{mock: &_m.Mock}
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *HttpServer) Close() error {
ret := _m.Called()
@@ -67,7 +67,7 @@ func (_c *HttpServer_Close_Call) RunAndReturn(run func() error) *HttpServer_Clos
return _c
}
-// GetPort provides a mock function with given fields:
+// GetPort provides a mock function with no fields
func (_m *HttpServer) GetPort() int {
ret := _m.Called()
@@ -141,7 +141,7 @@ func (_c *HttpServer_SetHTTPRequestHandler_Call) Return() *HttpServer_SetHTTPReq
}
func (_c *HttpServer_SetHTTPRequestHandler_Call) RunAndReturn(run func(network.HTTPRequestHandler)) *HttpServer_SetHTTPRequestHandler_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
diff --git a/core/services/gateway/network/mocks/web_socket_server.go b/core/services/gateway/network/mocks/web_socket_server.go
index 96e8a8ee22d..34e478533ee 100644
--- a/core/services/gateway/network/mocks/web_socket_server.go
+++ b/core/services/gateway/network/mocks/web_socket_server.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -21,7 +21,7 @@ func (_m *WebSocketServer) EXPECT() *WebSocketServer_Expecter {
return &WebSocketServer_Expecter{mock: &_m.Mock}
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *WebSocketServer) Close() error {
ret := _m.Called()
@@ -66,7 +66,7 @@ func (_c *WebSocketServer_Close_Call) RunAndReturn(run func() error) *WebSocketS
return _c
}
-// GetPort provides a mock function with given fields:
+// GetPort provides a mock function with no fields
func (_m *WebSocketServer) GetPort() int {
ret := _m.Called()
diff --git a/core/services/headreporter/head_reporter_mock.go b/core/services/headreporter/head_reporter_mock.go
index ad9923fd179..1636aec4631 100644
--- a/core/services/headreporter/head_reporter_mock.go
+++ b/core/services/headreporter/head_reporter_mock.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package headreporter
diff --git a/core/services/headreporter/prometheus_backend_mock.go b/core/services/headreporter/prometheus_backend_mock.go
index ebcc88b9722..031b5d4c937 100644
--- a/core/services/headreporter/prometheus_backend_mock.go
+++ b/core/services/headreporter/prometheus_backend_mock.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package headreporter
@@ -51,7 +51,7 @@ func (_c *MockPrometheusBackend_SetMaxUnconfirmedAge_Call) Return() *MockPrometh
}
func (_c *MockPrometheusBackend_SetMaxUnconfirmedAge_Call) RunAndReturn(run func(*big.Int, float64)) *MockPrometheusBackend_SetMaxUnconfirmedAge_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
@@ -85,7 +85,7 @@ func (_c *MockPrometheusBackend_SetMaxUnconfirmedBlocks_Call) Return() *MockProm
}
func (_c *MockPrometheusBackend_SetMaxUnconfirmedBlocks_Call) RunAndReturn(run func(*big.Int, int64)) *MockPrometheusBackend_SetMaxUnconfirmedBlocks_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
@@ -118,7 +118,7 @@ func (_c *MockPrometheusBackend_SetPipelineRunsQueued_Call) Return() *MockPromet
}
func (_c *MockPrometheusBackend_SetPipelineRunsQueued_Call) RunAndReturn(run func(int)) *MockPrometheusBackend_SetPipelineRunsQueued_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
@@ -151,7 +151,7 @@ func (_c *MockPrometheusBackend_SetPipelineTaskRunsQueued_Call) Return() *MockPr
}
func (_c *MockPrometheusBackend_SetPipelineTaskRunsQueued_Call) RunAndReturn(run func(int)) *MockPrometheusBackend_SetPipelineTaskRunsQueued_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
@@ -185,7 +185,7 @@ func (_c *MockPrometheusBackend_SetUnconfirmedTransactions_Call) Return() *MockP
}
func (_c *MockPrometheusBackend_SetUnconfirmedTransactions_Call) RunAndReturn(run func(*big.Int, int64)) *MockPrometheusBackend_SetUnconfirmedTransactions_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
diff --git a/core/services/job/mocks/kv_store.go b/core/services/job/mocks/kv_store.go
index 5a606b12cca..7fe62ded59e 100644
--- a/core/services/job/mocks/kv_store.go
+++ b/core/services/job/mocks/kv_store.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
diff --git a/core/services/job/mocks/orm.go b/core/services/job/mocks/orm.go
index 96513866f37..170dfa25c58 100644
--- a/core/services/job/mocks/orm.go
+++ b/core/services/job/mocks/orm.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -81,7 +81,7 @@ func (_c *ORM_AssertBridgesExist_Call) RunAndReturn(run func(context.Context, pi
return _c
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *ORM) Close() error {
ret := _m.Called()
@@ -230,7 +230,7 @@ func (_c *ORM_CreateJob_Call) RunAndReturn(run func(context.Context, *job.Job) e
return _c
}
-// DataSource provides a mock function with given fields:
+// DataSource provides a mock function with no fields
func (_m *ORM) DataSource() sqlutil.DataSource {
ret := _m.Called()
@@ -1609,7 +1609,7 @@ func (_c *ORM_TryRecordError_Call) Return() *ORM_TryRecordError_Call {
}
func (_c *ORM_TryRecordError_Call) RunAndReturn(run func(context.Context, int32, string)) *ORM_TryRecordError_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
diff --git a/core/services/job/mocks/service_ctx.go b/core/services/job/mocks/service_ctx.go
index d7ac02bfd0e..8f1ba3596c1 100644
--- a/core/services/job/mocks/service_ctx.go
+++ b/core/services/job/mocks/service_ctx.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -21,7 +21,7 @@ func (_m *ServiceCtx) EXPECT() *ServiceCtx_Expecter {
return &ServiceCtx_Expecter{mock: &_m.Mock}
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *ServiceCtx) Close() error {
ret := _m.Called()
diff --git a/core/services/job/mocks/spawner.go b/core/services/job/mocks/spawner.go
index aa40522f358..a8039643bd5 100644
--- a/core/services/job/mocks/spawner.go
+++ b/core/services/job/mocks/spawner.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -24,7 +24,7 @@ func (_m *Spawner) EXPECT() *Spawner_Expecter {
return &Spawner_Expecter{mock: &_m.Mock}
}
-// ActiveJobs provides a mock function with given fields:
+// ActiveJobs provides a mock function with no fields
func (_m *Spawner) ActiveJobs() map[int32]job.Job {
ret := _m.Called()
@@ -71,7 +71,7 @@ func (_c *Spawner_ActiveJobs_Call) RunAndReturn(run func() map[int32]job.Job) *S
return _c
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *Spawner) Close() error {
ret := _m.Called()
@@ -212,7 +212,7 @@ func (_c *Spawner_DeleteJob_Call) RunAndReturn(run func(context.Context, sqlutil
return _c
}
-// HealthReport provides a mock function with given fields:
+// HealthReport provides a mock function with no fields
func (_m *Spawner) HealthReport() map[string]error {
ret := _m.Called()
@@ -259,7 +259,7 @@ func (_c *Spawner_HealthReport_Call) RunAndReturn(run func() map[string]error) *
return _c
}
-// Name provides a mock function with given fields:
+// Name provides a mock function with no fields
func (_m *Spawner) Name() string {
ret := _m.Called()
@@ -304,7 +304,7 @@ func (_c *Spawner_Name_Call) RunAndReturn(run func() string) *Spawner_Name_Call
return _c
}
-// Ready provides a mock function with given fields:
+// Ready provides a mock function with no fields
func (_m *Spawner) Ready() error {
ret := _m.Called()
diff --git a/core/services/keystore/mocks/aptos.go b/core/services/keystore/mocks/aptos.go
index 9c9124aeeac..98cb90457b7 100644
--- a/core/services/keystore/mocks/aptos.go
+++ b/core/services/keystore/mocks/aptos.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -344,7 +344,7 @@ func (_c *Aptos_Get_Call) RunAndReturn(run func(string) (aptoskey.Key, error)) *
return _c
}
-// GetAll provides a mock function with given fields:
+// GetAll provides a mock function with no fields
func (_m *Aptos) GetAll() ([]aptoskey.Key, error) {
ret := _m.Called()
diff --git a/core/services/keystore/mocks/cosmos.go b/core/services/keystore/mocks/cosmos.go
index 74690bf6889..ef127191285 100644
--- a/core/services/keystore/mocks/cosmos.go
+++ b/core/services/keystore/mocks/cosmos.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -344,7 +344,7 @@ func (_c *Cosmos_Get_Call) RunAndReturn(run func(string) (cosmoskey.Key, error))
return _c
}
-// GetAll provides a mock function with given fields:
+// GetAll provides a mock function with no fields
func (_m *Cosmos) GetAll() ([]cosmoskey.Key, error) {
ret := _m.Called()
diff --git a/core/services/keystore/mocks/csa.go b/core/services/keystore/mocks/csa.go
index 5ce17c0beff..73a4c048806 100644
--- a/core/services/keystore/mocks/csa.go
+++ b/core/services/keystore/mocks/csa.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -344,7 +344,7 @@ func (_c *CSA_Get_Call) RunAndReturn(run func(string) (csakey.KeyV2, error)) *CS
return _c
}
-// GetAll provides a mock function with given fields:
+// GetAll provides a mock function with no fields
func (_m *CSA) GetAll() ([]csakey.KeyV2, error) {
ret := _m.Called()
diff --git a/core/services/keystore/mocks/eth.go b/core/services/keystore/mocks/eth.go
index 4f2486464eb..7ed960663f7 100644
--- a/core/services/keystore/mocks/eth.go
+++ b/core/services/keystore/mocks/eth.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -1293,7 +1293,7 @@ func (_c *Eth_XXXTestingOnlyAdd_Call) Return() *Eth_XXXTestingOnlyAdd_Call {
}
func (_c *Eth_XXXTestingOnlyAdd_Call) RunAndReturn(run func(context.Context, ethkey.KeyV2)) *Eth_XXXTestingOnlyAdd_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
@@ -1327,7 +1327,7 @@ func (_c *Eth_XXXTestingOnlySetState_Call) Return() *Eth_XXXTestingOnlySetState_
}
func (_c *Eth_XXXTestingOnlySetState_Call) RunAndReturn(run func(context.Context, ethkey.State)) *Eth_XXXTestingOnlySetState_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
diff --git a/core/services/keystore/mocks/master.go b/core/services/keystore/mocks/master.go
index 7c86001bc54..01b85a542d8 100644
--- a/core/services/keystore/mocks/master.go
+++ b/core/services/keystore/mocks/master.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -22,7 +22,7 @@ func (_m *Master) EXPECT() *Master_Expecter {
return &Master_Expecter{mock: &_m.Mock}
}
-// Aptos provides a mock function with given fields:
+// Aptos provides a mock function with no fields
func (_m *Master) Aptos() keystore.Aptos {
ret := _m.Called()
@@ -69,7 +69,7 @@ func (_c *Master_Aptos_Call) RunAndReturn(run func() keystore.Aptos) *Master_Apt
return _c
}
-// CSA provides a mock function with given fields:
+// CSA provides a mock function with no fields
func (_m *Master) CSA() keystore.CSA {
ret := _m.Called()
@@ -116,7 +116,7 @@ func (_c *Master_CSA_Call) RunAndReturn(run func() keystore.CSA) *Master_CSA_Cal
return _c
}
-// Cosmos provides a mock function with given fields:
+// Cosmos provides a mock function with no fields
func (_m *Master) Cosmos() keystore.Cosmos {
ret := _m.Called()
@@ -163,7 +163,7 @@ func (_c *Master_Cosmos_Call) RunAndReturn(run func() keystore.Cosmos) *Master_C
return _c
}
-// Eth provides a mock function with given fields:
+// Eth provides a mock function with no fields
func (_m *Master) Eth() keystore.Eth {
ret := _m.Called()
@@ -266,7 +266,7 @@ func (_c *Master_IsEmpty_Call) RunAndReturn(run func(context.Context) (bool, err
return _c
}
-// OCR provides a mock function with given fields:
+// OCR provides a mock function with no fields
func (_m *Master) OCR() keystore.OCR {
ret := _m.Called()
@@ -313,7 +313,7 @@ func (_c *Master_OCR_Call) RunAndReturn(run func() keystore.OCR) *Master_OCR_Cal
return _c
}
-// OCR2 provides a mock function with given fields:
+// OCR2 provides a mock function with no fields
func (_m *Master) OCR2() keystore.OCR2 {
ret := _m.Called()
@@ -360,7 +360,7 @@ func (_c *Master_OCR2_Call) RunAndReturn(run func() keystore.OCR2) *Master_OCR2_
return _c
}
-// P2P provides a mock function with given fields:
+// P2P provides a mock function with no fields
func (_m *Master) P2P() keystore.P2P {
ret := _m.Called()
@@ -407,7 +407,7 @@ func (_c *Master_P2P_Call) RunAndReturn(run func() keystore.P2P) *Master_P2P_Cal
return _c
}
-// Solana provides a mock function with given fields:
+// Solana provides a mock function with no fields
func (_m *Master) Solana() keystore.Solana {
ret := _m.Called()
@@ -454,7 +454,7 @@ func (_c *Master_Solana_Call) RunAndReturn(run func() keystore.Solana) *Master_S
return _c
}
-// StarkNet provides a mock function with given fields:
+// StarkNet provides a mock function with no fields
func (_m *Master) StarkNet() keystore.StarkNet {
ret := _m.Called()
@@ -548,7 +548,7 @@ func (_c *Master_Unlock_Call) RunAndReturn(run func(context.Context, string) err
return _c
}
-// VRF provides a mock function with given fields:
+// VRF provides a mock function with no fields
func (_m *Master) VRF() keystore.VRF {
ret := _m.Called()
@@ -595,7 +595,7 @@ func (_c *Master_VRF_Call) RunAndReturn(run func() keystore.VRF) *Master_VRF_Cal
return _c
}
-// Workflow provides a mock function with given fields:
+// Workflow provides a mock function with no fields
func (_m *Master) Workflow() keystore.Workflow {
ret := _m.Called()
diff --git a/core/services/keystore/mocks/ocr.go b/core/services/keystore/mocks/ocr.go
index d3bb15aec86..bb0ecae9bda 100644
--- a/core/services/keystore/mocks/ocr.go
+++ b/core/services/keystore/mocks/ocr.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -344,7 +344,7 @@ func (_c *OCR_Get_Call) RunAndReturn(run func(string) (ocrkey.KeyV2, error)) *OC
return _c
}
-// GetAll provides a mock function with given fields:
+// GetAll provides a mock function with no fields
func (_m *OCR) GetAll() ([]ocrkey.KeyV2, error) {
ret := _m.Called()
diff --git a/core/services/keystore/mocks/ocr2.go b/core/services/keystore/mocks/ocr2.go
index 6ff7d2f6b58..3497fa7ac4d 100644
--- a/core/services/keystore/mocks/ocr2.go
+++ b/core/services/keystore/mocks/ocr2.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -356,7 +356,7 @@ func (_c *OCR2_Get_Call) RunAndReturn(run func(string) (ocr2key.KeyBundle, error
return _c
}
-// GetAll provides a mock function with given fields:
+// GetAll provides a mock function with no fields
func (_m *OCR2) GetAll() ([]ocr2key.KeyBundle, error) {
ret := _m.Called()
diff --git a/core/services/keystore/mocks/p2p.go b/core/services/keystore/mocks/p2p.go
index 3dead948b14..917f620edd3 100644
--- a/core/services/keystore/mocks/p2p.go
+++ b/core/services/keystore/mocks/p2p.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -344,7 +344,7 @@ func (_c *P2P_Get_Call) RunAndReturn(run func(p2pkey.PeerID) (p2pkey.KeyV2, erro
return _c
}
-// GetAll provides a mock function with given fields:
+// GetAll provides a mock function with no fields
func (_m *P2P) GetAll() ([]p2pkey.KeyV2, error) {
ret := _m.Called()
diff --git a/core/services/keystore/mocks/solana.go b/core/services/keystore/mocks/solana.go
index d55e85436df..3478b83bbe7 100644
--- a/core/services/keystore/mocks/solana.go
+++ b/core/services/keystore/mocks/solana.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -344,7 +344,7 @@ func (_c *Solana_Get_Call) RunAndReturn(run func(string) (solkey.Key, error)) *S
return _c
}
-// GetAll provides a mock function with given fields:
+// GetAll provides a mock function with no fields
func (_m *Solana) GetAll() ([]solkey.Key, error) {
ret := _m.Called()
diff --git a/core/services/keystore/mocks/starknet.go b/core/services/keystore/mocks/starknet.go
index 720d16c9b2f..405408409b7 100644
--- a/core/services/keystore/mocks/starknet.go
+++ b/core/services/keystore/mocks/starknet.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -344,7 +344,7 @@ func (_c *StarkNet_Get_Call) RunAndReturn(run func(string) (starkkey.Key, error)
return _c
}
-// GetAll provides a mock function with given fields:
+// GetAll provides a mock function with no fields
func (_m *StarkNet) GetAll() ([]starkkey.Key, error) {
ret := _m.Called()
diff --git a/core/services/keystore/mocks/vrf.go b/core/services/keystore/mocks/vrf.go
index dff36e8a3e9..e1705daa64a 100644
--- a/core/services/keystore/mocks/vrf.go
+++ b/core/services/keystore/mocks/vrf.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -356,7 +356,7 @@ func (_c *VRF_Get_Call) RunAndReturn(run func(string) (vrfkey.KeyV2, error)) *VR
return _c
}
-// GetAll provides a mock function with given fields:
+// GetAll provides a mock function with no fields
func (_m *VRF) GetAll() ([]vrfkey.KeyV2, error) {
ret := _m.Called()
diff --git a/core/services/keystore/mocks/workflow.go b/core/services/keystore/mocks/workflow.go
index f19045cecc4..91929e13768 100644
--- a/core/services/keystore/mocks/workflow.go
+++ b/core/services/keystore/mocks/workflow.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -344,7 +344,7 @@ func (_c *Workflow_Get_Call) RunAndReturn(run func(string) (workflowkey.Key, err
return _c
}
-// GetAll provides a mock function with given fields:
+// GetAll provides a mock function with no fields
func (_m *Workflow) GetAll() ([]workflowkey.Key, error) {
ret := _m.Called()
diff --git a/core/services/mocks/checker.go b/core/services/mocks/checker.go
index 24b5315254c..cda33d50ad0 100644
--- a/core/services/mocks/checker.go
+++ b/core/services/mocks/checker.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -20,7 +20,7 @@ func (_m *Checker) EXPECT() *Checker_Expecter {
return &Checker_Expecter{mock: &_m.Mock}
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *Checker) Close() error {
ret := _m.Called()
@@ -65,7 +65,7 @@ func (_c *Checker_Close_Call) RunAndReturn(run func() error) *Checker_Close_Call
return _c
}
-// IsHealthy provides a mock function with given fields:
+// IsHealthy provides a mock function with no fields
func (_m *Checker) IsHealthy() (bool, map[string]error) {
ret := _m.Called()
@@ -122,7 +122,7 @@ func (_c *Checker_IsHealthy_Call) RunAndReturn(run func() (bool, map[string]erro
return _c
}
-// IsReady provides a mock function with given fields:
+// IsReady provides a mock function with no fields
func (_m *Checker) IsReady() (bool, map[string]error) {
ret := _m.Called()
@@ -225,7 +225,7 @@ func (_c *Checker_Register_Call) RunAndReturn(run func(pkgservices.HealthReporte
return _c
}
-// Start provides a mock function with given fields:
+// Start provides a mock function with no fields
func (_m *Checker) Start() error {
ret := _m.Called()
diff --git a/core/services/ocr/mocks/ocr_contract_tracker_db.go b/core/services/ocr/mocks/ocr_contract_tracker_db.go
index fc29c50f1ea..ea95cbd83f5 100644
--- a/core/services/ocr/mocks/ocr_contract_tracker_db.go
+++ b/core/services/ocr/mocks/ocr_contract_tracker_db.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
diff --git a/core/services/ocr2/plugins/ccip/internal/cache/mocks/chain_health_mock.go b/core/services/ocr2/plugins/ccip/internal/cache/mocks/chain_health_mock.go
index 316f38f8ffc..5f9d9dcccac 100644
--- a/core/services/ocr2/plugins/ccip/internal/cache/mocks/chain_health_mock.go
+++ b/core/services/ocr2/plugins/ccip/internal/cache/mocks/chain_health_mock.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -21,7 +21,7 @@ func (_m *ChainHealthcheck) EXPECT() *ChainHealthcheck_Expecter {
return &ChainHealthcheck_Expecter{mock: &_m.Mock}
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *ChainHealthcheck) Close() error {
ret := _m.Called()
diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/batchreader/mocks/token_pool_batched_reader_mock.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/batchreader/mocks/token_pool_batched_reader_mock.go
index 5302ae0b54f..9bda468b31d 100644
--- a/core/services/ocr2/plugins/ccip/internal/ccipdata/batchreader/mocks/token_pool_batched_reader_mock.go
+++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/batchreader/mocks/token_pool_batched_reader_mock.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -23,7 +23,7 @@ func (_m *TokenPoolBatchedReader) EXPECT() *TokenPoolBatchedReader_Expecter {
return &TokenPoolBatchedReader_Expecter{mock: &_m.Mock}
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *TokenPoolBatchedReader) Close() error {
ret := _m.Called()
diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/ccipdataprovider/mocks/price_registry_mock.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/ccipdataprovider/mocks/price_registry_mock.go
index 9a9cdb48cad..7655116ede4 100644
--- a/core/services/ocr2/plugins/ccip/internal/ccipdata/ccipdataprovider/mocks/price_registry_mock.go
+++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/ccipdataprovider/mocks/price_registry_mock.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/mocks/commit_store_reader_mock.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/mocks/commit_store_reader_mock.go
index a9de0fa0d88..eb8d8179c52 100644
--- a/core/services/ocr2/plugins/ccip/internal/ccipdata/mocks/commit_store_reader_mock.go
+++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/mocks/commit_store_reader_mock.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -87,7 +87,7 @@ func (_c *CommitStoreReader_ChangeConfig_Call) RunAndReturn(run func(context.Con
return _c
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *CommitStoreReader) Close() error {
ret := _m.Called()
diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/mocks/offramp_reader_mock.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/mocks/offramp_reader_mock.go
index e9766b48ddf..255102feed2 100644
--- a/core/services/ocr2/plugins/ccip/internal/ccipdata/mocks/offramp_reader_mock.go
+++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/mocks/offramp_reader_mock.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -144,7 +144,7 @@ func (_c *OffRampReader_ChangeConfig_Call) RunAndReturn(run func(context.Context
return _c
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *OffRampReader) Close() error {
ret := _m.Called()
diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/mocks/onramp_reader_mock.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/mocks/onramp_reader_mock.go
index 3b029054d17..1cd3d5ee49b 100644
--- a/core/services/ocr2/plugins/ccip/internal/ccipdata/mocks/onramp_reader_mock.go
+++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/mocks/onramp_reader_mock.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -79,7 +79,7 @@ func (_c *OnRampReader_Address_Call) RunAndReturn(run func(context.Context) (cci
return _c
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *OnRampReader) Close() error {
ret := _m.Called()
diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/mocks/price_registry_reader_mock.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/mocks/price_registry_reader_mock.go
index c312dda2a2f..49812d2a87c 100644
--- a/core/services/ocr2/plugins/ccip/internal/ccipdata/mocks/price_registry_reader_mock.go
+++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/mocks/price_registry_reader_mock.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -81,7 +81,7 @@ func (_c *PriceRegistryReader_Address_Call) RunAndReturn(run func(context.Contex
return _c
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *PriceRegistryReader) Close() error {
ret := _m.Called()
diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/mocks/token_pool_reader_mock.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/mocks/token_pool_reader_mock.go
index be50e765c91..6ae5a2478bb 100644
--- a/core/services/ocr2/plugins/ccip/internal/ccipdata/mocks/token_pool_reader_mock.go
+++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/mocks/token_pool_reader_mock.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -20,7 +20,7 @@ func (_m *TokenPoolReader) EXPECT() *TokenPoolReader_Expecter {
return &TokenPoolReader_Expecter{mock: &_m.Mock}
}
-// Address provides a mock function with given fields:
+// Address provides a mock function with no fields
func (_m *TokenPoolReader) Address() common.Address {
ret := _m.Called()
@@ -67,7 +67,7 @@ func (_c *TokenPoolReader_Address_Call) RunAndReturn(run func() common.Address)
return _c
}
-// Type provides a mock function with given fields:
+// Type provides a mock function with no fields
func (_m *TokenPoolReader) Type() string {
ret := _m.Called()
diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/mocks/usdc_reader_mock.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/mocks/usdc_reader_mock.go
index 03085bd8973..67823035e5e 100644
--- a/core/services/ocr2/plugins/ccip/internal/ccipdata/mocks/usdc_reader_mock.go
+++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/mocks/usdc_reader_mock.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdb/mocks/price_service_mock.go b/core/services/ocr2/plugins/ccip/internal/ccipdb/mocks/price_service_mock.go
index 76690f79bf8..8914ca0b987 100644
--- a/core/services/ocr2/plugins/ccip/internal/ccipdb/mocks/price_service_mock.go
+++ b/core/services/ocr2/plugins/ccip/internal/ccipdb/mocks/price_service_mock.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -28,7 +28,7 @@ func (_m *PriceService) EXPECT() *PriceService_Expecter {
return &PriceService_Expecter{mock: &_m.Mock}
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *PriceService) Close() error {
ret := _m.Called()
diff --git a/core/services/ocr2/plugins/ccip/internal/pricegetter/all_price_getter_mock.go b/core/services/ocr2/plugins/ccip/internal/pricegetter/all_price_getter_mock.go
index 8281137e524..d05b14b67d2 100644
--- a/core/services/ocr2/plugins/ccip/internal/pricegetter/all_price_getter_mock.go
+++ b/core/services/ocr2/plugins/ccip/internal/pricegetter/all_price_getter_mock.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package pricegetter
@@ -24,7 +24,7 @@ func (_m *MockAllTokensPriceGetter) EXPECT() *MockAllTokensPriceGetter_Expecter
return &MockAllTokensPriceGetter_Expecter{mock: &_m.Mock}
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *MockAllTokensPriceGetter) Close() error {
ret := _m.Called()
diff --git a/core/services/ocr2/plugins/ccip/internal/pricegetter/mock.go b/core/services/ocr2/plugins/ccip/internal/pricegetter/mock.go
index 6b520864a9f..abebd51494c 100644
--- a/core/services/ocr2/plugins/ccip/internal/pricegetter/mock.go
+++ b/core/services/ocr2/plugins/ccip/internal/pricegetter/mock.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package pricegetter
@@ -24,7 +24,7 @@ func (_m *MockPriceGetter) EXPECT() *MockPriceGetter_Expecter {
return &MockPriceGetter_Expecter{mock: &_m.Mock}
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *MockPriceGetter) Close() error {
ret := _m.Called()
diff --git a/core/services/ocr2/plugins/ccip/internal/rpclib/rpclibmocks/evm_mock.go b/core/services/ocr2/plugins/ccip/internal/rpclib/rpclibmocks/evm_mock.go
index 3fc2f46cb3e..4d1d3b92735 100644
--- a/core/services/ocr2/plugins/ccip/internal/rpclib/rpclibmocks/evm_mock.go
+++ b/core/services/ocr2/plugins/ccip/internal/rpclib/rpclibmocks/evm_mock.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package rpclibmocks
diff --git a/core/services/ocr2/plugins/ccip/prices/gas_price_estimator_commit_mock.go b/core/services/ocr2/plugins/ccip/prices/gas_price_estimator_commit_mock.go
index 099c50ea2c0..4632ee0ca24 100644
--- a/core/services/ocr2/plugins/ccip/prices/gas_price_estimator_commit_mock.go
+++ b/core/services/ocr2/plugins/ccip/prices/gas_price_estimator_commit_mock.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package prices
diff --git a/core/services/ocr2/plugins/ccip/prices/gas_price_estimator_exec_mock.go b/core/services/ocr2/plugins/ccip/prices/gas_price_estimator_exec_mock.go
index 02f69b01e08..d45cbb202c3 100644
--- a/core/services/ocr2/plugins/ccip/prices/gas_price_estimator_exec_mock.go
+++ b/core/services/ocr2/plugins/ccip/prices/gas_price_estimator_exec_mock.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package prices
diff --git a/core/services/ocr2/plugins/ccip/prices/gas_price_estimator_mock.go b/core/services/ocr2/plugins/ccip/prices/gas_price_estimator_mock.go
index 7323f88e3ab..7376f16c0c0 100644
--- a/core/services/ocr2/plugins/ccip/prices/gas_price_estimator_mock.go
+++ b/core/services/ocr2/plugins/ccip/prices/gas_price_estimator_mock.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package prices
diff --git a/core/services/ocr2/plugins/ccip/tokendata/reader_mock.go b/core/services/ocr2/plugins/ccip/tokendata/reader_mock.go
index 93163fdcfb4..c553eed3586 100644
--- a/core/services/ocr2/plugins/ccip/tokendata/reader_mock.go
+++ b/core/services/ocr2/plugins/ccip/tokendata/reader_mock.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package tokendata
@@ -23,7 +23,7 @@ func (_m *MockReader) EXPECT() *MockReader_Expecter {
return &MockReader_Expecter{mock: &_m.Mock}
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *MockReader) Close() error {
ret := _m.Called()
diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v20/mocks/registry.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v20/mocks/registry.go
index ab4a8d5280d..b4d2eea2508 100644
--- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v20/mocks/registry.go
+++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v20/mocks/registry.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/mocks/upkeep_state_reader.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/mocks/upkeep_state_reader.go
index 969c85efac3..24b5d92ace4 100644
--- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/mocks/upkeep_state_reader.go
+++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/mocks/upkeep_state_reader.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mocks/http_client.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mocks/http_client.go
index e0ad6ae9467..896e1daec97 100644
--- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mocks/http_client.go
+++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mocks/http_client.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mocks/registry.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mocks/registry.go
index bb431966763..e386b7e0fab 100644
--- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mocks/registry.go
+++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mocks/registry.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -6,7 +6,6 @@ import (
big "math/big"
bind "github.com/ethereum/go-ethereum/accounts/abi/bind"
- encoding "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding"
generated "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated"
@@ -206,22 +205,22 @@ func (_c *Registry_GetState_Call) RunAndReturn(run func(*bind.CallOpts) (i_autom
}
// GetUpkeep provides a mock function with given fields: opts, id
-func (_m *Registry) GetUpkeep(opts *bind.CallOpts, id *big.Int) (encoding.UpkeepInfo, error) {
+func (_m *Registry) GetUpkeep(opts *bind.CallOpts, id *big.Int) (i_automation_v21_plus_common.IAutomationV21PlusCommonUpkeepInfoLegacy, error) {
ret := _m.Called(opts, id)
if len(ret) == 0 {
panic("no return value specified for GetUpkeep")
}
- var r0 encoding.UpkeepInfo
+ var r0 i_automation_v21_plus_common.IAutomationV21PlusCommonUpkeepInfoLegacy
var r1 error
- if rf, ok := ret.Get(0).(func(*bind.CallOpts, *big.Int) (encoding.UpkeepInfo, error)); ok {
+ if rf, ok := ret.Get(0).(func(*bind.CallOpts, *big.Int) (i_automation_v21_plus_common.IAutomationV21PlusCommonUpkeepInfoLegacy, error)); ok {
return rf(opts, id)
}
- if rf, ok := ret.Get(0).(func(*bind.CallOpts, *big.Int) encoding.UpkeepInfo); ok {
+ if rf, ok := ret.Get(0).(func(*bind.CallOpts, *big.Int) i_automation_v21_plus_common.IAutomationV21PlusCommonUpkeepInfoLegacy); ok {
r0 = rf(opts, id)
} else {
- r0 = ret.Get(0).(encoding.UpkeepInfo)
+ r0 = ret.Get(0).(i_automation_v21_plus_common.IAutomationV21PlusCommonUpkeepInfoLegacy)
}
if rf, ok := ret.Get(1).(func(*bind.CallOpts, *big.Int) error); ok {
@@ -252,12 +251,12 @@ func (_c *Registry_GetUpkeep_Call) Run(run func(opts *bind.CallOpts, id *big.Int
return _c
}
-func (_c *Registry_GetUpkeep_Call) Return(_a0 encoding.UpkeepInfo, _a1 error) *Registry_GetUpkeep_Call {
+func (_c *Registry_GetUpkeep_Call) Return(_a0 i_automation_v21_plus_common.IAutomationV21PlusCommonUpkeepInfoLegacy, _a1 error) *Registry_GetUpkeep_Call {
_c.Call.Return(_a0, _a1)
return _c
}
-func (_c *Registry_GetUpkeep_Call) RunAndReturn(run func(*bind.CallOpts, *big.Int) (encoding.UpkeepInfo, error)) *Registry_GetUpkeep_Call {
+func (_c *Registry_GetUpkeep_Call) RunAndReturn(run func(*bind.CallOpts, *big.Int) (i_automation_v21_plus_common.IAutomationV21PlusCommonUpkeepInfoLegacy, error)) *Registry_GetUpkeep_Call {
_c.Call.Return(run)
return _c
}
diff --git a/core/services/ocr2/plugins/promwrapper/mocks/prometheus_backend.go b/core/services/ocr2/plugins/promwrapper/mocks/prometheus_backend.go
index 2d38cc77387..1e85e0d2735 100644
--- a/core/services/ocr2/plugins/promwrapper/mocks/prometheus_backend.go
+++ b/core/services/ocr2/plugins/promwrapper/mocks/prometheus_backend.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -47,7 +47,7 @@ func (_c *PrometheusBackend_SetAcceptFinalizedReportToTransmitAcceptedReportLate
}
func (_c *PrometheusBackend_SetAcceptFinalizedReportToTransmitAcceptedReportLatency_Call) RunAndReturn(run func([]string, float64)) *PrometheusBackend_SetAcceptFinalizedReportToTransmitAcceptedReportLatency_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
@@ -81,7 +81,7 @@ func (_c *PrometheusBackend_SetCloseDuration_Call) Return() *PrometheusBackend_S
}
func (_c *PrometheusBackend_SetCloseDuration_Call) RunAndReturn(run func([]string, float64)) *PrometheusBackend_SetCloseDuration_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
@@ -115,7 +115,7 @@ func (_c *PrometheusBackend_SetObservationDuration_Call) Return() *PrometheusBac
}
func (_c *PrometheusBackend_SetObservationDuration_Call) RunAndReturn(run func([]string, float64)) *PrometheusBackend_SetObservationDuration_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
@@ -149,7 +149,7 @@ func (_c *PrometheusBackend_SetObservationToReportLatency_Call) Return() *Promet
}
func (_c *PrometheusBackend_SetObservationToReportLatency_Call) RunAndReturn(run func([]string, float64)) *PrometheusBackend_SetObservationToReportLatency_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
@@ -183,7 +183,7 @@ func (_c *PrometheusBackend_SetQueryDuration_Call) Return() *PrometheusBackend_S
}
func (_c *PrometheusBackend_SetQueryDuration_Call) RunAndReturn(run func([]string, float64)) *PrometheusBackend_SetQueryDuration_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
@@ -217,7 +217,7 @@ func (_c *PrometheusBackend_SetQueryToObservationLatency_Call) Return() *Prometh
}
func (_c *PrometheusBackend_SetQueryToObservationLatency_Call) RunAndReturn(run func([]string, float64)) *PrometheusBackend_SetQueryToObservationLatency_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
@@ -251,7 +251,7 @@ func (_c *PrometheusBackend_SetReportDuration_Call) Return() *PrometheusBackend_
}
func (_c *PrometheusBackend_SetReportDuration_Call) RunAndReturn(run func([]string, float64)) *PrometheusBackend_SetReportDuration_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
@@ -285,7 +285,7 @@ func (_c *PrometheusBackend_SetReportToAcceptFinalizedReportLatency_Call) Return
}
func (_c *PrometheusBackend_SetReportToAcceptFinalizedReportLatency_Call) RunAndReturn(run func([]string, float64)) *PrometheusBackend_SetReportToAcceptFinalizedReportLatency_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
@@ -319,7 +319,7 @@ func (_c *PrometheusBackend_SetShouldAcceptFinalizedReportDuration_Call) Return(
}
func (_c *PrometheusBackend_SetShouldAcceptFinalizedReportDuration_Call) RunAndReturn(run func([]string, float64)) *PrometheusBackend_SetShouldAcceptFinalizedReportDuration_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
@@ -353,7 +353,7 @@ func (_c *PrometheusBackend_SetShouldTransmitAcceptedReportDuration_Call) Return
}
func (_c *PrometheusBackend_SetShouldTransmitAcceptedReportDuration_Call) RunAndReturn(run func([]string, float64)) *PrometheusBackend_SetShouldTransmitAcceptedReportDuration_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
diff --git a/core/services/ocr2/plugins/threshold/mocks/decryptor.go b/core/services/ocr2/plugins/threshold/mocks/decryptor.go
index edf19e8a284..6b001b73bcc 100644
--- a/core/services/ocr2/plugins/threshold/mocks/decryptor.go
+++ b/core/services/ocr2/plugins/threshold/mocks/decryptor.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
diff --git a/core/services/p2p/types/mocks/peer.go b/core/services/p2p/types/mocks/peer.go
index a50bad780b8..e24c662ffaf 100644
--- a/core/services/p2p/types/mocks/peer.go
+++ b/core/services/p2p/types/mocks/peer.go
@@ -1,12 +1,14 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
import (
context "context"
- types "github.com/smartcontractkit/chainlink/v2/core/services/p2p/types"
+ ragep2ptypes "github.com/smartcontractkit/libocr/ragep2p/types"
mock "github.com/stretchr/testify/mock"
+
+ types "github.com/smartcontractkit/chainlink/v2/core/services/p2p/types"
)
// Peer is an autogenerated mock type for the Peer type
@@ -22,7 +24,7 @@ func (_m *Peer) EXPECT() *Peer_Expecter {
return &Peer_Expecter{mock: &_m.Mock}
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *Peer) Close() error {
ret := _m.Called()
@@ -67,7 +69,7 @@ func (_c *Peer_Close_Call) RunAndReturn(run func() error) *Peer_Close_Call {
return _c
}
-// HealthReport provides a mock function with given fields:
+// HealthReport provides a mock function with no fields
func (_m *Peer) HealthReport() map[string]error {
ret := _m.Called()
@@ -114,20 +116,20 @@ func (_c *Peer_HealthReport_Call) RunAndReturn(run func() map[string]error) *Pee
return _c
}
-// ID provides a mock function with given fields:
-func (_m *Peer) ID() types.PeerID {
+// ID provides a mock function with no fields
+func (_m *Peer) ID() ragep2ptypes.PeerID {
ret := _m.Called()
if len(ret) == 0 {
panic("no return value specified for ID")
}
- var r0 types.PeerID
- if rf, ok := ret.Get(0).(func() types.PeerID); ok {
+ var r0 ragep2ptypes.PeerID
+ if rf, ok := ret.Get(0).(func() ragep2ptypes.PeerID); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
- r0 = ret.Get(0).(types.PeerID)
+ r0 = ret.Get(0).(ragep2ptypes.PeerID)
}
}
@@ -151,17 +153,17 @@ func (_c *Peer_ID_Call) Run(run func()) *Peer_ID_Call {
return _c
}
-func (_c *Peer_ID_Call) Return(_a0 types.PeerID) *Peer_ID_Call {
+func (_c *Peer_ID_Call) Return(_a0 ragep2ptypes.PeerID) *Peer_ID_Call {
_c.Call.Return(_a0)
return _c
}
-func (_c *Peer_ID_Call) RunAndReturn(run func() types.PeerID) *Peer_ID_Call {
+func (_c *Peer_ID_Call) RunAndReturn(run func() ragep2ptypes.PeerID) *Peer_ID_Call {
_c.Call.Return(run)
return _c
}
-// Name provides a mock function with given fields:
+// Name provides a mock function with no fields
func (_m *Peer) Name() string {
ret := _m.Called()
@@ -206,7 +208,7 @@ func (_c *Peer_Name_Call) RunAndReturn(run func() string) *Peer_Name_Call {
return _c
}
-// Ready provides a mock function with given fields:
+// Ready provides a mock function with no fields
func (_m *Peer) Ready() error {
ret := _m.Called()
@@ -251,7 +253,7 @@ func (_c *Peer_Ready_Call) RunAndReturn(run func() error) *Peer_Ready_Call {
return _c
}
-// Receive provides a mock function with given fields:
+// Receive provides a mock function with no fields
func (_m *Peer) Receive() <-chan types.Message {
ret := _m.Called()
@@ -299,7 +301,7 @@ func (_c *Peer_Receive_Call) RunAndReturn(run func() <-chan types.Message) *Peer
}
// Send provides a mock function with given fields: peerID, msg
-func (_m *Peer) Send(peerID types.PeerID, msg []byte) error {
+func (_m *Peer) Send(peerID ragep2ptypes.PeerID, msg []byte) error {
ret := _m.Called(peerID, msg)
if len(ret) == 0 {
@@ -307,7 +309,7 @@ func (_m *Peer) Send(peerID types.PeerID, msg []byte) error {
}
var r0 error
- if rf, ok := ret.Get(0).(func(types.PeerID, []byte) error); ok {
+ if rf, ok := ret.Get(0).(func(ragep2ptypes.PeerID, []byte) error); ok {
r0 = rf(peerID, msg)
} else {
r0 = ret.Error(0)
@@ -322,15 +324,15 @@ type Peer_Send_Call struct {
}
// Send is a helper method to define mock.On call
-// - peerID types.PeerID
+// - peerID ragep2ptypes.PeerID
// - msg []byte
func (_e *Peer_Expecter) Send(peerID interface{}, msg interface{}) *Peer_Send_Call {
return &Peer_Send_Call{Call: _e.mock.On("Send", peerID, msg)}
}
-func (_c *Peer_Send_Call) Run(run func(peerID types.PeerID, msg []byte)) *Peer_Send_Call {
+func (_c *Peer_Send_Call) Run(run func(peerID ragep2ptypes.PeerID, msg []byte)) *Peer_Send_Call {
_c.Call.Run(func(args mock.Arguments) {
- run(args[0].(types.PeerID), args[1].([]byte))
+ run(args[0].(ragep2ptypes.PeerID), args[1].([]byte))
})
return _c
}
@@ -340,7 +342,7 @@ func (_c *Peer_Send_Call) Return(_a0 error) *Peer_Send_Call {
return _c
}
-func (_c *Peer_Send_Call) RunAndReturn(run func(types.PeerID, []byte) error) *Peer_Send_Call {
+func (_c *Peer_Send_Call) RunAndReturn(run func(ragep2ptypes.PeerID, []byte) error) *Peer_Send_Call {
_c.Call.Return(run)
return _c
}
@@ -392,7 +394,7 @@ func (_c *Peer_Start_Call) RunAndReturn(run func(context.Context) error) *Peer_S
}
// UpdateConnections provides a mock function with given fields: peers
-func (_m *Peer) UpdateConnections(peers map[types.PeerID]types.StreamConfig) error {
+func (_m *Peer) UpdateConnections(peers map[ragep2ptypes.PeerID]types.StreamConfig) error {
ret := _m.Called(peers)
if len(ret) == 0 {
@@ -400,7 +402,7 @@ func (_m *Peer) UpdateConnections(peers map[types.PeerID]types.StreamConfig) err
}
var r0 error
- if rf, ok := ret.Get(0).(func(map[types.PeerID]types.StreamConfig) error); ok {
+ if rf, ok := ret.Get(0).(func(map[ragep2ptypes.PeerID]types.StreamConfig) error); ok {
r0 = rf(peers)
} else {
r0 = ret.Error(0)
@@ -415,14 +417,14 @@ type Peer_UpdateConnections_Call struct {
}
// UpdateConnections is a helper method to define mock.On call
-// - peers map[types.PeerID]types.StreamConfig
+// - peers map[ragep2ptypes.PeerID]types.StreamConfig
func (_e *Peer_Expecter) UpdateConnections(peers interface{}) *Peer_UpdateConnections_Call {
return &Peer_UpdateConnections_Call{Call: _e.mock.On("UpdateConnections", peers)}
}
-func (_c *Peer_UpdateConnections_Call) Run(run func(peers map[types.PeerID]types.StreamConfig)) *Peer_UpdateConnections_Call {
+func (_c *Peer_UpdateConnections_Call) Run(run func(peers map[ragep2ptypes.PeerID]types.StreamConfig)) *Peer_UpdateConnections_Call {
_c.Call.Run(func(args mock.Arguments) {
- run(args[0].(map[types.PeerID]types.StreamConfig))
+ run(args[0].(map[ragep2ptypes.PeerID]types.StreamConfig))
})
return _c
}
@@ -432,7 +434,7 @@ func (_c *Peer_UpdateConnections_Call) Return(_a0 error) *Peer_UpdateConnections
return _c
}
-func (_c *Peer_UpdateConnections_Call) RunAndReturn(run func(map[types.PeerID]types.StreamConfig) error) *Peer_UpdateConnections_Call {
+func (_c *Peer_UpdateConnections_Call) RunAndReturn(run func(map[ragep2ptypes.PeerID]types.StreamConfig) error) *Peer_UpdateConnections_Call {
_c.Call.Return(run)
return _c
}
diff --git a/core/services/p2p/types/mocks/peer_wrapper.go b/core/services/p2p/types/mocks/peer_wrapper.go
index 7d1744cb0c5..98ebff5f618 100644
--- a/core/services/p2p/types/mocks/peer_wrapper.go
+++ b/core/services/p2p/types/mocks/peer_wrapper.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -22,7 +22,7 @@ func (_m *PeerWrapper) EXPECT() *PeerWrapper_Expecter {
return &PeerWrapper_Expecter{mock: &_m.Mock}
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *PeerWrapper) Close() error {
ret := _m.Called()
@@ -67,7 +67,7 @@ func (_c *PeerWrapper_Close_Call) RunAndReturn(run func() error) *PeerWrapper_Cl
return _c
}
-// GetPeer provides a mock function with given fields:
+// GetPeer provides a mock function with no fields
func (_m *PeerWrapper) GetPeer() types.Peer {
ret := _m.Called()
@@ -114,7 +114,7 @@ func (_c *PeerWrapper_GetPeer_Call) RunAndReturn(run func() types.Peer) *PeerWra
return _c
}
-// HealthReport provides a mock function with given fields:
+// HealthReport provides a mock function with no fields
func (_m *PeerWrapper) HealthReport() map[string]error {
ret := _m.Called()
@@ -161,7 +161,7 @@ func (_c *PeerWrapper_HealthReport_Call) RunAndReturn(run func() map[string]erro
return _c
}
-// Name provides a mock function with given fields:
+// Name provides a mock function with no fields
func (_m *PeerWrapper) Name() string {
ret := _m.Called()
@@ -206,7 +206,7 @@ func (_c *PeerWrapper_Name_Call) RunAndReturn(run func() string) *PeerWrapper_Na
return _c
}
-// Ready provides a mock function with given fields:
+// Ready provides a mock function with no fields
func (_m *PeerWrapper) Ready() error {
ret := _m.Called()
diff --git a/core/services/p2p/types/mocks/signer.go b/core/services/p2p/types/mocks/signer.go
index 57afac0cecc..d027fca471a 100644
--- a/core/services/p2p/types/mocks/signer.go
+++ b/core/services/p2p/types/mocks/signer.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
diff --git a/core/services/pipeline/mocks/config.go b/core/services/pipeline/mocks/config.go
index 87bf48047bf..f5c6a11b1d7 100644
--- a/core/services/pipeline/mocks/config.go
+++ b/core/services/pipeline/mocks/config.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -22,7 +22,7 @@ func (_m *Config) EXPECT() *Config_Expecter {
return &Config_Expecter{mock: &_m.Mock}
}
-// DefaultHTTPLimit provides a mock function with given fields:
+// DefaultHTTPLimit provides a mock function with no fields
func (_m *Config) DefaultHTTPLimit() int64 {
ret := _m.Called()
@@ -67,7 +67,7 @@ func (_c *Config_DefaultHTTPLimit_Call) RunAndReturn(run func() int64) *Config_D
return _c
}
-// DefaultHTTPTimeout provides a mock function with given fields:
+// DefaultHTTPTimeout provides a mock function with no fields
func (_m *Config) DefaultHTTPTimeout() config.Duration {
ret := _m.Called()
@@ -112,7 +112,7 @@ func (_c *Config_DefaultHTTPTimeout_Call) RunAndReturn(run func() config.Duratio
return _c
}
-// MaxRunDuration provides a mock function with given fields:
+// MaxRunDuration provides a mock function with no fields
func (_m *Config) MaxRunDuration() time.Duration {
ret := _m.Called()
@@ -157,7 +157,7 @@ func (_c *Config_MaxRunDuration_Call) RunAndReturn(run func() time.Duration) *Co
return _c
}
-// ReaperInterval provides a mock function with given fields:
+// ReaperInterval provides a mock function with no fields
func (_m *Config) ReaperInterval() time.Duration {
ret := _m.Called()
@@ -202,7 +202,7 @@ func (_c *Config_ReaperInterval_Call) RunAndReturn(run func() time.Duration) *Co
return _c
}
-// ReaperThreshold provides a mock function with given fields:
+// ReaperThreshold provides a mock function with no fields
func (_m *Config) ReaperThreshold() time.Duration {
ret := _m.Called()
@@ -247,7 +247,7 @@ func (_c *Config_ReaperThreshold_Call) RunAndReturn(run func() time.Duration) *C
return _c
}
-// VerboseLogging provides a mock function with given fields:
+// VerboseLogging provides a mock function with no fields
func (_m *Config) VerboseLogging() bool {
ret := _m.Called()
diff --git a/core/services/pipeline/mocks/orm.go b/core/services/pipeline/mocks/orm.go
index d79825fcf10..7c13aac4c9e 100644
--- a/core/services/pipeline/mocks/orm.go
+++ b/core/services/pipeline/mocks/orm.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -30,7 +30,7 @@ func (_m *ORM) EXPECT() *ORM_Expecter {
return &ORM_Expecter{mock: &_m.Mock}
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *ORM) Close() error {
ret := _m.Called()
@@ -180,7 +180,7 @@ func (_c *ORM_CreateSpec_Call) RunAndReturn(run func(context.Context, pipeline.P
return _c
}
-// DataSource provides a mock function with given fields:
+// DataSource provides a mock function with no fields
func (_m *ORM) DataSource() sqlutil.DataSource {
ret := _m.Called()
@@ -484,7 +484,7 @@ func (_c *ORM_GetUnfinishedRuns_Call) RunAndReturn(run func(context.Context, tim
return _c
}
-// HealthReport provides a mock function with given fields:
+// HealthReport provides a mock function with no fields
func (_m *ORM) HealthReport() map[string]error {
ret := _m.Called()
@@ -722,7 +722,7 @@ func (_c *ORM_InsertRun_Call) RunAndReturn(run func(context.Context, *pipeline.R
return _c
}
-// Name provides a mock function with given fields:
+// Name provides a mock function with no fields
func (_m *ORM) Name() string {
ret := _m.Called()
@@ -767,7 +767,7 @@ func (_c *ORM_Name_Call) RunAndReturn(run func() string) *ORM_Name_Call {
return _c
}
-// Ready provides a mock function with given fields:
+// Ready provides a mock function with no fields
func (_m *ORM) Ready() error {
ret := _m.Called()
diff --git a/core/services/pipeline/mocks/pipeline_param_unmarshaler.go b/core/services/pipeline/mocks/pipeline_param_unmarshaler.go
index 3ca0dd54666..cb997d57081 100644
--- a/core/services/pipeline/mocks/pipeline_param_unmarshaler.go
+++ b/core/services/pipeline/mocks/pipeline_param_unmarshaler.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
diff --git a/core/services/pipeline/mocks/runner.go b/core/services/pipeline/mocks/runner.go
index 7a59569989a..9779d47d70d 100644
--- a/core/services/pipeline/mocks/runner.go
+++ b/core/services/pipeline/mocks/runner.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -26,7 +26,7 @@ func (_m *Runner) EXPECT() *Runner_Expecter {
return &Runner_Expecter{mock: &_m.Mock}
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *Runner) Close() error {
ret := _m.Called()
@@ -208,7 +208,7 @@ func (_c *Runner_ExecuteRun_Call) RunAndReturn(run func(context.Context, pipelin
return _c
}
-// HealthReport provides a mock function with given fields:
+// HealthReport provides a mock function with no fields
func (_m *Runner) HealthReport() map[string]error {
ret := _m.Called()
@@ -411,7 +411,7 @@ func (_c *Runner_InsertFinishedRuns_Call) RunAndReturn(run func(context.Context,
return _c
}
-// Name provides a mock function with given fields:
+// Name provides a mock function with no fields
func (_m *Runner) Name() string {
ret := _m.Called()
@@ -485,11 +485,11 @@ func (_c *Runner_OnRunFinished_Call) Return() *Runner_OnRunFinished_Call {
}
func (_c *Runner_OnRunFinished_Call) RunAndReturn(run func(func(*pipeline.Run))) *Runner_OnRunFinished_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
-// Ready provides a mock function with given fields:
+// Ready provides a mock function with no fields
func (_m *Runner) Ready() error {
ret := _m.Called()
diff --git a/core/services/registrysyncer/mocks/orm.go b/core/services/registrysyncer/mocks/orm.go
index 7fb44286a16..419fe9ae564 100644
--- a/core/services/registrysyncer/mocks/orm.go
+++ b/core/services/registrysyncer/mocks/orm.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
diff --git a/core/services/relay/evm/mercury/mocks/async_deleter.go b/core/services/relay/evm/mercury/mocks/async_deleter.go
index f94414af4a3..ce9dee690e5 100644
--- a/core/services/relay/evm/mercury/mocks/async_deleter.go
+++ b/core/services/relay/evm/mercury/mocks/async_deleter.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -49,7 +49,7 @@ func (_c *AsyncDeleter_AsyncDelete_Call) Return() *AsyncDeleter_AsyncDelete_Call
}
func (_c *AsyncDeleter_AsyncDelete_Call) RunAndReturn(run func(*pb.TransmitRequest)) *AsyncDeleter_AsyncDelete_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
diff --git a/core/services/relay/evm/mocks/codec.go b/core/services/relay/evm/mocks/codec.go
index acc9e3a7e3a..f4a1e097e5a 100644
--- a/core/services/relay/evm/mocks/codec.go
+++ b/core/services/relay/evm/mocks/codec.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -22,7 +22,7 @@ func (_m *Codec) EXPECT() *Codec_Expecter {
}
// Decode provides a mock function with given fields: ctx, raw, into, itemType
-func (_m *Codec) Decode(ctx context.Context, raw []byte, into any, itemType string) error {
+func (_m *Codec) Decode(ctx context.Context, raw []byte, into interface{}, itemType string) error {
ret := _m.Called(ctx, raw, into, itemType)
if len(ret) == 0 {
@@ -30,7 +30,7 @@ func (_m *Codec) Decode(ctx context.Context, raw []byte, into any, itemType stri
}
var r0 error
- if rf, ok := ret.Get(0).(func(context.Context, []byte, any, string) error); ok {
+ if rf, ok := ret.Get(0).(func(context.Context, []byte, interface{}, string) error); ok {
r0 = rf(ctx, raw, into, itemType)
} else {
r0 = ret.Error(0)
@@ -47,15 +47,15 @@ type Codec_Decode_Call struct {
// Decode is a helper method to define mock.On call
// - ctx context.Context
// - raw []byte
-// - into any
+// - into interface{}
// - itemType string
func (_e *Codec_Expecter) Decode(ctx interface{}, raw interface{}, into interface{}, itemType interface{}) *Codec_Decode_Call {
return &Codec_Decode_Call{Call: _e.mock.On("Decode", ctx, raw, into, itemType)}
}
-func (_c *Codec_Decode_Call) Run(run func(ctx context.Context, raw []byte, into any, itemType string)) *Codec_Decode_Call {
+func (_c *Codec_Decode_Call) Run(run func(ctx context.Context, raw []byte, into interface{}, itemType string)) *Codec_Decode_Call {
_c.Call.Run(func(args mock.Arguments) {
- run(args[0].(context.Context), args[1].([]byte), args[2].(any), args[3].(string))
+ run(args[0].(context.Context), args[1].([]byte), args[2].(interface{}), args[3].(string))
})
return _c
}
@@ -65,13 +65,13 @@ func (_c *Codec_Decode_Call) Return(_a0 error) *Codec_Decode_Call {
return _c
}
-func (_c *Codec_Decode_Call) RunAndReturn(run func(context.Context, []byte, any, string) error) *Codec_Decode_Call {
+func (_c *Codec_Decode_Call) RunAndReturn(run func(context.Context, []byte, interface{}, string) error) *Codec_Decode_Call {
_c.Call.Return(run)
return _c
}
// Encode provides a mock function with given fields: ctx, item, itemType
-func (_m *Codec) Encode(ctx context.Context, item any, itemType string) ([]byte, error) {
+func (_m *Codec) Encode(ctx context.Context, item interface{}, itemType string) ([]byte, error) {
ret := _m.Called(ctx, item, itemType)
if len(ret) == 0 {
@@ -80,10 +80,10 @@ func (_m *Codec) Encode(ctx context.Context, item any, itemType string) ([]byte,
var r0 []byte
var r1 error
- if rf, ok := ret.Get(0).(func(context.Context, any, string) ([]byte, error)); ok {
+ if rf, ok := ret.Get(0).(func(context.Context, interface{}, string) ([]byte, error)); ok {
return rf(ctx, item, itemType)
}
- if rf, ok := ret.Get(0).(func(context.Context, any, string) []byte); ok {
+ if rf, ok := ret.Get(0).(func(context.Context, interface{}, string) []byte); ok {
r0 = rf(ctx, item, itemType)
} else {
if ret.Get(0) != nil {
@@ -91,7 +91,7 @@ func (_m *Codec) Encode(ctx context.Context, item any, itemType string) ([]byte,
}
}
- if rf, ok := ret.Get(1).(func(context.Context, any, string) error); ok {
+ if rf, ok := ret.Get(1).(func(context.Context, interface{}, string) error); ok {
r1 = rf(ctx, item, itemType)
} else {
r1 = ret.Error(1)
@@ -107,15 +107,15 @@ type Codec_Encode_Call struct {
// Encode is a helper method to define mock.On call
// - ctx context.Context
-// - item any
+// - item interface{}
// - itemType string
func (_e *Codec_Expecter) Encode(ctx interface{}, item interface{}, itemType interface{}) *Codec_Encode_Call {
return &Codec_Encode_Call{Call: _e.mock.On("Encode", ctx, item, itemType)}
}
-func (_c *Codec_Encode_Call) Run(run func(ctx context.Context, item any, itemType string)) *Codec_Encode_Call {
+func (_c *Codec_Encode_Call) Run(run func(ctx context.Context, item interface{}, itemType string)) *Codec_Encode_Call {
_c.Call.Run(func(args mock.Arguments) {
- run(args[0].(context.Context), args[1].(any), args[2].(string))
+ run(args[0].(context.Context), args[1].(interface{}), args[2].(string))
})
return _c
}
@@ -125,7 +125,7 @@ func (_c *Codec_Encode_Call) Return(_a0 []byte, _a1 error) *Codec_Encode_Call {
return _c
}
-func (_c *Codec_Encode_Call) RunAndReturn(run func(context.Context, any, string) ([]byte, error)) *Codec_Encode_Call {
+func (_c *Codec_Encode_Call) RunAndReturn(run func(context.Context, interface{}, string) ([]byte, error)) *Codec_Encode_Call {
_c.Call.Return(run)
return _c
}
diff --git a/core/services/relay/evm/mocks/request_round_db.go b/core/services/relay/evm/mocks/request_round_db.go
index c3c58b0b125..97a499f5d8e 100644
--- a/core/services/relay/evm/mocks/request_round_db.go
+++ b/core/services/relay/evm/mocks/request_round_db.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
diff --git a/core/services/relay/evm/read/mocks/batch_caller.go b/core/services/relay/evm/read/mocks/batch_caller.go
index 3ae9ec97b2a..4ac1d28b3e0 100644
--- a/core/services/relay/evm/read/mocks/batch_caller.go
+++ b/core/services/relay/evm/read/mocks/batch_caller.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
diff --git a/core/services/relay/evm/read/mocks/reader.go b/core/services/relay/evm/read/mocks/reader.go
index 79df3cf4025..94fbeecd0f9 100644
--- a/core/services/relay/evm/read/mocks/reader.go
+++ b/core/services/relay/evm/read/mocks/reader.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -32,7 +32,7 @@ func (_m *Reader) EXPECT() *Reader_Expecter {
}
// BatchCall provides a mock function with given fields: address, params, retVal
-func (_m *Reader) BatchCall(address common.Address, params any, retVal any) (read.Call, error) {
+func (_m *Reader) BatchCall(address common.Address, params interface{}, retVal interface{}) (read.Call, error) {
ret := _m.Called(address, params, retVal)
if len(ret) == 0 {
@@ -41,16 +41,16 @@ func (_m *Reader) BatchCall(address common.Address, params any, retVal any) (rea
var r0 read.Call
var r1 error
- if rf, ok := ret.Get(0).(func(common.Address, any, any) (read.Call, error)); ok {
+ if rf, ok := ret.Get(0).(func(common.Address, interface{}, interface{}) (read.Call, error)); ok {
return rf(address, params, retVal)
}
- if rf, ok := ret.Get(0).(func(common.Address, any, any) read.Call); ok {
+ if rf, ok := ret.Get(0).(func(common.Address, interface{}, interface{}) read.Call); ok {
r0 = rf(address, params, retVal)
} else {
r0 = ret.Get(0).(read.Call)
}
- if rf, ok := ret.Get(1).(func(common.Address, any, any) error); ok {
+ if rf, ok := ret.Get(1).(func(common.Address, interface{}, interface{}) error); ok {
r1 = rf(address, params, retVal)
} else {
r1 = ret.Error(1)
@@ -66,15 +66,15 @@ type Reader_BatchCall_Call struct {
// BatchCall is a helper method to define mock.On call
// - address common.Address
-// - params any
-// - retVal any
+// - params interface{}
+// - retVal interface{}
func (_e *Reader_Expecter) BatchCall(address interface{}, params interface{}, retVal interface{}) *Reader_BatchCall_Call {
return &Reader_BatchCall_Call{Call: _e.mock.On("BatchCall", address, params, retVal)}
}
-func (_c *Reader_BatchCall_Call) Run(run func(address common.Address, params any, retVal any)) *Reader_BatchCall_Call {
+func (_c *Reader_BatchCall_Call) Run(run func(address common.Address, params interface{}, retVal interface{})) *Reader_BatchCall_Call {
_c.Call.Run(func(args mock.Arguments) {
- run(args[0].(common.Address), args[1].(any), args[2].(any))
+ run(args[0].(common.Address), args[1].(interface{}), args[2].(interface{}))
})
return _c
}
@@ -84,7 +84,7 @@ func (_c *Reader_BatchCall_Call) Return(_a0 read.Call, _a1 error) *Reader_BatchC
return _c
}
-func (_c *Reader_BatchCall_Call) RunAndReturn(run func(common.Address, any, any) (read.Call, error)) *Reader_BatchCall_Call {
+func (_c *Reader_BatchCall_Call) RunAndReturn(run func(common.Address, interface{}, interface{}) (read.Call, error)) *Reader_BatchCall_Call {
_c.Call.Return(run)
return _c
}
@@ -151,7 +151,7 @@ func (_c *Reader_Bind_Call) RunAndReturn(run func(context.Context, ...common.Add
}
// GetLatestValueWithHeadData provides a mock function with given fields: ctx, addr, confidence, params, returnVal
-func (_m *Reader) GetLatestValueWithHeadData(ctx context.Context, addr common.Address, confidence primitives.ConfidenceLevel, params any, returnVal any) (*types.Head, error) {
+func (_m *Reader) GetLatestValueWithHeadData(ctx context.Context, addr common.Address, confidence primitives.ConfidenceLevel, params interface{}, returnVal interface{}) (*types.Head, error) {
ret := _m.Called(ctx, addr, confidence, params, returnVal)
if len(ret) == 0 {
@@ -160,10 +160,10 @@ func (_m *Reader) GetLatestValueWithHeadData(ctx context.Context, addr common.Ad
var r0 *types.Head
var r1 error
- if rf, ok := ret.Get(0).(func(context.Context, common.Address, primitives.ConfidenceLevel, any, any) (*types.Head, error)); ok {
+ if rf, ok := ret.Get(0).(func(context.Context, common.Address, primitives.ConfidenceLevel, interface{}, interface{}) (*types.Head, error)); ok {
return rf(ctx, addr, confidence, params, returnVal)
}
- if rf, ok := ret.Get(0).(func(context.Context, common.Address, primitives.ConfidenceLevel, any, any) *types.Head); ok {
+ if rf, ok := ret.Get(0).(func(context.Context, common.Address, primitives.ConfidenceLevel, interface{}, interface{}) *types.Head); ok {
r0 = rf(ctx, addr, confidence, params, returnVal)
} else {
if ret.Get(0) != nil {
@@ -171,7 +171,7 @@ func (_m *Reader) GetLatestValueWithHeadData(ctx context.Context, addr common.Ad
}
}
- if rf, ok := ret.Get(1).(func(context.Context, common.Address, primitives.ConfidenceLevel, any, any) error); ok {
+ if rf, ok := ret.Get(1).(func(context.Context, common.Address, primitives.ConfidenceLevel, interface{}, interface{}) error); ok {
r1 = rf(ctx, addr, confidence, params, returnVal)
} else {
r1 = ret.Error(1)
@@ -189,15 +189,15 @@ type Reader_GetLatestValueWithHeadData_Call struct {
// - ctx context.Context
// - addr common.Address
// - confidence primitives.ConfidenceLevel
-// - params any
-// - returnVal any
+// - params interface{}
+// - returnVal interface{}
func (_e *Reader_Expecter) GetLatestValueWithHeadData(ctx interface{}, addr interface{}, confidence interface{}, params interface{}, returnVal interface{}) *Reader_GetLatestValueWithHeadData_Call {
return &Reader_GetLatestValueWithHeadData_Call{Call: _e.mock.On("GetLatestValueWithHeadData", ctx, addr, confidence, params, returnVal)}
}
-func (_c *Reader_GetLatestValueWithHeadData_Call) Run(run func(ctx context.Context, addr common.Address, confidence primitives.ConfidenceLevel, params any, returnVal any)) *Reader_GetLatestValueWithHeadData_Call {
+func (_c *Reader_GetLatestValueWithHeadData_Call) Run(run func(ctx context.Context, addr common.Address, confidence primitives.ConfidenceLevel, params interface{}, returnVal interface{})) *Reader_GetLatestValueWithHeadData_Call {
_c.Call.Run(func(args mock.Arguments) {
- run(args[0].(context.Context), args[1].(common.Address), args[2].(primitives.ConfidenceLevel), args[3].(any), args[4].(any))
+ run(args[0].(context.Context), args[1].(common.Address), args[2].(primitives.ConfidenceLevel), args[3].(interface{}), args[4].(interface{}))
})
return _c
}
@@ -207,13 +207,13 @@ func (_c *Reader_GetLatestValueWithHeadData_Call) Return(_a0 *types.Head, _a1 er
return _c
}
-func (_c *Reader_GetLatestValueWithHeadData_Call) RunAndReturn(run func(context.Context, common.Address, primitives.ConfidenceLevel, any, any) (*types.Head, error)) *Reader_GetLatestValueWithHeadData_Call {
+func (_c *Reader_GetLatestValueWithHeadData_Call) RunAndReturn(run func(context.Context, common.Address, primitives.ConfidenceLevel, interface{}, interface{}) (*types.Head, error)) *Reader_GetLatestValueWithHeadData_Call {
_c.Call.Return(run)
return _c
}
// QueryKey provides a mock function with given fields: _a0, _a1, _a2, _a3, _a4
-func (_m *Reader) QueryKey(_a0 context.Context, _a1 common.Address, _a2 query.KeyFilter, _a3 query.LimitAndSort, _a4 any) ([]types.Sequence, error) {
+func (_m *Reader) QueryKey(_a0 context.Context, _a1 common.Address, _a2 query.KeyFilter, _a3 query.LimitAndSort, _a4 interface{}) ([]types.Sequence, error) {
ret := _m.Called(_a0, _a1, _a2, _a3, _a4)
if len(ret) == 0 {
@@ -222,10 +222,10 @@ func (_m *Reader) QueryKey(_a0 context.Context, _a1 common.Address, _a2 query.Ke
var r0 []types.Sequence
var r1 error
- if rf, ok := ret.Get(0).(func(context.Context, common.Address, query.KeyFilter, query.LimitAndSort, any) ([]types.Sequence, error)); ok {
+ if rf, ok := ret.Get(0).(func(context.Context, common.Address, query.KeyFilter, query.LimitAndSort, interface{}) ([]types.Sequence, error)); ok {
return rf(_a0, _a1, _a2, _a3, _a4)
}
- if rf, ok := ret.Get(0).(func(context.Context, common.Address, query.KeyFilter, query.LimitAndSort, any) []types.Sequence); ok {
+ if rf, ok := ret.Get(0).(func(context.Context, common.Address, query.KeyFilter, query.LimitAndSort, interface{}) []types.Sequence); ok {
r0 = rf(_a0, _a1, _a2, _a3, _a4)
} else {
if ret.Get(0) != nil {
@@ -233,7 +233,7 @@ func (_m *Reader) QueryKey(_a0 context.Context, _a1 common.Address, _a2 query.Ke
}
}
- if rf, ok := ret.Get(1).(func(context.Context, common.Address, query.KeyFilter, query.LimitAndSort, any) error); ok {
+ if rf, ok := ret.Get(1).(func(context.Context, common.Address, query.KeyFilter, query.LimitAndSort, interface{}) error); ok {
r1 = rf(_a0, _a1, _a2, _a3, _a4)
} else {
r1 = ret.Error(1)
@@ -252,14 +252,14 @@ type Reader_QueryKey_Call struct {
// - _a1 common.Address
// - _a2 query.KeyFilter
// - _a3 query.LimitAndSort
-// - _a4 any
+// - _a4 interface{}
func (_e *Reader_Expecter) QueryKey(_a0 interface{}, _a1 interface{}, _a2 interface{}, _a3 interface{}, _a4 interface{}) *Reader_QueryKey_Call {
return &Reader_QueryKey_Call{Call: _e.mock.On("QueryKey", _a0, _a1, _a2, _a3, _a4)}
}
-func (_c *Reader_QueryKey_Call) Run(run func(_a0 context.Context, _a1 common.Address, _a2 query.KeyFilter, _a3 query.LimitAndSort, _a4 any)) *Reader_QueryKey_Call {
+func (_c *Reader_QueryKey_Call) Run(run func(_a0 context.Context, _a1 common.Address, _a2 query.KeyFilter, _a3 query.LimitAndSort, _a4 interface{})) *Reader_QueryKey_Call {
_c.Call.Run(func(args mock.Arguments) {
- run(args[0].(context.Context), args[1].(common.Address), args[2].(query.KeyFilter), args[3].(query.LimitAndSort), args[4].(any))
+ run(args[0].(context.Context), args[1].(common.Address), args[2].(query.KeyFilter), args[3].(query.LimitAndSort), args[4].(interface{}))
})
return _c
}
@@ -269,7 +269,7 @@ func (_c *Reader_QueryKey_Call) Return(_a0 []types.Sequence, _a1 error) *Reader_
return _c
}
-func (_c *Reader_QueryKey_Call) RunAndReturn(run func(context.Context, common.Address, query.KeyFilter, query.LimitAndSort, any) ([]types.Sequence, error)) *Reader_QueryKey_Call {
+func (_c *Reader_QueryKey_Call) RunAndReturn(run func(context.Context, common.Address, query.KeyFilter, query.LimitAndSort, interface{}) ([]types.Sequence, error)) *Reader_QueryKey_Call {
_c.Call.Return(run)
return _c
}
@@ -349,7 +349,7 @@ func (_c *Reader_SetCodec_Call) Return() *Reader_SetCodec_Call {
}
func (_c *Reader_SetCodec_Call) RunAndReturn(run func(types.RemoteCodec)) *Reader_SetCodec_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
diff --git a/core/services/relay/evm/read/mocks/registrar.go b/core/services/relay/evm/read/mocks/registrar.go
index 433a8771396..472e68622be 100644
--- a/core/services/relay/evm/read/mocks/registrar.go
+++ b/core/services/relay/evm/read/mocks/registrar.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
diff --git a/core/services/relay/evm/statuschecker/mocks/ccip_transaction_status_checker.go b/core/services/relay/evm/statuschecker/mocks/ccip_transaction_status_checker.go
index 4bbda1d75bc..82841882328 100644
--- a/core/services/relay/evm/statuschecker/mocks/ccip_transaction_status_checker.go
+++ b/core/services/relay/evm/statuschecker/mocks/ccip_transaction_status_checker.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
diff --git a/core/services/relay/evm/types/mocks/log_poller_wrapper.go b/core/services/relay/evm/types/mocks/log_poller_wrapper.go
index ee4946b0e4d..de9f9deca6c 100644
--- a/core/services/relay/evm/types/mocks/log_poller_wrapper.go
+++ b/core/services/relay/evm/types/mocks/log_poller_wrapper.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -22,7 +22,7 @@ func (_m *LogPollerWrapper) EXPECT() *LogPollerWrapper_Expecter {
return &LogPollerWrapper_Expecter{mock: &_m.Mock}
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *LogPollerWrapper) Close() error {
ret := _m.Called()
@@ -67,7 +67,7 @@ func (_c *LogPollerWrapper_Close_Call) RunAndReturn(run func() error) *LogPoller
return _c
}
-// HealthReport provides a mock function with given fields:
+// HealthReport provides a mock function with no fields
func (_m *LogPollerWrapper) HealthReport() map[string]error {
ret := _m.Called()
@@ -181,7 +181,7 @@ func (_c *LogPollerWrapper_LatestEvents_Call) RunAndReturn(run func(context.Cont
return _c
}
-// Name provides a mock function with given fields:
+// Name provides a mock function with no fields
func (_m *LogPollerWrapper) Name() string {
ret := _m.Called()
@@ -226,7 +226,7 @@ func (_c *LogPollerWrapper_Name_Call) RunAndReturn(run func() string) *LogPoller
return _c
}
-// Ready provides a mock function with given fields:
+// Ready provides a mock function with no fields
func (_m *LogPollerWrapper) Ready() error {
ret := _m.Called()
@@ -348,7 +348,7 @@ func (_c *LogPollerWrapper_SubscribeToUpdates_Call) Return() *LogPollerWrapper_S
}
func (_c *LogPollerWrapper_SubscribeToUpdates_Call) RunAndReturn(run func(context.Context, string, types.RouteUpdateSubscriber)) *LogPollerWrapper_SubscribeToUpdates_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
diff --git a/core/services/s4/mocks/orm.go b/core/services/s4/mocks/orm.go
index 36593dbe85c..a4ac8f7af37 100644
--- a/core/services/s4/mocks/orm.go
+++ b/core/services/s4/mocks/orm.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
diff --git a/core/services/s4/mocks/storage.go b/core/services/s4/mocks/storage.go
index e6fdb3b41b9..00ec97962a9 100644
--- a/core/services/s4/mocks/storage.go
+++ b/core/services/s4/mocks/storage.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -25,7 +25,7 @@ func (_m *Storage) EXPECT() *Storage_Expecter {
return &Storage_Expecter{mock: &_m.Mock}
}
-// Constraints provides a mock function with given fields:
+// Constraints provides a mock function with no fields
func (_m *Storage) Constraints() s4.Constraints {
ret := _m.Called()
diff --git a/core/services/synchronization/mocks/telem_client.go b/core/services/synchronization/mocks/telem_client.go
index 5e4066d07b0..bc90bf2ffd6 100644
--- a/core/services/synchronization/mocks/telem_client.go
+++ b/core/services/synchronization/mocks/telem_client.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
diff --git a/core/services/synchronization/mocks/telemetry_service.go b/core/services/synchronization/mocks/telemetry_service.go
index c6450abd7ea..cd3d64de352 100644
--- a/core/services/synchronization/mocks/telemetry_service.go
+++ b/core/services/synchronization/mocks/telemetry_service.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -22,7 +22,7 @@ func (_m *TelemetryService) EXPECT() *TelemetryService_Expecter {
return &TelemetryService_Expecter{mock: &_m.Mock}
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *TelemetryService) Close() error {
ret := _m.Called()
@@ -67,7 +67,7 @@ func (_c *TelemetryService_Close_Call) RunAndReturn(run func() error) *Telemetry
return _c
}
-// HealthReport provides a mock function with given fields:
+// HealthReport provides a mock function with no fields
func (_m *TelemetryService) HealthReport() map[string]error {
ret := _m.Called()
@@ -114,7 +114,7 @@ func (_c *TelemetryService_HealthReport_Call) RunAndReturn(run func() map[string
return _c
}
-// Name provides a mock function with given fields:
+// Name provides a mock function with no fields
func (_m *TelemetryService) Name() string {
ret := _m.Called()
@@ -159,7 +159,7 @@ func (_c *TelemetryService_Name_Call) RunAndReturn(run func() string) *Telemetry
return _c
}
-// Ready provides a mock function with given fields:
+// Ready provides a mock function with no fields
func (_m *TelemetryService) Ready() error {
ret := _m.Called()
@@ -236,7 +236,7 @@ func (_c *TelemetryService_Send_Call) Return() *TelemetryService_Send_Call {
}
func (_c *TelemetryService_Send_Call) RunAndReturn(run func(context.Context, []byte, string, synchronization.TelemetryType)) *TelemetryService_Send_Call {
- _c.Call.Return(run)
+ _c.Run(run)
return _c
}
diff --git a/core/services/telemetry/monitoring_endpoint_generator_mock.go b/core/services/telemetry/monitoring_endpoint_generator_mock.go
index 9715d864f59..0f0c6a07dab 100644
--- a/core/services/telemetry/monitoring_endpoint_generator_mock.go
+++ b/core/services/telemetry/monitoring_endpoint_generator_mock.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package telemetry
diff --git a/core/services/vrf/mocks/aggregator_v3_interface.go b/core/services/vrf/mocks/aggregator_v3_interface.go
index 3bd209b538f..d4e9fd255e2 100644
--- a/core/services/vrf/mocks/aggregator_v3_interface.go
+++ b/core/services/vrf/mocks/aggregator_v3_interface.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -27,7 +27,7 @@ func (_m *AggregatorV3InterfaceInterface) EXPECT() *AggregatorV3InterfaceInterfa
return &AggregatorV3InterfaceInterface_Expecter{mock: &_m.Mock}
}
-// Address provides a mock function with given fields:
+// Address provides a mock function with no fields
func (_m *AggregatorV3InterfaceInterface) Address() common.Address {
ret := _m.Called()
diff --git a/core/services/vrf/mocks/config.go b/core/services/vrf/mocks/config.go
index fa908d30f56..b6195933599 100644
--- a/core/services/vrf/mocks/config.go
+++ b/core/services/vrf/mocks/config.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -17,7 +17,7 @@ func (_m *Config) EXPECT() *Config_Expecter {
return &Config_Expecter{mock: &_m.Mock}
}
-// FinalityDepth provides a mock function with given fields:
+// FinalityDepth provides a mock function with no fields
func (_m *Config) FinalityDepth() uint32 {
ret := _m.Called()
@@ -62,7 +62,7 @@ func (_c *Config_FinalityDepth_Call) RunAndReturn(run func() uint32) *Config_Fin
return _c
}
-// MinIncomingConfirmations provides a mock function with given fields:
+// MinIncomingConfirmations provides a mock function with no fields
func (_m *Config) MinIncomingConfirmations() uint32 {
ret := _m.Called()
diff --git a/core/services/vrf/mocks/fee_config.go b/core/services/vrf/mocks/fee_config.go
index 3f3ca570460..ee0e869ffba 100644
--- a/core/services/vrf/mocks/fee_config.go
+++ b/core/services/vrf/mocks/fee_config.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -24,7 +24,7 @@ func (_m *FeeConfig) EXPECT() *FeeConfig_Expecter {
return &FeeConfig_Expecter{mock: &_m.Mock}
}
-// LimitDefault provides a mock function with given fields:
+// LimitDefault provides a mock function with no fields
func (_m *FeeConfig) LimitDefault() uint64 {
ret := _m.Called()
@@ -69,7 +69,7 @@ func (_c *FeeConfig_LimitDefault_Call) RunAndReturn(run func() uint64) *FeeConfi
return _c
}
-// LimitJobType provides a mock function with given fields:
+// LimitJobType provides a mock function with no fields
func (_m *FeeConfig) LimitJobType() config.LimitJobType {
ret := _m.Called()
diff --git a/core/services/vrf/mocks/vrf_coordinator_v2.go b/core/services/vrf/mocks/vrf_coordinator_v2.go
index 578ea83ce61..8db8fca7219 100644
--- a/core/services/vrf/mocks/vrf_coordinator_v2.go
+++ b/core/services/vrf/mocks/vrf_coordinator_v2.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -209,7 +209,7 @@ func (_c *VRFCoordinatorV2Interface_AddConsumer_Call) RunAndReturn(run func(*bin
return _c
}
-// Address provides a mock function with given fields:
+// Address provides a mock function with no fields
func (_m *VRFCoordinatorV2Interface) Address() common.Address {
ret := _m.Called()
diff --git a/core/services/webhook/mocks/external_initiator_manager.go b/core/services/webhook/mocks/external_initiator_manager.go
index e622217f0e5..3d3afc4d895 100644
--- a/core/services/webhook/mocks/external_initiator_manager.go
+++ b/core/services/webhook/mocks/external_initiator_manager.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
diff --git a/core/services/webhook/mocks/http_client.go b/core/services/webhook/mocks/http_client.go
index ff79cd20c8d..e795914057e 100644
--- a/core/services/webhook/mocks/http_client.go
+++ b/core/services/webhook/mocks/http_client.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
diff --git a/core/services/workflows/syncer/mocks/orm.go b/core/services/workflows/syncer/mocks/orm.go
index 09a543d65e3..29f26701bd0 100644
--- a/core/services/workflows/syncer/mocks/orm.go
+++ b/core/services/workflows/syncer/mocks/orm.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
diff --git a/core/sessions/ldapauth/mocks/ldap_client.go b/core/sessions/ldapauth/mocks/ldap_client.go
index 8f905c3cf70..7a09b6e1658 100644
--- a/core/sessions/ldapauth/mocks/ldap_client.go
+++ b/core/sessions/ldapauth/mocks/ldap_client.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -20,7 +20,7 @@ func (_m *LDAPClient) EXPECT() *LDAPClient_Expecter {
return &LDAPClient_Expecter{mock: &_m.Mock}
}
-// CreateEphemeralConnection provides a mock function with given fields:
+// CreateEphemeralConnection provides a mock function with no fields
func (_m *LDAPClient) CreateEphemeralConnection() (ldapauth.LDAPConn, error) {
ret := _m.Called()
diff --git a/core/sessions/ldapauth/mocks/ldap_conn.go b/core/sessions/ldapauth/mocks/ldap_conn.go
index 63ba3ae5757..ccd9948f2f4 100644
--- a/core/sessions/ldapauth/mocks/ldap_conn.go
+++ b/core/sessions/ldapauth/mocks/ldap_conn.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
@@ -68,7 +68,7 @@ func (_c *LDAPConn_Bind_Call) RunAndReturn(run func(string, string) error) *LDAP
return _c
}
-// Close provides a mock function with given fields:
+// Close provides a mock function with no fields
func (_m *LDAPConn) Close() error {
ret := _m.Called()
diff --git a/core/sessions/mocks/authentication_provider.go b/core/sessions/mocks/authentication_provider.go
index c319ebb0e60..844dc0f7d65 100644
--- a/core/sessions/mocks/authentication_provider.go
+++ b/core/sessions/mocks/authentication_provider.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
diff --git a/core/sessions/mocks/basic_admin_users_orm.go b/core/sessions/mocks/basic_admin_users_orm.go
index bf9c7f919cb..b69479f248e 100644
--- a/core/sessions/mocks/basic_admin_users_orm.go
+++ b/core/sessions/mocks/basic_admin_users_orm.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package mocks
diff --git a/deployment/mocks/offchain_client_mock.go b/deployment/mocks/offchain_client_mock.go
index de7a6df3a0d..f760cd1fda3 100644
--- a/deployment/mocks/offchain_client_mock.go
+++ b/deployment/mocks/offchain_client_mock.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.46.3. DO NOT EDIT.
+// Code generated by mockery v2.50.0. DO NOT EDIT.
package deployment
From 5b503a3c02801809533012cd73b5f7c492f73ac8 Mon Sep 17 00:00:00 2001
From: Awbrey Hughlett
Date: Tue, 7 Jan 2025 12:52:19 -0500
Subject: [PATCH 07/91] Custom Fallback TOML Config (#15617)
* Custom Fallback TOML Config
This commit provides using an existing env var `CL_CHAIN_DEFAULTS` as a path to a custom `fallback.toml`. This allows
plugins to define their own set of fallback options apart from the core node which override the default fallback
options.
* collapse helper functions into single helper function and reduce indirection
* fix test
---
.changeset/tall-falcons-yawn.md | 5 +
core/chains/evm/config/toml/defaults.go | 146 +++--
.../node/validate/fallback-override.txtar | 552 ++++++++++++++++++
3 files changed, 640 insertions(+), 63 deletions(-)
create mode 100644 .changeset/tall-falcons-yawn.md
create mode 100644 testdata/scripts/node/validate/fallback-override.txtar
diff --git a/.changeset/tall-falcons-yawn.md b/.changeset/tall-falcons-yawn.md
new file mode 100644
index 00000000000..98b90e5994b
--- /dev/null
+++ b/.changeset/tall-falcons-yawn.md
@@ -0,0 +1,5 @@
+---
+"chainlink": patch
+---
+
+#added the ability to define a fallback.toml override config using CL_CHAIN_DEFAULTS env var
diff --git a/core/chains/evm/config/toml/defaults.go b/core/chains/evm/config/toml/defaults.go
index 6f03575056b..60da9bded1b 100644
--- a/core/chains/evm/config/toml/defaults.go
+++ b/core/chains/evm/config/toml/defaults.go
@@ -4,7 +4,7 @@ import (
"bytes"
"embed"
"fmt"
- "io"
+ "io/fs"
"log"
"os"
"path/filepath"
@@ -19,7 +19,6 @@ import (
)
var (
-
//go:embed defaults/*.toml
defaultsFS embed.FS
fallback Chain
@@ -33,48 +32,24 @@ var (
)
func init() {
- // read the defaults first
+ var (
+ fb *Chain
+ err error
+ )
- fes, err := defaultsFS.ReadDir("defaults")
+ // read all default configs
+ DefaultIDs, defaultNames, defaults, fb, err = initDefaults(defaultsFS.ReadDir, defaultsFS.ReadFile, "defaults")
if err != nil {
- log.Fatalf("failed to read defaults/: %v", err)
+ log.Fatalf("failed to read defaults: %s", err)
}
- for _, fe := range fes {
- path := filepath.Join("defaults", fe.Name())
- b, err2 := defaultsFS.ReadFile(path)
- if err2 != nil {
- log.Fatalf("failed to read %q: %v", path, err2)
- }
- var config = struct {
- ChainID *big.Big
- Chain
- }{}
- if err3 := cconfig.DecodeTOML(bytes.NewReader(b), &config); err3 != nil {
- log.Fatalf("failed to decode %q: %v", path, err3)
- }
- if fe.Name() == "fallback.toml" {
- if config.ChainID != nil {
- log.Fatalf("fallback ChainID must be nil, not: %s", config.ChainID)
- }
- fallback = config.Chain
- continue
- }
- if config.ChainID == nil {
- log.Fatalf("missing ChainID: %s", path)
- }
- DefaultIDs = append(DefaultIDs, config.ChainID)
- id := config.ChainID.String()
- if _, ok := defaults[id]; ok {
- log.Fatalf("%q contains duplicate ChainID: %s", path, id)
- }
- defaults[id] = config.Chain
- defaultNames[id] = strings.ReplaceAll(strings.TrimSuffix(fe.Name(), ".toml"), "_", " ")
+ if fb == nil {
+ log.Fatal("failed to set fallback chain config")
}
- slices.SortFunc(DefaultIDs, func(a, b *big.Big) int {
- return a.Cmp(b)
- })
+ fallback = *fb
+
+ // check for and apply any overrides
// read the custom defaults overrides
dir := env.CustomDefaults.Get()
if dir == "" {
@@ -83,54 +58,99 @@ func init() {
}
// use evm overrides specifically
- evmDir := fmt.Sprintf("%s/evm", dir)
+ _, _, customDefaults, fb, err = initDefaults(os.ReadDir, os.ReadFile, dir+"/evm")
+ if err != nil {
+ log.Fatalf("failed to read custom overrides: %s", err)
+ }
- // Read directory contents for evm only
- entries, err := os.ReadDir(evmDir)
+ if fb != nil {
+ fallback = *fb
+ }
+}
+
+func initDefaults(
+ dirReader func(name string) ([]fs.DirEntry, error),
+ fileReader func(name string) ([]byte, error),
+ root string,
+) ([]*big.Big, map[string]string, map[string]Chain, *Chain, error) {
+ entries, err := dirReader(root)
if err != nil {
- log.Fatalf("error reading evm custom defaults override directory: %v", err)
- return
+ return nil, nil, nil, nil, fmt.Errorf("failed to read directory: %w", err)
}
+ var fb *Chain
+
+ ids := make([]*big.Big, 0)
+ configs := make(map[string]Chain)
+ names := make(map[string]string)
+
for _, entry := range entries {
if entry.IsDir() {
// Skip directories
continue
}
- path := evmDir + "/" + entry.Name()
- file, err := os.Open(path)
- if err != nil {
- log.Fatalf("error opening file (name: %v) in custom defaults override directory: %v", entry.Name(), err)
- }
+ // read the file to bytes
+ path := filepath.Join(root, entry.Name())
- // Read file contents
- b, err := io.ReadAll(file)
- file.Close()
+ chainID, chain, err := readConfig(path, fileReader)
if err != nil {
- log.Fatalf("error reading file (name: %v) contents in custom defaults override directory: %v", entry.Name(), err)
+ return nil, nil, nil, nil, err
}
- var config = struct {
- ChainID *big.Big
- Chain
- }{}
+ if entry.Name() == "fallback.toml" {
+ if chainID != nil {
+ return nil, nil, nil, nil, fmt.Errorf("fallback ChainID must be nil: found: %s", chainID)
+ }
+
+ fb = &chain
- if err := cconfig.DecodeTOML(bytes.NewReader(b), &config); err != nil {
- log.Fatalf("failed to decode %q in custom defaults override directory: %v", path, err)
+ continue
}
- if config.ChainID == nil {
- log.Fatalf("missing ChainID in: %s in custom defaults override directory. exiting", path)
+ // ensure ChainID is set
+ if chainID == nil {
+ return nil, nil, nil, nil, fmt.Errorf("missing ChainID: %s", path)
}
- id := config.ChainID.String()
+ ids = append(ids, chainID)
- if _, ok := customDefaults[id]; ok {
+ // ChainID as a default should not be duplicated
+ id := chainID.String()
+ if _, ok := configs[id]; ok {
log.Fatalf("%q contains duplicate ChainID: %s", path, id)
}
- customDefaults[id] = config.Chain
+
+ // set lookups
+ configs[id] = chain
+ names[id] = strings.ReplaceAll(strings.TrimSuffix(entry.Name(), ".toml"), "_", " ")
}
+
+ // sort IDs in numeric order
+ slices.SortFunc(ids, func(a, b *big.Big) int {
+ return a.Cmp(b)
+ })
+
+ return ids, names, configs, fb, nil
+}
+
+func readConfig(path string, reader func(name string) ([]byte, error)) (*big.Big, Chain, error) {
+ bts, err := reader(path)
+ if err != nil {
+ return nil, Chain{}, fmt.Errorf("error reading file: %w", err)
+ }
+
+ var config = struct {
+ ChainID *big.Big
+ Chain
+ }{}
+
+ // decode from toml to a chain config
+ if err := cconfig.DecodeTOML(bytes.NewReader(bts), &config); err != nil {
+ return nil, Chain{}, fmt.Errorf("error in TOML decoding %s: %w", path, err)
+ }
+
+ return config.ChainID, config.Chain, nil
}
// DefaultsNamed returns the default Chain values, optionally for the given chainID, as well as a name if the chainID is known.
diff --git a/testdata/scripts/node/validate/fallback-override.txtar b/testdata/scripts/node/validate/fallback-override.txtar
new file mode 100644
index 00000000000..91feb48693d
--- /dev/null
+++ b/testdata/scripts/node/validate/fallback-override.txtar
@@ -0,0 +1,552 @@
+# test with defaults
+env CL_CHAIN_DEFAULTS=
+exec chainlink node -c config.toml -s secrets.toml validate
+cmp stdout out.txt
+
+# test with fallback override
+env CL_CHAIN_DEFAULTS=default_overrides
+exec chainlink node -c config.toml -s secrets.toml validate
+! cmp stdout out.txt
+
+-- default_overrides/evm/fallback.toml --
+AutoCreateKey = true
+BlockBackfillDepth = 1000000
+BlockBackfillSkip = false
+FinalityDepth = 50
+FinalityTagEnabled = false
+LogBackfillBatchSize = 1000
+LogPollInterval = '15s'
+LogKeepBlocksDepth = 100000
+LogPrunePageSize = 0
+BackupLogPollerBlockDelay = 100
+MinContractPayment = '.00001 link'
+MinIncomingConfirmations = 3
+NonceAutoSync = true
+NoNewHeadsThreshold = '3m'
+RPCDefaultBatchSize = 250
+RPCBlockQueryDelay = 1
+FinalizedBlockOffset = 0
+NoNewFinalizedHeadsThreshold = '0'
+LogBroadcasterEnabled = true
+
+[Transactions]
+ForwardersEnabled = false
+MaxInFlight = 16
+MaxQueued = 250
+ReaperInterval = '1h'
+ReaperThreshold = '168h'
+ResendAfterThreshold = '1m'
+
+[Transactions.AutoPurge]
+Enabled = false
+
+[BalanceMonitor]
+Enabled = true
+
+[GasEstimator]
+Mode = 'BlockHistory'
+PriceDefault = '20 gwei'
+PriceMax = '115792089237316195423570985008687907853269984665.640564039457584007913129639935 tether'
+PriceMin = '1 gwei'
+LimitDefault = 500_000
+LimitMax = 500_000
+LimitMultiplier = '1'
+LimitTransfer = 21_000
+BumpMin = '5 gwei'
+BumpPercent = 20
+BumpThreshold = 3
+EIP1559DynamicFees = false
+FeeCapDefault = '100 gwei'
+TipCapDefault = '1'
+TipCapMin = '1'
+EstimateLimit = false
+
+[GasEstimator.BlockHistory]
+BatchSize = 25
+BlockHistorySize = 8
+CheckInclusionBlocks = 12
+CheckInclusionPercentile = 90
+TransactionPercentile = 60
+
+[GasEstimator.FeeHistory]
+CacheTimeout = '10s'
+
+[HeadTracker]
+HistoryDepth = 100
+MaxBufferSize = 3
+SamplingInterval = '1s'
+FinalityTagBypass = true
+MaxAllowedFinalityDepth = 10000
+PersistenceEnabled = true
+
+[NodePool]
+PollFailureThreshold = 5
+PollInterval = '10s'
+SelectionMode = 'HighestHead'
+SyncThreshold = 5
+LeaseDuration = '0s'
+NodeIsSyncingEnabled = false
+FinalizedBlockPollInterval = '5s'
+EnforceRepeatableRead = true
+DeathDeclarationDelay = '1m'
+NewHeadsPollInterval = '0s'
+
+[OCR]
+ContractConfirmations = 4
+ContractTransmitterTransmitTimeout = '10s'
+DatabaseTimeout = '10s'
+DeltaCOverride = '168h'
+DeltaCJitterOverride = '1h'
+ObservationGracePeriod = '1s'
+
+[OCR2.Automation]
+GasLimit = 5400000
+
+[Workflow]
+GasLimitDefault = 400_000
+
+-- config.toml --
+Log.Level = 'debug'
+
+[[EVM]]
+ChainID = '1'
+
+[[EVM.Nodes]]
+Name = 'fake'
+WSURL = 'wss://foo.bar/ws'
+HTTPURL = 'https://foo.bar'
+
+-- secrets.toml --
+[Database]
+URL = 'postgresql://user:pass1234567890abcd@localhost:5432/dbname?sslmode=disable'
+
+[Password]
+Keystore = 'keystore_pass'
+
+-- out.txt --
+# Secrets:
+[Database]
+URL = 'xxxxx'
+AllowSimplePasswords = false
+
+[Password]
+Keystore = 'xxxxx'
+
+# Input Configuration:
+[Log]
+Level = 'debug'
+
+[[EVM]]
+ChainID = '1'
+
+[[EVM.Nodes]]
+Name = 'fake'
+WSURL = 'wss://foo.bar/ws'
+HTTPURL = 'https://foo.bar'
+
+# Effective Configuration, with defaults applied:
+InsecureFastScrypt = false
+RootDir = '~/.chainlink'
+ShutdownGracePeriod = '5s'
+
+[Feature]
+FeedsManager = true
+LogPoller = false
+UICSAKeys = false
+CCIP = true
+MultiFeedsManagers = false
+
+[Database]
+DefaultIdleInTxSessionTimeout = '1h0m0s'
+DefaultLockTimeout = '15s'
+DefaultQueryTimeout = '10s'
+LogQueries = false
+MaxIdleConns = 10
+MaxOpenConns = 100
+MigrateOnStartup = true
+
+[Database.Backup]
+Dir = ''
+Frequency = '1h0m0s'
+Mode = 'none'
+OnVersionUpgrade = true
+
+[Database.Listener]
+MaxReconnectDuration = '10m0s'
+MinReconnectInterval = '1m0s'
+FallbackPollInterval = '30s'
+
+[Database.Lock]
+Enabled = true
+LeaseDuration = '10s'
+LeaseRefreshInterval = '1s'
+
+[TelemetryIngress]
+UniConn = false
+Logging = false
+BufferSize = 100
+MaxBatchSize = 50
+SendInterval = '500ms'
+SendTimeout = '10s'
+UseBatchSend = true
+
+[AuditLogger]
+Enabled = false
+ForwardToUrl = ''
+JsonWrapperKey = ''
+Headers = []
+
+[Log]
+Level = 'debug'
+JSONConsole = false
+UnixTS = false
+
+[Log.File]
+Dir = ''
+MaxSize = '5.12gb'
+MaxAgeDays = 0
+MaxBackups = 1
+
+[WebServer]
+AuthenticationMethod = 'local'
+AllowOrigins = 'http://localhost:3000,http://localhost:6688'
+BridgeResponseURL = ''
+BridgeCacheTTL = '0s'
+HTTPWriteTimeout = '10s'
+HTTPPort = 6688
+SecureCookies = true
+SessionTimeout = '15m0s'
+SessionReaperExpiration = '240h0m0s'
+HTTPMaxSize = '32.77kb'
+StartTimeout = '15s'
+ListenIP = '0.0.0.0'
+
+[WebServer.LDAP]
+ServerTLS = true
+SessionTimeout = '15m0s'
+QueryTimeout = '2m0s'
+BaseUserAttr = 'uid'
+BaseDN = ''
+UsersDN = 'ou=users'
+GroupsDN = 'ou=groups'
+ActiveAttribute = ''
+ActiveAttributeAllowedValue = ''
+AdminUserGroupCN = 'NodeAdmins'
+EditUserGroupCN = 'NodeEditors'
+RunUserGroupCN = 'NodeRunners'
+ReadUserGroupCN = 'NodeReadOnly'
+UserApiTokenEnabled = false
+UserAPITokenDuration = '240h0m0s'
+UpstreamSyncInterval = '0s'
+UpstreamSyncRateLimit = '2m0s'
+
+[WebServer.MFA]
+RPID = ''
+RPOrigin = ''
+
+[WebServer.RateLimit]
+Authenticated = 1000
+AuthenticatedPeriod = '1m0s'
+Unauthenticated = 5
+UnauthenticatedPeriod = '20s'
+
+[WebServer.TLS]
+CertPath = ''
+ForceRedirect = false
+Host = ''
+HTTPSPort = 6689
+KeyPath = ''
+ListenIP = '0.0.0.0'
+
+[JobPipeline]
+ExternalInitiatorsEnabled = false
+MaxRunDuration = '10m0s'
+MaxSuccessfulRuns = 10000
+ReaperInterval = '1h0m0s'
+ReaperThreshold = '24h0m0s'
+ResultWriteQueueDepth = 100
+VerboseLogging = true
+
+[JobPipeline.HTTPRequest]
+DefaultTimeout = '15s'
+MaxSize = '32.77kb'
+
+[FluxMonitor]
+DefaultTransactionQueueDepth = 1
+SimulateTransactions = false
+
+[OCR2]
+Enabled = false
+ContractConfirmations = 3
+BlockchainTimeout = '20s'
+ContractPollInterval = '1m0s'
+ContractSubscribeInterval = '2m0s'
+ContractTransmitterTransmitTimeout = '10s'
+DatabaseTimeout = '10s'
+KeyBundleID = '0000000000000000000000000000000000000000000000000000000000000000'
+CaptureEATelemetry = false
+CaptureAutomationCustomTelemetry = true
+DefaultTransactionQueueDepth = 1
+SimulateTransactions = false
+TraceLogging = false
+
+[OCR]
+Enabled = false
+ObservationTimeout = '5s'
+BlockchainTimeout = '20s'
+ContractPollInterval = '1m0s'
+ContractSubscribeInterval = '2m0s'
+DefaultTransactionQueueDepth = 1
+KeyBundleID = '0000000000000000000000000000000000000000000000000000000000000000'
+SimulateTransactions = false
+TransmitterAddress = ''
+CaptureEATelemetry = false
+TraceLogging = false
+
+[P2P]
+IncomingMessageBufferSize = 10
+OutgoingMessageBufferSize = 10
+PeerID = ''
+TraceLogging = false
+
+[P2P.V2]
+Enabled = true
+AnnounceAddresses = []
+DefaultBootstrappers = []
+DeltaDial = '15s'
+DeltaReconcile = '1m0s'
+ListenAddresses = []
+
+[Keeper]
+DefaultTransactionQueueDepth = 1
+GasPriceBufferPercent = 20
+GasTipCapBufferPercent = 20
+BaseFeeBufferPercent = 20
+MaxGracePeriod = 100
+TurnLookBack = 1000
+
+[Keeper.Registry]
+CheckGasOverhead = 200000
+PerformGasOverhead = 300000
+MaxPerformDataSize = 5000
+SyncInterval = '30m0s'
+SyncUpkeepQueueSize = 10
+
+[AutoPprof]
+Enabled = false
+ProfileRoot = ''
+PollInterval = '10s'
+GatherDuration = '10s'
+GatherTraceDuration = '5s'
+MaxProfileSize = '100.00mb'
+CPUProfileRate = 1
+MemProfileRate = 1
+BlockProfileRate = 1
+MutexProfileFraction = 1
+MemThreshold = '4.00gb'
+GoroutineThreshold = 5000
+
+[Pyroscope]
+ServerAddress = ''
+Environment = 'mainnet'
+
+[Sentry]
+Debug = false
+DSN = ''
+Environment = ''
+Release = ''
+
+[Insecure]
+DevWebServer = false
+OCRDevelopmentMode = false
+InfiniteDepthQueries = false
+DisableRateLimiting = false
+
+[Tracing]
+Enabled = false
+CollectorTarget = ''
+NodeID = ''
+SamplingRatio = 0.0
+Mode = 'tls'
+TLSCertPath = ''
+
+[Mercury]
+VerboseLogging = false
+
+[Mercury.Cache]
+LatestReportTTL = '1s'
+MaxStaleAge = '1h0m0s'
+LatestReportDeadline = '5s'
+
+[Mercury.TLS]
+CertFile = ''
+
+[Mercury.Transmitter]
+TransmitQueueMaxSize = 10000
+TransmitTimeout = '5s'
+TransmitConcurrency = 100
+
+[Capabilities]
+[Capabilities.Peering]
+IncomingMessageBufferSize = 10
+OutgoingMessageBufferSize = 10
+PeerID = ''
+TraceLogging = false
+
+[Capabilities.Peering.V2]
+Enabled = false
+AnnounceAddresses = []
+DefaultBootstrappers = []
+DeltaDial = '15s'
+DeltaReconcile = '1m0s'
+ListenAddresses = []
+
+[Capabilities.Dispatcher]
+SupportedVersion = 1
+ReceiverBufferSize = 10000
+
+[Capabilities.Dispatcher.RateLimit]
+GlobalRPS = 800.0
+GlobalBurst = 1000
+PerSenderRPS = 10.0
+PerSenderBurst = 50
+
+[Capabilities.ExternalRegistry]
+Address = ''
+NetworkID = 'evm'
+ChainID = '1'
+
+[Capabilities.WorkflowRegistry]
+Address = ''
+NetworkID = 'evm'
+ChainID = '1'
+
+[Capabilities.GatewayConnector]
+ChainIDForNodeKey = ''
+NodeAddress = ''
+DonID = ''
+WSHandshakeTimeoutMillis = 0
+AuthMinChallengeLen = 0
+AuthTimestampToleranceSec = 0
+
+[[Capabilities.GatewayConnector.Gateways]]
+ID = ''
+URL = ''
+
+[Telemetry]
+Enabled = false
+CACertFile = ''
+Endpoint = ''
+InsecureConnection = false
+TraceSampleRatio = 0.01
+EmitterBatchProcessor = true
+EmitterExportTimeout = '1s'
+
+[[EVM]]
+ChainID = '1'
+AutoCreateKey = true
+BlockBackfillDepth = 10
+BlockBackfillSkip = false
+FinalityDepth = 50
+FinalityTagEnabled = true
+LinkContractAddress = '0x514910771AF9Ca656af840dff83E8264EcF986CA'
+LogBackfillBatchSize = 1000
+LogPollInterval = '15s'
+LogKeepBlocksDepth = 100000
+LogPrunePageSize = 0
+BackupLogPollerBlockDelay = 100
+MinIncomingConfirmations = 3
+MinContractPayment = '0.1 link'
+NonceAutoSync = true
+NoNewHeadsThreshold = '3m0s'
+OperatorFactoryAddress = '0x3E64Cd889482443324F91bFA9c84fE72A511f48A'
+LogBroadcasterEnabled = true
+RPCDefaultBatchSize = 250
+RPCBlockQueryDelay = 1
+FinalizedBlockOffset = 0
+NoNewFinalizedHeadsThreshold = '9m0s'
+
+[EVM.Transactions]
+Enabled = true
+ForwardersEnabled = false
+MaxInFlight = 16
+MaxQueued = 250
+ReaperInterval = '1h0m0s'
+ReaperThreshold = '168h0m0s'
+ResendAfterThreshold = '1m0s'
+
+[EVM.Transactions.AutoPurge]
+Enabled = false
+
+[EVM.BalanceMonitor]
+Enabled = true
+
+[EVM.GasEstimator]
+Mode = 'BlockHistory'
+PriceDefault = '20 gwei'
+PriceMax = '115792089237316195423570985008687907853269984665.640564039457584007913129639935 tether'
+PriceMin = '1 gwei'
+LimitDefault = 500000
+LimitMax = 500000
+LimitMultiplier = '1'
+LimitTransfer = 21000
+EstimateLimit = false
+BumpMin = '5 gwei'
+BumpPercent = 20
+BumpThreshold = 3
+EIP1559DynamicFees = true
+FeeCapDefault = '100 gwei'
+TipCapDefault = '1 wei'
+TipCapMin = '1 wei'
+
+[EVM.GasEstimator.BlockHistory]
+BatchSize = 25
+BlockHistorySize = 4
+CheckInclusionBlocks = 12
+CheckInclusionPercentile = 90
+TransactionPercentile = 50
+
+[EVM.GasEstimator.FeeHistory]
+CacheTimeout = '10s'
+
+[EVM.HeadTracker]
+HistoryDepth = 100
+MaxBufferSize = 3
+SamplingInterval = '1s'
+MaxAllowedFinalityDepth = 10000
+FinalityTagBypass = true
+PersistenceEnabled = true
+
+[EVM.NodePool]
+PollFailureThreshold = 5
+PollInterval = '10s'
+SelectionMode = 'HighestHead'
+SyncThreshold = 5
+LeaseDuration = '0s'
+NodeIsSyncingEnabled = false
+FinalizedBlockPollInterval = '5s'
+EnforceRepeatableRead = true
+DeathDeclarationDelay = '1m0s'
+NewHeadsPollInterval = '0s'
+
+[EVM.OCR]
+ContractConfirmations = 4
+ContractTransmitterTransmitTimeout = '10s'
+DatabaseTimeout = '10s'
+DeltaCOverride = '168h0m0s'
+DeltaCJitterOverride = '1h0m0s'
+ObservationGracePeriod = '1s'
+
+[EVM.OCR2]
+[EVM.OCR2.Automation]
+GasLimit = 10500000
+
+[EVM.Workflow]
+GasLimitDefault = 400000
+
+[[EVM.Nodes]]
+Name = 'fake'
+WSURL = 'wss://foo.bar/ws'
+HTTPURL = 'https://foo.bar'
+
+Valid configuration.
From 99f21e9b99b51874a7f2b730ead592adabd379c3 Mon Sep 17 00:00:00 2001
From: Jordan Krage
Date: Tue, 7 Jan 2025 13:22:20 -0600
Subject: [PATCH 08/91] deployment: golangci-lint run --fix (#15838)
---
.golangci.yml | 1 -
deployment/.golangci.yml | 1 -
deployment/address_book.go | 10 ++---
deployment/ccip/changeset/cs_ccip_home.go | 45 ++++++++++---------
.../ccip/changeset/cs_ccip_home_test.go | 4 +-
.../ccip/changeset/cs_chain_contracts.go | 9 ++--
.../ccip/changeset/cs_chain_contracts_test.go | 4 +-
deployment/ccip/changeset/cs_deploy_chain.go | 17 +++----
deployment/ccip/changeset/cs_home_chain.go | 18 ++++----
.../ccip/changeset/cs_home_chain_test.go | 4 +-
.../ccip/changeset/cs_update_rmn_config.go | 7 +--
deployment/ccip/changeset/state.go | 3 +-
deployment/ccip/changeset/test_assertions.go | 11 ++---
deployment/ccip/changeset/test_environment.go | 10 ++---
deployment/ccip/changeset/test_helpers.go | 10 ++---
.../ccip/changeset/test_usdc_helpers.go | 4 +-
deployment/ccip/changeset/v1_5/cs_jobspec.go | 5 ++-
.../ccip/view/v1_0/rmn_proxy_contract.go | 3 +-
deployment/ccip/view/v1_2/price_registry.go | 3 +-
.../ccip/view/v1_2/price_registry_test.go | 8 ++--
deployment/ccip/view/v1_5/commit_store.go | 3 +-
deployment/ccip/view/v1_5/offramp.go | 3 +-
deployment/ccip/view/v1_5/offramp_test.go | 2 +-
deployment/ccip/view/v1_5/onramp.go | 3 +-
deployment/ccip/view/v1_5/onramp_test.go | 3 +-
deployment/ccip/view/v1_5/rmn.go | 3 +-
deployment/ccip/view/v1_5/rmn_test.go | 4 +-
.../ccip/view/v1_5/tokenadminregistry.go | 3 +-
deployment/ccip/view/v1_6/ccip_home.go | 3 +-
deployment/ccip/view/v1_6/ccip_home_test.go | 2 +-
deployment/ccip/view/v1_6/rmnhome.go | 1 +
.../common/changeset/deploy_link_token.go | 4 +-
.../example/add_mint_burners_link.go | 2 -
.../common/changeset/example/link_transfer.go | 3 --
.../changeset/example/link_transfer_test.go | 1 -
.../common/changeset/example/mint_link.go | 2 -
deployment/common/changeset/internal/mcms.go | 6 +--
deployment/common/changeset/save_existing.go | 6 +--
.../common/changeset/set_config_mcms.go | 1 -
.../common/changeset/set_config_mcms_test.go | 4 +-
deployment/common/changeset/test_helpers.go | 1 +
.../transfer_to_mcms_with_timelock.go | 16 +++----
.../common/proposalutils/mcms_helpers.go | 11 ++---
deployment/common/proposalutils/propose.go | 4 +-
deployment/common/types/types.go | 26 +++++------
deployment/common/view/nops.go | 6 ++-
deployment/common/view/v1_0/capreg_test.go | 4 +-
deployment/common/view/v1_0/link_token.go | 4 +-
.../common/view/v1_0/link_token_test.go | 12 ++---
.../common/view/v1_0/static_link_token.go | 4 +-
.../view/v1_0/static_link_token_test.go | 6 +--
deployment/environment.go | 3 +-
deployment/environment/crib/types.go | 1 +
deployment/environment/devenv/chain.go | 3 +-
deployment/environment/devenv/don.go | 6 ++-
deployment/environment/devenv/don_test.go | 1 -
deployment/environment/devenv/environment.go | 5 ++-
deployment/environment/devenv/jd.go | 5 ++-
deployment/environment/memory/job_client.go | 30 ++++++-------
.../environment/nodeclient/chainlink.go | 11 ++---
.../environment/web/sdk/client/client.go | 38 ++++++++--------
.../environment/web/sdk/client/types.go | 4 +-
deployment/evm_kmsclient.go | 15 ++++---
deployment/helpers.go | 3 +-
.../changeset/append_node_capabilities.go | 4 +-
.../append_node_capabilities_test.go | 7 ++-
.../keystone/changeset/deploy_consumer.go | 3 +-
.../changeset/deploy_consumer_test.go | 2 +-
.../keystone/changeset/deploy_forwarder.go | 6 ++-
.../changeset/deploy_forwarder_test.go | 11 ++---
deployment/keystone/changeset/deploy_ocr3.go | 8 ++--
.../keystone/changeset/deploy_ocr3_test.go | 7 +--
.../keystone/changeset/deploy_registry.go | 3 +-
.../changeset/deploy_registry_test.go | 2 +-
.../internal/append_node_capabilities.go | 5 ++-
.../internal/capability_management.go | 1 +
.../changeset/internal/contract_set.go | 1 -
.../keystone/changeset/internal/deploy.go | 10 ++---
.../changeset/internal/forwarder_deployer.go | 1 +
.../changeset/internal/ocr3_deployer.go | 1 +
.../keystone/changeset/internal/ocr3config.go | 4 +-
.../changeset/internal/ocr3config_test.go | 2 +-
.../keystone/changeset/internal/types.go | 13 +++---
.../keystone/changeset/internal/types_test.go | 7 +--
.../keystone/changeset/internal/update_don.go | 8 ++--
.../changeset/internal/update_don_test.go | 7 ++-
.../internal/update_node_capabilities.go | 5 ++-
.../changeset/internal/update_nodes.go | 3 +-
.../changeset/internal/update_nodes_test.go | 2 -
deployment/keystone/changeset/update_don.go | 11 +++--
.../keystone/changeset/update_don_test.go | 6 +--
.../changeset/update_node_capabilities.go | 6 ++-
.../update_node_capabilities_test.go | 9 ++--
deployment/keystone/changeset/update_nodes.go | 6 ++-
.../keystone/changeset/update_nodes_test.go | 7 ++-
deployment/keystone/changeset/view.go | 1 -
.../changeset/workflowregistry/deploy.go | 3 +-
.../changeset/workflowregistry/deploy_test.go | 2 +-
.../changeset/workflowregistry/setup_test.go | 3 +-
.../update_allowed_dons_test.go | 6 +--
.../update_authorized_addresses_test.go | 4 +-
.../workflow_registry_deployer.go | 1 -
deployment/multiclient_test.go | 4 +-
integration-tests/.golangci.yml | 1 -
104 files changed, 330 insertions(+), 318 deletions(-)
diff --git a/.golangci.yml b/.golangci.yml
index 63b061c2951..d35b6459e05 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -8,7 +8,6 @@ linters:
- errname
- errorlint
- exhaustive
- - exportloopref
- fatcontext
- ginkgolinter
- gocritic
diff --git a/deployment/.golangci.yml b/deployment/.golangci.yml
index ff1303e26ce..7341210ce00 100644
--- a/deployment/.golangci.yml
+++ b/deployment/.golangci.yml
@@ -8,7 +8,6 @@ linters:
- errname
- errorlint
- exhaustive
- - exportloopref
- fatcontext
- ginkgolinter
- gocritic
diff --git a/deployment/address_book.go b/deployment/address_book.go
index 3ce0332a4c3..fde0adc2d97 100644
--- a/deployment/address_book.go
+++ b/deployment/address_book.go
@@ -14,9 +14,9 @@ import (
)
var (
- ErrInvalidChainSelector = fmt.Errorf("invalid chain selector")
- ErrInvalidAddress = fmt.Errorf("invalid address")
- ErrChainNotFound = fmt.Errorf("chain not found")
+ ErrInvalidChainSelector = errors.New("invalid chain selector")
+ ErrInvalidAddress = errors.New("invalid address")
+ ErrChainNotFound = errors.New("chain not found")
)
// ContractType is a simple string type for identifying contract types.
@@ -117,7 +117,7 @@ func (m *AddressBookMap) save(chainSelector uint64, address string, typeAndVersi
// TODO NONEVM-960: Add validation for non-EVM chain addresses
if typeAndVersion.Type == "" {
- return fmt.Errorf("type cannot be empty")
+ return errors.New("type cannot be empty")
}
if _, exists := m.addressesByChain[chainSelector]; !exists {
@@ -256,7 +256,7 @@ func SearchAddressBook(ab AddressBook, chain uint64, typ ContractType) (string,
}
}
- return "", fmt.Errorf("not found")
+ return "", errors.New("not found")
}
func AddressBookContains(ab AddressBook, chain uint64, addrToFind string) (bool, error) {
diff --git a/deployment/ccip/changeset/cs_ccip_home.go b/deployment/ccip/changeset/cs_ccip_home.go
index 0c82afee261..7d3327a31f2 100644
--- a/deployment/ccip/changeset/cs_ccip_home.go
+++ b/deployment/ccip/changeset/cs_ccip_home.go
@@ -3,6 +3,7 @@ package changeset
import (
"bytes"
"encoding/hex"
+ "errors"
"fmt"
"math/big"
"os"
@@ -139,7 +140,7 @@ func (p PromoteCandidatesChangesetConfig) Validate(e deployment.Environment) ([]
if p.PluginType != types.PluginTypeCCIPCommit &&
p.PluginType != types.PluginTypeCCIPExec {
- return nil, fmt.Errorf("PluginType must be set to either CCIPCommit or CCIPExec")
+ return nil, errors.New("PluginType must be set to either CCIPCommit or CCIPExec")
}
var donIDs []uint32
@@ -153,7 +154,7 @@ func (p PromoteCandidatesChangesetConfig) Validate(e deployment.Environment) ([]
}
if chainState.OffRamp == nil {
// should not be possible, but a defensive check.
- return nil, fmt.Errorf("OffRamp contract does not exist")
+ return nil, errors.New("OffRamp contract does not exist")
}
donID, err := internal.DonIDForChain(
@@ -182,13 +183,13 @@ func (p PromoteCandidatesChangesetConfig) Validate(e deployment.Environment) ([]
donIDs = append(donIDs, donID)
}
if len(e.NodeIDs) == 0 {
- return nil, fmt.Errorf("NodeIDs must be set")
+ return nil, errors.New("NodeIDs must be set")
}
if state.Chains[p.HomeChainSelector].CCIPHome == nil {
- return nil, fmt.Errorf("CCIPHome contract does not exist")
+ return nil, errors.New("CCIPHome contract does not exist")
}
if state.Chains[p.HomeChainSelector].CapabilityRegistry == nil {
- return nil, fmt.Errorf("CapabilityRegistry contract does not exist")
+ return nil, errors.New("CapabilityRegistry contract does not exist")
}
return donIDs, nil
@@ -316,7 +317,7 @@ func (s SetCandidateConfigBase) Validate(e deployment.Environment, state CCIPOnC
}
if s.PluginType != types.PluginTypeCCIPCommit &&
s.PluginType != types.PluginTypeCCIPExec {
- return fmt.Errorf("PluginType must be set to either CCIPCommit or CCIPExec")
+ return errors.New("PluginType must be set to either CCIPCommit or CCIPExec")
}
// no donID check since this config is used for both adding a new DON and updating an existing one.
@@ -340,17 +341,17 @@ func (s SetCandidateConfigBase) Validate(e deployment.Environment, state CCIPOnC
// TODO: validate gas config in the chain config in cciphome for this RemoteChainSelectors.
}
if len(e.NodeIDs) == 0 {
- return fmt.Errorf("nodeIDs must be set")
+ return errors.New("nodeIDs must be set")
}
if state.Chains[s.HomeChainSelector].CCIPHome == nil {
- return fmt.Errorf("CCIPHome contract does not exist")
+ return errors.New("CCIPHome contract does not exist")
}
if state.Chains[s.HomeChainSelector].CapabilityRegistry == nil {
- return fmt.Errorf("CapabilityRegistry contract does not exist")
+ return errors.New("CapabilityRegistry contract does not exist")
}
if e.OCRSecrets.IsEmpty() {
- return fmt.Errorf("OCR secrets must be set")
+ return errors.New("OCR secrets must be set")
}
return nil
@@ -443,7 +444,7 @@ func AddDonAndSetCandidateChangeset(
pluginOCR3Config, ok := newDONArgs[cfg.PluginType]
if !ok {
- return deployment.ChangesetOutput{}, fmt.Errorf("missing commit plugin in ocr3Configs")
+ return deployment.ChangesetOutput{}, errors.New("missing commit plugin in ocr3Configs")
}
expectedDonID := latestDon.Id + 1
@@ -476,7 +477,7 @@ func AddDonAndSetCandidateChangeset(
ChainIdentifier: mcms.ChainIdentifier(cfg.HomeChainSelector),
Batch: donOps,
}},
- fmt.Sprintf("addDON on new Chain && setCandidate for plugin %s", cfg.PluginType.String()),
+ "addDON on new Chain && setCandidate for plugin "+cfg.PluginType.String(),
cfg.MCMS.MinDelay,
)
if err != nil {
@@ -671,7 +672,7 @@ func setCandidateOnExistingDon(
mcmsEnabled bool,
) ([]mcms.Operation, error) {
if donID == 0 {
- return nil, fmt.Errorf("donID is zero")
+ return nil, errors.New("donID is zero")
}
encodedSetCandidateCall, err := internal.CCIPHomeABI.Pack(
@@ -791,7 +792,7 @@ func promoteAllCandidatesForChainOps(
mcmsEnabled bool,
) (mcms.Operation, error) {
if donID == 0 {
- return mcms.Operation{}, fmt.Errorf("donID is zero")
+ return mcms.Operation{}, errors.New("donID is zero")
}
updatePluginOp, err := promoteCandidateOp(
@@ -831,13 +832,13 @@ func (r RevokeCandidateChangesetConfig) Validate(e deployment.Environment, state
return 0, fmt.Errorf("don chain selector invalid: %w", err)
}
if len(e.NodeIDs) == 0 {
- return 0, fmt.Errorf("NodeIDs must be set")
+ return 0, errors.New("NodeIDs must be set")
}
if state.Chains[r.HomeChainSelector].CCIPHome == nil {
- return 0, fmt.Errorf("CCIPHome contract does not exist")
+ return 0, errors.New("CCIPHome contract does not exist")
}
if state.Chains[r.HomeChainSelector].CapabilityRegistry == nil {
- return 0, fmt.Errorf("CapabilityRegistry contract does not exist")
+ return 0, errors.New("CapabilityRegistry contract does not exist")
}
homeChainState, exists := state.Chains[r.HomeChainSelector]
if !exists {
@@ -866,7 +867,7 @@ func (r RevokeCandidateChangesetConfig) Validate(e deployment.Environment, state
return 0, fmt.Errorf("fetching candidate digest from cciphome: %w", err)
}
if candidateDigest == [32]byte{} {
- return 0, fmt.Errorf("candidate config digest is zero, can't revoke it")
+ return 0, errors.New("candidate config digest is zero, can't revoke it")
}
return donID, nil
@@ -947,7 +948,7 @@ func revokeCandidateOps(
mcmsEnabled bool,
) ([]mcms.Operation, error) {
if donID == 0 {
- return nil, fmt.Errorf("donID is zero")
+ return nil, errors.New("donID is zero")
}
candidateDigest, err := ccipHome.GetCandidateDigest(nil, donID, pluginType)
@@ -1017,7 +1018,7 @@ func (c UpdateChainConfigConfig) Validate(e deployment.Environment) error {
return fmt.Errorf("home chain selector invalid: %w", err)
}
if len(c.RemoteChainRemoves) == 0 && len(c.RemoteChainAdds) == 0 {
- return fmt.Errorf("no chain adds or removes")
+ return errors.New("no chain adds or removes")
}
homeChainState, exists := state.Chains[c.HomeChainSelector]
if !exists {
@@ -1042,10 +1043,10 @@ func (c UpdateChainConfigConfig) Validate(e deployment.Environment) error {
return fmt.Errorf("chain to add %d is not supported", add)
}
if ccfg.FChain == 0 {
- return fmt.Errorf("FChain must be set")
+ return errors.New("FChain must be set")
}
if len(ccfg.Readers) == 0 {
- return fmt.Errorf("Readers must be set")
+ return errors.New("Readers must be set")
}
}
return nil
diff --git a/deployment/ccip/changeset/cs_ccip_home_test.go b/deployment/ccip/changeset/cs_ccip_home_test.go
index dae32557f8b..eb22f05a703 100644
--- a/deployment/ccip/changeset/cs_ccip_home_test.go
+++ b/deployment/ccip/changeset/cs_ccip_home_test.go
@@ -459,7 +459,7 @@ func Test_UpdateChainConfigs(t *testing.T) {
ccipHome := state.Chains[tenv.HomeChainSel].CCIPHome
otherChainConfig, err := ccipHome.GetChainConfig(nil, otherChain)
require.NoError(t, err)
- assert.True(t, otherChainConfig.FChain != 0)
+ assert.NotZero(t, otherChainConfig.FChain)
var mcmsConfig *MCMSConfig
if tc.mcmsEnabled {
@@ -488,7 +488,7 @@ func Test_UpdateChainConfigs(t *testing.T) {
// other chain should be gone
chainConfigAfter, err := ccipHome.GetChainConfig(nil, otherChain)
require.NoError(t, err)
- assert.True(t, chainConfigAfter.FChain == 0)
+ assert.Zero(t, chainConfigAfter.FChain)
// Lets add it back now.
_, err = commonchangeset.ApplyChangesets(t, tenv.Env, map[uint64]*proposalutils.TimelockExecutionContracts{
diff --git a/deployment/ccip/changeset/cs_chain_contracts.go b/deployment/ccip/changeset/cs_chain_contracts.go
index f85814f1768..e87e66e06b5 100644
--- a/deployment/ccip/changeset/cs_chain_contracts.go
+++ b/deployment/ccip/changeset/cs_chain_contracts.go
@@ -221,7 +221,7 @@ func UpdateNonceManagersCS(e deployment.Environment, cfg UpdateNonceManagerConfi
type UpdateOnRampDestsConfig struct {
UpdatesByChain map[uint64]map[uint64]OnRampDestinationUpdate
// Disallow mixing MCMS/non-MCMS per chain for simplicity.
- // (can still be acheived by calling this function multiple times)
+ // (can still be achieved by calling this function multiple times)
MCMS *MCMSConfig
}
@@ -265,7 +265,7 @@ func (cfg UpdateOnRampDestsConfig) Validate(e deployment.Environment) error {
return fmt.Errorf("failed to get onramp static config %s: %w", chainState.OnRamp.Address(), err)
}
if destination == sc.ChainSelector {
- return fmt.Errorf("cannot update onramp destination to the same chain")
+ return errors.New("cannot update onramp destination to the same chain")
}
}
}
@@ -514,7 +514,7 @@ func UpdateFeeQuoterPricesCS(e deployment.Environment, cfg UpdateFeeQuoterPrices
type UpdateFeeQuoterDestsConfig struct {
UpdatesByChain map[uint64]map[uint64]fee_quoter.FeeQuoterDestChainConfig
// Disallow mixing MCMS/non-MCMS per chain for simplicity.
- // (can still be acheived by calling this function multiple times)
+ // (can still be achieved by calling this function multiple times)
MCMS *MCMSConfig
}
@@ -552,7 +552,7 @@ func (cfg UpdateFeeQuoterDestsConfig) Validate(e deployment.Environment) error {
return fmt.Errorf("failed to get onramp static config %s: %w", chainState.OnRamp.Address(), err)
}
if destination == sc.ChainSelector {
- return fmt.Errorf("source and destination chain cannot be the same")
+ return errors.New("source and destination chain cannot be the same")
}
}
}
@@ -824,7 +824,6 @@ func (cfg UpdateRouterRampsConfig) Validate(e deployment.Environment) error {
return fmt.Errorf("missing offramp for dest %d", destination)
}
}
-
}
return nil
}
diff --git a/deployment/ccip/changeset/cs_chain_contracts_test.go b/deployment/ccip/changeset/cs_chain_contracts_test.go
index 0a1e0ce3b7b..adbcc078373 100644
--- a/deployment/ccip/changeset/cs_chain_contracts_test.go
+++ b/deployment/ccip/changeset/cs_chain_contracts_test.go
@@ -81,11 +81,11 @@ func TestUpdateOnRampsDests(t *testing.T) {
sourceCfg, err := state.Chains[source].OnRamp.GetDestChainConfig(&bind.CallOpts{Context: ctx}, dest)
require.NoError(t, err)
require.Equal(t, state.Chains[source].TestRouter.Address(), sourceCfg.Router)
- require.Equal(t, false, sourceCfg.AllowlistEnabled)
+ require.False(t, sourceCfg.AllowlistEnabled)
destCfg, err := state.Chains[dest].OnRamp.GetDestChainConfig(&bind.CallOpts{Context: ctx}, source)
require.NoError(t, err)
require.Equal(t, state.Chains[dest].Router.Address(), destCfg.Router)
- require.Equal(t, true, destCfg.AllowlistEnabled)
+ require.True(t, destCfg.AllowlistEnabled)
})
}
}
diff --git a/deployment/ccip/changeset/cs_deploy_chain.go b/deployment/ccip/changeset/cs_deploy_chain.go
index 5a6085202a9..68655377f2e 100644
--- a/deployment/ccip/changeset/cs_deploy_chain.go
+++ b/deployment/ccip/changeset/cs_deploy_chain.go
@@ -1,6 +1,7 @@
package changeset
import (
+ "errors"
"fmt"
"math/big"
@@ -81,7 +82,7 @@ func deployChainContractsForChains(
capReg := existingState.Chains[homeChainSel].CapabilityRegistry
if capReg == nil {
e.Logger.Errorw("Failed to get capability registry")
- return fmt.Errorf("capability registry not found")
+ return errors.New("capability registry not found")
}
cr, err := capReg.GetHashedCapabilityId(
&bind.CallOpts{}, internal.CapabilityLabelledName, internal.CapabilityVersion)
@@ -105,12 +106,12 @@ func deployChainContractsForChains(
return err
}
if ccipHome.Address() != existingState.Chains[homeChainSel].CCIPHome.Address() {
- return fmt.Errorf("ccip home address mismatch")
+ return errors.New("ccip home address mismatch")
}
rmnHome := existingState.Chains[homeChainSel].RMNHome
if rmnHome == nil {
e.Logger.Errorw("Failed to get rmn home", "err", err)
- return fmt.Errorf("rmn home not found")
+ return errors.New("rmn home not found")
}
deployGrp := errgroup.Group{}
for _, chainSel := range chainsToDeploy {
@@ -203,7 +204,7 @@ func deployChainContracts(
rmnLegacyAddr,
)
return deployment.ContractDeploy[*rmn_remote.RMNRemote]{
- rmnRemoteAddr, rmnRemote, tx, deployment.NewTypeAndVersion(RMNRemote, deployment.Version1_6_0_dev), err2,
+ Address: rmnRemoteAddr, Contract: rmnRemote, Tx: tx, Tv: deployment.NewTypeAndVersion(RMNRemote, deployment.Version1_6_0_dev), Err: err2,
}
})
if err != nil {
@@ -243,7 +244,7 @@ func deployChainContracts(
RMNProxy.Address(),
)
return deployment.ContractDeploy[*router.Router]{
- routerAddr, routerC, tx2, deployment.NewTypeAndVersion(TestRouter, deployment.Version1_2_0), err2,
+ Address: routerAddr, Contract: routerC, Tx: tx2, Tv: deployment.NewTypeAndVersion(TestRouter, deployment.Version1_2_0), Err: err2,
}
})
if err != nil {
@@ -264,7 +265,7 @@ func deployChainContracts(
[]common.Address{}, // Need to add onRamp after
)
return deployment.ContractDeploy[*nonce_manager.NonceManager]{
- nonceManagerAddr, nonceManager, tx2, deployment.NewTypeAndVersion(NonceManager, deployment.Version1_6_0_dev), err2,
+ Address: nonceManagerAddr, Contract: nonceManager, Tx: tx2, Tv: deployment.NewTypeAndVersion(NonceManager, deployment.Version1_6_0_dev), Err: err2,
}
})
if err != nil {
@@ -304,7 +305,7 @@ func deployChainContracts(
[]fee_quoter.FeeQuoterDestChainConfigArgs{},
)
return deployment.ContractDeploy[*fee_quoter.FeeQuoter]{
- prAddr, pr, tx2, deployment.NewTypeAndVersion(FeeQuoter, deployment.Version1_6_0_dev), err2,
+ Address: prAddr, Contract: pr, Tx: tx2, Tv: deployment.NewTypeAndVersion(FeeQuoter, deployment.Version1_6_0_dev), Err: err2,
}
})
if err != nil {
@@ -335,7 +336,7 @@ func deployChainContracts(
[]onramp.OnRampDestChainConfigArgs{},
)
return deployment.ContractDeploy[*onramp.OnRamp]{
- onRampAddr, onRamp, tx2, deployment.NewTypeAndVersion(OnRamp, deployment.Version1_6_0_dev), err2,
+ Address: onRampAddr, Contract: onRamp, Tx: tx2, Tv: deployment.NewTypeAndVersion(OnRamp, deployment.Version1_6_0_dev), Err: err2,
}
})
if err != nil {
diff --git a/deployment/ccip/changeset/cs_home_chain.go b/deployment/ccip/changeset/cs_home_chain.go
index b92a8d132a4..3b985f5c526 100644
--- a/deployment/ccip/changeset/cs_home_chain.go
+++ b/deployment/ccip/changeset/cs_home_chain.go
@@ -61,23 +61,23 @@ type DeployHomeChainConfig struct {
func (c DeployHomeChainConfig) Validate() error {
if c.HomeChainSel == 0 {
- return fmt.Errorf("home chain selector must be set")
+ return errors.New("home chain selector must be set")
}
if c.RMNDynamicConfig.OffchainConfig == nil {
- return fmt.Errorf("offchain config for RMNHomeDynamicConfig must be set")
+ return errors.New("offchain config for RMNHomeDynamicConfig must be set")
}
if c.RMNStaticConfig.OffchainConfig == nil {
- return fmt.Errorf("offchain config for RMNHomeStaticConfig must be set")
+ return errors.New("offchain config for RMNHomeStaticConfig must be set")
}
if len(c.NodeOperators) == 0 {
- return fmt.Errorf("node operators must be set")
+ return errors.New("node operators must be set")
}
for _, nop := range c.NodeOperators {
if nop.Admin == (common.Address{}) {
- return fmt.Errorf("node operator admin address must be set")
+ return errors.New("node operator admin address must be set")
}
if nop.Name == "" {
- return fmt.Errorf("node operator name must be set")
+ return errors.New("node operator name must be set")
}
if len(c.NodeP2PIDsPerNodeOpAdmin[nop.Name]) == 0 {
return fmt.Errorf("node operator %s must have node p2p ids provided", nop.Name)
@@ -338,14 +338,14 @@ func (c RemoveDONsConfig) Validate(homeChain CCIPChainState) error {
return fmt.Errorf("home chain selector must be set %w", err)
}
if len(c.DonIDs) == 0 {
- return fmt.Errorf("don ids must be set")
+ return errors.New("don ids must be set")
}
// Cap reg must exist
if homeChain.CapabilityRegistry == nil {
- return fmt.Errorf("cap reg does not exist")
+ return errors.New("cap reg does not exist")
}
if homeChain.CCIPHome == nil {
- return fmt.Errorf("ccip home does not exist")
+ return errors.New("ccip home does not exist")
}
if err := internal.DONIdExists(homeChain.CapabilityRegistry, c.DonIDs); err != nil {
return err
diff --git a/deployment/ccip/changeset/cs_home_chain_test.go b/deployment/ccip/changeset/cs_home_chain_test.go
index 8a2d4f87709..e96cd878305 100644
--- a/deployment/ccip/changeset/cs_home_chain_test.go
+++ b/deployment/ccip/changeset/cs_home_chain_test.go
@@ -52,12 +52,12 @@ func TestDeployHomeChain(t *testing.T) {
capRegSnap, ok := snap[chainName].CapabilityRegistry[state.Chains[homeChainSel].CapabilityRegistry.Address().String()]
require.True(t, ok)
require.NotNil(t, capRegSnap)
- require.Equal(t, capRegSnap.Nops, []v1_0.NopView{
+ require.Equal(t, []v1_0.NopView{
{
Admin: e.Chains[homeChainSel].DeployerKey.From,
Name: "NodeOperator",
},
- })
+ }, capRegSnap.Nops)
require.Len(t, capRegSnap.Nodes, len(p2pIds))
}
diff --git a/deployment/ccip/changeset/cs_update_rmn_config.go b/deployment/ccip/changeset/cs_update_rmn_config.go
index 96f8eacb4cc..337b3756881 100644
--- a/deployment/ccip/changeset/cs_update_rmn_config.go
+++ b/deployment/ccip/changeset/cs_update_rmn_config.go
@@ -1,6 +1,7 @@
package changeset
import (
+ "errors"
"fmt"
"math/big"
"reflect"
@@ -178,14 +179,14 @@ func (c SetRMNHomeCandidateConfig) Validate(state CCIPOnChainState) error {
}
if len(c.RMNDynamicConfig.OffchainConfig) != 0 {
- return fmt.Errorf("RMNDynamicConfig.OffchainConfig must be empty")
+ return errors.New("RMNDynamicConfig.OffchainConfig must be empty")
}
if len(c.RMNStaticConfig.OffchainConfig) != 0 {
- return fmt.Errorf("RMNStaticConfig.OffchainConfig must be empty")
+ return errors.New("RMNStaticConfig.OffchainConfig must be empty")
}
if len(c.RMNStaticConfig.Nodes) > 256 {
- return fmt.Errorf("RMNStaticConfig.Nodes must be less than 256")
+ return errors.New("RMNStaticConfig.Nodes must be less than 256")
}
var (
diff --git a/deployment/ccip/changeset/state.go b/deployment/ccip/changeset/state.go
index b50724eaa16..aa07168a6d2 100644
--- a/deployment/ccip/changeset/state.go
+++ b/deployment/ccip/changeset/state.go
@@ -2,6 +2,7 @@ package changeset
import (
"fmt"
+ "strconv"
"github.com/smartcontractkit/ccip-owner-contracts/pkg/gethwrappers"
@@ -366,7 +367,7 @@ func (s CCIPOnChainState) View(chains []uint64) (map[string]view.ChainView, erro
}
name := chainInfo.ChainName
if chainInfo.ChainName == "" {
- name = fmt.Sprintf("%d", chainSelector)
+ name = strconv.FormatUint(chainSelector, 10)
}
m[name] = chainView
}
diff --git a/deployment/ccip/changeset/test_assertions.go b/deployment/ccip/changeset/test_assertions.go
index bcfb49250d4..c83d6e3597a 100644
--- a/deployment/ccip/changeset/test_assertions.go
+++ b/deployment/ccip/changeset/test_assertions.go
@@ -2,6 +2,7 @@ package changeset
import (
"context"
+ "errors"
"fmt"
"math/big"
"sync"
@@ -360,12 +361,12 @@ func ConfirmCommitWithExpectedSeqNumRange(
if mr.SourceChainSelector == src.Selector &&
uint64(expectedSeqNumRange.Start()) >= mr.MinSeqNr &&
uint64(expectedSeqNumRange.End()) <= mr.MaxSeqNr {
- t.Logf("All sequence numbers commited in a single report [%d, %d]", expectedSeqNumRange.Start(), expectedSeqNumRange.End())
+ t.Logf("All sequence numbers committed in a single report [%d, %d]", expectedSeqNumRange.Start(), expectedSeqNumRange.End())
return event, nil
}
if !enforceSingleCommit && seenMessages.allCommited(src.Selector) {
- t.Logf("All sequence numbers already commited from range [%d, %d]", expectedSeqNumRange.Start(), expectedSeqNumRange.End())
+ t.Logf("All sequence numbers already committed from range [%d, %d]", expectedSeqNumRange.Start(), expectedSeqNumRange.End())
return event, nil
}
}
@@ -389,12 +390,12 @@ func ConfirmCommitWithExpectedSeqNumRange(
if mr.SourceChainSelector == src.Selector &&
uint64(expectedSeqNumRange.Start()) >= mr.MinSeqNr &&
uint64(expectedSeqNumRange.End()) <= mr.MaxSeqNr {
- t.Logf("All sequence numbers commited in a single report [%d, %d]", expectedSeqNumRange.Start(), expectedSeqNumRange.End())
+ t.Logf("All sequence numbers committed in a single report [%d, %d]", expectedSeqNumRange.Start(), expectedSeqNumRange.End())
return report, nil
}
if !enforceSingleCommit && seenMessages.allCommited(src.Selector) {
- t.Logf("All sequence numbers already commited from range [%d, %d]", expectedSeqNumRange.Start(), expectedSeqNumRange.End())
+ t.Logf("All sequence numbers already committed from range [%d, %d]", expectedSeqNumRange.Start(), expectedSeqNumRange.End())
return report, nil
}
}
@@ -482,7 +483,7 @@ func ConfirmExecWithSeqNrs(
expectedSeqNrs []uint64,
) (executionStates map[uint64]int, err error) {
if len(expectedSeqNrs) == 0 {
- return nil, fmt.Errorf("no expected sequence numbers provided")
+ return nil, errors.New("no expected sequence numbers provided")
}
timer := time.NewTimer(8 * time.Minute)
diff --git a/deployment/ccip/changeset/test_environment.go b/deployment/ccip/changeset/test_environment.go
index f723efbf619..a1307d9820b 100644
--- a/deployment/ccip/changeset/test_environment.go
+++ b/deployment/ccip/changeset/test_environment.go
@@ -2,7 +2,7 @@ package changeset
import (
"context"
- "fmt"
+ "errors"
"math/big"
"os"
"testing"
@@ -61,16 +61,16 @@ type TestConfigs struct {
func (tc *TestConfigs) Validate() error {
if tc.Chains < 2 {
- return fmt.Errorf("chains must be at least 2")
+ return errors.New("chains must be at least 2")
}
if tc.Nodes < 4 {
- return fmt.Errorf("nodes must be at least 4")
+ return errors.New("nodes must be at least 4")
}
if tc.Bootstraps < 1 {
- return fmt.Errorf("bootstraps must be at least 1")
+ return errors.New("bootstraps must be at least 1")
}
if tc.Type == Memory && tc.RMNEnabled {
- return fmt.Errorf("cannot run RMN tests in memory mode")
+ return errors.New("cannot run RMN tests in memory mode")
}
return nil
}
diff --git a/deployment/ccip/changeset/test_helpers.go b/deployment/ccip/changeset/test_helpers.go
index 2069030191c..03c3ffb175d 100644
--- a/deployment/ccip/changeset/test_helpers.go
+++ b/deployment/ccip/changeset/test_helpers.go
@@ -582,7 +582,7 @@ func deploySingleFeed(
deployFunc func(deployment.Chain) deployment.ContractDeploy[*aggregator_v3_interface.AggregatorV3Interface],
symbol TokenSymbol,
) (common.Address, string, error) {
- //tokenTV := deployment.NewTypeAndVersion(PriceFeed, deployment.Version1_0_0)
+ // tokenTV := deployment.NewTypeAndVersion(PriceFeed, deployment.Version1_0_0)
mockTokenFeed, err := deployment.DeployContract(lggr, chain, ab, deployFunc)
if err != nil {
lggr.Errorw("Failed to deploy token feed", "err", err, "symbol", symbol)
@@ -736,7 +736,7 @@ func deployTokenPoolsInParallel(
return nil, nil, nil, nil, err
}
if srcToken == nil || srcPool == nil || dstToken == nil || dstPool == nil {
- return nil, nil, nil, nil, fmt.Errorf("failed to deploy token and pool")
+ return nil, nil, nil, nil, errors.New("failed to deploy token and pool")
}
return srcToken, srcPool, dstToken, dstPool, nil
}
@@ -763,7 +763,7 @@ func setUSDCTokenPoolCounterPart(
var fixedAddr [32]byte
copy(fixedAddr[:], allowedCaller[:32])
- domain, _ := reader.AllAvailableDomains()[destChainSelector]
+ domain := reader.AllAvailableDomains()[destChainSelector]
domains := []usdc_token_pool.USDCTokenPoolDomainUpdate{
{
@@ -917,7 +917,7 @@ func deployTransferTokenOneEnd(
big.NewInt(0).Mul(big.NewInt(1e9), big.NewInt(1e18)),
)
return deployment.ContractDeploy[*burn_mint_erc677.BurnMintERC677]{
- tokenAddress, token, tx, deployment.NewTypeAndVersion(BurnMintToken, deployment.Version1_0_0), err2,
+ Address: tokenAddress, Contract: token, Tx: tx, Tv: deployment.NewTypeAndVersion(BurnMintToken, deployment.Version1_0_0), Err: err2,
}
})
if err != nil {
@@ -946,7 +946,7 @@ func deployTransferTokenOneEnd(
common.HexToAddress(routerAddress),
)
return deployment.ContractDeploy[*burn_mint_token_pool.BurnMintTokenPool]{
- tokenPoolAddress, tokenPoolContract, tx, deployment.NewTypeAndVersion(BurnMintTokenPool, deployment.Version1_5_1), err2,
+ Address: tokenPoolAddress, Contract: tokenPoolContract, Tx: tx, Tv: deployment.NewTypeAndVersion(BurnMintTokenPool, deployment.Version1_5_1), Err: err2,
}
})
if err != nil {
diff --git a/deployment/ccip/changeset/test_usdc_helpers.go b/deployment/ccip/changeset/test_usdc_helpers.go
index 55f1bd25a36..c9dd87b866e 100644
--- a/deployment/ccip/changeset/test_usdc_helpers.go
+++ b/deployment/ccip/changeset/test_usdc_helpers.go
@@ -115,8 +115,8 @@ func UpdateFeeQuoterForUSDC(
DestChainSelector: dstChain,
TokenTransferFeeConfigs: []fee_quoter.FeeQuoterTokenTransferFeeConfigSingleTokenArgs{
{
- usdcToken.Address(),
- fee_quoter.FeeQuoterTokenTransferFeeConfig{
+ Token: usdcToken.Address(),
+ TokenTransferFeeConfig: fee_quoter.FeeQuoterTokenTransferFeeConfig{
MinFeeUSDCents: 50,
MaxFeeUSDCents: 50_000,
DeciBps: 0,
diff --git a/deployment/ccip/changeset/v1_5/cs_jobspec.go b/deployment/ccip/changeset/v1_5/cs_jobspec.go
index bdb36d531f8..e1cd73f1e30 100644
--- a/deployment/ccip/changeset/v1_5/cs_jobspec.go
+++ b/deployment/ccip/changeset/v1_5/cs_jobspec.go
@@ -1,6 +1,7 @@
package v1_5
import (
+ "errors"
"fmt"
"github.com/smartcontractkit/chainlink/deployment"
@@ -41,14 +42,14 @@ func (j JobSpecInput) Validate() error {
return fmt.Errorf("DestinationChainSelector is invalid: %w", err)
}
if j.TokenPricesUSDPipeline == "" && j.PriceGetterConfigJson == "" {
- return fmt.Errorf("TokenPricesUSDPipeline or PriceGetterConfigJson is required")
+ return errors.New("TokenPricesUSDPipeline or PriceGetterConfigJson is required")
}
if j.USDCCfg != nil {
if err := j.USDCCfg.ValidateUSDCConfig(); err != nil {
return fmt.Errorf("USDCCfg is invalid: %w", err)
}
if j.USDCAttestationAPI == "" {
- return fmt.Errorf("USDCAttestationAPI is required")
+ return errors.New("USDCAttestationAPI is required")
}
}
return nil
diff --git a/deployment/ccip/view/v1_0/rmn_proxy_contract.go b/deployment/ccip/view/v1_0/rmn_proxy_contract.go
index 818b9fcac93..5a2ea2807f6 100644
--- a/deployment/ccip/view/v1_0/rmn_proxy_contract.go
+++ b/deployment/ccip/view/v1_0/rmn_proxy_contract.go
@@ -1,6 +1,7 @@
package v1_0
import (
+ "errors"
"fmt"
"github.com/ethereum/go-ethereum/common"
@@ -16,7 +17,7 @@ type RMNProxyView struct {
func GenerateRMNProxyView(r *rmn_proxy_contract.RMNProxy) (RMNProxyView, error) {
if r == nil {
- return RMNProxyView{}, fmt.Errorf("cannot generate view for nil RMNProxy")
+ return RMNProxyView{}, errors.New("cannot generate view for nil RMNProxy")
}
meta, err := types.NewContractMetaData(r, r.Address())
if err != nil {
diff --git a/deployment/ccip/view/v1_2/price_registry.go b/deployment/ccip/view/v1_2/price_registry.go
index ee0f1067b6c..269c48fccaf 100644
--- a/deployment/ccip/view/v1_2/price_registry.go
+++ b/deployment/ccip/view/v1_2/price_registry.go
@@ -1,6 +1,7 @@
package v1_2
import (
+ "errors"
"fmt"
"github.com/ethereum/go-ethereum/common"
@@ -18,7 +19,7 @@ type PriceRegistryView struct {
func GeneratePriceRegistryView(pr *price_registry_1_2_0.PriceRegistry) (PriceRegistryView, error) {
if pr == nil {
- return PriceRegistryView{}, fmt.Errorf("cannot generate view for nil PriceRegistry")
+ return PriceRegistryView{}, errors.New("cannot generate view for nil PriceRegistry")
}
meta, err := types.NewContractMetaData(pr, pr.Address())
if err != nil {
diff --git a/deployment/ccip/view/v1_2/price_registry_test.go b/deployment/ccip/view/v1_2/price_registry_test.go
index cbcdbe253ce..8248f55335b 100644
--- a/deployment/ccip/view/v1_2/price_registry_test.go
+++ b/deployment/ccip/view/v1_2/price_registry_test.go
@@ -29,10 +29,10 @@ func TestGeneratePriceRegistryView(t *testing.T) {
v, err := GeneratePriceRegistryView(c)
require.NoError(t, err)
assert.Equal(t, v.Owner, chain.DeployerKey.From)
- assert.Equal(t, v.TypeAndVersion, "PriceRegistry 1.2.0")
- assert.Equal(t, v.FeeTokens, []common.Address{f1, f2})
- assert.Equal(t, v.StalenessThreshold, "10")
- assert.Equal(t, v.Updaters, []common.Address{chain.DeployerKey.From})
+ assert.Equal(t, "PriceRegistry 1.2.0", v.TypeAndVersion)
+ assert.Equal(t, []common.Address{f1, f2}, v.FeeTokens)
+ assert.Equal(t, "10", v.StalenessThreshold)
+ assert.Equal(t, []common.Address{chain.DeployerKey.From}, v.Updaters)
_, err = json.MarshalIndent(v, "", " ")
require.NoError(t, err)
}
diff --git a/deployment/ccip/view/v1_5/commit_store.go b/deployment/ccip/view/v1_5/commit_store.go
index ffea3b61f5f..396aa8b737a 100644
--- a/deployment/ccip/view/v1_5/commit_store.go
+++ b/deployment/ccip/view/v1_5/commit_store.go
@@ -1,6 +1,7 @@
package v1_5
import (
+ "errors"
"fmt"
"github.com/ethereum/go-ethereum/common"
@@ -24,7 +25,7 @@ type CommitStoreView struct {
func GenerateCommitStoreView(c *commit_store.CommitStore) (CommitStoreView, error) {
if c == nil {
- return CommitStoreView{}, fmt.Errorf("cannot generate view for nil CommitStore")
+ return CommitStoreView{}, errors.New("cannot generate view for nil CommitStore")
}
meta, err := types.NewContractMetaData(c, c.Address())
if err != nil {
diff --git a/deployment/ccip/view/v1_5/offramp.go b/deployment/ccip/view/v1_5/offramp.go
index 95e40d9da27..95d92c445e4 100644
--- a/deployment/ccip/view/v1_5/offramp.go
+++ b/deployment/ccip/view/v1_5/offramp.go
@@ -1,6 +1,7 @@
package v1_5
import (
+ "errors"
"fmt"
"github.com/smartcontractkit/chainlink/deployment/common/view/types"
@@ -15,7 +16,7 @@ type OffRampView struct {
func GenerateOffRampView(r *evm_2_evm_offramp.EVM2EVMOffRamp) (OffRampView, error) {
if r == nil {
- return OffRampView{}, fmt.Errorf("cannot generate view for nil OffRamp")
+ return OffRampView{}, errors.New("cannot generate view for nil OffRamp")
}
meta, err := types.NewContractMetaData(r, r.Address())
if err != nil {
diff --git a/deployment/ccip/view/v1_5/offramp_test.go b/deployment/ccip/view/v1_5/offramp_test.go
index d6539fe2ba5..47370501424 100644
--- a/deployment/ccip/view/v1_5/offramp_test.go
+++ b/deployment/ccip/view/v1_5/offramp_test.go
@@ -54,7 +54,7 @@ func TestOffRampView(t *testing.T) {
v, err := GenerateOffRampView(c2)
require.NoError(t, err)
assert.Equal(t, v.StaticConfig, sc)
- assert.Equal(t, v.TypeAndVersion, "EVM2EVMOffRamp 1.5.0")
+ assert.Equal(t, "EVM2EVMOffRamp 1.5.0", v.TypeAndVersion)
_, err = json.MarshalIndent(v, "", " ")
require.NoError(t, err)
}
diff --git a/deployment/ccip/view/v1_5/onramp.go b/deployment/ccip/view/v1_5/onramp.go
index d679f6c14c0..c211c493cbc 100644
--- a/deployment/ccip/view/v1_5/onramp.go
+++ b/deployment/ccip/view/v1_5/onramp.go
@@ -1,6 +1,7 @@
package v1_5
import (
+ "errors"
"fmt"
"github.com/smartcontractkit/chainlink/deployment/common/view/types"
@@ -15,7 +16,7 @@ type OnRampView struct {
func GenerateOnRampView(r *evm_2_evm_onramp.EVM2EVMOnRamp) (OnRampView, error) {
if r == nil {
- return OnRampView{}, fmt.Errorf("cannot generate view for nil OnRamp")
+ return OnRampView{}, errors.New("cannot generate view for nil OnRamp")
}
meta, err := types.NewContractMetaData(r, r.Address())
if err != nil {
diff --git a/deployment/ccip/view/v1_5/onramp_test.go b/deployment/ccip/view/v1_5/onramp_test.go
index 4d7ef0225a6..6ce21c9f032 100644
--- a/deployment/ccip/view/v1_5/onramp_test.go
+++ b/deployment/ccip/view/v1_5/onramp_test.go
@@ -64,8 +64,7 @@ func TestOnRampView(t *testing.T) {
// Check a few fields.
assert.Equal(t, v.StaticConfig.ChainSelector, chain.Selector)
assert.Equal(t, v.DynamicConfig.Router, common.HexToAddress("0x4"))
- assert.Equal(t, v.TypeAndVersion, "EVM2EVMOnRamp 1.5.0")
+ assert.Equal(t, "EVM2EVMOnRamp 1.5.0", v.TypeAndVersion)
_, err = json.MarshalIndent(v, "", " ")
require.NoError(t, err)
-
}
diff --git a/deployment/ccip/view/v1_5/rmn.go b/deployment/ccip/view/v1_5/rmn.go
index cef55460446..19535cf508e 100644
--- a/deployment/ccip/view/v1_5/rmn.go
+++ b/deployment/ccip/view/v1_5/rmn.go
@@ -1,6 +1,7 @@
package v1_5
import (
+ "errors"
"fmt"
"github.com/smartcontractkit/chainlink/deployment/common/view/types"
@@ -14,7 +15,7 @@ type RMNView struct {
func GenerateRMNView(r *rmn_contract.RMNContract) (RMNView, error) {
if r == nil {
- return RMNView{}, fmt.Errorf("cannot generate view for nil RMN")
+ return RMNView{}, errors.New("cannot generate view for nil RMN")
}
meta, err := types.NewContractMetaData(r, r.Address())
if err != nil {
diff --git a/deployment/ccip/view/v1_5/rmn_test.go b/deployment/ccip/view/v1_5/rmn_test.go
index 3ec7d7a9cc9..f4ea35a116f 100644
--- a/deployment/ccip/view/v1_5/rmn_test.go
+++ b/deployment/ccip/view/v1_5/rmn_test.go
@@ -45,8 +45,8 @@ func TestGenerateRMNView(t *testing.T) {
v, err := GenerateRMNView(c)
require.NoError(t, err)
assert.Equal(t, v.Owner, chain.DeployerKey.From)
- assert.Equal(t, v.TypeAndVersion, "RMN 1.5.0")
- assert.Equal(t, v.ConfigDetails.Version, uint32(1))
+ assert.Equal(t, "RMN 1.5.0", v.TypeAndVersion)
+ assert.Equal(t, uint32(1), v.ConfigDetails.Version)
assert.Equal(t, v.ConfigDetails.Config, cfg)
_, err = json.MarshalIndent(v, "", " ")
require.NoError(t, err)
diff --git a/deployment/ccip/view/v1_5/tokenadminregistry.go b/deployment/ccip/view/v1_5/tokenadminregistry.go
index 2fd36615bcd..e4a88996247 100644
--- a/deployment/ccip/view/v1_5/tokenadminregistry.go
+++ b/deployment/ccip/view/v1_5/tokenadminregistry.go
@@ -1,6 +1,7 @@
package v1_5
import (
+ "errors"
"fmt"
"github.com/ethereum/go-ethereum/common"
@@ -20,7 +21,7 @@ type TokenAdminRegistryView struct {
func GenerateTokenAdminRegistryView(taContract *token_admin_registry.TokenAdminRegistry) (TokenAdminRegistryView, error) {
if taContract == nil {
- return TokenAdminRegistryView{}, fmt.Errorf("token admin registry contract is nil")
+ return TokenAdminRegistryView{}, errors.New("token admin registry contract is nil")
}
tokens, err := getAllConfiguredTokensPaginated(taContract)
if err != nil {
diff --git a/deployment/ccip/view/v1_6/ccip_home.go b/deployment/ccip/view/v1_6/ccip_home.go
index b188c32c079..04b7dc8c1af 100644
--- a/deployment/ccip/view/v1_6/ccip_home.go
+++ b/deployment/ccip/view/v1_6/ccip_home.go
@@ -1,6 +1,7 @@
package v1_6
import (
+ "errors"
"fmt"
"math/big"
@@ -33,7 +34,7 @@ type CCIPHomeView struct {
func GenerateCCIPHomeView(cr *capabilities_registry.CapabilitiesRegistry, ch *ccip_home.CCIPHome) (CCIPHomeView, error) {
if ch == nil {
- return CCIPHomeView{}, fmt.Errorf("cannot generate view for nil CCIPHome")
+ return CCIPHomeView{}, errors.New("cannot generate view for nil CCIPHome")
}
meta, err := types.NewContractMetaData(ch, ch.Address())
if err != nil {
diff --git a/deployment/ccip/view/v1_6/ccip_home_test.go b/deployment/ccip/view/v1_6/ccip_home_test.go
index 8ea79e8eac3..3d4701d705a 100644
--- a/deployment/ccip/view/v1_6/ccip_home_test.go
+++ b/deployment/ccip/view/v1_6/ccip_home_test.go
@@ -33,7 +33,7 @@ func TestCCIPHomeView(t *testing.T) {
v, err := GenerateCCIPHomeView(cr, ch)
require.NoError(t, err)
- assert.Equal(t, v.TypeAndVersion, "CCIPHome 1.6.0-dev")
+ assert.Equal(t, "CCIPHome 1.6.0-dev", v.TypeAndVersion)
_, err = json.MarshalIndent(v, "", " ")
require.NoError(t, err)
diff --git a/deployment/ccip/view/v1_6/rmnhome.go b/deployment/ccip/view/v1_6/rmnhome.go
index 82d39074d6f..b05d15bc223 100644
--- a/deployment/ccip/view/v1_6/rmnhome.go
+++ b/deployment/ccip/view/v1_6/rmnhome.go
@@ -7,6 +7,7 @@ import (
"math/big"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
+
"github.com/smartcontractkit/chainlink/deployment/common/view/types"
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/rmn_home"
)
diff --git a/deployment/common/changeset/deploy_link_token.go b/deployment/common/changeset/deploy_link_token.go
index c115a7ee083..0c648939c9f 100644
--- a/deployment/common/changeset/deploy_link_token.go
+++ b/deployment/common/changeset/deploy_link_token.go
@@ -1,7 +1,7 @@
package changeset
import (
- "fmt"
+ "errors"
"github.com/smartcontractkit/chainlink-common/pkg/logger"
@@ -17,7 +17,7 @@ func DeployLinkToken(e deployment.Environment, chains []uint64) (deployment.Chan
for _, chain := range chains {
_, ok := e.Chains[chain]
if !ok {
- return deployment.ChangesetOutput{}, fmt.Errorf("chain not found in environment")
+ return deployment.ChangesetOutput{}, errors.New("chain not found in environment")
}
}
newAddresses := deployment.NewMemoryAddressBook()
diff --git a/deployment/common/changeset/example/add_mint_burners_link.go b/deployment/common/changeset/example/add_mint_burners_link.go
index 7322f99dd60..d41734519d7 100644
--- a/deployment/common/changeset/example/add_mint_burners_link.go
+++ b/deployment/common/changeset/example/add_mint_burners_link.go
@@ -18,7 +18,6 @@ var _ deployment.ChangeSet[*AddMintersBurnersLinkConfig] = AddMintersBurnersLink
// AddMintersBurnersLink grants the minter / burner role to the provided addresses.
func AddMintersBurnersLink(e deployment.Environment, cfg *AddMintersBurnersLinkConfig) (deployment.ChangesetOutput, error) {
-
chain := e.Chains[cfg.ChainSelector]
addresses, err := e.ExistingAddresses.AddressesForChain(cfg.ChainSelector)
if err != nil {
@@ -66,5 +65,4 @@ func AddMintersBurnersLink(e deployment.Environment, cfg *AddMintersBurnersLinkC
}
}
return deployment.ChangesetOutput{}, nil
-
}
diff --git a/deployment/common/changeset/example/link_transfer.go b/deployment/common/changeset/example/link_transfer.go
index 2e3be48a4d1..6253be187c0 100644
--- a/deployment/common/changeset/example/link_transfer.go
+++ b/deployment/common/changeset/example/link_transfer.go
@@ -135,7 +135,6 @@ func initStatePerChain(cfg *LinkTransferConfig, e deployment.Environment) (
mcmsStatePerChain, err = changeset.MaybeLoadMCMSWithTimelockState(e, chainSelectors)
if err != nil {
return nil, nil, err
-
}
return linkStatePerChain, mcmsStatePerChain, nil
}
@@ -160,12 +159,10 @@ func transferOrBuildTx(
}
}
return tx, nil
-
}
// LinkTransfer takes the given link transfers and executes them or creates an MCMS proposal for them.
func LinkTransfer(e deployment.Environment, cfg *LinkTransferConfig) (deployment.ChangesetOutput, error) {
-
err := cfg.Validate(e)
if err != nil {
return deployment.ChangesetOutput{}, fmt.Errorf("invalid LinkTransferConfig: %w", err)
diff --git a/deployment/common/changeset/example/link_transfer_test.go b/deployment/common/changeset/example/link_transfer_test.go
index eecfbd37c95..4cc2bd0880b 100644
--- a/deployment/common/changeset/example/link_transfer_test.go
+++ b/deployment/common/changeset/example/link_transfer_test.go
@@ -25,7 +25,6 @@ import (
// setupLinkTransferContracts deploys all required contracts for the link transfer tests and returns the updated env.
func setupLinkTransferTestEnv(t *testing.T) deployment.Environment {
-
lggr := logger.TestLogger(t)
cfg := memory.MemoryEnvironmentConfig{
Nodes: 1,
diff --git a/deployment/common/changeset/example/mint_link.go b/deployment/common/changeset/example/mint_link.go
index dc50f8a1a27..8a71555928e 100644
--- a/deployment/common/changeset/example/mint_link.go
+++ b/deployment/common/changeset/example/mint_link.go
@@ -19,7 +19,6 @@ var _ deployment.ChangeSet[*MintLinkConfig] = MintLink
// MintLink mints LINK to the provided contract.
func MintLink(e deployment.Environment, cfg *MintLinkConfig) (deployment.ChangesetOutput, error) {
-
chain := e.Chains[cfg.ChainSelector]
addresses, err := e.ExistingAddresses.AddressesForChain(cfg.ChainSelector)
if err != nil {
@@ -39,5 +38,4 @@ func MintLink(e deployment.Environment, cfg *MintLinkConfig) (deployment.Changes
return deployment.ChangesetOutput{}, err
}
return deployment.ChangesetOutput{}, nil
-
}
diff --git a/deployment/common/changeset/internal/mcms.go b/deployment/common/changeset/internal/mcms.go
index baa82d77c8f..61808e1cbbd 100644
--- a/deployment/common/changeset/internal/mcms.go
+++ b/deployment/common/changeset/internal/mcms.go
@@ -27,7 +27,7 @@ func DeployMCMSWithConfig(
chain.Client,
)
return deployment.ContractDeploy[*owner_helpers.ManyChainMultiSig]{
- mcmAddr, mcm, tx, deployment.NewTypeAndVersion(contractType, deployment.Version1_0_0), err2,
+ Address: mcmAddr, Contract: mcm, Tx: tx, Tv: deployment.NewTypeAndVersion(contractType, deployment.Version1_0_0), Err: err2,
}
})
if err != nil {
@@ -115,7 +115,7 @@ func DeployMCMSWithTimelockContracts(
[]common.Address{bypasser.Address}, // bypassers
)
return deployment.ContractDeploy[*owner_helpers.RBACTimelock]{
- timelock, cc, tx2, deployment.NewTypeAndVersion(types.RBACTimelock, deployment.Version1_0_0), err2,
+ Address: timelock, Contract: cc, Tx: tx2, Tv: deployment.NewTypeAndVersion(types.RBACTimelock, deployment.Version1_0_0), Err: err2,
}
})
if err != nil {
@@ -131,7 +131,7 @@ func DeployMCMSWithTimelockContracts(
timelock.Address,
)
return deployment.ContractDeploy[*owner_helpers.CallProxy]{
- callProxy, cc, tx2, deployment.NewTypeAndVersion(types.CallProxy, deployment.Version1_0_0), err2,
+ Address: callProxy, Contract: cc, Tx: tx2, Tv: deployment.NewTypeAndVersion(types.CallProxy, deployment.Version1_0_0), Err: err2,
}
})
if err != nil {
diff --git a/deployment/common/changeset/save_existing.go b/deployment/common/changeset/save_existing.go
index a5177c8e49b..57e53607cdc 100644
--- a/deployment/common/changeset/save_existing.go
+++ b/deployment/common/changeset/save_existing.go
@@ -30,13 +30,13 @@ func (cfg ExistingContractsConfig) Validate() error {
return fmt.Errorf("invalid chain selector: %d - %w", ec.ChainSelector, err)
}
if ec.Address == (common.Address{}) {
- return fmt.Errorf("address must be set")
+ return errors.New("address must be set")
}
if ec.TypeAndVersion.Type == "" {
- return fmt.Errorf("type must be set")
+ return errors.New("type must be set")
}
if val, err := ec.TypeAndVersion.Version.Value(); err != nil || val == "" {
- return fmt.Errorf("version must be set")
+ return errors.New("version must be set")
}
}
return nil
diff --git a/deployment/common/changeset/set_config_mcms.go b/deployment/common/changeset/set_config_mcms.go
index 3ba5d2db4b6..5e2dc718b95 100644
--- a/deployment/common/changeset/set_config_mcms.go
+++ b/deployment/common/changeset/set_config_mcms.go
@@ -192,7 +192,6 @@ func SetConfigMCMS(e deployment.Environment, cfg MCMSConfig) (deployment.Changes
batch := addTxsToProposalBatch(setConfigTxsChain, chainSelector, *state)
batches = append(batches, batch)
}
-
}
if useMCMS {
diff --git a/deployment/common/changeset/set_config_mcms_test.go b/deployment/common/changeset/set_config_mcms_test.go
index 7220bdd755a..207b37c00f3 100644
--- a/deployment/common/changeset/set_config_mcms_test.go
+++ b/deployment/common/changeset/set_config_mcms_test.go
@@ -7,6 +7,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/smartcontractkit/ccip-owner-contracts/pkg/config"
chain_selectors "github.com/smartcontractkit/chain-selectors"
+
"github.com/smartcontractkit/chainlink-common/pkg/utils/tests"
commonchangeset "github.com/smartcontractkit/chainlink/deployment/common/changeset"
@@ -24,7 +25,6 @@ import (
// setupSetConfigTestEnv deploys all required contracts for the setConfig MCMS contract call.
func setupSetConfigTestEnv(t *testing.T) deployment.Environment {
-
lggr := logger.TestLogger(t)
cfg := memory.MemoryEnvironmentConfig{
Nodes: 1,
@@ -53,7 +53,6 @@ func setupSetConfigTestEnv(t *testing.T) deployment.Environment {
// TestSetConfigMCMSVariants tests the SetConfigMCMS changeset variants.
func TestSetConfigMCMSVariants(t *testing.T) {
-
// Add the timelock as a signer to check state changes
for _, tc := range []struct {
name string
@@ -62,7 +61,6 @@ func TestSetConfigMCMSVariants(t *testing.T) {
{
name: "MCMS disabled",
changeSets: func(mcmsState *commonchangeset.MCMSWithTimelockState, chainSel uint64, cfgProp, cfgCancel, cfgBypass config.Config) []commonchangeset.ChangesetApplication {
-
return []commonchangeset.ChangesetApplication{
{
Changeset: commonchangeset.WrapChangeSet(commonchangeset.SetConfigMCMS),
diff --git a/deployment/common/changeset/test_helpers.go b/deployment/common/changeset/test_helpers.go
index e92b36e5b55..5d524e542ad 100644
--- a/deployment/common/changeset/test_helpers.go
+++ b/deployment/common/changeset/test_helpers.go
@@ -5,6 +5,7 @@ import (
"testing"
mapset "github.com/deckarep/golang-set/v2"
+
jobv1 "github.com/smartcontractkit/chainlink-protos/job-distributor/v1/job"
"github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext"
diff --git a/deployment/common/changeset/transfer_to_mcms_with_timelock.go b/deployment/common/changeset/transfer_to_mcms_with_timelock.go
index 45efccefd2e..ab0883cc9a7 100644
--- a/deployment/common/changeset/transfer_to_mcms_with_timelock.go
+++ b/deployment/common/changeset/transfer_to_mcms_with_timelock.go
@@ -36,11 +36,11 @@ func LoadOwnableContract(addr common.Address, client bind.ContractBackend) (comm
// Just using the ownership interface from here.
c, err := burn_mint_erc677.NewBurnMintERC677(addr, client)
if err != nil {
- return common.Address{}, nil, fmt.Errorf("failed to create contract: %v", err)
+ return common.Address{}, nil, fmt.Errorf("failed to create contract: %w", err)
}
owner, err := c.Owner(nil)
if err != nil {
- return common.Address{}, nil, fmt.Errorf("failed to get owner of contract: %v", err)
+ return common.Address{}, nil, fmt.Errorf("failed to get owner of contract: %w", err)
}
return owner, c, nil
}
@@ -52,13 +52,13 @@ func (t TransferToMCMSWithTimelockConfig) Validate(e deployment.Environment) err
// Note this also assures non-zero addresses.
if exists, err := deployment.AddressBookContains(e.ExistingAddresses, chainSelector, contract.String()); err != nil || !exists {
if err != nil {
- return fmt.Errorf("failed to check address book: %v", err)
+ return fmt.Errorf("failed to check address book: %w", err)
}
return fmt.Errorf("contract %s not found in address book", contract)
}
owner, _, err := LoadOwnableContract(contract, e.Chains[chainSelector].Client)
if err != nil {
- return fmt.Errorf("failed to load ownable: %v", err)
+ return fmt.Errorf("failed to load ownable: %w", err)
}
if owner != e.Chains[chainSelector].DeployerKey.From {
return fmt.Errorf("contract %s is not owned by the deployer key", contract)
@@ -66,10 +66,10 @@ func (t TransferToMCMSWithTimelockConfig) Validate(e deployment.Environment) err
}
// If there is no timelock and mcms proposer on the chain, the transfer will fail.
if _, err := deployment.SearchAddressBook(e.ExistingAddresses, chainSelector, types.RBACTimelock); err != nil {
- return fmt.Errorf("timelock not present on the chain %v", err)
+ return fmt.Errorf("timelock not present on the chain %w", err)
}
if _, err := deployment.SearchAddressBook(e.ExistingAddresses, chainSelector, types.ProposerManyChainMultisig); err != nil {
- return fmt.Errorf("mcms proposer not present on the chain %v", err)
+ return fmt.Errorf("mcms proposer not present on the chain %w", err)
}
}
@@ -101,7 +101,7 @@ func TransferToMCMSWithTimelock(
timelocksByChain[chainSelector] = common.HexToAddress(timelockAddr)
proposer, err := owner_helpers.NewManyChainMultiSig(common.HexToAddress(proposerAddr), e.Chains[chainSelector].Client)
if err != nil {
- return deployment.ChangesetOutput{}, fmt.Errorf("failed to create proposer mcms: %v", err)
+ return deployment.ChangesetOutput{}, fmt.Errorf("failed to create proposer mcms: %w", err)
}
proposersByChain[chainSelector] = proposer
@@ -118,7 +118,7 @@ func TransferToMCMSWithTimelock(
tx, err := c.TransferOwnership(e.Chains[chainSelector].DeployerKey, common.HexToAddress(timelockAddr))
_, err = deployment.ConfirmIfNoError(e.Chains[chainSelector], tx, err)
if err != nil {
- return deployment.ChangesetOutput{}, fmt.Errorf("failed to transfer ownership of contract %T: %v", contract, err)
+ return deployment.ChangesetOutput{}, fmt.Errorf("failed to transfer ownership of contract %T: %w", contract, err)
}
tx, err = c.AcceptOwnership(deployment.SimTransactOpts())
if err != nil {
diff --git a/deployment/common/proposalutils/mcms_helpers.go b/deployment/common/proposalutils/mcms_helpers.go
index 51a720a4389..8b7153e526b 100644
--- a/deployment/common/proposalutils/mcms_helpers.go
+++ b/deployment/common/proposalutils/mcms_helpers.go
@@ -10,6 +10,7 @@ import (
"github.com/ethereum/go-ethereum/common"
owner_helpers "github.com/smartcontractkit/ccip-owner-contracts/pkg/gethwrappers"
"github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/mcms"
+
"github.com/smartcontractkit/chainlink-common/pkg/logger"
"github.com/smartcontractkit/chainlink/deployment"
"github.com/smartcontractkit/chainlink/deployment/common/types"
@@ -76,21 +77,21 @@ type RunTimelockExecutorConfig struct {
func (cfg RunTimelockExecutorConfig) Validate() error {
if cfg.Executor == nil {
- return fmt.Errorf("executor is nil")
+ return errors.New("executor is nil")
}
if cfg.TimelockContracts == nil {
- return fmt.Errorf("timelock contracts is nil")
+ return errors.New("timelock contracts is nil")
}
if cfg.ChainSelector == 0 {
- return fmt.Errorf("chain selector is 0")
+ return errors.New("chain selector is 0")
}
if cfg.BlockStart != nil && cfg.BlockEnd == nil {
if *cfg.BlockStart > *cfg.BlockEnd {
- return fmt.Errorf("block start is greater than block end")
+ return errors.New("block start is greater than block end")
}
}
if cfg.BlockStart == nil && cfg.BlockEnd != nil {
- return fmt.Errorf("block start must not be nil when block end is not nil")
+ return errors.New("block start must not be nil when block end is not nil")
}
if len(cfg.Executor.Operations[mcms.ChainIdentifier(cfg.ChainSelector)]) == 0 {
diff --git a/deployment/common/proposalutils/propose.go b/deployment/common/proposalutils/propose.go
index baf506cb2f8..874bbecbdb8 100644
--- a/deployment/common/proposalutils/propose.go
+++ b/deployment/common/proposalutils/propose.go
@@ -1,6 +1,7 @@
package proposalutils
import (
+ "errors"
"fmt"
"time"
@@ -15,7 +16,6 @@ const (
DefaultValidUntil = 72 * time.Hour
)
-
func BuildProposalMetadata(
chainSelectors []uint64,
proposerMcmsesPerChain map[uint64]*gethwrappers.ManyChainMultiSig,
@@ -55,7 +55,7 @@ func BuildProposalFromBatches(
minDelay time.Duration,
) (*timelock.MCMSWithTimelockProposal, error) {
if len(batches) == 0 {
- return nil, fmt.Errorf("no operations in batch")
+ return nil, errors.New("no operations in batch")
}
chains := mapset.NewSet[uint64]()
diff --git a/deployment/common/types/types.go b/deployment/common/types/types.go
index 0f04421af43..c86fb3e9887 100644
--- a/deployment/common/types/types.go
+++ b/deployment/common/types/types.go
@@ -1,7 +1,7 @@
package types
import (
- "fmt"
+ "errors"
"math/big"
"time"
@@ -52,40 +52,40 @@ type OCRParameters struct {
func (params OCRParameters) Validate() error {
if params.DeltaProgress <= 0 {
- return fmt.Errorf("deltaProgress must be positive")
+ return errors.New("deltaProgress must be positive")
}
if params.DeltaResend <= 0 {
- return fmt.Errorf("deltaResend must be positive")
+ return errors.New("deltaResend must be positive")
}
if params.DeltaInitial <= 0 {
- return fmt.Errorf("deltaInitial must be positive")
+ return errors.New("deltaInitial must be positive")
}
if params.DeltaRound <= 0 {
- return fmt.Errorf("deltaRound must be positive")
+ return errors.New("deltaRound must be positive")
}
if params.DeltaGrace <= 0 {
- return fmt.Errorf("deltaGrace must be positive")
+ return errors.New("deltaGrace must be positive")
}
if params.DeltaCertifiedCommitRequest <= 0 {
- return fmt.Errorf("deltaCertifiedCommitRequest must be positive")
+ return errors.New("deltaCertifiedCommitRequest must be positive")
}
if params.DeltaStage <= 0 {
- return fmt.Errorf("deltaStage must be positive")
+ return errors.New("deltaStage must be positive")
}
if params.Rmax <= 0 {
- return fmt.Errorf("rmax must be positive")
+ return errors.New("rmax must be positive")
}
if params.MaxDurationQuery <= 0 {
- return fmt.Errorf("maxDurationQuery must be positive")
+ return errors.New("maxDurationQuery must be positive")
}
if params.MaxDurationObservation <= 0 {
- return fmt.Errorf("maxDurationObservation must be positive")
+ return errors.New("maxDurationObservation must be positive")
}
if params.MaxDurationShouldAcceptAttestedReport <= 0 {
- return fmt.Errorf("maxDurationShouldAcceptAttestedReport must be positive")
+ return errors.New("maxDurationShouldAcceptAttestedReport must be positive")
}
if params.MaxDurationShouldTransmitAcceptedReport <= 0 {
- return fmt.Errorf("maxDurationShouldTransmitAcceptedReport must be positive")
+ return errors.New("maxDurationShouldTransmitAcceptedReport must be positive")
}
return nil
}
diff --git a/deployment/common/view/nops.go b/deployment/common/view/nops.go
index 61e16d59145..74f011dfe44 100644
--- a/deployment/common/view/nops.go
+++ b/deployment/common/view/nops.go
@@ -2,9 +2,11 @@ package view
import (
"context"
+ "encoding/hex"
"fmt"
"github.com/pkg/errors"
+
nodev1 "github.com/smartcontractkit/chainlink-protos/job-distributor/v1/node"
"github.com/smartcontractkit/chainlink/deployment"
@@ -62,11 +64,11 @@ func GenerateNopsView(nodeIds []string, oc deployment.OffchainClient) (map[strin
}
for details, ocrConfig := range node.SelToOCRConfig {
nop.OCRKeys[details.ChainName] = OCRKeyView{
- OffchainPublicKey: fmt.Sprintf("%x", ocrConfig.OffchainPublicKey[:]),
+ OffchainPublicKey: hex.EncodeToString(ocrConfig.OffchainPublicKey[:]),
OnchainPublicKey: fmt.Sprintf("%x", ocrConfig.OnchainPublicKey[:]),
PeerID: ocrConfig.PeerID.String(),
TransmitAccount: string(ocrConfig.TransmitAccount),
- ConfigEncryptionPublicKey: fmt.Sprintf("%x", ocrConfig.ConfigEncryptionPublicKey[:]),
+ ConfigEncryptionPublicKey: hex.EncodeToString(ocrConfig.ConfigEncryptionPublicKey[:]),
KeyBundleID: ocrConfig.KeyBundleID,
}
}
diff --git a/deployment/common/view/v1_0/capreg_test.go b/deployment/common/view/v1_0/capreg_test.go
index 15fe23be00e..eb7c8a83bd4 100644
--- a/deployment/common/view/v1_0/capreg_test.go
+++ b/deployment/common/view/v1_0/capreg_test.go
@@ -149,7 +149,7 @@ func TestCapRegView_Denormalize(t *testing.T) {
{Name: "third nop"},
},
Capabilities: []CapabilityView{
- //capabilities for don1
+ // capabilities for don1
NewCapabilityView(cr.CapabilitiesRegistryCapabilityInfo{
HashedId: [32]byte{0: 1},
LabelledName: "cap1",
@@ -161,7 +161,7 @@ func TestCapRegView_Denormalize(t *testing.T) {
Version: "1.0.0",
}),
- //capabilities for don2
+ // capabilities for don2
NewCapabilityView(cr.CapabilitiesRegistryCapabilityInfo{
HashedId: [32]byte{2: 2}, // matches don ID 2, capabitility ID 1
LabelledName: "other cap",
diff --git a/deployment/common/view/v1_0/link_token.go b/deployment/common/view/v1_0/link_token.go
index 6dd1a00be3b..2d345f1fd3c 100644
--- a/deployment/common/view/v1_0/link_token.go
+++ b/deployment/common/view/v1_0/link_token.go
@@ -44,8 +44,8 @@ func GenerateLinkTokenView(lt *link_token.LinkToken) (LinkTokenView, error) {
return LinkTokenView{
ContractMetaData: types.ContractMetaData{
TypeAndVersion: deployment.TypeAndVersion{
- commontypes.LinkToken,
- deployment.Version1_0_0,
+ Type: commontypes.LinkToken,
+ Version: deployment.Version1_0_0,
}.String(),
Address: lt.Address(),
Owner: owner,
diff --git a/deployment/common/view/v1_0/link_token_test.go b/deployment/common/view/v1_0/link_token_test.go
index c83c0b3e3c2..735d7789169 100644
--- a/deployment/common/view/v1_0/link_token_test.go
+++ b/deployment/common/view/v1_0/link_token_test.go
@@ -26,12 +26,12 @@ func TestLinkTokenView(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, v.Owner, chain.DeployerKey.From)
- assert.Equal(t, v.TypeAndVersion, "LinkToken 1.0.0")
- assert.Equal(t, v.Decimals, uint8(18))
+ assert.Equal(t, "LinkToken 1.0.0", v.TypeAndVersion)
+ assert.Equal(t, uint8(18), v.Decimals)
// Initially nothing minted and no minters/burners.
- assert.Equal(t, v.Supply.String(), "0")
- require.Len(t, v.Minters, 0)
- require.Len(t, v.Burners, 0)
+ assert.Equal(t, "0", v.Supply.String())
+ require.Empty(t, v.Minters)
+ require.Empty(t, v.Burners)
// Add some minters
tx, err = lt.GrantMintAndBurnRoles(chain.DeployerKey, chain.DeployerKey.From)
@@ -45,7 +45,7 @@ func TestLinkTokenView(t *testing.T) {
v, err = GenerateLinkTokenView(lt)
require.NoError(t, err)
- assert.Equal(t, v.Supply.String(), "100")
+ assert.Equal(t, "100", v.Supply.String())
require.Len(t, v.Minters, 1)
require.Equal(t, v.Minters[0].String(), chain.DeployerKey.From.String())
require.Len(t, v.Burners, 1)
diff --git a/deployment/common/view/v1_0/static_link_token.go b/deployment/common/view/v1_0/static_link_token.go
index 525f1a9f0c5..2c9c60531b2 100644
--- a/deployment/common/view/v1_0/static_link_token.go
+++ b/deployment/common/view/v1_0/static_link_token.go
@@ -28,8 +28,8 @@ func GenerateStaticLinkTokenView(lt *link_token_interface.LinkToken) (StaticLink
return StaticLinkTokenView{
ContractMetaData: types.ContractMetaData{
TypeAndVersion: deployment.TypeAndVersion{
- commontypes.StaticLinkToken,
- deployment.Version1_0_0,
+ Type: commontypes.StaticLinkToken,
+ Version: deployment.Version1_0_0,
}.String(),
Address: lt.Address(),
// No owner.
diff --git a/deployment/common/view/v1_0/static_link_token_test.go b/deployment/common/view/v1_0/static_link_token_test.go
index 517efac9438..b276a80fb2e 100644
--- a/deployment/common/view/v1_0/static_link_token_test.go
+++ b/deployment/common/view/v1_0/static_link_token_test.go
@@ -26,7 +26,7 @@ func TestStaticLinkTokenView(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, v.Owner, common.HexToAddress("0x0")) // Ownerless
- assert.Equal(t, v.TypeAndVersion, "StaticLinkToken 1.0.0")
- assert.Equal(t, v.Decimals, uint8(18))
- assert.Equal(t, v.Supply.String(), "1000000000000000000000000000")
+ assert.Equal(t, "StaticLinkToken 1.0.0", v.TypeAndVersion)
+ assert.Equal(t, uint8(18), v.Decimals)
+ assert.Equal(t, "1000000000000000000000000000", v.Supply.String())
}
diff --git a/deployment/environment.go b/deployment/environment.go
index bfbeac2f0c4..6fc28fac764 100644
--- a/deployment/environment.go
+++ b/deployment/environment.go
@@ -7,6 +7,7 @@ import (
"fmt"
"math/big"
"sort"
+ "strconv"
"strings"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
@@ -75,7 +76,7 @@ func (c Chain) Name() string {
panic(err)
}
if chainInfo.ChainName == "" {
- return fmt.Sprintf("%d", c.Selector)
+ return strconv.FormatUint(c.Selector, 10)
}
return chainInfo.ChainName
}
diff --git a/deployment/environment/crib/types.go b/deployment/environment/crib/types.go
index d19c8424443..99baf8e8774 100644
--- a/deployment/environment/crib/types.go
+++ b/deployment/environment/crib/types.go
@@ -2,6 +2,7 @@ package crib
import (
"context"
+
"github.com/smartcontractkit/chainlink-common/pkg/logger"
"github.com/smartcontractkit/chainlink/deployment"
"github.com/smartcontractkit/chainlink/deployment/environment/devenv"
diff --git a/deployment/environment/devenv/chain.go b/deployment/environment/devenv/chain.go
index 5c6c4336ed7..265a6647050 100644
--- a/deployment/environment/devenv/chain.go
+++ b/deployment/environment/devenv/chain.go
@@ -2,6 +2,7 @@ package devenv
import (
"context"
+ "errors"
"fmt"
"math/big"
"time"
@@ -39,7 +40,7 @@ func (c *ChainConfig) SetUsers(pvtkeys []string) error {
c.Users = []*bind.TransactOpts{c.DeployerKey}
return nil
} else {
- return fmt.Errorf("no private keys provided for users, deployer key is also not set")
+ return errors.New("no private keys provided for users, deployer key is also not set")
}
}
for _, pvtKeyStr := range pvtkeys {
diff --git a/deployment/environment/devenv/don.go b/deployment/environment/devenv/don.go
index 76f6ee92b68..a132fe72a2f 100644
--- a/deployment/environment/devenv/don.go
+++ b/deployment/environment/devenv/don.go
@@ -4,14 +4,16 @@ import (
"context"
"errors"
"fmt"
- chainsel "github.com/smartcontractkit/chain-selectors"
"strconv"
"strings"
"time"
+ chainsel "github.com/smartcontractkit/chain-selectors"
+
"github.com/hashicorp/go-multierror"
"github.com/rs/zerolog"
"github.com/sethvargo/go-retry"
+
nodev1 "github.com/smartcontractkit/chainlink-protos/job-distributor/v1/node"
clclient "github.com/smartcontractkit/chainlink/deployment/environment/nodeclient"
"github.com/smartcontractkit/chainlink/deployment/environment/web/sdk/client"
@@ -395,7 +397,7 @@ func (n *Node) CreateJobDistributor(ctx context.Context, jd JobDistributor) (str
// create the job distributor in the node with the csa key
resp, err := n.gqlClient.ListJobDistributors(ctx)
if err != nil {
- return "", fmt.Errorf("could not list job distrubutors: %w", err)
+ return "", fmt.Errorf("could not list job distributors: %w", err)
}
if len(resp.FeedsManagers.Results) > 0 {
for _, fm := range resp.FeedsManagers.Results {
diff --git a/deployment/environment/devenv/don_test.go b/deployment/environment/devenv/don_test.go
index f93436f72f5..0e0578f8275 100644
--- a/deployment/environment/devenv/don_test.go
+++ b/deployment/environment/devenv/don_test.go
@@ -7,7 +7,6 @@ import (
)
func TestPtrVal(t *testing.T) {
-
x := "hello"
xptr := ptr(x)
got := value(xptr)
diff --git a/deployment/environment/devenv/environment.go b/deployment/environment/devenv/environment.go
index 121caea43bb..2fffe6adf2b 100644
--- a/deployment/environment/devenv/environment.go
+++ b/deployment/environment/devenv/environment.go
@@ -2,6 +2,7 @@ package devenv
import (
"context"
+ "errors"
"fmt"
"github.com/smartcontractkit/chainlink-common/pkg/logger"
@@ -30,10 +31,10 @@ func NewEnvironment(ctx func() context.Context, lggr logger.Logger, config Envir
jd, ok := offChain.(*JobDistributor)
if !ok {
- return nil, nil, fmt.Errorf("offchain client does not implement JobDistributor")
+ return nil, nil, errors.New("offchain client does not implement JobDistributor")
}
if jd == nil {
- return nil, nil, fmt.Errorf("offchain client is not set up")
+ return nil, nil, errors.New("offchain client is not set up")
}
var nodeIDs []string
if jd.don != nil {
diff --git a/deployment/environment/devenv/jd.go b/deployment/environment/devenv/jd.go
index 818f9b09400..844068e50da 100644
--- a/deployment/environment/devenv/jd.go
+++ b/deployment/environment/devenv/jd.go
@@ -2,6 +2,7 @@ package devenv
import (
"context"
+ "errors"
"fmt"
"golang.org/x/oauth2"
@@ -121,7 +122,7 @@ func (jd JobDistributor) GetCSAPublicKey(ctx context.Context) (string, error) {
return "", err
}
if keypairs == nil || len(keypairs.Keypairs) == 0 {
- return "", fmt.Errorf("no keypairs found")
+ return "", errors.New("no keypairs found")
}
csakey := keypairs.Keypairs[0].PublicKey
return csakey, nil
@@ -138,7 +139,7 @@ func (jd JobDistributor) ProposeJob(ctx context.Context, in *jobv1.ProposeJobReq
return nil, fmt.Errorf("failed to propose job. err: %w", err)
}
if res.Proposal == nil {
- return nil, fmt.Errorf("failed to propose job. err: proposal is nil")
+ return nil, errors.New("failed to propose job. err: proposal is nil")
}
if jd.don == nil || len(jd.don.Nodes) == 0 {
return res, nil
diff --git a/deployment/environment/memory/job_client.go b/deployment/environment/memory/job_client.go
index a3cfee41608..e44c664b77e 100644
--- a/deployment/environment/memory/job_client.go
+++ b/deployment/environment/memory/job_client.go
@@ -29,42 +29,42 @@ type JobClient struct {
}
func (j JobClient) BatchProposeJob(ctx context.Context, in *jobv1.BatchProposeJobRequest, opts ...grpc.CallOption) (*jobv1.BatchProposeJobResponse, error) {
- //TODO CCIP-3108 implement me
+ // TODO CCIP-3108 implement me
panic("implement me")
}
func (j JobClient) UpdateJob(ctx context.Context, in *jobv1.UpdateJobRequest, opts ...grpc.CallOption) (*jobv1.UpdateJobResponse, error) {
- //TODO CCIP-3108 implement me
+ // TODO CCIP-3108 implement me
panic("implement me")
}
func (j JobClient) DisableNode(ctx context.Context, in *nodev1.DisableNodeRequest, opts ...grpc.CallOption) (*nodev1.DisableNodeResponse, error) {
- //TODO CCIP-3108 implement me
+ // TODO CCIP-3108 implement me
panic("implement me")
}
func (j JobClient) EnableNode(ctx context.Context, in *nodev1.EnableNodeRequest, opts ...grpc.CallOption) (*nodev1.EnableNodeResponse, error) {
- //TODO CCIP-3108 implement me
+ // TODO CCIP-3108 implement me
panic("implement me")
}
func (j JobClient) RegisterNode(ctx context.Context, in *nodev1.RegisterNodeRequest, opts ...grpc.CallOption) (*nodev1.RegisterNodeResponse, error) {
- //TODO implement me
+ // TODO implement me
panic("implement me")
}
func (j JobClient) UpdateNode(ctx context.Context, in *nodev1.UpdateNodeRequest, opts ...grpc.CallOption) (*nodev1.UpdateNodeResponse, error) {
- //TODO CCIP-3108 implement me
+ // TODO CCIP-3108 implement me
panic("implement me")
}
func (j JobClient) GetKeypair(ctx context.Context, in *csav1.GetKeypairRequest, opts ...grpc.CallOption) (*csav1.GetKeypairResponse, error) {
- //TODO implement me
+ // TODO implement me
panic("implement me")
}
func (j JobClient) ListKeypairs(ctx context.Context, in *csav1.ListKeypairsRequest, opts ...grpc.CallOption) (*csav1.ListKeypairsResponse, error) {
- //TODO CCIP-3108 implement me
+ // TODO CCIP-3108 implement me
panic("implement me")
}
@@ -84,7 +84,7 @@ func (j JobClient) GetNode(ctx context.Context, in *nodev1.GetNodeRequest, opts
}
func (j JobClient) ListNodes(ctx context.Context, in *nodev1.ListNodesRequest, opts ...grpc.CallOption) (*nodev1.ListNodesResponse, error) {
- //TODO CCIP-3108
+ // TODO CCIP-3108
include := func(node *nodev1.Node) bool {
if in.Filter == nil {
return true
@@ -273,22 +273,22 @@ func (j JobClient) ListNodeChainConfigs(ctx context.Context, in *nodev1.ListNode
}
func (j JobClient) GetJob(ctx context.Context, in *jobv1.GetJobRequest, opts ...grpc.CallOption) (*jobv1.GetJobResponse, error) {
- //TODO CCIP-3108 implement me
+ // TODO CCIP-3108 implement me
panic("implement me")
}
func (j JobClient) GetProposal(ctx context.Context, in *jobv1.GetProposalRequest, opts ...grpc.CallOption) (*jobv1.GetProposalResponse, error) {
- //TODO CCIP-3108 implement me
+ // TODO CCIP-3108 implement me
panic("implement me")
}
func (j JobClient) ListJobs(ctx context.Context, in *jobv1.ListJobsRequest, opts ...grpc.CallOption) (*jobv1.ListJobsResponse, error) {
- //TODO CCIP-3108 implement me
+ // TODO CCIP-3108 implement me
panic("implement me")
}
func (j JobClient) ListProposals(ctx context.Context, in *jobv1.ListProposalsRequest, opts ...grpc.CallOption) (*jobv1.ListProposalsResponse, error) {
- //TODO CCIP-3108 implement me
+ // TODO CCIP-3108 implement me
panic("implement me")
}
@@ -338,12 +338,12 @@ func (j JobClient) ProposeJob(ctx context.Context, in *jobv1.ProposeJobRequest,
}
func (j JobClient) RevokeJob(ctx context.Context, in *jobv1.RevokeJobRequest, opts ...grpc.CallOption) (*jobv1.RevokeJobResponse, error) {
- //TODO CCIP-3108 implement me
+ // TODO CCIP-3108 implement me
panic("implement me")
}
func (j JobClient) DeleteJob(ctx context.Context, in *jobv1.DeleteJobRequest, opts ...grpc.CallOption) (*jobv1.DeleteJobResponse, error) {
- //TODO CCIP-3108 implement me
+ // TODO CCIP-3108 implement me
panic("implement me")
}
diff --git a/deployment/environment/nodeclient/chainlink.go b/deployment/environment/nodeclient/chainlink.go
index 9b92dd12759..9aed808d5be 100644
--- a/deployment/environment/nodeclient/chainlink.go
+++ b/deployment/environment/nodeclient/chainlink.go
@@ -7,6 +7,7 @@ import (
"math/big"
"net/http"
"os"
+ "strconv"
"strings"
"sync"
"time"
@@ -492,7 +493,7 @@ func (c *ChainlinkClient) DeleteP2PKey(id int) (*http.Response, error) {
c.l.Info().Str(NodeURL, c.Config.URL).Int("ID", id).Msg("Deleting P2P Key")
resp, err := c.APIClient.R().
SetPathParams(map[string]string{
- "id": fmt.Sprint(id),
+ "id": strconv.Itoa(id),
}).
Delete("/v2/keys/p2p/{id}")
if err != nil {
@@ -528,7 +529,7 @@ func (c *ChainlinkClient) UpdateEthKeyMaxGasPriceGWei(keyId string, gWei int) (*
"keyId": keyId,
}).
SetQueryParams(map[string]string{
- "maxGasPriceGWei": fmt.Sprint(gWei),
+ "maxGasPriceGWei": strconv.Itoa(gWei),
}).
SetResult(ethKey).
Put("/v2/keys/eth/{keyId}")
@@ -1031,7 +1032,7 @@ func (c *ChainlinkClient) Profile(profileTime time.Duration, profileFunction fun
"reportType": profileReport.Type,
}).
SetQueryParams(map[string]string{
- "seconds": fmt.Sprint(profileSeconds),
+ "seconds": strconv.Itoa(profileSeconds),
}).
Get("/v2/debug/pprof/{reportType}")
if err != nil {
@@ -1222,10 +1223,10 @@ func (c *ChainlinkClient) ReplayLogPollerFromBlock(fromBlock, evmChainID int64)
resp, err := c.APIClient.R().
SetResult(&specObj).
SetQueryParams(map[string]string{
- "evmChainID": fmt.Sprint(evmChainID),
+ "evmChainID": strconv.FormatInt(evmChainID, 10),
}).
SetPathParams(map[string]string{
- "fromBlock": fmt.Sprint(fromBlock),
+ "fromBlock": strconv.FormatInt(fromBlock, 10),
}).
Post("/v2/replay_from_block/{fromBlock}")
if err != nil {
diff --git a/deployment/environment/web/sdk/client/client.go b/deployment/environment/web/sdk/client/client.go
index e0a56b9e642..331376b2e9f 100644
--- a/deployment/environment/web/sdk/client/client.go
+++ b/deployment/environment/web/sdk/client/client.go
@@ -3,13 +3,15 @@ package client
import (
"context"
"encoding/json"
+ "errors"
"fmt"
- "github.com/Khan/genqlient/graphql"
- "github.com/sethvargo/go-retry"
"net/http"
"strings"
"time"
+ "github.com/Khan/genqlient/graphql"
+ "github.com/sethvargo/go-retry"
+
"github.com/smartcontractkit/chainlink/deployment/environment/web/sdk/client/doer"
"github.com/smartcontractkit/chainlink/deployment/environment/web/sdk/internal/generated"
)
@@ -61,7 +63,7 @@ func New(baseURI string, creds Credentials) (Client, error) {
endpoints: ep,
credentials: creds,
}
-
+
err := retry.Do(context.Background(), retry.WithMaxDuration(10*time.Second, retry.NewFibonacci(2*time.Second)), func(ctx context.Context) error {
err := c.login()
if err != nil {
@@ -87,7 +89,7 @@ func (c *client) FetchCSAPublicKey(ctx context.Context) (*string, error) {
return nil, err
}
if keys == nil || len(keys.CsaKeys.GetResults()) == 0 {
- return nil, fmt.Errorf("no CSA keys found")
+ return nil, errors.New("no CSA keys found")
}
return &keys.CsaKeys.GetResults()[0].PublicKey, nil
}
@@ -98,7 +100,7 @@ func (c *client) FetchP2PPeerID(ctx context.Context) (*string, error) {
return nil, err
}
if keys == nil || len(keys.P2pKeys.GetResults()) == 0 {
- return nil, fmt.Errorf("no P2P keys found")
+ return nil, errors.New("no P2P keys found")
}
return &keys.P2pKeys.GetResults()[0].PeerID, nil
}
@@ -109,7 +111,7 @@ func (c *client) FetchOCR2KeyBundleID(ctx context.Context, chainType string) (st
return "", err
}
if keyBundles == nil || len(keyBundles.GetOcr2KeyBundles().Results) == 0 {
- return "", fmt.Errorf("no ocr2 keybundle found, check if ocr2 is enabled")
+ return "", errors.New("no ocr2 keybundle found, check if ocr2 is enabled")
}
for _, keyBundle := range keyBundles.GetOcr2KeyBundles().Results {
if keyBundle.ChainType == generated.OCR2ChainType(chainType) {
@@ -125,7 +127,7 @@ func (c *client) FetchAccountAddress(ctx context.Context, chainID string) (*stri
return nil, err
}
if keys == nil || len(keys.EthKeys.GetResults()) == 0 {
- return nil, fmt.Errorf("no accounts found")
+ return nil, errors.New("no accounts found")
}
for _, keyDetail := range keys.EthKeys.GetResults() {
if keyDetail.GetChain().Enabled && keyDetail.GetChain().Id == chainID {
@@ -141,7 +143,7 @@ func (c *client) FetchKeys(ctx context.Context, chainType string) ([]string, err
return nil, err
}
if keys == nil {
- return nil, fmt.Errorf("no accounts found")
+ return nil, errors.New("no accounts found")
}
switch generated.OCR2ChainType(chainType) {
case generated.OCR2ChainTypeAptos:
@@ -183,12 +185,12 @@ func (c *client) GetJobDistributor(ctx context.Context, id string) (generated.Fe
return generated.FeedsManagerParts{}, err
}
if res == nil {
- return generated.FeedsManagerParts{}, fmt.Errorf("no feeds manager found")
+ return generated.FeedsManagerParts{}, errors.New("no feeds manager found")
}
if success, ok := res.GetFeedsManager().(*generated.GetFeedsManagerFeedsManager); ok {
return success.FeedsManagerParts, nil
}
- return generated.FeedsManagerParts{}, fmt.Errorf("failed to get feeds manager")
+ return generated.FeedsManagerParts{}, errors.New("failed to get feeds manager")
}
func (c *client) ListJobDistributors(ctx context.Context) (*generated.ListFeedsManagersResponse, error) {
@@ -238,12 +240,12 @@ func (c *client) CreateJobDistributorChainConfig(ctx context.Context, in JobDist
return "", err
}
if res == nil {
- return "", fmt.Errorf("failed to create feeds manager chain config")
+ return "", errors.New("failed to create feeds manager chain config")
}
if success, ok := res.GetCreateFeedsManagerChainConfig().(*generated.CreateFeedsManagerChainConfigCreateFeedsManagerChainConfigCreateFeedsManagerChainConfigSuccess); ok {
return success.ChainConfig.Id, nil
}
- return "", fmt.Errorf("failed to create feeds manager chain config")
+ return "", errors.New("failed to create feeds manager chain config")
}
func (c *client) DeleteJobDistributorChainConfig(ctx context.Context, id string) error {
@@ -252,12 +254,12 @@ func (c *client) DeleteJobDistributorChainConfig(ctx context.Context, id string)
return err
}
if res == nil {
- return fmt.Errorf("failed to delete feeds manager chain config")
+ return errors.New("failed to delete feeds manager chain config")
}
if _, ok := res.GetDeleteFeedsManagerChainConfig().(*generated.DeleteFeedsManagerChainConfigDeleteFeedsManagerChainConfigDeleteFeedsManagerChainConfigSuccess); ok {
return nil
}
- return fmt.Errorf("failed to delete feeds manager chain config")
+ return errors.New("failed to delete feeds manager chain config")
}
func (c *client) GetJobProposal(ctx context.Context, id string) (*generated.GetJobProposalJobProposal, error) {
@@ -266,12 +268,12 @@ func (c *client) GetJobProposal(ctx context.Context, id string) (*generated.GetJ
return nil, err
}
if proposal == nil {
- return nil, fmt.Errorf("no job proposal found")
+ return nil, errors.New("no job proposal found")
}
if success, ok := proposal.GetJobProposal().(*generated.GetJobProposalJobProposal); ok {
return success, nil
}
- return nil, fmt.Errorf("failed to get job proposal")
+ return nil, errors.New("failed to get job proposal")
}
func (c *client) ApproveJobProposalSpec(ctx context.Context, id string, force bool) (*JobProposalApprovalSuccessSpec, error) {
@@ -289,7 +291,7 @@ func (c *client) ApproveJobProposalSpec(ctx context.Context, id string, force bo
return &cmd, nil
}
}
- return nil, fmt.Errorf("failed to approve job proposal spec")
+ return nil, errors.New("failed to approve job proposal spec")
}
func (c *client) CancelJobProposalSpec(ctx context.Context, id string) (*generated.CancelJobProposalSpecResponse, error) {
@@ -327,7 +329,7 @@ func (c *client) login() error {
cookieHeader := res.Header.Get("Set-Cookie")
if cookieHeader == "" {
- return fmt.Errorf("no cookie found in header")
+ return errors.New("no cookie found in header")
}
c.cookie = strings.Split(cookieHeader, ";")[0]
diff --git a/deployment/environment/web/sdk/client/types.go b/deployment/environment/web/sdk/client/types.go
index d213ee161c6..9ecc2cc72ea 100644
--- a/deployment/environment/web/sdk/client/types.go
+++ b/deployment/environment/web/sdk/client/types.go
@@ -3,7 +3,7 @@ package client
import (
"bytes"
"encoding/json"
- "fmt"
+ "errors"
"reflect"
)
@@ -47,7 +47,7 @@ type JobProposalApprovalSuccessSpec struct {
func DecodeInput(in, out any) error {
if reflect.TypeOf(out).Kind() != reflect.Ptr || reflect.ValueOf(out).IsNil() {
- return fmt.Errorf("out type must be a non-nil pointer")
+ return errors.New("out type must be a non-nil pointer")
}
jsonBytes, err := json.Marshal(in)
if err != nil {
diff --git a/deployment/evm_kmsclient.go b/deployment/evm_kmsclient.go
index b28a3842930..811125827af 100644
--- a/deployment/evm_kmsclient.go
+++ b/deployment/evm_kmsclient.go
@@ -11,6 +11,7 @@ import (
"os"
"github.com/aws/aws-sdk-go/aws/session"
+ "github.com/pkg/errors"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/kms"
@@ -59,10 +60,10 @@ type KMS struct {
func NewKMSClient(config KMS) (KMSClient, error) {
if config.KmsDeployerKeyId == "" {
- return nil, fmt.Errorf("KMS key ID is required")
+ return nil, errors.New("KMS key ID is required")
}
if config.KmsDeployerKeyRegion == "" {
- return nil, fmt.Errorf("KMS key region is required")
+ return nil, errors.New("KMS key region is required")
}
var awsSessionFn AwsSessionFn
if config.AwsProfileName != "" {
@@ -94,7 +95,7 @@ func (c *EVMKMSClient) GetKMSTransactOpts(ctx context.Context, chainID *big.Int)
pubKeyBytes := secp256k1.S256().Marshal(ecdsaPublicKey.X, ecdsaPublicKey.Y)
keyAddr := crypto.PubkeyToAddress(*ecdsaPublicKey)
if chainID == nil {
- return nil, fmt.Errorf("chainID is required")
+ return nil, errors.New("chainID is required")
}
signer := types.LatestSignerForChainID(chainID)
@@ -195,7 +196,7 @@ func recoverEthSignature(expectedPublicKeyBytes, txHash, r, s []byte) ([]byte, e
}
if hex.EncodeToString(recoveredPublicKeyBytes) != hex.EncodeToString(expectedPublicKeyBytes) {
- return nil, fmt.Errorf("can not reconstruct public key from sig")
+ return nil, errors.New("can not reconstruct public key from sig")
}
}
@@ -238,15 +239,15 @@ func KMSConfigFromEnvVars() (KMS, error) {
var exists bool
config.KmsDeployerKeyId, exists = os.LookupEnv("KMS_DEPLOYER_KEY_ID")
if !exists {
- return config, fmt.Errorf("KMS_DEPLOYER_KEY_ID is required")
+ return config, errors.New("KMS_DEPLOYER_KEY_ID is required")
}
config.KmsDeployerKeyRegion, exists = os.LookupEnv("KMS_DEPLOYER_KEY_REGION")
if !exists {
- return config, fmt.Errorf("KMS_DEPLOYER_KEY_REGION is required")
+ return config, errors.New("KMS_DEPLOYER_KEY_REGION is required")
}
config.AwsProfileName, exists = os.LookupEnv("AWS_PROFILE")
if !exists {
- return config, fmt.Errorf("AWS_PROFILE is required")
+ return config, errors.New("AWS_PROFILE is required")
}
return config, nil
}
diff --git a/deployment/helpers.go b/deployment/helpers.go
index 34a2584a544..d8e15d0200d 100644
--- a/deployment/helpers.go
+++ b/deployment/helpers.go
@@ -132,7 +132,6 @@ func DecodeErr(encodedABI string, err error) error {
return fmt.Errorf("failed to decode error '%s' with abi: %w", encErr, parseErr)
}
return fmt.Errorf("contract error: %s", errStr)
-
}
return fmt.Errorf("cannot decode error with abi: %w", err)
}
@@ -182,7 +181,7 @@ func DeployContract[C any](
func IsValidChainSelector(cs uint64) error {
if cs == 0 {
- return fmt.Errorf("chain selector must be set")
+ return errors.New("chain selector must be set")
}
_, err := chain_selectors.GetSelectorFamily(cs)
if err != nil {
diff --git a/deployment/keystone/changeset/append_node_capabilities.go b/deployment/keystone/changeset/append_node_capabilities.go
index d558cf39c95..9ae1923d270 100644
--- a/deployment/keystone/changeset/append_node_capabilities.go
+++ b/deployment/keystone/changeset/append_node_capabilities.go
@@ -1,11 +1,13 @@
package changeset
import (
+ "errors"
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/smartcontractkit/ccip-owner-contracts/pkg/gethwrappers"
"github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/timelock"
+
"github.com/smartcontractkit/chainlink/deployment"
"github.com/smartcontractkit/chainlink/deployment/common/proposalutils"
"github.com/smartcontractkit/chainlink/deployment/keystone/changeset/internal"
@@ -30,7 +32,7 @@ func AppendNodeCapabilities(env deployment.Environment, req *AppendNodeCapabilit
out := deployment.ChangesetOutput{}
if req.UseMCMS() {
if r.Ops == nil {
- return out, fmt.Errorf("expected MCMS operation to be non-nil")
+ return out, errors.New("expected MCMS operation to be non-nil")
}
timelocksPerChain := map[uint64]common.Address{
c.Chain.Selector: c.ContractSet.Timelock.Address(),
diff --git a/deployment/keystone/changeset/append_node_capabilities_test.go b/deployment/keystone/changeset/append_node_capabilities_test.go
index fb2c99ed15e..3cf6081e966 100644
--- a/deployment/keystone/changeset/append_node_capabilities_test.go
+++ b/deployment/keystone/changeset/append_node_capabilities_test.go
@@ -38,7 +38,7 @@ func TestAppendNodeCapabilities(t *testing.T) {
})
newCapabilities := make(map[p2pkey.PeerID][]kcr.CapabilitiesRegistryCapability)
- for id, _ := range te.WFNodes {
+ for id := range te.WFNodes {
k, err := p2pkey.MakePeerID(id)
require.NoError(t, err)
newCapabilities[k] = caps
@@ -52,7 +52,7 @@ func TestAppendNodeCapabilities(t *testing.T) {
csOut, err := changeset.AppendNodeCapabilities(te.Env, &cfg)
require.NoError(t, err)
- require.Len(t, csOut.Proposals, 0)
+ require.Empty(t, csOut.Proposals)
require.Nil(t, csOut.AddressBook)
validateCapabilityAppends(t, te, newCapabilities)
@@ -68,7 +68,7 @@ func TestAppendNodeCapabilities(t *testing.T) {
})
newCapabilities := make(map[p2pkey.PeerID][]kcr.CapabilitiesRegistryCapability)
- for id, _ := range te.WFNodes {
+ for id := range te.WFNodes {
k, err := p2pkey.MakePeerID(id)
require.NoError(t, err)
newCapabilities[k] = caps
@@ -105,7 +105,6 @@ func TestAppendNodeCapabilities(t *testing.T) {
require.NoError(t, err)
validateCapabilityAppends(t, te, newCapabilities)
})
-
}
// validateUpdate checks reads nodes from the registry and checks they have the expected updates
diff --git a/deployment/keystone/changeset/deploy_consumer.go b/deployment/keystone/changeset/deploy_consumer.go
index d94d7ac0adc..5442a21576a 100644
--- a/deployment/keystone/changeset/deploy_consumer.go
+++ b/deployment/keystone/changeset/deploy_consumer.go
@@ -1,6 +1,7 @@
package changeset
import (
+ "errors"
"fmt"
"github.com/smartcontractkit/chainlink/deployment"
@@ -19,7 +20,7 @@ func DeployFeedsConsumer(env deployment.Environment, req *DeployFeedsConsumerReq
lggr := env.Logger
chain, ok := env.Chains[chainSelector]
if !ok {
- return deployment.ChangesetOutput{}, fmt.Errorf("chain not found in environment")
+ return deployment.ChangesetOutput{}, errors.New("chain not found in environment")
}
ab := deployment.NewMemoryAddressBook()
deployResp, err := kslib.DeployFeedsConsumer(chain, ab)
diff --git a/deployment/keystone/changeset/deploy_consumer_test.go b/deployment/keystone/changeset/deploy_consumer_test.go
index 9a1e8f57da7..e73986b6ecf 100644
--- a/deployment/keystone/changeset/deploy_consumer_test.go
+++ b/deployment/keystone/changeset/deploy_consumer_test.go
@@ -36,5 +36,5 @@ func TestDeployFeedsConsumer(t *testing.T) {
// no feeds consumer registry on chain 1
require.NotEqual(t, registrySel, env.AllChainSelectors()[1])
oaddrs, _ := resp.AddressBook.AddressesForChain(env.AllChainSelectors()[1])
- require.Len(t, oaddrs, 0)
+ require.Empty(t, oaddrs)
}
diff --git a/deployment/keystone/changeset/deploy_forwarder.go b/deployment/keystone/changeset/deploy_forwarder.go
index 66923140e6a..8a9cdf4d681 100644
--- a/deployment/keystone/changeset/deploy_forwarder.go
+++ b/deployment/keystone/changeset/deploy_forwarder.go
@@ -1,6 +1,7 @@
package changeset
import (
+ "errors"
"fmt"
"maps"
"slices"
@@ -8,6 +9,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/smartcontractkit/ccip-owner-contracts/pkg/gethwrappers"
"github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/timelock"
+
"github.com/smartcontractkit/chainlink/deployment"
"github.com/smartcontractkit/chainlink/deployment/common/proposalutils"
"github.com/smartcontractkit/chainlink/deployment/keystone/changeset/internal"
@@ -59,7 +61,7 @@ type ConfigureForwardContractsRequest struct {
func (r ConfigureForwardContractsRequest) Validate() error {
if len(r.WFNodeIDs) == 0 {
- return fmt.Errorf("WFNodeIDs must not be empty")
+ return errors.New("WFNodeIDs must not be empty")
}
return nil
}
@@ -96,7 +98,7 @@ func ConfigureForwardContracts(env deployment.Environment, req ConfigureForwardC
var out deployment.ChangesetOutput
if req.UseMCMS() {
if len(r.OpsPerChain) == 0 {
- return out, fmt.Errorf("expected MCMS operation to be non-nil")
+ return out, errors.New("expected MCMS operation to be non-nil")
}
for chainSelector, op := range r.OpsPerChain {
contracts := cresp.ContractSets[chainSelector]
diff --git a/deployment/keystone/changeset/deploy_forwarder_test.go b/deployment/keystone/changeset/deploy_forwarder_test.go
index ec80a9432b0..40ef0c02aeb 100644
--- a/deployment/keystone/changeset/deploy_forwarder_test.go
+++ b/deployment/keystone/changeset/deploy_forwarder_test.go
@@ -4,9 +4,8 @@ import (
"fmt"
"testing"
- "go.uber.org/zap/zapcore"
-
"github.com/stretchr/testify/require"
+ "go.uber.org/zap/zapcore"
"github.com/smartcontractkit/chainlink-common/pkg/logger"
"github.com/smartcontractkit/chainlink/deployment"
@@ -65,7 +64,7 @@ func TestConfigureForwarders(t *testing.T) {
})
var wfNodes []string
- for id, _ := range te.WFNodes {
+ for id := range te.WFNodes {
wfNodes = append(wfNodes, id)
}
@@ -77,7 +76,7 @@ func TestConfigureForwarders(t *testing.T) {
csOut, err := changeset.ConfigureForwardContracts(te.Env, cfg)
require.NoError(t, err)
require.Nil(t, csOut.AddressBook)
- require.Len(t, csOut.Proposals, 0)
+ require.Empty(t, csOut.Proposals)
// check that forwarder
// TODO set up a listener to check that the forwarder is configured
contractSet := te.ContractSets()
@@ -103,7 +102,7 @@ func TestConfigureForwarders(t *testing.T) {
})
var wfNodes []string
- for id, _ := range te.WFNodes {
+ for id := range te.WFNodes {
wfNodes = append(wfNodes, id)
}
@@ -134,9 +133,7 @@ func TestConfigureForwarders(t *testing.T) {
},
})
require.NoError(t, err)
-
})
}
})
-
}
diff --git a/deployment/keystone/changeset/deploy_ocr3.go b/deployment/keystone/changeset/deploy_ocr3.go
index ba5ea2921d9..75f9b75ecd1 100644
--- a/deployment/keystone/changeset/deploy_ocr3.go
+++ b/deployment/keystone/changeset/deploy_ocr3.go
@@ -2,6 +2,7 @@ package changeset
import (
"encoding/json"
+ "errors"
"fmt"
"io"
@@ -23,7 +24,7 @@ func DeployOCR3(env deployment.Environment, registryChainSel uint64) (deployment
// ocr3 only deployed on registry chain
c, ok := env.Chains[registryChainSel]
if !ok {
- return deployment.ChangesetOutput{}, fmt.Errorf("chain not found in environment")
+ return deployment.ChangesetOutput{}, errors.New("chain not found in environment")
}
ocr3Resp, err := kslib.DeployOCR3(c, ab)
if err != nil {
@@ -74,14 +75,14 @@ func ConfigureOCR3Contract(env deployment.Environment, cfg ConfigureOCR3Config)
return deployment.ChangesetOutput{}, fmt.Errorf("failed to write response output: %w", err)
}
if n != len(b) {
- return deployment.ChangesetOutput{}, fmt.Errorf("failed to write all bytes")
+ return deployment.ChangesetOutput{}, errors.New("failed to write all bytes")
}
}
// does not create any new addresses
var out deployment.ChangesetOutput
if cfg.UseMCMS() {
if resp.Ops == nil {
- return out, fmt.Errorf("expected MCMS operation to be non-nil")
+ return out, errors.New("expected MCMS operation to be non-nil")
}
r, err := kslib.GetContractSets(env.Logger, &kslib.GetContractSetsRequest{
Chains: env.Chains,
@@ -109,7 +110,6 @@ func ConfigureOCR3Contract(env deployment.Environment, cfg ConfigureOCR3Config)
return out, fmt.Errorf("failed to build proposal: %w", err)
}
out.Proposals = []timelock.MCMSWithTimelockProposal{*proposal}
-
}
return out, nil
}
diff --git a/deployment/keystone/changeset/deploy_ocr3_test.go b/deployment/keystone/changeset/deploy_ocr3_test.go
index ea984989703..5ede6c5e6c7 100644
--- a/deployment/keystone/changeset/deploy_ocr3_test.go
+++ b/deployment/keystone/changeset/deploy_ocr3_test.go
@@ -5,11 +5,10 @@ import (
"encoding/json"
"testing"
- "go.uber.org/zap/zapcore"
-
"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "go.uber.org/zap/zapcore"
"github.com/smartcontractkit/chainlink-common/pkg/logger"
@@ -43,7 +42,7 @@ func TestDeployOCR3(t *testing.T) {
// nothing on chain 1
require.NotEqual(t, registrySel, env.AllChainSelectors()[1])
oaddrs, _ := resp.AddressBook.AddressesForChain(env.AllChainSelectors()[1])
- assert.Len(t, oaddrs, 0)
+ assert.Empty(t, oaddrs)
}
func TestConfigureOCR3(t *testing.T) {
@@ -55,7 +54,6 @@ func TestConfigureOCR3(t *testing.T) {
}
t.Run("no mcms", func(t *testing.T) {
-
te := test.SetupTestEnv(t, test.TestConfig{
WFDonConfig: test.DonConfig{N: 4},
AssetDonConfig: test.DonConfig{N: 4},
@@ -295,5 +293,4 @@ func TestConfigureOCR3(t *testing.T) {
})
require.NoError(t, err)
})
-
}
diff --git a/deployment/keystone/changeset/deploy_registry.go b/deployment/keystone/changeset/deploy_registry.go
index 2b8342c06dd..f78b6762f9e 100644
--- a/deployment/keystone/changeset/deploy_registry.go
+++ b/deployment/keystone/changeset/deploy_registry.go
@@ -1,6 +1,7 @@
package changeset
import (
+ "errors"
"fmt"
"github.com/smartcontractkit/chainlink/deployment"
@@ -13,7 +14,7 @@ func DeployCapabilityRegistry(env deployment.Environment, registrySelector uint6
lggr := env.Logger
chain, ok := env.Chains[registrySelector]
if !ok {
- return deployment.ChangesetOutput{}, fmt.Errorf("chain not found in environment")
+ return deployment.ChangesetOutput{}, errors.New("chain not found in environment")
}
ab := deployment.NewMemoryAddressBook()
capabilitiesRegistryResp, err := kslib.DeployCapabilitiesRegistry(chain, ab)
diff --git a/deployment/keystone/changeset/deploy_registry_test.go b/deployment/keystone/changeset/deploy_registry_test.go
index 9abf357f2a8..713ef897197 100644
--- a/deployment/keystone/changeset/deploy_registry_test.go
+++ b/deployment/keystone/changeset/deploy_registry_test.go
@@ -34,5 +34,5 @@ func TestDeployCapabilityRegistry(t *testing.T) {
// no capabilities registry on chain 1
require.NotEqual(t, registrySel, env.AllChainSelectors()[1])
oaddrs, _ := resp.AddressBook.AddressesForChain(env.AllChainSelectors()[1])
- require.Len(t, oaddrs, 0)
+ require.Empty(t, oaddrs)
}
diff --git a/deployment/keystone/changeset/internal/append_node_capabilities.go b/deployment/keystone/changeset/internal/append_node_capabilities.go
index 32fe8572da3..c6379fd24fd 100644
--- a/deployment/keystone/changeset/internal/append_node_capabilities.go
+++ b/deployment/keystone/changeset/internal/append_node_capabilities.go
@@ -1,6 +1,7 @@
package internal
import (
+ "errors"
"fmt"
"github.com/smartcontractkit/chainlink-common/pkg/logger"
@@ -20,10 +21,10 @@ type AppendNodeCapabilitiesRequest struct {
func (req *AppendNodeCapabilitiesRequest) Validate() error {
if len(req.P2pToCapabilities) == 0 {
- return fmt.Errorf("p2pToCapabilities is empty")
+ return errors.New("p2pToCapabilities is empty")
}
if req.ContractSet.CapabilitiesRegistry == nil {
- return fmt.Errorf("registry is nil")
+ return errors.New("registry is nil")
}
return nil
}
diff --git a/deployment/keystone/changeset/internal/capability_management.go b/deployment/keystone/changeset/internal/capability_management.go
index 268b4fd0d01..d85c3f0dfff 100644
--- a/deployment/keystone/changeset/internal/capability_management.go
+++ b/deployment/keystone/changeset/internal/capability_management.go
@@ -6,6 +6,7 @@ import (
"github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/mcms"
"github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/timelock"
+
"github.com/smartcontractkit/chainlink-common/pkg/logger"
"github.com/smartcontractkit/chainlink/deployment"
kcr "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry_1_1_0"
diff --git a/deployment/keystone/changeset/internal/contract_set.go b/deployment/keystone/changeset/internal/contract_set.go
index e60f37d6f76..540ab80097b 100644
--- a/deployment/keystone/changeset/internal/contract_set.go
+++ b/deployment/keystone/changeset/internal/contract_set.go
@@ -108,7 +108,6 @@ func DeployFeedsConsumer(chain deployment.Chain, ab deployment.AddressBook) (*De
}
err = ab.Save(chain.Selector, consumerResp.Address.String(), consumerResp.Tv)
if err != nil {
-
return nil, fmt.Errorf("failed to save FeedsConsumer: %w", err)
}
return consumerResp, nil
diff --git a/deployment/keystone/changeset/internal/deploy.go b/deployment/keystone/changeset/internal/deploy.go
index acaabd22131..b52d269518d 100644
--- a/deployment/keystone/changeset/internal/deploy.go
+++ b/deployment/keystone/changeset/internal/deploy.go
@@ -19,6 +19,7 @@ import (
"github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/mcms"
"github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/timelock"
+
"github.com/smartcontractkit/chainlink/deployment"
"google.golang.org/protobuf/proto"
@@ -187,7 +188,7 @@ func GetRegistryContract(e *deployment.Environment, registryChainSel uint64) (*c
}
registry = registryChainContracts.CapabilitiesRegistry
if registry == nil {
- return nil, deployment.Chain{}, fmt.Errorf("no registry contract found")
+ return nil, deployment.Chain{}, errors.New("no registry contract found")
}
e.Logger.Debugf("registry contract address: %s, chain %d", registry.Address().String(), registryChainSel)
return registry, registryChain, nil
@@ -409,7 +410,6 @@ func ConfigureOCR3ContractFromJD(env *deployment.Environment, cfg ConfigureOCR3C
OCR2OracleConfig: r.ocrConfig,
Ops: r.ops,
}, nil
-
}
type RegisterCapabilitiesRequest struct {
@@ -445,7 +445,7 @@ func FromCapabilitiesRegistryCapability(cap *capabilities_registry.CapabilitiesR
// RegisterCapabilities add computes the capability id, adds it to the registry and associates the registered capabilities with appropriate don(s)
func RegisterCapabilities(lggr logger.Logger, req RegisterCapabilitiesRequest) (*RegisterCapabilitiesResponse, error) {
if len(req.DonToCapabilities) == 0 {
- return nil, fmt.Errorf("no capabilities to register")
+ return nil, errors.New("no capabilities to register")
}
cresp, err := GetContractSets(req.Env.Logger, &GetContractSetsRequest{
Chains: req.Env.Chains,
@@ -891,7 +891,7 @@ func RegisterDons(lggr logger.Logger, req RegisterDonsRequest) (*RegisterDonsRes
return nil, fmt.Errorf("failed to call GetDONs: %w", err)
}
if !foundAll {
- return nil, fmt.Errorf("did not find all desired DONS")
+ return nil, errors.New("did not find all desired DONS")
}
resp := RegisterDonsResponse{
@@ -903,7 +903,7 @@ func RegisterDons(lggr logger.Logger, req RegisterDonsRequest) (*RegisterDonsRes
lggr.Debugw("irrelevant DON found in the registry, ignoring", "p2p sorted hash", sortedHash(donInfo.NodeP2PIds))
continue
}
- lggr.Debugw("adding don info to the reponse (keyed by DON name)", "don", donName)
+ lggr.Debugw("adding don info to the response (keyed by DON name)", "don", donName)
resp.DonInfos[donName] = donInfos[i]
}
return &resp, nil
diff --git a/deployment/keystone/changeset/internal/forwarder_deployer.go b/deployment/keystone/changeset/internal/forwarder_deployer.go
index 2ce3ae88146..6e374e200d7 100644
--- a/deployment/keystone/changeset/internal/forwarder_deployer.go
+++ b/deployment/keystone/changeset/internal/forwarder_deployer.go
@@ -5,6 +5,7 @@ import (
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/timelock"
+
"github.com/smartcontractkit/chainlink-common/pkg/logger"
"github.com/smartcontractkit/chainlink/deployment"
diff --git a/deployment/keystone/changeset/internal/ocr3_deployer.go b/deployment/keystone/changeset/internal/ocr3_deployer.go
index beafe9bb9e2..35e75b5ec43 100644
--- a/deployment/keystone/changeset/internal/ocr3_deployer.go
+++ b/deployment/keystone/changeset/internal/ocr3_deployer.go
@@ -4,6 +4,7 @@ import (
"fmt"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
+
"github.com/smartcontractkit/chainlink-common/pkg/logger"
"github.com/smartcontractkit/chainlink/deployment"
diff --git a/deployment/keystone/changeset/internal/ocr3config.go b/deployment/keystone/changeset/internal/ocr3config.go
index 74f8a9dabd5..d1d2e337efb 100644
--- a/deployment/keystone/changeset/internal/ocr3config.go
+++ b/deployment/keystone/changeset/internal/ocr3config.go
@@ -222,7 +222,7 @@ func GenerateOCR3Config(cfg OracleConfig, nca []NodeKeys, secrets deployment.OCR
for index := range nca {
identities = append(identities, confighelper.OracleIdentityExtra{
OracleIdentity: confighelper.OracleIdentity{
- OnchainPublicKey: onchainPubKeys[index][:],
+ OnchainPublicKey: onchainPubKeys[index],
OffchainPublicKey: offchainPubKeysBytes[index],
PeerID: nca[index].P2PPeerID,
TransmitAccount: types.Account(nca[index].EthAddress),
@@ -303,7 +303,7 @@ type configureOCR3Response struct {
func configureOCR3contract(req configureOCR3Request) (*configureOCR3Response, error) {
if req.contract == nil {
- return nil, fmt.Errorf("OCR3 contract is nil")
+ return nil, errors.New("OCR3 contract is nil")
}
ocrConfig, err := req.generateOCR3Config()
if err != nil {
diff --git a/deployment/keystone/changeset/internal/ocr3config_test.go b/deployment/keystone/changeset/internal/ocr3config_test.go
index b412a727eb9..55769fdaece 100644
--- a/deployment/keystone/changeset/internal/ocr3config_test.go
+++ b/deployment/keystone/changeset/internal/ocr3config_test.go
@@ -115,7 +115,7 @@ func loadTestData(t *testing.T, path string) []deployment.Node {
// in general we can map from the view to the node, but we know the test data
var nodes []deployment.Node
- //for _, nv := range nodeViews {
+ // for _, nv := range nodeViews {
for _, name := range names {
nv := nodeViews[name]
node := deployment.Node{
diff --git a/deployment/keystone/changeset/internal/types.go b/deployment/keystone/changeset/internal/types.go
index 173e3ba1ad0..cffd69f85e6 100644
--- a/deployment/keystone/changeset/internal/types.go
+++ b/deployment/keystone/changeset/internal/types.go
@@ -1,6 +1,7 @@
package internal
import (
+ "encoding/hex"
"errors"
"fmt"
"slices"
@@ -14,7 +15,7 @@ import (
"github.com/smartcontractkit/chainlink/deployment"
- "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry_1_1_0"
+ capabilities_registry "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry_1_1_0"
kcr "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry_1_1_0"
"github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey"
)
@@ -81,9 +82,9 @@ func toNodeKeys(o *deployment.Node, registryChainSel uint64) NodeKeys {
EthAddress: string(evmCC.TransmitAccount),
P2PPeerID: strings.TrimPrefix(o.PeerID.String(), "p2p_"),
OCR2BundleID: evmCC.KeyBundleID,
- OCR2OffchainPublicKey: fmt.Sprintf("%x", evmCC.OffchainPublicKey[:]),
+ OCR2OffchainPublicKey: hex.EncodeToString(evmCC.OffchainPublicKey[:]),
OCR2OnchainPublicKey: fmt.Sprintf("%x", evmCC.OnchainPublicKey[:]),
- OCR2ConfigPublicKey: fmt.Sprintf("%x", evmCC.ConfigEncryptionPublicKey[:]),
+ OCR2ConfigPublicKey: hex.EncodeToString(evmCC.ConfigEncryptionPublicKey[:]),
CSAPublicKey: o.CSAKey,
// default value of encryption public key is the CSA public key
// TODO: DEVSVCS-760
@@ -266,7 +267,7 @@ func NewRegisteredDon(env deployment.Environment, cfg RegisteredDonConfig) (*Reg
}
}
if don == nil {
- return nil, fmt.Errorf("don not found in registry")
+ return nil, errors.New("don not found in registry")
}
return &RegisteredDon{
Name: cfg.Name,
@@ -286,11 +287,10 @@ func (d RegisteredDon) Signers(chainFamily string) []common.Address {
}
var found bool
var registryChainDetails chainsel.ChainDetails
- for details, _ := range n.SelToOCRConfig {
+ for details := range n.SelToOCRConfig {
if family, err := chainsel.GetSelectorFamily(details.ChainSelector); err == nil && family == chainFamily {
found = true
registryChainDetails = details
-
}
}
if !found {
@@ -319,7 +319,6 @@ func joinInfoAndNodes(donInfos map[string]kcr.CapabilitiesRegistryDONInfo, dons
}
var out []RegisteredDon
for donName, info := range donInfos {
-
ocr2nodes, ok := nodes[donName]
if !ok {
return nil, fmt.Errorf("nodes not found for don %s", donName)
diff --git a/deployment/keystone/changeset/internal/types_test.go b/deployment/keystone/changeset/internal/types_test.go
index cfc953d6126..e8d02f51df0 100644
--- a/deployment/keystone/changeset/internal/types_test.go
+++ b/deployment/keystone/changeset/internal/types_test.go
@@ -10,10 +10,11 @@ import (
"github.com/ethereum/go-ethereum/common"
chainsel "github.com/smartcontractkit/chain-selectors"
- "github.com/smartcontractkit/chainlink/deployment"
- "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey"
"github.com/smartcontractkit/libocr/offchainreporting2plus/types"
"github.com/stretchr/testify/require"
+
+ "github.com/smartcontractkit/chainlink/deployment"
+ "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey"
)
func Test_toNodeKeys(t *testing.T) {
@@ -49,7 +50,7 @@ func Test_toNodeKeys(t *testing.T) {
SelToOCRConfig: map[chainsel.ChainDetails]deployment.OCRConfig{
registryChainDetails: {
OffchainPublicKey: types.OffchainPublicKey(common.FromHex("1111111111111111111111111111111111111111111111111111111111111111")),
- OnchainPublicKey: signing_1[:],
+ OnchainPublicKey: signing_1,
PeerID: p2pID.PeerID(),
TransmitAccount: types.Account(admin_1.String()),
ConfigEncryptionPublicKey: encryptionpubkey,
diff --git a/deployment/keystone/changeset/internal/update_don.go b/deployment/keystone/changeset/internal/update_don.go
index 3cfc386b2ba..aa3e203e5e4 100644
--- a/deployment/keystone/changeset/internal/update_don.go
+++ b/deployment/keystone/changeset/internal/update_don.go
@@ -5,6 +5,7 @@ import (
"crypto/sha256"
"encoding/hex"
"encoding/json"
+ "errors"
"fmt"
"math/big"
"sort"
@@ -12,10 +13,11 @@ import (
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/mcms"
"github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/timelock"
+ "google.golang.org/protobuf/proto"
+
"github.com/smartcontractkit/chainlink-common/pkg/logger"
"github.com/smartcontractkit/chainlink/deployment"
"github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey"
- "google.golang.org/protobuf/proto"
kcr "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry_1_1_0"
)
@@ -56,10 +58,10 @@ func (r *UpdateDonRequest) AppendNodeCapabilitiesRequest() *AppendNodeCapabiliti
func (r *UpdateDonRequest) Validate() error {
if r.ContractSet.CapabilitiesRegistry == nil {
- return fmt.Errorf("registry is required")
+ return errors.New("registry is required")
}
if len(r.P2PIDs) == 0 {
- return fmt.Errorf("p2pIDs is required")
+ return errors.New("p2pIDs is required")
}
return nil
}
diff --git a/deployment/keystone/changeset/internal/update_don_test.go b/deployment/keystone/changeset/internal/update_don_test.go
index 57b15138538..bf9ab96fecb 100644
--- a/deployment/keystone/changeset/internal/update_don_test.go
+++ b/deployment/keystone/changeset/internal/update_don_test.go
@@ -11,6 +11,9 @@ import (
"github.com/ethereum/go-ethereum/common"
chainsel "github.com/smartcontractkit/chain-selectors"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+
"github.com/smartcontractkit/chainlink-common/pkg/logger"
"github.com/smartcontractkit/chainlink/deployment"
kscs "github.com/smartcontractkit/chainlink/deployment/keystone/changeset"
@@ -18,8 +21,6 @@ import (
kstest "github.com/smartcontractkit/chainlink/deployment/keystone/changeset/internal/test"
kcr "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry_1_1_0"
"github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
)
var (
@@ -153,7 +154,6 @@ func TestUpdateDon(t *testing.T) {
assert.Equal(t, want.DonInfo.ConfigCount, got.DonInfo.ConfigCount)
assert.Equal(t, sortedP2Pids(want.DonInfo.NodeP2PIds), sortedP2Pids(got.DonInfo.NodeP2PIds))
assert.Equal(t, capIds(want.DonInfo.CapabilityConfigurations), capIds(got.DonInfo.CapabilityConfigurations))
-
})
}
@@ -234,7 +234,6 @@ func registerTestDon(t *testing.T, lggr logger.Logger, cfg setupUpdateDonTestCon
t.Helper()
req := newSetupTestRegistryRequest(t, cfg.dons, cfg.nops)
return kstest.SetupTestRegistry(t, lggr, req)
-
}
func newSetupTestRegistryRequest(t *testing.T, dons []internal.DonInfo, nops []internal.NOP) *kstest.SetupTestRegistryRequest {
diff --git a/deployment/keystone/changeset/internal/update_node_capabilities.go b/deployment/keystone/changeset/internal/update_node_capabilities.go
index 16c37267060..23e3d66965c 100644
--- a/deployment/keystone/changeset/internal/update_node_capabilities.go
+++ b/deployment/keystone/changeset/internal/update_node_capabilities.go
@@ -1,6 +1,7 @@
package internal
import (
+ "errors"
"fmt"
"github.com/smartcontractkit/chainlink-common/pkg/logger"
@@ -19,10 +20,10 @@ type UpdateNodeCapabilitiesImplRequest struct {
func (req *UpdateNodeCapabilitiesImplRequest) Validate() error {
if len(req.P2pToCapabilities) == 0 {
- return fmt.Errorf("p2pToCapabilities is empty")
+ return errors.New("p2pToCapabilities is empty")
}
if req.ContractSet == nil {
- return fmt.Errorf("registry is nil")
+ return errors.New("registry is nil")
}
return nil
diff --git a/deployment/keystone/changeset/internal/update_nodes.go b/deployment/keystone/changeset/internal/update_nodes.go
index b27c17ad19f..976125e582d 100644
--- a/deployment/keystone/changeset/internal/update_nodes.go
+++ b/deployment/keystone/changeset/internal/update_nodes.go
@@ -12,6 +12,7 @@ import (
"github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/mcms"
"github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/timelock"
+
"github.com/smartcontractkit/chainlink-common/pkg/logger"
kcr "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry_1_1_0"
"github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey"
@@ -194,7 +195,6 @@ func AppendCapabilities(lggr logger.Logger, registry *kcr.CapabilitiesRegistry,
func makeNodeParams(registry *kcr.CapabilitiesRegistry,
p2pToUpdates map[p2pkey.PeerID]NodeUpdate) ([]kcr.CapabilitiesRegistryNodeParams, error) {
-
var out []kcr.CapabilitiesRegistryNodeParams
var p2pIds []p2pkey.PeerID
for p2pID := range p2pToUpdates {
@@ -257,7 +257,6 @@ func makeNodeParams(registry *kcr.CapabilitiesRegistry,
})
return out, nil
-
}
// fetchCapabilityIDs fetches the capability ids for the given capabilities
diff --git a/deployment/keystone/changeset/internal/update_nodes_test.go b/deployment/keystone/changeset/internal/update_nodes_test.go
index 0f22120998a..1b532129e48 100644
--- a/deployment/keystone/changeset/internal/update_nodes_test.go
+++ b/deployment/keystone/changeset/internal/update_nodes_test.go
@@ -597,7 +597,6 @@ func TestUpdateNodes(t *testing.T) {
}
func TestAppendCapabilities(t *testing.T) {
-
var (
capMap = map[p2pkey.PeerID][]kcr.CapabilitiesRegistryCapability{
testPeerID(t, "peerID_1"): []kcr.CapabilitiesRegistryCapability{
@@ -663,7 +662,6 @@ func TestAppendCapabilities(t *testing.T) {
gotCaps2 := appendedResp2[testPeerID(t, "peerID_1")]
require.Len(t, gotCaps2, 3)
require.EqualValues(t, gotCaps, gotCaps2)
-
}
func testPeerID(t *testing.T, s string) p2pkey.PeerID {
diff --git a/deployment/keystone/changeset/update_don.go b/deployment/keystone/changeset/update_don.go
index 5b381a4e498..47cb7c82507 100644
--- a/deployment/keystone/changeset/update_don.go
+++ b/deployment/keystone/changeset/update_don.go
@@ -1,6 +1,7 @@
package changeset
import (
+ "errors"
"fmt"
"github.com/smartcontractkit/chainlink/deployment"
@@ -26,10 +27,10 @@ type UpdateDonRequest struct {
func (r *UpdateDonRequest) Validate() error {
if len(r.P2PIDs) == 0 {
- return fmt.Errorf("p2pIDs is required")
+ return errors.New("p2pIDs is required")
}
if len(r.CapabilityConfigs) == 0 {
- return fmt.Errorf("capabilityConfigs is required")
+ return errors.New("capabilityConfigs is required")
}
return nil
}
@@ -63,10 +64,10 @@ func UpdateDon(env deployment.Environment, req *UpdateDonRequest) (deployment.Ch
out := deployment.ChangesetOutput{}
if req.UseMCMS() {
if updateResult.Ops == nil {
- return out, fmt.Errorf("expected MCMS operation to be non-nil")
+ return out, errors.New("expected MCMS operation to be non-nil")
}
if len(appendResult.Proposals) == 0 {
- return out, fmt.Errorf("expected append node capabilities to return proposals")
+ return out, errors.New("expected append node capabilities to return proposals")
}
out.Proposals = appendResult.Proposals
@@ -75,10 +76,8 @@ func UpdateDon(env deployment.Environment, req *UpdateDonRequest) (deployment.Ch
// this makes the proposal all-or-nothing because all the operations are in the same batch, there is only one tr
// transaction and only one proposal
out.Proposals[0].Transactions[0].Batch = append(out.Proposals[0].Transactions[0].Batch, updateResult.Ops.Batch...)
-
}
return out, nil
-
}
func appendRequest(r *UpdateDonRequest) *AppendNodeCapabilitiesRequest {
diff --git a/deployment/keystone/changeset/update_don_test.go b/deployment/keystone/changeset/update_don_test.go
index 2487087e235..74e2609b0a1 100644
--- a/deployment/keystone/changeset/update_don_test.go
+++ b/deployment/keystone/changeset/update_don_test.go
@@ -41,7 +41,7 @@ func TestUpdateDon(t *testing.T) {
// we have to keep track of the existing capabilities to add to the new ones
var p2pIDs []p2pkey.PeerID
newCapabilities := make(map[p2pkey.PeerID][]kcr.CapabilitiesRegistryCapability)
- for id, _ := range te.WFNodes {
+ for id := range te.WFNodes {
k, err := p2pkey.MakePeerID(id)
require.NoError(t, err)
p2pIDs = append(p2pIDs, k)
@@ -64,7 +64,7 @@ func TestUpdateDon(t *testing.T) {
csOut, err := changeset.UpdateDon(te.Env, &cfg)
require.NoError(t, err)
- require.Len(t, csOut.Proposals, 0)
+ require.Empty(t, csOut.Proposals)
require.Nil(t, csOut.AddressBook)
assertDonContainsCapabilities(t, te.ContractSets()[te.RegistrySelector].CapabilitiesRegistry, caps, p2pIDs)
@@ -82,7 +82,7 @@ func TestUpdateDon(t *testing.T) {
// contract set is already deployed with capabilities
// we have to keep track of the existing capabilities to add to the new ones
var p2pIDs []p2pkey.PeerID
- for id, _ := range te.WFNodes {
+ for id := range te.WFNodes {
k, err := p2pkey.MakePeerID(id)
require.NoError(t, err)
p2pIDs = append(p2pIDs, k)
diff --git a/deployment/keystone/changeset/update_node_capabilities.go b/deployment/keystone/changeset/update_node_capabilities.go
index 8c4d01159ed..c96393328db 100644
--- a/deployment/keystone/changeset/update_node_capabilities.go
+++ b/deployment/keystone/changeset/update_node_capabilities.go
@@ -1,6 +1,7 @@
package changeset
import (
+ "errors"
"fmt"
"strconv"
@@ -9,6 +10,7 @@ import (
"github.com/smartcontractkit/ccip-owner-contracts/pkg/gethwrappers"
"github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/timelock"
+
"github.com/smartcontractkit/chainlink/deployment"
"github.com/smartcontractkit/chainlink/deployment/common/proposalutils"
"github.com/smartcontractkit/chainlink/deployment/keystone/changeset/internal"
@@ -61,7 +63,7 @@ type MutateNodeCapabilitiesRequest struct {
func (req *MutateNodeCapabilitiesRequest) Validate() error {
if len(req.P2pToCapabilities) == 0 {
- return fmt.Errorf("p2pToCapabilities is empty")
+ return errors.New("p2pToCapabilities is empty")
}
_, exists := chainsel.ChainBySelector(req.RegistryChainSel)
if !exists {
@@ -118,7 +120,7 @@ func UpdateNodeCapabilities(env deployment.Environment, req *UpdateNodeCapabilit
out := deployment.ChangesetOutput{}
if req.UseMCMS() {
if r.Ops == nil {
- return out, fmt.Errorf("expected MCMS operation to be non-nil")
+ return out, errors.New("expected MCMS operation to be non-nil")
}
timelocksPerChain := map[uint64]common.Address{
c.Chain.Selector: c.ContractSet.Timelock.Address(),
diff --git a/deployment/keystone/changeset/update_node_capabilities_test.go b/deployment/keystone/changeset/update_node_capabilities_test.go
index cf6b9601039..8962dfc389d 100644
--- a/deployment/keystone/changeset/update_node_capabilities_test.go
+++ b/deployment/keystone/changeset/update_node_capabilities_test.go
@@ -41,7 +41,7 @@ func TestUpdateNodeCapabilities(t *testing.T) {
// we have to keep track of the existing capabilities to add to the new ones
var p2pIDs []p2pkey.PeerID
newCapabilities := make(map[p2pkey.PeerID][]kcr.CapabilitiesRegistryCapability)
- for id, _ := range te.WFNodes {
+ for id := range te.WFNodes {
k, err := p2pkey.MakePeerID(id)
require.NoError(t, err)
p2pIDs = append(p2pIDs, k)
@@ -49,7 +49,6 @@ func TestUpdateNodeCapabilities(t *testing.T) {
}
t.Run("fails if update drops existing capabilities", func(t *testing.T) {
-
cfg := changeset.UpdateNodeCapabilitiesRequest{
RegistryChainSel: te.RegistrySelector,
P2pToCapabilities: newCapabilities,
@@ -73,7 +72,7 @@ func TestUpdateNodeCapabilities(t *testing.T) {
csOut, err := changeset.UpdateNodeCapabilities(te.Env, &cfg)
require.NoError(t, err)
- require.Len(t, csOut.Proposals, 0)
+ require.Empty(t, csOut.Proposals)
require.Nil(t, csOut.AddressBook)
validateCapabilityUpdates(t, te, capabiltiesToSet)
@@ -92,7 +91,7 @@ func TestUpdateNodeCapabilities(t *testing.T) {
// we have to keep track of the existing capabilities to add to the new ones
var p2pIDs []p2pkey.PeerID
newCapabilities := make(map[p2pkey.PeerID][]kcr.CapabilitiesRegistryCapability)
- for id, _ := range te.WFNodes {
+ for id := range te.WFNodes {
k, err := p2pkey.MakePeerID(id)
require.NoError(t, err)
p2pIDs = append(p2pIDs, k)
@@ -135,9 +134,7 @@ func TestUpdateNodeCapabilities(t *testing.T) {
})
require.NoError(t, err)
validateCapabilityUpdates(t, te, capabiltiesToSet)
-
})
-
}
// validateUpdate checks reads nodes from the registry and checks they have the expected updates
diff --git a/deployment/keystone/changeset/update_nodes.go b/deployment/keystone/changeset/update_nodes.go
index 10a7ad4e441..4a98f8b06e9 100644
--- a/deployment/keystone/changeset/update_nodes.go
+++ b/deployment/keystone/changeset/update_nodes.go
@@ -1,12 +1,14 @@
package changeset
import (
+ "errors"
"fmt"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/smartcontractkit/ccip-owner-contracts/pkg/gethwrappers"
"github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/timelock"
+
"github.com/smartcontractkit/chainlink/deployment"
"github.com/smartcontractkit/chainlink/deployment/common/proposalutils"
@@ -30,7 +32,7 @@ type UpdateNodesRequest struct {
func (r *UpdateNodesRequest) Validate() error {
if r.P2pToUpdates == nil {
- return fmt.Errorf("P2pToUpdates must be non-nil")
+ return errors.New("P2pToUpdates must be non-nil")
}
return nil
}
@@ -74,7 +76,7 @@ func UpdateNodes(env deployment.Environment, req *UpdateNodesRequest) (deploymen
out := deployment.ChangesetOutput{}
if req.UseMCMS() {
if resp.Ops == nil {
- return out, fmt.Errorf("expected MCMS operation to be non-nil")
+ return out, errors.New("expected MCMS operation to be non-nil")
}
timelocksPerChain := map[uint64]common.Address{
req.RegistryChainSel: contracts.Timelock.Address(),
diff --git a/deployment/keystone/changeset/update_nodes_test.go b/deployment/keystone/changeset/update_nodes_test.go
index 33662aa669d..5709482ddb3 100644
--- a/deployment/keystone/changeset/update_nodes_test.go
+++ b/deployment/keystone/changeset/update_nodes_test.go
@@ -28,7 +28,7 @@ func TestUpdateNodes(t *testing.T) {
updates := make(map[p2pkey.PeerID]changeset.NodeUpdate)
i := uint8(0)
- for id, _ := range te.WFNodes {
+ for id := range te.WFNodes {
k, err := p2pkey.MakePeerID(id)
require.NoError(t, err)
pubKey := [32]byte{31: i + 1}
@@ -48,7 +48,7 @@ func TestUpdateNodes(t *testing.T) {
csOut, err := changeset.UpdateNodes(te.Env, &cfg)
require.NoError(t, err)
- require.Len(t, csOut.Proposals, 0)
+ require.Empty(t, csOut.Proposals)
require.Nil(t, csOut.AddressBook)
validateUpdate(t, te, updates)
@@ -65,7 +65,7 @@ func TestUpdateNodes(t *testing.T) {
updates := make(map[p2pkey.PeerID]changeset.NodeUpdate)
i := uint8(0)
- for id, _ := range te.WFNodes {
+ for id := range te.WFNodes {
k, err := p2pkey.MakePeerID(id)
require.NoError(t, err)
pubKey := [32]byte{31: i + 1}
@@ -111,7 +111,6 @@ func TestUpdateNodes(t *testing.T) {
validateUpdate(t, te, updates)
})
-
}
// validateUpdate checks reads nodes from the registry and checks they have the expected updates
diff --git a/deployment/keystone/changeset/view.go b/deployment/keystone/changeset/view.go
index 9c8678d8778..f6f495fd30b 100644
--- a/deployment/keystone/changeset/view.go
+++ b/deployment/keystone/changeset/view.go
@@ -37,7 +37,6 @@ func ViewKeystone(e deployment.Environment) (json.Marshaler, error) {
return nil, fmt.Errorf("failed to view contract set: %w", err)
}
chainViews[chainName] = v
-
}
nopsView, err := commonview.GenerateNopsView(e.NodeIDs, e.Offchain)
if err != nil {
diff --git a/deployment/keystone/changeset/workflowregistry/deploy.go b/deployment/keystone/changeset/workflowregistry/deploy.go
index e55484aa711..bb88918594c 100644
--- a/deployment/keystone/changeset/workflowregistry/deploy.go
+++ b/deployment/keystone/changeset/workflowregistry/deploy.go
@@ -1,6 +1,7 @@
package workflowregistry
import (
+ "errors"
"fmt"
"github.com/smartcontractkit/chainlink/deployment"
@@ -12,7 +13,7 @@ func Deploy(env deployment.Environment, registrySelector uint64) (deployment.Cha
lggr := env.Logger
chain, ok := env.Chains[registrySelector]
if !ok {
- return deployment.ChangesetOutput{}, fmt.Errorf("chain not found in environment")
+ return deployment.ChangesetOutput{}, errors.New("chain not found in environment")
}
ab := deployment.NewMemoryAddressBook()
wrResp, err := deployWorkflowRegistry(chain, ab)
diff --git a/deployment/keystone/changeset/workflowregistry/deploy_test.go b/deployment/keystone/changeset/workflowregistry/deploy_test.go
index 16eb6fa8512..ec40646b378 100644
--- a/deployment/keystone/changeset/workflowregistry/deploy_test.go
+++ b/deployment/keystone/changeset/workflowregistry/deploy_test.go
@@ -34,5 +34,5 @@ func Test_Deploy(t *testing.T) {
// nothing on chain 1
require.NotEqual(t, registrySel, env.AllChainSelectors()[1])
oaddrs, _ := resp.AddressBook.AddressesForChain(env.AllChainSelectors()[1])
- assert.Len(t, oaddrs, 0)
+ assert.Empty(t, oaddrs)
}
diff --git a/deployment/keystone/changeset/workflowregistry/setup_test.go b/deployment/keystone/changeset/workflowregistry/setup_test.go
index 78e7d852080..ec4d448b93c 100644
--- a/deployment/keystone/changeset/workflowregistry/setup_test.go
+++ b/deployment/keystone/changeset/workflowregistry/setup_test.go
@@ -3,12 +3,13 @@ package workflowregistry
import (
"testing"
+ "github.com/stretchr/testify/require"
+
"github.com/smartcontractkit/chainlink-common/pkg/logger"
"github.com/smartcontractkit/chainlink/deployment"
"github.com/smartcontractkit/chainlink/deployment/environment/memory"
"github.com/smartcontractkit/chainlink/deployment/keystone/changeset"
workflow_registry "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/workflow/generated/workflow_registry_wrapper"
- "github.com/stretchr/testify/require"
)
type SetupTestWorkflowRegistryResponse struct {
diff --git a/deployment/keystone/changeset/workflowregistry/update_allowed_dons_test.go b/deployment/keystone/changeset/workflowregistry/update_allowed_dons_test.go
index f24db609553..aa869ce1517 100644
--- a/deployment/keystone/changeset/workflowregistry/update_allowed_dons_test.go
+++ b/deployment/keystone/changeset/workflowregistry/update_allowed_dons_test.go
@@ -29,7 +29,7 @@ func TestUpdateAllowedDons(t *testing.T) {
dons, err := registry.GetAllAllowedDONs(&bind.CallOpts{})
require.NoError(t, err)
- assert.Len(t, dons, 0)
+ assert.Empty(t, dons)
env := deployment.Environment{
Logger: lggr,
@@ -53,7 +53,7 @@ func TestUpdateAllowedDons(t *testing.T) {
require.NoError(t, err)
assert.Len(t, dons, 1)
- assert.Equal(t, dons[0], uint32(1))
+ assert.Equal(t, uint32(1), dons[0])
_, err = workflowregistry.UpdateAllowedDons(
env,
@@ -68,7 +68,7 @@ func TestUpdateAllowedDons(t *testing.T) {
dons, err = registry.GetAllAllowedDONs(&bind.CallOpts{})
require.NoError(t, err)
- assert.Len(t, dons, 0)
+ assert.Empty(t, dons)
}
func Test_UpdateAllowedDons_WithMCMS(t *testing.T) {
diff --git a/deployment/keystone/changeset/workflowregistry/update_authorized_addresses_test.go b/deployment/keystone/changeset/workflowregistry/update_authorized_addresses_test.go
index a8d969fce0c..ed650ed52c6 100644
--- a/deployment/keystone/changeset/workflowregistry/update_authorized_addresses_test.go
+++ b/deployment/keystone/changeset/workflowregistry/update_authorized_addresses_test.go
@@ -30,7 +30,7 @@ func TestUpdateAuthorizedAddresses(t *testing.T) {
dons, err := registry.GetAllAuthorizedAddresses(&bind.CallOpts{})
require.NoError(t, err)
- assert.Len(t, dons, 0)
+ assert.Empty(t, dons)
env := deployment.Environment{
Logger: lggr,
@@ -70,7 +70,7 @@ func TestUpdateAuthorizedAddresses(t *testing.T) {
dons, err = registry.GetAllAuthorizedAddresses(&bind.CallOpts{})
require.NoError(t, err)
- assert.Len(t, dons, 0)
+ assert.Empty(t, dons)
}
func Test_UpdateAuthorizedAddresses_WithMCMS(t *testing.T) {
diff --git a/deployment/keystone/changeset/workflowregistry/workflow_registry_deployer.go b/deployment/keystone/changeset/workflowregistry/workflow_registry_deployer.go
index ac5bbd16cc8..6ebe6693482 100644
--- a/deployment/keystone/changeset/workflowregistry/workflow_registry_deployer.go
+++ b/deployment/keystone/changeset/workflowregistry/workflow_registry_deployer.go
@@ -30,7 +30,6 @@ func (c *workflowRegistryDeployer) Contract() *workflow_registry.WorkflowRegistr
}
func (c *workflowRegistryDeployer) Deploy(req changeset.DeployRequest) (*changeset.DeployResponse, error) {
-
addr, tx, wr, err := workflow_registry.DeployWorkflowRegistry(
req.Chain.DeployerKey,
req.Chain.Client)
diff --git a/deployment/multiclient_test.go b/deployment/multiclient_test.go
index 2e10c46e33f..152cdbc8d0e 100644
--- a/deployment/multiclient_test.go
+++ b/deployment/multiclient_test.go
@@ -38,7 +38,7 @@ func TestMultiClient(t *testing.T) {
require.NoError(t, err)
require.NotNil(t, mc)
assert.Equal(t, mc.RetryConfig.Attempts, uint(RPC_DEFAULT_RETRY_ATTEMPTS))
- assert.Equal(t, mc.RetryConfig.Delay, RPC_DEFAULT_RETRY_DELAY)
+ assert.Equal(t, RPC_DEFAULT_RETRY_DELAY, mc.RetryConfig.Delay)
_, err = NewMultiClient(lggr, []RPC{})
require.Error(t, err)
@@ -49,5 +49,5 @@ func TestMultiClient(t *testing.T) {
{WSURL: s.URL},
})
require.NoError(t, err)
- require.Equal(t, len(mc.Backups), 1)
+ require.Len(t, mc.Backups, 1)
}
diff --git a/integration-tests/.golangci.yml b/integration-tests/.golangci.yml
index 337555e17cb..957d11e04ff 100644
--- a/integration-tests/.golangci.yml
+++ b/integration-tests/.golangci.yml
@@ -8,7 +8,6 @@ linters:
- errname
- errorlint
- exhaustive
- - exportloopref
- fatcontext
- ginkgolinter
- gocritic
From 7debe85cc458774c0d94c8d2221a9cb17679fbff Mon Sep 17 00:00:00 2001
From: Dmytro Haidashenko <34754799+dhaidashenko@users.noreply.github.com>
Date: Tue, 7 Jan 2025 23:29:55 +0100
Subject: [PATCH 09/91] BCFR-1099 sei custom log index (#15858)
* add sei chain and error mapping
* fix changeset and config_test
* remove sei chain type
* add pricemax
* custom calculation of log's index for Sei
* fix lint issues & tests
---------
Co-authored-by: flodesi
---
.changeset/clever-knives-tap.md | 5 +
ccip/config/evm/Sei_Testnet_Atlantic.toml | 18 +++
core/build/platform_arch_guard.go | 3 +
core/chains/evm/client/errors.go | 12 +-
core/chains/evm/client/errors_test.go | 6 +
core/chains/evm/client/helpers_test.go | 8 +-
core/chains/evm/client/rpc_client.go | 51 ++++++-
.../evm/client/rpc_client_internal_test.go | 93 ++++++++++++
core/chains/evm/client/rpc_client_test.go | 137 +++++++++++++++++-
core/chains/evm/client/sub_forwarder.go | 30 ++--
core/chains/evm/client/sub_forwarder_test.go | 48 ++++--
core/chains/evm/config/chaintype/chaintype.go | 6 +-
core/services/chainlink/config_test.go | 4 +-
core/services/ocr/contract_tracker.go | 2 +-
core/services/ocrcommon/block_translator.go | 2 +-
15 files changed, 383 insertions(+), 42 deletions(-)
create mode 100644 .changeset/clever-knives-tap.md
create mode 100644 ccip/config/evm/Sei_Testnet_Atlantic.toml
create mode 100644 core/build/platform_arch_guard.go
create mode 100644 core/chains/evm/client/rpc_client_internal_test.go
diff --git a/.changeset/clever-knives-tap.md b/.changeset/clever-knives-tap.md
new file mode 100644
index 00000000000..8683e89f77d
--- /dev/null
+++ b/.changeset/clever-knives-tap.md
@@ -0,0 +1,5 @@
+---
+"chainlink": patch
+---
+
+#added Sei config and error mapping
diff --git a/ccip/config/evm/Sei_Testnet_Atlantic.toml b/ccip/config/evm/Sei_Testnet_Atlantic.toml
new file mode 100644
index 00000000000..f8c23d95c54
--- /dev/null
+++ b/ccip/config/evm/Sei_Testnet_Atlantic.toml
@@ -0,0 +1,18 @@
+ChainID = '1328'
+ChainType = 'sei'
+# finality_depth: instant
+FinalityDepth = 10
+# block_time: ~0.4s, adding 1 second buffer
+LogPollInterval = '2s'
+# finality_depth * block_time / 60 secs = ~0.8 min (finality time)
+NoNewFinalizedHeadsThreshold = '5m'
+# "RPC node returned multiple missing blocks on query for block numbers [31592085 31592084] even though the WS subscription already sent us these blocks. It might help to increase EVM.RPCBlockQueryDelay (currently 1)"
+RPCBlockQueryDelay = 5
+
+[GasEstimator]
+EIP1559DynamicFees = false
+Mode = 'BlockHistory'
+PriceMax = '3000 gwei' # recommended by ds&a
+
+[GasEstimator.BlockHistory]
+BlockHistorySize = 200
diff --git a/core/build/platform_arch_guard.go b/core/build/platform_arch_guard.go
new file mode 100644
index 00000000000..3a22f7df537
--- /dev/null
+++ b/core/build/platform_arch_guard.go
@@ -0,0 +1,3 @@
+//go:build !amd64 && !arm64
+package build
+"non-64-bits architectures are not supported"
diff --git a/core/chains/evm/client/errors.go b/core/chains/evm/client/errors.go
index bde97185580..eaa33f041ac 100644
--- a/core/chains/evm/client/errors.go
+++ b/core/chains/evm/client/errors.go
@@ -284,6 +284,16 @@ var gnosis = ClientErrors{
TransactionAlreadyInMempool: regexp.MustCompile(`(: |^)(alreadyknown)`),
}
+var sei = ClientErrors{
+ // https://github.com/sei-protocol/sei-tendermint/blob/e9a22c961e83579d8a68cd045c532980d82fb2a0/types/mempool.go#L12
+ TransactionAlreadyInMempool: regexp.MustCompile("tx already exists in cache"),
+ // https://github.com/sei-protocol/sei-cosmos/blob/a4eb451c957b1ca7ca9118406682f93fe83d1f61/types/errors/errors.go#L50
+ // https://github.com/sei-protocol/sei-cosmos/blob/a4eb451c957b1ca7ca9118406682f93fe83d1f61/types/errors/errors.go#L56
+ // https://github.com/sei-protocol/sei-cosmos/blob/a4eb451c957b1ca7ca9118406682f93fe83d1f61/client/broadcast.go#L27
+ // https://github.com/sei-protocol/sei-cosmos/blob/a4eb451c957b1ca7ca9118406682f93fe83d1f61/types/errors/errors.go#L32
+ Fatal: regexp.MustCompile(`(: |^)'*out of gas|insufficient fee|Tx too large. Max size is \d+, but got \d+|: insufficient funds`),
+}
+
const TerminallyStuckMsg = "transaction terminally stuck"
// Tx.Error messages that are set internally so they are not chain or client specific
@@ -291,7 +301,7 @@ var internal = ClientErrors{
TerminallyStuck: regexp.MustCompile(TerminallyStuckMsg),
}
-var clients = []ClientErrors{parity, geth, arbitrum, metis, substrate, avalanche, nethermind, harmony, besu, erigon, klaytn, celo, zkSync, zkEvm, treasure, mantle, aStar, hedera, gnosis, internal}
+var clients = []ClientErrors{parity, geth, arbitrum, metis, substrate, avalanche, nethermind, harmony, besu, erigon, klaytn, celo, zkSync, zkEvm, treasure, mantle, aStar, hedera, gnosis, sei, internal}
// ClientErrorRegexes returns a map of compiled regexes for each error type
func ClientErrorRegexes(errsRegex config.ClientErrors) *ClientErrors {
diff --git a/core/chains/evm/client/errors_test.go b/core/chains/evm/client/errors_test.go
index 1f9aaa53365..7ba042ab5c6 100644
--- a/core/chains/evm/client/errors_test.go
+++ b/core/chains/evm/client/errors_test.go
@@ -143,6 +143,7 @@ func Test_Eth_Errors(t *testing.T) {
{"ErrorObject { code: ServerError(3), message: \\\"known transaction. transaction with hash 0xf016…ad63 is already in the system\\\", data: Some(RawValue(\\\"0x\\\")) }", true, "zkSync"},
{"client error transaction already in mempool", true, "tomlConfig"},
{"alreadyknown", true, "Gnosis"},
+ {"tx already exists in cache", true, "Sei"},
}
for _, test := range tests {
err = evmclient.NewSendErrorS(test.message)
@@ -442,6 +443,11 @@ func Test_Eth_Errors_Fatal(t *testing.T) {
{"client error fatal", true, "tomlConfig"},
{"[Request ID: d9711488-4c1e-4af2-bc1f-7969913d7b60] Error invoking RPC: transaction 0.0.4425573@1718213476.914320044 failed precheck with status INVALID_SIGNATURE", true, "hedera"},
{"invalid chain id for signer", true, "Treasure"},
+
+ {": out of gas", true, "Sei"},
+ {"Tx too large. Max size is 2048576, but got 2097431", true, "Sei"},
+ {": insufficient funds", true, "Sei"},
+ {"insufficient fee", true, "Sei"},
}
for _, test := range tests {
diff --git a/core/chains/evm/client/helpers_test.go b/core/chains/evm/client/helpers_test.go
index f9751be765c..6369c9dca12 100644
--- a/core/chains/evm/client/helpers_test.go
+++ b/core/chains/evm/client/helpers_test.go
@@ -4,6 +4,7 @@ import (
"fmt"
"math/big"
"net/url"
+ "sync"
"testing"
"time"
@@ -216,6 +217,7 @@ const HeadResult = `{"difficulty":"0xf3a00","extraData":"0xd88301050384676574688
type mockSubscription struct {
unsubscribed bool
Errors chan error
+ unsub sync.Once
}
func NewMockSubscription() *mockSubscription {
@@ -225,8 +227,10 @@ func NewMockSubscription() *mockSubscription {
func (mes *mockSubscription) Err() <-chan error { return mes.Errors }
func (mes *mockSubscription) Unsubscribe() {
- mes.unsubscribed = true
- close(mes.Errors)
+ mes.unsub.Do(func() {
+ mes.unsubscribed = true
+ close(mes.Errors)
+ })
}
func ParseTestNodeConfigs(nodes []NodeConfig) ([]*toml.Node, error) {
diff --git a/core/chains/evm/client/rpc_client.go b/core/chains/evm/client/rpc_client.go
index 97046b4eff2..35d2a6dcd6b 100644
--- a/core/chains/evm/client/rpc_client.go
+++ b/core/chains/evm/client/rpc_client.go
@@ -5,6 +5,7 @@ import (
"encoding/json"
"errors"
"fmt"
+ "math"
"math/big"
"net/url"
"strconv"
@@ -376,6 +377,10 @@ func (r *RPCClient) BatchCallContext(rootCtx context.Context, b []rpc.BatchElem)
var requestedFinalizedBlock bool
if r.chainType == chaintype.ChainAstar {
for _, el := range b {
+ if el.Method == "eth_getLogs" {
+ r.rpcLog.Critical("evmclient.BatchCallContext: eth_getLogs is not supported")
+ return errors.New("evmclient.BatchCallContext: eth_getLogs is not supported")
+ }
if !isRequestingFinalizedBlock(el) {
continue
}
@@ -490,10 +495,10 @@ func (r *RPCClient) SubscribeToHeads(ctx context.Context) (ch <-chan *evmtypes.H
}()
channel := make(chan *evmtypes.Head)
- forwarder := newSubForwarder(channel, func(head *evmtypes.Head) *evmtypes.Head {
+ forwarder := newSubForwarder(channel, func(head *evmtypes.Head) (*evmtypes.Head, error) {
head.EVMChainID = ubig.New(r.chainID)
r.onNewHead(ctx, chStopInFlight, head)
- return head
+ return head, nil
}, r.wrapRPCClientError)
err = forwarder.start(ws.rpc.EthSubscribe(ctx, forwarder.srcCh, args...))
@@ -1199,8 +1204,11 @@ func (r *RPCClient) FilterLogs(ctx context.Context, q ethereum.FilterQuery) (l [
l, err = ws.geth.FilterLogs(ctx, q)
err = r.wrapWS(err)
}
- duration := time.Since(start)
+ if err == nil {
+ err = r.makeLogsValid(l)
+ }
+ duration := time.Since(start)
r.logResult(lggr, err, duration, r.getRPCDomain(), "FilterLogs",
"log", l,
)
@@ -1228,7 +1236,7 @@ func (r *RPCClient) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQu
r.logResult(lggr, err, duration, r.getRPCDomain(), "SubscribeFilterLogs")
err = r.wrapWS(err)
}()
- sub := newSubForwarder(ch, nil, r.wrapRPCClientError)
+ sub := newSubForwarder(ch, r.makeLogValid, r.wrapRPCClientError)
err = sub.start(ws.geth.SubscribeFilterLogs(ctx, q, sub.srcCh))
if err != nil {
return
@@ -1452,3 +1460,38 @@ func ToBlockNumArg(number *big.Int) string {
}
return hexutil.EncodeBig(number)
}
+
+func (r *RPCClient) makeLogsValid(logs []types.Log) error {
+ if r.chainType != chaintype.ChainSei {
+ return nil
+ }
+
+ for i := range logs {
+ var err error
+ logs[i], err = r.makeLogValid(logs[i])
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func (r *RPCClient) makeLogValid(log types.Log) (types.Log, error) {
+ if r.chainType != chaintype.ChainSei {
+ return log, nil
+ }
+
+ if log.TxIndex > math.MaxUint32 {
+ return types.Log{}, fmt.Errorf("TxIndex of tx %s exceeds max supported value of %d", log.TxHash, math.MaxUint32)
+ }
+
+ if log.Index > math.MaxUint32 {
+ return types.Log{}, fmt.Errorf("log's index %d of tx %s exceeds max supported value of %d", log.Index, log.TxHash, math.MaxUint32)
+ }
+
+ // it's safe as we have a build guard to guarantee 64-bit system
+ newIndex := uint64(log.TxIndex<<32) | uint64(log.Index)
+ log.Index = uint(newIndex)
+ return log, nil
+}
diff --git a/core/chains/evm/client/rpc_client_internal_test.go b/core/chains/evm/client/rpc_client_internal_test.go
new file mode 100644
index 00000000000..ef321645fc2
--- /dev/null
+++ b/core/chains/evm/client/rpc_client_internal_test.go
@@ -0,0 +1,93 @@
+package client
+
+import (
+ "errors"
+ "math"
+ "testing"
+
+ ethtypes "github.com/ethereum/go-ethereum/core/types"
+ "github.com/stretchr/testify/require"
+
+ commonclient "github.com/smartcontractkit/chainlink/v2/common/client"
+ "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/chaintype"
+ "github.com/smartcontractkit/chainlink/v2/core/logger"
+)
+
+func TestRPCClient_MakeLogsValid(t *testing.T) {
+ testCases := []struct {
+ Name string
+ TxIndex uint
+ LogIndex uint
+ ExpectedLogIndex uint
+ ExpectedError error
+ }{
+ {
+ Name: "TxIndex = 0 LogIndex = 0",
+ TxIndex: 0,
+ LogIndex: 0,
+ ExpectedLogIndex: 0,
+ ExpectedError: nil,
+ },
+ {
+ Name: "TxIndex = 0 LogIndex = 1",
+ TxIndex: 0,
+ LogIndex: 1,
+ ExpectedLogIndex: 1,
+ ExpectedError: nil,
+ },
+ {
+ Name: "TxIndex = 0 LogIndex = MaxUint32",
+ TxIndex: 0,
+ LogIndex: math.MaxUint32,
+ ExpectedLogIndex: math.MaxUint32,
+ ExpectedError: nil,
+ },
+ {
+ Name: "LogIndex = MaxUint32 + 1 => returns an error",
+ TxIndex: 0,
+ LogIndex: math.MaxUint32 + 1,
+ ExpectedLogIndex: 0,
+ ExpectedError: errors.New("log's index 4294967296 of tx 0x0000000000000000000000000000000000000000000000000000000000000000 exceeds max supported value of 4294967295"),
+ },
+ {
+ Name: "TxIndex = 1 LogIndex = 0",
+ TxIndex: 1,
+ LogIndex: 0,
+ ExpectedLogIndex: math.MaxUint32 + 1,
+ ExpectedError: nil,
+ },
+ {
+ Name: "TxIndex = MaxUint32 LogIndex = MaxUint32",
+ TxIndex: math.MaxUint32,
+ LogIndex: math.MaxUint32,
+ ExpectedLogIndex: math.MaxUint64,
+ ExpectedError: nil,
+ },
+ {
+ Name: "TxIndex = MaxUint32 + 1 => returns an error",
+ TxIndex: math.MaxUint32 + 1,
+ LogIndex: 0,
+ ExpectedLogIndex: 0,
+ ExpectedError: errors.New("TxIndex of tx 0x0000000000000000000000000000000000000000000000000000000000000000 exceeds max supported value of 4294967295"),
+ },
+ }
+ for _, tc := range testCases {
+ t.Run(tc.Name, func(t *testing.T) {
+ rpc := NewRPCClient(TestNodePoolConfig{}, logger.TestLogger(t), nil, nil, "eth-primary-rpc-0", 0, nil, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "")
+ log, err := rpc.makeLogValid(ethtypes.Log{TxIndex: tc.TxIndex, Index: tc.LogIndex})
+ // non sei should return as is
+ require.NoError(t, err)
+ require.Equal(t, tc.TxIndex, log.TxIndex)
+ require.Equal(t, tc.LogIndex, log.Index)
+ seiRPC := NewRPCClient(TestNodePoolConfig{}, logger.TestLogger(t), nil, nil, "eth-primary-rpc-0", 0, nil, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, chaintype.ChainSei)
+ log, err = seiRPC.makeLogValid(ethtypes.Log{TxIndex: tc.TxIndex, Index: tc.LogIndex})
+ if tc.ExpectedError != nil {
+ require.EqualError(t, err, tc.ExpectedError.Error())
+ return
+ }
+
+ require.Equal(t, tc.ExpectedLogIndex, log.Index)
+ require.Equal(t, tc.TxIndex, log.TxIndex)
+ })
+ }
+}
diff --git a/core/chains/evm/client/rpc_client_test.go b/core/chains/evm/client/rpc_client_test.go
index d5286e9acf0..f6e7f9ee338 100644
--- a/core/chains/evm/client/rpc_client_test.go
+++ b/core/chains/evm/client/rpc_client_test.go
@@ -5,6 +5,7 @@ import (
"encoding/json"
"errors"
"fmt"
+ "math"
"math/big"
"net/url"
"sync"
@@ -13,6 +14,7 @@ import (
"time"
"github.com/ethereum/go-ethereum"
+ "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rpc"
"github.com/stretchr/testify/assert"
@@ -31,14 +33,16 @@ import (
evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types"
)
-func makeNewHeadWSMessage(head *evmtypes.Head) string {
- asJSON, err := json.Marshal(head)
+func makeNewWSMessage[T any](v T) string {
+ asJSON, err := json.Marshal(v)
if err != nil {
panic(fmt.Errorf("failed to marshal head: %w", err))
}
return fmt.Sprintf(`{"jsonrpc":"2.0","method":"eth_subscription","params":{"subscription":"0x00","result":%s}}`, string(asJSON))
}
+var makeNewHeadWSMessage = makeNewWSMessage[*evmtypes.Head]
+
func TestRPCClient_SubscribeToHeads(t *testing.T) {
t.Parallel()
ctx, cancel := context.WithTimeout(tests.Context(t), tests.WaitTimeout(t))
@@ -385,6 +389,135 @@ func TestRPCClient_SubscribeFilterLogs(t *testing.T) {
t.Errorf("Expected subscription to return an error, but test timeout instead")
}
})
+ t.Run("Log's index is properly set for Sei chain type", func(t *testing.T) {
+ server := testutils.NewWSServer(t, chainId, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) {
+ if method == "eth_unsubscribe" {
+ resp.Result = "true"
+ return
+ } else if method == "eth_subscribe" {
+ if assert.True(t, params.IsArray()) && assert.Equal(t, "logs", params.Array()[0].String()) {
+ resp.Result = `"0x00"`
+ }
+ return
+ }
+ return
+ })
+ wsURL := server.WSURL()
+ rpc := client.NewRPCClient(nodePoolCfg, lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, chaintype.ChainSei)
+ defer rpc.Close()
+ require.NoError(t, rpc.Dial(ctx))
+ ch := make(chan types.Log)
+ sub, err := rpc.SubscribeFilterLogs(ctx, ethereum.FilterQuery{}, ch)
+ require.NoError(t, err)
+ testCases := []struct {
+ TxIndex uint
+ Index uint
+ ExpectedIndex uint
+ }{
+ {
+ TxIndex: 0,
+ Index: 0,
+ ExpectedIndex: 0,
+ },
+ {
+ TxIndex: 0,
+ Index: 1,
+ ExpectedIndex: 1,
+ },
+ {
+ TxIndex: 1,
+ Index: 0,
+ ExpectedIndex: math.MaxUint32 + 1,
+ },
+ }
+ go func() {
+ for _, testCase := range testCases {
+ server.MustWriteBinaryMessageSync(t, makeNewWSMessage(types.Log{TxIndex: testCase.TxIndex, Index: testCase.Index, Topics: []common.Hash{{}}}))
+ }
+ }()
+ defer sub.Unsubscribe()
+ for _, testCase := range testCases {
+ select {
+ case <-tests.Context(t).Done():
+ require.Fail(t, "context timed out")
+ case err := <-sub.Err():
+ require.NoError(t, err)
+ require.Fail(t, "Did not expect error channel to be closed or return error before all testcases were consumed")
+ case log := <-ch:
+ require.Equal(t, testCase.ExpectedIndex, log.Index, "Unexpected log index %d for test case %v", log.Index, testCase)
+ }
+ }
+ })
+}
+
+func TestRPCClientFilterLogs(t *testing.T) {
+ t.Parallel()
+
+ nodePoolCfg := client.TestNodePoolConfig{
+ NodeNewHeadsPollInterval: 1 * time.Second,
+ NodeFinalizedBlockPollInterval: 1 * time.Second,
+ }
+
+ chainID := big.NewInt(123456)
+ lggr := logger.Test(t)
+ ctx, cancel := context.WithTimeout(tests.Context(t), tests.WaitTimeout(t))
+ defer cancel()
+ t.Run("Log's index is properly set for Sei chain type", func(t *testing.T) {
+ testCases := []struct {
+ TxIndex uint
+ Index uint
+ ExpectedIndex uint
+ }{
+ {
+ TxIndex: 0,
+ Index: 0,
+ ExpectedIndex: 0,
+ },
+ {
+ TxIndex: 0,
+ Index: 1,
+ ExpectedIndex: 1,
+ },
+ {
+ TxIndex: 1,
+ Index: 0,
+ ExpectedIndex: math.MaxUint32 + 1,
+ },
+ }
+ server := testutils.NewWSServer(t, chainID, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) {
+ if method != "eth_getLogs" {
+ return
+ }
+ var logs []types.Log
+ for _, testCase := range testCases {
+ logs = append(logs, types.Log{TxIndex: testCase.TxIndex, Index: testCase.Index, Topics: []common.Hash{{}}})
+ }
+ raw, err := json.Marshal(logs)
+ require.NoError(t, err)
+ resp.Result = string(raw)
+ return
+ })
+ wsURL := server.WSURL()
+ seiRPC := client.NewRPCClient(nodePoolCfg, lggr, wsURL, nil, "rpc", 1, chainID, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, chaintype.ChainSei)
+ defer seiRPC.Close()
+ require.NoError(t, seiRPC.Dial(ctx))
+ logs, err := seiRPC.FilterLogs(ctx, ethereum.FilterQuery{})
+ require.NoError(t, err)
+ for i, testCase := range testCases {
+ require.Equal(t, testCase.ExpectedIndex, logs[i].Index, "Unexpected log index %d for test case %v", logs[i].Index, testCase)
+ }
+
+ // non sei should return index as is
+ rpc := client.NewRPCClient(nodePoolCfg, lggr, wsURL, nil, "rpc", 1, chainID, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "")
+ defer rpc.Close()
+ require.NoError(t, rpc.Dial(ctx))
+ logs, err = rpc.FilterLogs(ctx, ethereum.FilterQuery{})
+ require.NoError(t, err)
+ for i, testCase := range testCases {
+ require.Equal(t, testCase.Index, logs[i].Index, "Expected non sei log to be returned as is")
+ require.Equal(t, testCase.TxIndex, logs[i].TxIndex, "Expected non sei log to be returned as is")
+ }
+ })
}
func TestRPCClient_LatestFinalizedBlock(t *testing.T) {
diff --git a/core/chains/evm/client/sub_forwarder.go b/core/chains/evm/client/sub_forwarder.go
index 93e9b106b4a..a9b5a97eee0 100644
--- a/core/chains/evm/client/sub_forwarder.go
+++ b/core/chains/evm/client/sub_forwarder.go
@@ -13,7 +13,7 @@ type subForwarder[T any] struct {
srcCh chan T
srcSub ethereum.Subscription
- interceptResult func(T) T
+ interceptResult func(T) (T, error)
interceptError func(error) error
done chan struct{}
@@ -21,14 +21,14 @@ type subForwarder[T any] struct {
unSub chan struct{}
}
-func newSubForwarder[T any](destCh chan<- T, interceptResult func(T) T, interceptError func(error) error) *subForwarder[T] {
+func newSubForwarder[T any](destCh chan<- T, interceptResult func(T) (T, error), interceptError func(error) error) *subForwarder[T] {
return &subForwarder[T]{
interceptResult: interceptResult,
interceptError: interceptError,
destCh: destCh,
srcCh: make(chan T),
done: make(chan struct{}),
- err: make(chan error),
+ err: make(chan error, 1),
unSub: make(chan struct{}, 1),
}
}
@@ -44,6 +44,14 @@ func (c *subForwarder[T]) start(sub ethereum.Subscription, err error) error {
return nil
}
+func (c *subForwarder[T]) handleError(err error) {
+ if c.interceptError != nil {
+ err = c.interceptError(err)
+ }
+ c.err <- err // err is buffered, and we never write twice, so write is not blocking
+ c.srcSub.Unsubscribe()
+}
+
// forwardLoop receives from src, adds the chainID, and then sends to dest.
// It also handles Unsubscribing, which may interrupt either forwarding operation.
func (c *subForwarder[T]) forwardLoop() {
@@ -54,19 +62,17 @@ func (c *subForwarder[T]) forwardLoop() {
for {
select {
case err := <-c.srcSub.Err():
- if c.interceptError != nil {
- err = c.interceptError(err)
- }
- select {
- case c.err <- err:
- case <-c.unSub:
- c.srcSub.Unsubscribe()
- }
+ c.handleError(err)
return
case h := <-c.srcCh:
if c.interceptResult != nil {
- h = c.interceptResult(h)
+ var err error
+ h, err = c.interceptResult(h)
+ if err != nil {
+ c.handleError(err)
+ return
+ }
}
select {
case c.destCh <- h:
diff --git a/core/chains/evm/client/sub_forwarder_test.go b/core/chains/evm/client/sub_forwarder_test.go
index 1bc0122603b..267fa1b8467 100644
--- a/core/chains/evm/client/sub_forwarder_test.go
+++ b/core/chains/evm/client/sub_forwarder_test.go
@@ -21,9 +21,9 @@ func TestChainIDSubForwarder(t *testing.T) {
t.Parallel()
newChainIDSubForwarder := func(chainID *big.Int, ch chan<- *evmtypes.Head) *subForwarder[*evmtypes.Head] {
- return newSubForwarder(ch, func(head *evmtypes.Head) *evmtypes.Head {
+ return newSubForwarder(ch, func(head *evmtypes.Head) (*evmtypes.Head, error) {
head.EVMChainID = ubig.New(chainID)
- return head
+ return head, nil
}, nil)
}
@@ -54,12 +54,14 @@ func TestChainIDSubForwarder(t *testing.T) {
sub := NewMockSubscription()
err := forwarder.start(sub, nil)
assert.NoError(t, err)
- sub.Errors <- errors.New("boo")
+ expectedError := errors.New("boo")
+ sub.Errors <- expectedError
forwarder.Unsubscribe()
assert.True(t, sub.unsubscribed)
- _, ok := <-sub.Err()
- assert.False(t, ok)
+ err, ok := <-forwarder.Err()
+ assert.True(t, ok)
+ require.ErrorIs(t, err, expectedError)
_, ok = <-forwarder.Err()
assert.False(t, ok)
})
@@ -117,6 +119,31 @@ func TestChainIDSubForwarder(t *testing.T) {
})
}
+func TestSubscriptionForwarder(t *testing.T) {
+ t.Run("Error returned by interceptResult is forwarded to err channel", func(t *testing.T) {
+ t.Parallel()
+
+ ch := make(chan *evmtypes.Head)
+ expectedErr := errors.New("something went wrong during result interception")
+ forwarder := newSubForwarder(ch, func(head *evmtypes.Head) (*evmtypes.Head, error) {
+ return nil, expectedErr
+ }, nil)
+ mockedSub := NewMockSubscription()
+ require.NoError(t, forwarder.start(mockedSub, nil))
+
+ head := &evmtypes.Head{
+ ID: 1,
+ }
+ forwarder.srcCh <- head
+ err := <-forwarder.Err()
+ require.ErrorIs(t, err, expectedErr)
+ // ensure forwarder is closed
+ _, ok := <-forwarder.Err()
+ assert.False(t, ok)
+ assert.True(t, mockedSub.unsubscribed)
+ })
+}
+
func TestSubscriptionErrorWrapper(t *testing.T) {
t.Parallel()
newSubscriptionErrorWrapper := func(t *testing.T, sub commontypes.Subscription, errorPrefix string) ethereum.Subscription {
@@ -145,17 +172,6 @@ func TestSubscriptionErrorWrapper(t *testing.T) {
// subsequence unsubscribe does not causes panic
wrapper.Unsubscribe()
})
- t.Run("Unsubscribe interrupts error delivery", func(t *testing.T) {
- t.Parallel()
- sub := NewMockSubscription()
- const prefix = "RPC returned error"
- wrapper := newSubscriptionErrorWrapper(t, sub, prefix)
- sub.Errors <- fmt.Errorf("error")
-
- wrapper.Unsubscribe()
- _, ok := <-wrapper.Err()
- assert.False(t, ok)
- })
t.Run("Successfully wraps error", func(t *testing.T) {
t.Parallel()
sub := NewMockSubscription()
diff --git a/core/chains/evm/config/chaintype/chaintype.go b/core/chains/evm/config/chaintype/chaintype.go
index b2eff02834b..be3afa0ea62 100644
--- a/core/chains/evm/config/chaintype/chaintype.go
+++ b/core/chains/evm/config/chaintype/chaintype.go
@@ -17,6 +17,7 @@ const (
ChainMantle ChainType = "mantle"
ChainMetis ChainType = "metis"
ChainOptimismBedrock ChainType = "optimismBedrock"
+ ChainSei ChainType = "sei"
ChainScroll ChainType = "scroll"
ChainWeMix ChainType = "wemix"
ChainXLayer ChainType = "xlayer"
@@ -39,7 +40,7 @@ func (c ChainType) IsL2() bool {
func (c ChainType) IsValid() bool {
switch c {
- case "", ChainArbitrum, ChainAstar, ChainCelo, ChainGnosis, ChainHedera, ChainKroma, ChainMantle, ChainMetis, ChainOptimismBedrock, ChainScroll, ChainWeMix, ChainXLayer, ChainZkEvm, ChainZkSync, ChainZircuit:
+ case "", ChainArbitrum, ChainAstar, ChainCelo, ChainGnosis, ChainHedera, ChainKroma, ChainMantle, ChainMetis, ChainOptimismBedrock, ChainSei, ChainScroll, ChainWeMix, ChainXLayer, ChainZkEvm, ChainZkSync, ChainZircuit:
return true
}
return false
@@ -65,6 +66,8 @@ func FromSlug(slug string) ChainType {
return ChainMetis
case "optimismBedrock":
return ChainOptimismBedrock
+ case "sei":
+ return ChainSei
case "scroll":
return ChainScroll
case "wemix":
@@ -138,6 +141,7 @@ var ErrInvalid = fmt.Errorf("must be one of %s or omitted", strings.Join([]strin
string(ChainMantle),
string(ChainMetis),
string(ChainOptimismBedrock),
+ string(ChainSei),
string(ChainScroll),
string(ChainWeMix),
string(ChainXLayer),
diff --git a/core/services/chainlink/config_test.go b/core/services/chainlink/config_test.go
index 65ece5a88c0..9a08b356c66 100644
--- a/core/services/chainlink/config_test.go
+++ b/core/services/chainlink/config_test.go
@@ -1472,7 +1472,7 @@ func TestConfig_Validate(t *testing.T) {
- 1: 10 errors:
- ChainType: invalid value (Foo): must not be set with this chain id
- Nodes: missing: must have at least one node
- - ChainType: invalid value (Foo): must be one of arbitrum, astar, celo, gnosis, hedera, kroma, mantle, metis, optimismBedrock, scroll, wemix, xlayer, zkevm, zksync, zircuit or omitted
+ - ChainType: invalid value (Foo): must be one of arbitrum, astar, celo, gnosis, hedera, kroma, mantle, metis, optimismBedrock, sei, scroll, wemix, xlayer, zkevm, zksync, zircuit or omitted
- HeadTracker.HistoryDepth: invalid value (30): must be greater than or equal to FinalizedBlockOffset
- GasEstimator.BumpThreshold: invalid value (0): cannot be 0 if auto-purge feature is enabled for Foo
- Transactions.AutoPurge.Threshold: missing: needs to be set if auto-purge feature is enabled for Foo
@@ -1485,7 +1485,7 @@ func TestConfig_Validate(t *testing.T) {
- 2: 5 errors:
- ChainType: invalid value (Arbitrum): only "optimismBedrock" can be used with this chain id
- Nodes: missing: must have at least one node
- - ChainType: invalid value (Arbitrum): must be one of arbitrum, astar, celo, gnosis, hedera, kroma, mantle, metis, optimismBedrock, scroll, wemix, xlayer, zkevm, zksync, zircuit or omitted
+ - ChainType: invalid value (Arbitrum): must be one of arbitrum, astar, celo, gnosis, hedera, kroma, mantle, metis, optimismBedrock, sei, scroll, wemix, xlayer, zkevm, zksync, zircuit or omitted
- FinalityDepth: invalid value (0): must be greater than or equal to 1
- MinIncomingConfirmations: invalid value (0): must be greater than or equal to 1
- 3: 3 errors:
diff --git a/core/services/ocr/contract_tracker.go b/core/services/ocr/contract_tracker.go
index 618567f0bdb..f2cf1fee9d3 100644
--- a/core/services/ocr/contract_tracker.go
+++ b/core/services/ocr/contract_tracker.go
@@ -399,7 +399,7 @@ func (t *OCRContractTracker) LatestBlockHeight(ctx context.Context) (blockheight
// care about the block height; we have no way of getting the L1 block
// height anyway
return 0, nil
- case "", chaintype.ChainArbitrum, chaintype.ChainAstar, chaintype.ChainCelo, chaintype.ChainGnosis, chaintype.ChainHedera, chaintype.ChainKroma, chaintype.ChainOptimismBedrock, chaintype.ChainScroll, chaintype.ChainWeMix, chaintype.ChainXLayer, chaintype.ChainZkEvm, chaintype.ChainZkSync, chaintype.ChainZircuit:
+ case "", chaintype.ChainArbitrum, chaintype.ChainAstar, chaintype.ChainCelo, chaintype.ChainGnosis, chaintype.ChainHedera, chaintype.ChainKroma, chaintype.ChainOptimismBedrock, chaintype.ChainSei, chaintype.ChainScroll, chaintype.ChainWeMix, chaintype.ChainXLayer, chaintype.ChainZkEvm, chaintype.ChainZkSync, chaintype.ChainZircuit:
// continue
}
latestBlockHeight := t.getLatestBlockHeight()
diff --git a/core/services/ocrcommon/block_translator.go b/core/services/ocrcommon/block_translator.go
index b25d617e2ab..8a755f767b9 100644
--- a/core/services/ocrcommon/block_translator.go
+++ b/core/services/ocrcommon/block_translator.go
@@ -22,7 +22,7 @@ func NewBlockTranslator(cfg Config, client evmclient.Client, lggr logger.Logger)
switch cfg.ChainType() {
case chaintype.ChainArbitrum:
return NewArbitrumBlockTranslator(client, lggr)
- case "", chaintype.ChainCelo, chaintype.ChainGnosis, chaintype.ChainKroma, chaintype.ChainMetis, chaintype.ChainOptimismBedrock, chaintype.ChainScroll, chaintype.ChainWeMix, chaintype.ChainXLayer, chaintype.ChainZkEvm, chaintype.ChainZkSync, chaintype.ChainZircuit:
+ case "", chaintype.ChainCelo, chaintype.ChainGnosis, chaintype.ChainKroma, chaintype.ChainMetis, chaintype.ChainOptimismBedrock, chaintype.ChainSei, chaintype.ChainScroll, chaintype.ChainWeMix, chaintype.ChainXLayer, chaintype.ChainZkEvm, chaintype.ChainZkSync, chaintype.ChainZircuit:
fallthrough
default:
return &l1BlockTranslator{}
From 9bcb3db1fb7eb3ca942ebaa34f3db240bb6f57fd Mon Sep 17 00:00:00 2001
From: Lukasz <120112546+lukaszcl@users.noreply.github.com>
Date: Wed, 8 Jan 2025 10:01:32 +0100
Subject: [PATCH 10/91] Flakeguard: Unskip TestChainComponents test (#15851)
* Flakeguard: Unskip TestChainComponents test
* fail test
* Revert "fail test"
This reverts commit c4ae8ccb71b01407c8c13c6872c5a28697e21e5f.
---
.github/workflows/ci-flakeguard.yml | 2 +-
.github/workflows/flakeguard-nightly.yml | 2 +-
.github/workflows/flakeguard-on-demand.yml | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/.github/workflows/ci-flakeguard.yml b/.github/workflows/ci-flakeguard.yml
index caf6a62a3fb..1bd60b20c94 100644
--- a/.github/workflows/ci-flakeguard.yml
+++ b/.github/workflows/ci-flakeguard.yml
@@ -41,7 +41,7 @@ jobs:
findByTestFilesDiff: true
findByAffectedPackages: false
slackNotificationAfterTestsChannelId: 'C07TRF65CNS' #flaky-test-detector-notifications
- extraArgs: '{ "skipped_tests": "TestChainComponents", "run_with_race": "true", "print_failed_tests": "true", "test_repeat_count": "3", "omit_test_outputs_on_success": "true" }'
+ extraArgs: '{ "skipped_tests": "", "run_with_race": "true", "print_failed_tests": "true", "test_repeat_count": "3", "omit_test_outputs_on_success": "true" }'
secrets:
SLACK_BOT_TOKEN: ${{ secrets.QA_SLACK_API_KEY }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/flakeguard-nightly.yml b/.github/workflows/flakeguard-nightly.yml
index 025cca6d0a0..3d62f1f521d 100644
--- a/.github/workflows/flakeguard-nightly.yml
+++ b/.github/workflows/flakeguard-nightly.yml
@@ -16,7 +16,7 @@ jobs:
projectPath: '.'
maxPassRatio: '1.0'
runAllTests: true
- extraArgs: '{ "skipped_tests": "TestChainComponents", "test_repeat_count": "5", "all_tests_runner": "ubuntu22.04-32cores-128GB", "all_tests_runner_count": "3", "run_with_race": "false" }'
+ extraArgs: '{ "skipped_tests": "", "test_repeat_count": "5", "all_tests_runner": "ubuntu22.04-32cores-128GB", "all_tests_runner_count": "3", "run_with_race": "false" }'
slackNotificationAfterTestsChannelId: 'C07TRF65CNS' #flaky-test-detector-notifications
secrets:
SLACK_BOT_TOKEN: ${{ secrets.QA_SLACK_API_KEY }}
diff --git a/.github/workflows/flakeguard-on-demand.yml b/.github/workflows/flakeguard-on-demand.yml
index f6df40616f7..a8a71be3ba2 100644
--- a/.github/workflows/flakeguard-on-demand.yml
+++ b/.github/workflows/flakeguard-on-demand.yml
@@ -48,7 +48,7 @@ on:
extraArgs:
required: false
type: string
- default: '{ "skipped_tests": "TestChainComponents", "test_repeat_count": "5", "all_tests_runner": "ubuntu22.04-32cores-128GB", "all_tests_runner_count": "3", "run_with_race": "false" }'
+ default: '{ "skipped_tests": "", "test_repeat_count": "5", "all_tests_runner": "ubuntu22.04-32cores-128GB", "all_tests_runner_count": "3", "run_with_race": "false" }'
description: 'JSON of extra arguments for the workflow.'
jobs:
From fcefd62068d6be4fea1820d1a9edef4e16fbfa3b Mon Sep 17 00:00:00 2001
From: Rens Rooimans
Date: Wed, 8 Jan 2025 16:16:15 +0100
Subject: [PATCH 11/91] Remove old Solidity code & move misplaced files
(#15852)
* rm dead code
* move ChainSpecificUtil_v0_8_6.sol to vrf
* remove flags
* move chainlink client to operatorforwarder
* move automation test wrappers to automation
* more automation cleanup
* move interfaces to operatorforwarder
* remove TypeAndVersionInterface.sol in favor of ITypeAndVersion
* move and remove mocks
* move ChainSpecificUtil to shared
* move more testhelpers
* move MockV3Aggregator
* move logpoller related contracts to shared
* clean up broken references
* fix broken ref
* rebase
* fix lint & compile feeds
* gen wrapper for ITypeAndVersion
* fix lint
---
.changeset/cold-coats-battle.md | 5 +
.github/CODEOWNERS | 3 -
.../workflows/solidity-foundry-artifacts.yml | 1 -
contracts/.changeset/angry-needles-approve.md | 5 +
contracts/STYLE_GUIDE.md | 3 -
contracts/scripts/lcov_prune | 2 -
contracts/scripts/native_solc_compile_all | 2 +-
.../native_solc_compile_all_automation | 10 +-
.../scripts/native_solc_compile_all_feeds | 4 +-
.../scripts/native_solc_compile_all_logpoller | 33 --
.../scripts/native_solc_compile_all_shared | 7 +-
contracts/src/v0.8/Denominations.sol | 28 --
contracts/src/v0.8/Flags.sol | 124 -----
.../src/v0.8/PermissionedForwardProxy.sol | 65 ---
contracts/src/v0.8/ValidatorProxy.sol | 230 ---------
.../v0.8/automation/HeartbeatRequester.sol | 4 +-
.../src/v0.8/automation/UpkeepTranscoder.sol | 4 +-
.../v0.8/automation/dev/MercuryRegistry.sol | 2 +-
.../mocks}/MockArbGasInfo.sol | 0
.../{ => automation}/mocks/MockArbSys.sol | 0
.../mocks}/MockGasBoundCaller.sol | 0
.../mocks}/MockZKSyncSystemContext.sol | 0
.../v0.8/automation/test/v2_3/BaseTest.t.sol | 2 +-
.../test/v2_3_zksync/BaseTest.t.sol | 6 +-
.../AutomationConsumerBenchmark.sol | 0
.../testhelpers}/CronReceiver.sol | 0
.../ERC20BalanceMonitorExposed.sol | 2 +-
.../testhelpers}/EthBalanceMonitorExposed.sol | 2 +-
.../KeeperCompatibleTestHelper.sol | 2 +-
.../testhelpers}/MockOVMGasPriceOracle.sol | 0
.../testhelpers}/ReceiveEmitter.sol | 0
.../testhelpers}/ReceiveFallbackEmitter.sol | 0
.../testhelpers}/ReceiveReverter.sol | 0
.../testhelpers}/StreamsLookupUpkeep.sol | 6 +-
.../testhelpers}/VerifiableLoadBase.sol | 10 +-
.../VerifiableLoadLogTriggerUpkeep.sol | 4 +-
.../VerifiableLoadStreamsLookupUpkeep.sol | 2 +-
.../testhelpers}/VerifiableLoadUpkeep.sol | 0
.../automation/v1_2/KeeperRegistrar1_2.sol | 4 +-
.../automation/v1_2/KeeperRegistry1_2.sol | 4 +-
.../automation/v1_3/KeeperRegistry1_3.sol | 4 +-
.../automation/v2_0/KeeperRegistrar2_0.sol | 4 +-
.../automation/v2_0/UpkeepTranscoder3_0.sol | 4 +-
.../v2_1/AutomationRegistrar2_1.sol | 4 +-
.../automation/v2_1/UpkeepTranscoder4_0.sol | 4 +-
.../v2_3/AutomationRegistrar2_3.sol | 4 +-
.../automation/v2_3/UpkeepTranscoder5_0.sol | 4 +-
.../feeQuoter/FeeQuoter.getTokenPrice.t.sol | 2 +-
.../FeeQuoter.getValidatedTokenPrice.t.sol | 2 +-
.../ccip/test/feeQuoter/FeeQuoterSetup.t.sol | 2 +-
.../src/v0.8/functions/tests/v1_X/Setup.t.sol | 4 +-
.../tests/v1_X/testhelpers}/MockLinkToken.sol | 2 +-
.../v0.8/interfaces/FeedRegistryInterface.sol | 124 -----
.../src/v0.8/interfaces/FlagsInterface.sol | 17 -
.../src/v0.8/interfaces/PoRAddressList.sol | 29 --
.../interfaces/TypeAndVersionInterface.sol | 6 -
.../{tests => l2ep/test}/FeedConsumer.sol | 3 +-
.../src/v0.8/{tests => l2ep/test}/Greeter.sol | 3 +-
.../test/mocks}/MockArbitrumInbox.sol | 4 +-
.../MockOptimismL1CrossDomainMessenger.sol | 0
.../MockOptimismL2CrossDomainMessenger.sol | 0
.../src/v0.8/l2ep/test/v1_0_0/L2EPTest.t.sol | 2 +-
.../ArbitrumCrossDomainForwarder.t.sol | 2 +-
.../ArbitrumCrossDomainGovernor.t.sol | 2 +-
.../ArbitrumSequencerUptimeFeed.t.sol | 2 +-
.../v1_0_0/arbitrum/ArbitrumValidator.t.sol | 2 +-
.../OptimismCrossDomainForwarder.t.sol | 2 +-
.../OptimismCrossDomainGovernor.t.sol | 2 +-
.../OptimismSequencerUptimeFeed.t.sol | 4 +-
.../v1_0_0/optimism/OptimismValidator.t.sol | 4 +-
.../scroll/ScrollCrossDomainForwarder.t.sol | 2 +-
.../scroll/ScrollCrossDomainGovernor.t.sol | 2 +-
.../shared/BaseSequencerUptimeFeed.t.sol | 2 +-
.../src/v0.8/llo-feeds/v0.3.0/FeeManager.sol | 6 +-
.../v0.8/llo-feeds/v0.3.0/RewardManager.sol | 6 +-
.../src/v0.8/llo-feeds/v0.3.0/Verifier.sol | 6 +-
.../v0.8/llo-feeds/v0.3.0/VerifierProxy.sol | 6 +-
.../v0.4.0/DestinationFeeManager.sol | 6 +-
.../v0.4.0/DestinationRewardManager.sol | 6 +-
.../llo-feeds/v0.4.0/DestinationVerifier.sol | 6 +-
.../v0.4.0/DestinationVerifierProxy.sol | 6 +-
.../configuration/ChannelConfigStore.sol | 4 +-
.../v0.5.0/configuration/Configurator.sol | 6 +-
.../v0.8/mocks/MockAggregatorValidator.sol | 30 --
.../src/v0.8/mocks/MockOffchainAggregator.sol | 14 -
.../{ => operatorforwarder}/Chainlink.sol | 4 +-
.../ChainlinkClient.sol | 4 +-
.../src/v0.8/operatorforwarder/Operator.sol | 4 +-
.../interfaces/ChainlinkRequestInterface.sol | 0
.../interfaces/ENSInterface.sol | 0
.../interfaces/OperatorInterface.sol | 0
.../interfaces/OracleInterface.sol | 0
.../interfaces/PointerInterface.sol | 0
.../test}/Broken.sol | 1 +
.../testhelpers/ChainlinkClientHelper.sol | 2 +-
.../test/testhelpers/Chainlinked.sol | 2 +-
.../test/testhelpers/Consumer.sol | 4 +-
.../test/testhelpers/EmptyOracle.sol | 4 +-
.../test/testhelpers/GasGuzzlingConsumer.sol | 2 +-
.../MaliciousMultiWordConsumer.sol | 4 +-
.../test/testhelpers/MaliciousRequester.sol | 2 +-
.../test/testhelpers/MultiWordConsumer.sol | 4 +-
.../mocks}/MockV3Aggregator.sol | 3 +-
.../test/helpers}/LogEmitter.sol | 1 +
.../test/helpers}/VRFLogEmitter.sol | 0
.../{ => shared/util}/ChainSpecificUtil.sol | 6 +-
.../v0.8/tests/ChainlinkClientTestHelper.sol | 83 ----
.../src/v0.8/tests/ChainlinkTestHelper.sol | 57 ---
contracts/src/v0.8/tests/Counter.sol | 26 -
contracts/src/v0.8/tests/FlagsTestHelper.sol | 20 -
.../src/v0.8/tests/MockETHLINKAggregator.sol | 44 --
.../src/v0.8/vrf/BatchBlockhashStore.sol | 2 +-
.../{ => vrf}/ChainSpecificUtil_v0_8_6.sol | 6 +-
contracts/src/v0.8/vrf/VRFCoordinatorV2.sol | 6 +-
contracts/src/v0.8/vrf/VRFV2Wrapper.sol | 6 +-
contracts/src/v0.8/vrf/dev/BlockhashStore.sol | 2 +-
.../v0.8/vrf/dev/TrustedBlockhashStore.sol | 2 +-
.../src/v0.8/vrf/dev/VRFV2PlusWrapper.sol | 4 +-
.../testhelpers/VRFCoordinatorTestV2_5.sol | 2 +-
.../VRFCoordinatorV2PlusUpgradedVersion.sol | 2 +-
.../VRFV2PlusLoadTestWithMetrics.sol | 2 +-
.../VRFV2PlusWrapperLoadTestConsumer.sol | 2 +-
.../src/v0.8/vrf/test/ChainSpecificUtil.t.sol | 2 +-
.../vrf/test/FixtureVRFCoordinatorV2_5.t.sol | 4 +-
.../v0.8/vrf/test/VRFCoordinatorV2Mock.t.sol | 4 +-
.../test/VRFCoordinatorV2Plus_Migration.t.sol | 4 +-
.../vrf/test/VRFCoordinatorV2_5Mock.t.sol | 2 +-
.../test/VRFCoordinatorV2_5_Arbitrum.t.sol | 4 +-
.../test/VRFCoordinatorV2_5_Optimism.t.sol | 4 +-
contracts/src/v0.8/vrf/test/VRFV2Plus.t.sol | 4 +-
.../vrf/test/VRFV2PlusSubscriptionAPI.t.sol | 4 +-
.../src/v0.8/vrf/test/VRFV2PlusWrapper.t.sol | 4 +-
.../vrf/test/VRFV2PlusWrapper_Arbitrum.t.sol | 4 +-
.../vrf/test/VRFV2PlusWrapper_Migration.t.sol | 4 +-
.../vrf/test/VRFV2PlusWrapper_Optimism.t.sol | 4 +-
.../testhelpers/ChainSpecificUtilHelper.sol | 2 +-
.../vrf/testhelpers/VRFCoordinatorTestV2.sol | 10 +-
.../testhelpers/VRFV2LoadTestWithMetrics.sol | 2 +-
.../testhelpers/VRFV2OwnerTestConsumer.sol | 2 +-
.../VRFV2WrapperLoadTestConsumer.sol | 2 +-
contracts/test/v0.8/Chainlink.test.ts | 182 -------
contracts/test/v0.8/ChainlinkClient.test.ts | 452 ------------------
contracts/test/v0.8/Flags.test.ts | 405 ----------------
.../test/v0.8/HeartbeatRequester.test.ts | 142 ------
.../v0.8/PermissionedForwardProxy.test.ts | 176 -------
contracts/test/v0.8/ValidatorProxy.test.ts | 403 ----------------
.../automation/AutomationGasAnalysis.test.ts | 2 +-
.../automation/AutomationRegistrar2_1.test.ts | 2 +-
.../automation/AutomationRegistrar2_3.test.ts | 2 +-
.../automation/AutomationRegistry2_2.test.ts | 2 +-
.../automation/AutomationRegistry2_3.test.ts | 2 +-
.../v0.8/automation/KeeperCompatible.test.ts | 2 +-
.../automation/UpkeepTranscoder3_0.test.ts | 2 +-
.../automation/UpkeepTranscoder4_0.test.ts | 2 +-
.../ZKSyncAutomationRegistry2_3.test.ts | 2 +-
.../AuthorizedForwarder.test.ts | 2 +-
core/chains/evm/logpoller/helper_test.go | 2 +-
.../evm/logpoller/log_poller_internal_test.go | 2 +-
core/chains/evm/logpoller/log_poller_test.go | 2 +-
core/gethwrappers/abigen_test.go | 2 +-
.../type_and_version_interface_wrapper.go | 183 -------
...rapper-dependency-versions-do-not-edit.txt | 3 -
core/gethwrappers/go_generate.go | 1 -
core/gethwrappers/go_generate_logpoller.go | 7 -
.../generated/log_emitter/log_emitter.go | 2 +-
.../type_and_version/type_and_version.go | 183 +++++++
.../vrf_log_emitter/vrf_log_emitter.go | 2 +-
...rapper-dependency-versions-do-not-edit.txt | 5 +-
core/gethwrappers/shared/go_generate.go | 5 +-
core/services/keeper/registry_interface.go | 8 +-
.../plugins/ccip/config/type_and_version.go | 4 +-
.../batchreader/token_pool_batch_reader.go | 4 +-
.../capabilities/testutils/chain_reader.go | 2 +-
.../vrf/v2/listener_v2_log_listener_test.go | 4 +-
.../ccip-tests/contracts/contract_deployer.go | 4 +-
integration-tests/contracts/test_contracts.go | 2 +-
.../automationv2_1/automationv2_1_test.go | 2 +-
integration-tests/load/automationv2_1/gun.go | 2 +-
.../universal/log_poller/helpers.go | 2 +-
tools/ci/ccip_lcov_prune | 3 -
180 files changed, 416 insertions(+), 3111 deletions(-)
create mode 100644 .changeset/cold-coats-battle.md
create mode 100644 contracts/.changeset/angry-needles-approve.md
delete mode 100755 contracts/scripts/native_solc_compile_all_logpoller
delete mode 100644 contracts/src/v0.8/Denominations.sol
delete mode 100644 contracts/src/v0.8/Flags.sol
delete mode 100644 contracts/src/v0.8/PermissionedForwardProxy.sol
delete mode 100644 contracts/src/v0.8/ValidatorProxy.sol
rename contracts/src/v0.8/{tests => automation/mocks}/MockArbGasInfo.sol (100%)
rename contracts/src/v0.8/{ => automation}/mocks/MockArbSys.sol (100%)
rename contracts/src/v0.8/{tests => automation/mocks}/MockGasBoundCaller.sol (100%)
rename contracts/src/v0.8/{tests => automation/mocks}/MockZKSyncSystemContext.sol (100%)
rename contracts/src/v0.8/{tests => automation/testhelpers}/AutomationConsumerBenchmark.sol (100%)
rename contracts/src/v0.8/{tests => automation/testhelpers}/CronReceiver.sol (100%)
rename contracts/src/v0.8/{tests => automation/testhelpers}/ERC20BalanceMonitorExposed.sol (89%)
rename contracts/src/v0.8/{tests => automation/testhelpers}/EthBalanceMonitorExposed.sol (88%)
rename contracts/src/v0.8/{tests => automation/testhelpers}/KeeperCompatibleTestHelper.sol (88%)
rename contracts/src/v0.8/{tests => automation/testhelpers}/MockOVMGasPriceOracle.sol (100%)
rename contracts/src/v0.8/{tests => automation/testhelpers}/ReceiveEmitter.sol (100%)
rename contracts/src/v0.8/{tests => automation/testhelpers}/ReceiveFallbackEmitter.sol (100%)
rename contracts/src/v0.8/{tests => automation/testhelpers}/ReceiveReverter.sol (100%)
rename contracts/src/v0.8/{tests => automation/testhelpers}/StreamsLookupUpkeep.sol (95%)
rename contracts/src/v0.8/{tests => automation/testhelpers}/VerifiableLoadBase.sol (98%)
rename contracts/src/v0.8/{tests => automation/testhelpers}/VerifiableLoadLogTriggerUpkeep.sol (97%)
rename contracts/src/v0.8/{tests => automation/testhelpers}/VerifiableLoadStreamsLookupUpkeep.sol (97%)
rename contracts/src/v0.8/{tests => automation/testhelpers}/VerifiableLoadUpkeep.sol (100%)
rename contracts/src/v0.8/{mocks => functions/tests/v1_X/testhelpers}/MockLinkToken.sol (94%)
delete mode 100644 contracts/src/v0.8/interfaces/FeedRegistryInterface.sol
delete mode 100644 contracts/src/v0.8/interfaces/FlagsInterface.sol
delete mode 100644 contracts/src/v0.8/interfaces/PoRAddressList.sol
delete mode 100644 contracts/src/v0.8/interfaces/TypeAndVersionInterface.sol
rename contracts/src/v0.8/{tests => l2ep/test}/FeedConsumer.sol (92%)
rename contracts/src/v0.8/{tests => l2ep/test}/Greeter.sol (82%)
rename contracts/src/v0.8/{tests => l2ep/test/mocks}/MockArbitrumInbox.sol (94%)
rename contracts/src/v0.8/{tests => l2ep/test/mocks}/MockOptimismL1CrossDomainMessenger.sol (100%)
rename contracts/src/v0.8/{tests => l2ep/test/mocks}/MockOptimismL2CrossDomainMessenger.sol (100%)
delete mode 100644 contracts/src/v0.8/mocks/MockAggregatorValidator.sol
delete mode 100644 contracts/src/v0.8/mocks/MockOffchainAggregator.sol
rename contracts/src/v0.8/{ => operatorforwarder}/Chainlink.sol (96%)
rename contracts/src/v0.8/{ => operatorforwarder}/ChainlinkClient.sol (98%)
rename contracts/src/v0.8/{ => operatorforwarder}/interfaces/ChainlinkRequestInterface.sol (100%)
rename contracts/src/v0.8/{ => operatorforwarder}/interfaces/ENSInterface.sol (100%)
rename contracts/src/v0.8/{ => operatorforwarder}/interfaces/OperatorInterface.sol (100%)
rename contracts/src/v0.8/{ => operatorforwarder}/interfaces/OracleInterface.sol (100%)
rename contracts/src/v0.8/{ => operatorforwarder}/interfaces/PointerInterface.sol (100%)
rename contracts/src/v0.8/{tests => operatorforwarder/test}/Broken.sol (95%)
rename contracts/src/v0.8/{tests => shared/mocks}/MockV3Aggregator.sol (95%)
rename contracts/src/v0.8/{tests => shared/test/helpers}/LogEmitter.sol (97%)
rename contracts/src/v0.8/{tests => shared/test/helpers}/VRFLogEmitter.sol (100%)
rename contracts/src/v0.8/{ => shared/util}/ChainSpecificUtil.sol (95%)
delete mode 100644 contracts/src/v0.8/tests/ChainlinkClientTestHelper.sol
delete mode 100644 contracts/src/v0.8/tests/ChainlinkTestHelper.sol
delete mode 100644 contracts/src/v0.8/tests/Counter.sol
delete mode 100644 contracts/src/v0.8/tests/FlagsTestHelper.sol
delete mode 100644 contracts/src/v0.8/tests/MockETHLINKAggregator.sol
rename contracts/src/v0.8/{ => vrf}/ChainSpecificUtil_v0_8_6.sol (96%)
delete mode 100644 contracts/test/v0.8/Chainlink.test.ts
delete mode 100644 contracts/test/v0.8/ChainlinkClient.test.ts
delete mode 100644 contracts/test/v0.8/Flags.test.ts
delete mode 100644 contracts/test/v0.8/HeartbeatRequester.test.ts
delete mode 100644 contracts/test/v0.8/PermissionedForwardProxy.test.ts
delete mode 100644 contracts/test/v0.8/ValidatorProxy.test.ts
delete mode 100644 core/gethwrappers/generated/type_and_version_interface_wrapper/type_and_version_interface_wrapper.go
delete mode 100644 core/gethwrappers/go_generate_logpoller.go
rename core/gethwrappers/{ => shared}/generated/log_emitter/log_emitter.go (93%)
create mode 100644 core/gethwrappers/shared/generated/type_and_version/type_and_version.go
rename core/gethwrappers/{ => shared}/generated/vrf_log_emitter/vrf_log_emitter.go (88%)
diff --git a/.changeset/cold-coats-battle.md b/.changeset/cold-coats-battle.md
new file mode 100644
index 00000000000..1a72d025bde
--- /dev/null
+++ b/.changeset/cold-coats-battle.md
@@ -0,0 +1,5 @@
+---
+"chainlink": patch
+---
+
+#internal minor rename of various gethwrappers
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 9f19d52b7ea..6e05a6f1c10 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -89,14 +89,11 @@ core/scripts/gateway @smartcontractkit/dev-services
/contracts/src/v0.8/automation @smartcontractkit/dev-services
/contracts/src/v0.8/ccip @smartcontractkit/ccip-onchain
/contracts/src/v0.8/functions @smartcontractkit/dev-services
-# TODO: interfaces folder, folder should be removed and files moved to the correct folders
/contracts/src/v0.8/l2ep @smartcontractkit/bix-build
/contracts/src/v0.8/llo-feeds @smartcontractkit/data-streams-engineers
# TODO: mocks folder, folder should be removed and files moved to the correct folders
/contracts/src/v0.8/operatorforwarder @smartcontractkit/data-feeds-engineers
/contracts/src/v0.8/shared @smartcontractkit/core-solidity
-# TODO: tests folder, folder should be removed and files moved to the correct folders
-# TODO: transmission folder, owner should be found
/contracts/src/v0.8/vrf @smartcontractkit/dev-services
/contracts/src/v0.8/keystone @smartcontractkit/keystone
/contracts/src/v0.8/workflow @smartcontractkit/dev-services
diff --git a/.github/workflows/solidity-foundry-artifacts.yml b/.github/workflows/solidity-foundry-artifacts.yml
index 620d491e82d..5665c786057 100644
--- a/.github/workflows/solidity-foundry-artifacts.yml
+++ b/.github/workflows/solidity-foundry-artifacts.yml
@@ -69,7 +69,6 @@ jobs:
- '!contracts/src/v0.8/**/*.t.sol'
- '!contracts/src/v0.8/*.t.sol'
- '!contracts/src/v0.8/**/testhelpers/**'
- - '!contracts/src/v0.8/testhelpers/**'
- '!contracts/src/v0.8/vendor/**'
other_shared:
- modified|added: 'contracts/src/v0.8/(interfaces/**/*.sol|*.sol)'
diff --git a/contracts/.changeset/angry-needles-approve.md b/contracts/.changeset/angry-needles-approve.md
new file mode 100644
index 00000000000..689f2ac6063
--- /dev/null
+++ b/contracts/.changeset/angry-needles-approve.md
@@ -0,0 +1,5 @@
+---
+'@chainlink/contracts': minor
+---
+
+#internal Removal and moving of various older Solidity contracts. Unused test helpers are removed, used files are now in their proper product folders
diff --git a/contracts/STYLE_GUIDE.md b/contracts/STYLE_GUIDE.md
index f1faab09644..1fbdc061f23 100644
--- a/contracts/STYLE_GUIDE.md
+++ b/contracts/STYLE_GUIDE.md
@@ -265,9 +265,6 @@ All contracts will expose a `typeAndVersion` constant.
The string has the following format: `-` with the `-dev` part only being applicable to contracts that have not been fully released.
Try to fit it into 32 bytes to keep the impact on contract sizes minimal.
-Note that `ITypeAndVersion` should be used, not `TypeAndVersionInterface`.
-
-
diff --git a/contracts/scripts/lcov_prune b/contracts/scripts/lcov_prune
index 9d5d592c646..9dbd6781d96 100755
--- a/contracts/scripts/lcov_prune
+++ b/contracts/scripts/lcov_prune
@@ -27,8 +27,6 @@ exclusion_list_ccip=(
"src/v0.8/ccip/libraries/USDPriceWith18Decimals.sol"
"src/v0.8/ccip/libraries/MerkleMultiProof.sol"
"src/v0.8/ccip/libraries/Pool.sol"
- "src/v0.8/ConfirmedOwnerWithProposal.sol"
- "src/v0.8/tests/MockV3Aggregator.sol"
"src/v0.8/ccip/applications/CCIPClientExample.sol"
"src/v0.8/keystone/*"
)
diff --git a/contracts/scripts/native_solc_compile_all b/contracts/scripts/native_solc_compile_all
index 42abac3c6b3..a66456bb6d5 100755
--- a/contracts/scripts/native_solc_compile_all
+++ b/contracts/scripts/native_solc_compile_all
@@ -12,7 +12,7 @@ python3 -m pip install --require-hashes -r $SCRIPTPATH/requirements.txt
# 6 and 7 are legacy contracts, for each other product we have a native_solc_compile_all_$product script
# These scripts can be run individually, or all together with this script.
# To add new CL products, simply write a native_solc_compile_all_$product script and add it to the list below.
-for product in automation events_mock feeds functions keystone llo-feeds logpoller operatorforwarder shared vrf ccip liquiditymanager workflow
+for product in automation events_mock feeds functions keystone llo-feeds operatorforwarder shared vrf ccip liquiditymanager workflow
do
$SCRIPTPATH/native_solc_compile_all_$product
done
diff --git a/contracts/scripts/native_solc_compile_all_automation b/contracts/scripts/native_solc_compile_all_automation
index e189e78cb0f..eb4b39201ba 100755
--- a/contracts/scripts/native_solc_compile_all_automation
+++ b/contracts/scripts/native_solc_compile_all_automation
@@ -73,11 +73,11 @@ compileContract automation/testhelpers/UpkeepCounter.sol
compileContract automation/interfaces/StreamsLookupCompatibleInterface.sol
-compileContract tests/VerifiableLoadUpkeep.sol
-compileContract tests/VerifiableLoadStreamsLookupUpkeep.sol
-compileContract tests/VerifiableLoadLogTriggerUpkeep.sol
-compileContract tests/AutomationConsumerBenchmark.sol
-compileContract tests/StreamsLookupUpkeep.sol
+compileContract automation/testhelpers/VerifiableLoadUpkeep.sol
+compileContract automation/testhelpers/VerifiableLoadStreamsLookupUpkeep.sol
+compileContract automation/testhelpers/VerifiableLoadLogTriggerUpkeep.sol
+compileContract automation/testhelpers/AutomationConsumerBenchmark.sol
+compileContract automation/testhelpers/StreamsLookupUpkeep.sol
SOLC_VERSION="0.8.19"
diff --git a/contracts/scripts/native_solc_compile_all_feeds b/contracts/scripts/native_solc_compile_all_feeds
index 66cb3f19161..c6b80958156 100755
--- a/contracts/scripts/native_solc_compile_all_feeds
+++ b/contracts/scripts/native_solc_compile_all_feeds
@@ -30,5 +30,5 @@ compileContract () {
}
# Aggregators
-compileContract Chainlink.sol
-compileContract ChainlinkClient.sol
+compileContract operatorforwarder/Chainlink.sol
+compileContract operatorforwarder/ChainlinkClient.sol
diff --git a/contracts/scripts/native_solc_compile_all_logpoller b/contracts/scripts/native_solc_compile_all_logpoller
deleted file mode 100755
index e8ea2a2be80..00000000000
--- a/contracts/scripts/native_solc_compile_all_logpoller
+++ /dev/null
@@ -1,33 +0,0 @@
-#!/usr/bin/env bash
-
-set -e
-
-echo " ┌──────────────────────────────────────────────┐"
-echo " │ Compiling LogPoller contracts... │"
-echo " └──────────────────────────────────────────────┘"
-
-SOLC_VERSION="0.8.19"
-OPTIMIZE_RUNS=1000000
-
-
-SCRIPTPATH="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
-python3 -m pip install --require-hashes -r "$SCRIPTPATH"/requirements.txt
-solc-select install $SOLC_VERSION
-solc-select use $SOLC_VERSION
-export SOLC_VERSION=$SOLC_VERSION
-
-ROOT="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; cd ../../ && pwd -P )"
-
-compileContract () {
- local contract
- contract=$(basename "$1" ".sol")
-
- solc --overwrite --optimize --optimize-runs $OPTIMIZE_RUNS --metadata-hash none \
- -o "$ROOT"/contracts/solc/v$SOLC_VERSION/"$contract" \
- --abi --bin --allow-paths "$ROOT"/contracts/src/v0.8\
- "$ROOT"/contracts/src/v0.8/"$1"
-}
-
-
-compileContract tests/LogEmitter.sol
-compileContract tests/VRFLogEmitter.sol
\ No newline at end of file
diff --git a/contracts/scripts/native_solc_compile_all_shared b/contracts/scripts/native_solc_compile_all_shared
index d205b51321c..58f24fdaa22 100755
--- a/contracts/scripts/native_solc_compile_all_shared
+++ b/contracts/scripts/native_solc_compile_all_shared
@@ -33,13 +33,16 @@ compileContract() {
$command
}
+compileContract interfaces/AggregatorV3Interface
+compileContract interfaces/ITypeAndVersion
compileContract token/ERC677/BurnMintERC677
compileContract token/ERC677/LinkToken
compileContract token/ERC20/BurnMintERC20
compileContract test/helpers/ChainReaderTester
+compileContract test/helpers/LogEmitter
+compileContract test/helpers/VRFLogEmitter
+compileContract mocks/MockV3Aggregator
compileContract mocks/WERC20Mock
-compileContract interfaces/AggregatorV3Interface
compileContract openzeppelin-solidity/v4.8.3/contracts/token/ERC20/ERC20 vendor
compileContract multicall/ebd8b64/src/Multicall3 vendor
-compileContract MockV3Aggregator tests
\ No newline at end of file
diff --git a/contracts/src/v0.8/Denominations.sol b/contracts/src/v0.8/Denominations.sol
deleted file mode 100644
index 6e9aa778ec7..00000000000
--- a/contracts/src/v0.8/Denominations.sol
+++ /dev/null
@@ -1,28 +0,0 @@
-// SPDX-License-Identifier: MIT
-
-pragma solidity ^0.8.0;
-
-library Denominations {
- address public constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
- address public constant BTC = 0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB;
-
- // Fiat currencies follow https://en.wikipedia.org/wiki/ISO_4217
- address public constant USD = address(840);
- address public constant GBP = address(826);
- address public constant EUR = address(978);
- address public constant JPY = address(392);
- address public constant KRW = address(410);
- address public constant CNY = address(156);
- address public constant AUD = address(36);
- address public constant CAD = address(124);
- address public constant CHF = address(756);
- address public constant ARS = address(32);
- address public constant PHP = address(608);
- address public constant NZD = address(554);
- address public constant SGD = address(702);
- address public constant NGN = address(566);
- address public constant ZAR = address(710);
- address public constant RUB = address(643);
- address public constant INR = address(356);
- address public constant BRL = address(986);
-}
diff --git a/contracts/src/v0.8/Flags.sol b/contracts/src/v0.8/Flags.sol
deleted file mode 100644
index de14583bcb4..00000000000
--- a/contracts/src/v0.8/Flags.sol
+++ /dev/null
@@ -1,124 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.0;
-
-import {SimpleReadAccessController} from "./shared/access/SimpleReadAccessController.sol";
-import {AccessControllerInterface} from "./shared/interfaces/AccessControllerInterface.sol";
-import {FlagsInterface} from "./interfaces/FlagsInterface.sol";
-
-/**
- * @title The Flags contract
- * @notice Allows flags to signal to any reader on the access control list.
- * The owner can set flags, or designate other addresses to set flags. The
- * owner must turn the flags off, other setters cannot. An expected pattern is
- * to allow addresses to raise flags on themselves, so if you are subscribing to
- * FlagOn events you should filter for addresses you care about.
- */
-// solhint-disable gas-custom-errors
-contract Flags is FlagsInterface, SimpleReadAccessController {
- AccessControllerInterface public raisingAccessController;
-
- mapping(address => bool) private s_flags;
-
- event FlagRaised(address indexed subject);
- event FlagLowered(address indexed subject);
- event RaisingAccessControllerUpdated(address indexed previous, address indexed current);
-
- /**
- * @param racAddress address for the raising access controller.
- */
- constructor(address racAddress) {
- setRaisingAccessController(racAddress);
- }
-
- /**
- * @notice read the warning flag status of a contract address.
- * @param subject The contract address being checked for a flag.
- * @return A true value indicates that a flag was raised and a
- * false value indicates that no flag was raised.
- */
- function getFlag(address subject) external view override checkAccess returns (bool) {
- return s_flags[subject];
- }
-
- /**
- * @notice read the warning flag status of a contract address.
- * @param subjects An array of addresses being checked for a flag.
- * @return An array of bools where a true value for any flag indicates that
- * a flag was raised and a false value indicates that no flag was raised.
- */
- function getFlags(address[] calldata subjects) external view override checkAccess returns (bool[] memory) {
- bool[] memory responses = new bool[](subjects.length);
- for (uint256 i = 0; i < subjects.length; i++) {
- responses[i] = s_flags[subjects[i]];
- }
- return responses;
- }
-
- /**
- * @notice enable the warning flag for an address.
- * Access is controlled by raisingAccessController, except for owner
- * who always has access.
- * @param subject The contract address whose flag is being raised
- */
- function raiseFlag(address subject) external override {
- require(_allowedToRaiseFlags(), "Not allowed to raise flags");
-
- _tryToRaiseFlag(subject);
- }
-
- /**
- * @notice enable the warning flags for multiple addresses.
- * Access is controlled by raisingAccessController, except for owner
- * who always has access.
- * @param subjects List of the contract addresses whose flag is being raised
- */
- function raiseFlags(address[] calldata subjects) external override {
- require(_allowedToRaiseFlags(), "Not allowed to raise flags");
-
- for (uint256 i = 0; i < subjects.length; i++) {
- _tryToRaiseFlag(subjects[i]);
- }
- }
-
- /**
- * @notice allows owner to disable the warning flags for multiple addresses.
- * @param subjects List of the contract addresses whose flag is being lowered
- */
- function lowerFlags(address[] calldata subjects) external override onlyOwner {
- for (uint256 i = 0; i < subjects.length; i++) {
- address subject = subjects[i];
-
- if (s_flags[subject]) {
- s_flags[subject] = false;
- emit FlagLowered(subject);
- }
- }
- }
-
- /**
- * @notice allows owner to change the access controller for raising flags.
- * @param racAddress new address for the raising access controller.
- */
- function setRaisingAccessController(address racAddress) public override onlyOwner {
- address previous = address(raisingAccessController);
-
- if (previous != racAddress) {
- raisingAccessController = AccessControllerInterface(racAddress);
-
- emit RaisingAccessControllerUpdated(previous, racAddress);
- }
- }
-
- // PRIVATE
-
- function _allowedToRaiseFlags() private view returns (bool) {
- return msg.sender == owner() || raisingAccessController.hasAccess(msg.sender, msg.data);
- }
-
- function _tryToRaiseFlag(address subject) private {
- if (!s_flags[subject]) {
- s_flags[subject] = true;
- emit FlagRaised(subject);
- }
- }
-}
diff --git a/contracts/src/v0.8/PermissionedForwardProxy.sol b/contracts/src/v0.8/PermissionedForwardProxy.sol
deleted file mode 100644
index 544f89065c0..00000000000
--- a/contracts/src/v0.8/PermissionedForwardProxy.sol
+++ /dev/null
@@ -1,65 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity 0.8.6;
-
-import {Address} from "@openzeppelin/contracts/utils/Address.sol";
-import {ConfirmedOwner} from "./shared/access/ConfirmedOwner.sol";
-
-/**
- * @title PermissionedForwardProxy
- * @notice This proxy is used to forward calls from sender to target. It maintains
- * a permission list to check which sender is allowed to call which target
- */
-contract PermissionedForwardProxy is ConfirmedOwner {
- using Address for address;
-
- error PermissionNotSet();
-
- event PermissionSet(address indexed sender, address target);
- event PermissionRemoved(address indexed sender);
-
- mapping(address => address) private s_forwardPermissionList;
-
- constructor() ConfirmedOwner(msg.sender) {}
-
- /**
- * @notice Verifies if msg.sender has permission to forward to target address and then forwards the handler
- * @param target address of the contract to forward the handler to
- * @param handler bytes to be passed to target in call data
- */
- function forward(address target, bytes calldata handler) external {
- if (s_forwardPermissionList[msg.sender] != target) {
- revert PermissionNotSet();
- }
- target.functionCall(handler);
- }
-
- /**
- * @notice Adds permission for sender to forward calls to target via this proxy.
- * Note that it allows to overwrite an existing permission
- * @param sender The address who will use this proxy to forward calls
- * @param target The address where sender will be allowed to forward calls
- */
- function setPermission(address sender, address target) external onlyOwner {
- s_forwardPermissionList[sender] = target;
-
- emit PermissionSet(sender, target);
- }
-
- /**
- * @notice Removes permission for sender to forward calls via this proxy
- * @param sender The address who will use this proxy to forward calls
- */
- function removePermission(address sender) external onlyOwner {
- delete s_forwardPermissionList[sender];
-
- emit PermissionRemoved(sender);
- }
-
- /**
- * @notice Returns the target address that the sender can use this proxy for
- * @param sender The address to fetch the permissioned target for
- */
- function getPermission(address sender) external view returns (address) {
- return s_forwardPermissionList[sender];
- }
-}
diff --git a/contracts/src/v0.8/ValidatorProxy.sol b/contracts/src/v0.8/ValidatorProxy.sol
deleted file mode 100644
index 58e0e28a899..00000000000
--- a/contracts/src/v0.8/ValidatorProxy.sol
+++ /dev/null
@@ -1,230 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.0;
-
-import {ConfirmedOwner} from "./shared/access/ConfirmedOwner.sol";
-import {AggregatorValidatorInterface} from "./shared/interfaces/AggregatorValidatorInterface.sol";
-import {TypeAndVersionInterface} from "./interfaces/TypeAndVersionInterface.sol";
-
-// solhint-disable gas-custom-errors
-contract ValidatorProxy is AggregatorValidatorInterface, TypeAndVersionInterface, ConfirmedOwner {
- /// @notice Uses a single storage slot to store the current address
- struct AggregatorConfiguration {
- address target;
- bool hasNewProposal;
- }
-
- struct ValidatorConfiguration {
- AggregatorValidatorInterface target;
- bool hasNewProposal;
- }
-
- // Configuration for the current aggregator
- AggregatorConfiguration private s_currentAggregator;
- // Proposed aggregator address
- address private s_proposedAggregator;
-
- // Configuration for the current validator
- ValidatorConfiguration private s_currentValidator;
- // Proposed validator address
- AggregatorValidatorInterface private s_proposedValidator;
-
- event AggregatorProposed(address indexed aggregator);
- event AggregatorUpgraded(address indexed previous, address indexed current);
- event ValidatorProposed(AggregatorValidatorInterface indexed validator);
- event ValidatorUpgraded(AggregatorValidatorInterface indexed previous, AggregatorValidatorInterface indexed current);
- /// @notice The proposed aggregator called validate, but the call was not passed on to any validators
- event ProposedAggregatorValidateCall(
- address indexed proposed,
- uint256 previousRoundId,
- int256 previousAnswer,
- uint256 currentRoundId,
- int256 currentAnswer
- );
-
- /**
- * @notice Construct the ValidatorProxy with an aggregator and a validator
- * @param aggregator address
- * @param validator address
- */
- constructor(address aggregator, AggregatorValidatorInterface validator) ConfirmedOwner(msg.sender) {
- s_currentAggregator = AggregatorConfiguration({target: aggregator, hasNewProposal: false});
- s_currentValidator = ValidatorConfiguration({target: validator, hasNewProposal: false});
- }
-
- /**
- * @notice Validate a transmission
- * @dev Must be called by either the `s_currentAggregator.target`, or the `s_proposedAggregator`.
- * If called by the `s_currentAggregator.target` this function passes the call on to the `s_currentValidator.target`
- * and the `s_proposedValidator`, if it is set.
- * If called by the `s_proposedAggregator` this function emits a `ProposedAggregatorValidateCall` to signal that
- * the call was received.
- * @dev To guard against external `validate` calls reverting, we use raw calls here.
- * We favour `call` over try-catch to ensure that failures are avoided even if the validator address is incorrectly
- * set as a non-contract address.
- * @dev If the `aggregator` and `validator` are the same contract or collude, this could exhibit reentrancy behavior.
- * However, since that contract would have to be explicitly written for reentrancy and that the `owner` would have
- * to configure this contract to use that malicious contract, we refrain from using mutex or check here.
- * @dev This does not perform any checks on any roundId, so it is possible that a validator receive different reports
- * for the same roundId at different points in time. Validator implementations should be aware of this.
- * @param previousRoundId uint256
- * @param previousAnswer int256
- * @param currentRoundId uint256
- * @param currentAnswer int256
- * @return bool
- */
- function validate(
- uint256 previousRoundId,
- int256 previousAnswer,
- uint256 currentRoundId,
- int256 currentAnswer
- ) external override returns (bool) {
- address currentAggregator = s_currentAggregator.target;
- if (msg.sender != currentAggregator) {
- address proposedAggregator = s_proposedAggregator;
- require(msg.sender == proposedAggregator, "Not a configured aggregator");
- // If the aggregator is still in proposed state, emit an event and don't push to any validator.
- // This is to confirm that `validate` is being called prior to upgrade.
- emit ProposedAggregatorValidateCall(
- proposedAggregator,
- previousRoundId,
- previousAnswer,
- currentRoundId,
- currentAnswer
- );
- return true;
- }
-
- // Send the validate call to the current validator
- ValidatorConfiguration memory currentValidator = s_currentValidator;
- address currentValidatorAddress = address(currentValidator.target);
- require(currentValidatorAddress != address(0), "No validator set");
- // solhint-disable-next-line avoid-low-level-calls
- currentValidatorAddress.call(
- abi.encodeWithSelector(
- AggregatorValidatorInterface.validate.selector,
- previousRoundId,
- previousAnswer,
- currentRoundId,
- currentAnswer
- )
- );
- // If there is a new proposed validator, send the validate call to that validator also
- if (currentValidator.hasNewProposal) {
- // solhint-disable-next-line avoid-low-level-calls
- address(s_proposedValidator).call(
- abi.encodeWithSelector(
- AggregatorValidatorInterface.validate.selector,
- previousRoundId,
- previousAnswer,
- currentRoundId,
- currentAnswer
- )
- );
- }
- return true;
- }
-
- /** AGGREGATOR CONFIGURATION FUNCTIONS **/
-
- /**
- * @notice Propose an aggregator
- * @dev A zero address can be used to unset the proposed aggregator. Only owner can call.
- * @param proposed address
- */
- function proposeNewAggregator(address proposed) external onlyOwner {
- require(s_proposedAggregator != proposed && s_currentAggregator.target != proposed, "Invalid proposal");
- s_proposedAggregator = proposed;
- // If proposed is zero address, hasNewProposal = false
- s_currentAggregator.hasNewProposal = (proposed != address(0));
- emit AggregatorProposed(proposed);
- }
-
- /**
- * @notice Upgrade the aggregator by setting the current aggregator as the proposed aggregator.
- * @dev Must have a proposed aggregator. Only owner can call.
- */
- function upgradeAggregator() external onlyOwner {
- // Get configuration in memory
- AggregatorConfiguration memory current = s_currentAggregator;
- address previous = current.target;
- address proposed = s_proposedAggregator;
-
- // Perform the upgrade
- require(current.hasNewProposal, "No proposal");
- s_currentAggregator = AggregatorConfiguration({target: proposed, hasNewProposal: false});
- delete s_proposedAggregator;
-
- emit AggregatorUpgraded(previous, proposed);
- }
-
- /**
- * @notice Get aggregator details
- * @return current address
- * @return hasProposal bool
- * @return proposed address
- */
- function getAggregators() external view returns (address current, bool hasProposal, address proposed) {
- current = s_currentAggregator.target;
- hasProposal = s_currentAggregator.hasNewProposal;
- proposed = s_proposedAggregator;
- return (current, hasProposal, proposed);
- }
-
- /** VALIDATOR CONFIGURATION FUNCTIONS **/
-
- /**
- * @notice Propose an validator
- * @dev A zero address can be used to unset the proposed validator. Only owner can call.
- * @param proposed address
- */
- function proposeNewValidator(AggregatorValidatorInterface proposed) external onlyOwner {
- require(s_proposedValidator != proposed && s_currentValidator.target != proposed, "Invalid proposal");
- s_proposedValidator = proposed;
- // If proposed is zero address, hasNewProposal = false
- s_currentValidator.hasNewProposal = (address(proposed) != address(0));
- emit ValidatorProposed(proposed);
- }
-
- /**
- * @notice Upgrade the validator by setting the current validator as the proposed validator.
- * @dev Must have a proposed validator. Only owner can call.
- */
- function upgradeValidator() external onlyOwner {
- // Get configuration in memory
- ValidatorConfiguration memory current = s_currentValidator;
- AggregatorValidatorInterface previous = current.target;
- AggregatorValidatorInterface proposed = s_proposedValidator;
-
- // Perform the upgrade
- require(current.hasNewProposal, "No proposal");
- s_currentValidator = ValidatorConfiguration({target: proposed, hasNewProposal: false});
- delete s_proposedValidator;
-
- emit ValidatorUpgraded(previous, proposed);
- }
-
- /**
- * @notice Get validator details
- * @return current address
- * @return hasProposal bool
- * @return proposed address
- */
- function getValidators()
- external
- view
- returns (AggregatorValidatorInterface current, bool hasProposal, AggregatorValidatorInterface proposed)
- {
- current = s_currentValidator.target;
- hasProposal = s_currentValidator.hasNewProposal;
- proposed = s_proposedValidator;
- return (current, hasProposal, proposed);
- }
-
- /**
- * @notice The type and version of this contract
- * @return Type and version string
- */
- function typeAndVersion() external pure virtual override returns (string memory) {
- return "ValidatorProxy 1.0.0";
- }
-}
diff --git a/contracts/src/v0.8/automation/HeartbeatRequester.sol b/contracts/src/v0.8/automation/HeartbeatRequester.sol
index 8ef7fa44422..077bb93d18f 100644
--- a/contracts/src/v0.8/automation/HeartbeatRequester.sol
+++ b/contracts/src/v0.8/automation/HeartbeatRequester.sol
@@ -2,7 +2,7 @@
// solhint-disable-next-line one-contract-per-file
pragma solidity 0.8.6;
-import {TypeAndVersionInterface} from "./../interfaces/TypeAndVersionInterface.sol";
+import {ITypeAndVersion} from "./../shared/interfaces/ITypeAndVersion.sol";
import {ConfirmedOwner} from "../shared/access/ConfirmedOwner.sol";
// defines some interfaces for type safety and reduces encoding/decoding
@@ -20,7 +20,7 @@ interface IOffchainAggregator {
* by eligible caller, it will call a proxy for an aggregator address and request a new round. The aggregator
* is gated by permissions and this requester address needs to be whitelisted.
*/
-contract HeartbeatRequester is TypeAndVersionInterface, ConfirmedOwner {
+contract HeartbeatRequester is ITypeAndVersion, ConfirmedOwner {
event HeartbeatPermitted(address indexed permittedCaller, address newProxy, address oldProxy);
event HeartbeatRemoved(address indexed permittedCaller, address removedProxy);
diff --git a/contracts/src/v0.8/automation/UpkeepTranscoder.sol b/contracts/src/v0.8/automation/UpkeepTranscoder.sol
index 03f40d890b8..5e60270d355 100644
--- a/contracts/src/v0.8/automation/UpkeepTranscoder.sol
+++ b/contracts/src/v0.8/automation/UpkeepTranscoder.sol
@@ -3,14 +3,14 @@
pragma solidity ^0.8.0;
import {UpkeepTranscoderInterface} from "./interfaces/UpkeepTranscoderInterface.sol";
-import {TypeAndVersionInterface} from "../interfaces/TypeAndVersionInterface.sol";
+import {ITypeAndVersion} from "../shared/interfaces/ITypeAndVersion.sol";
import {UpkeepFormat} from "./UpkeepFormat.sol";
/**
* @notice Transcoder for converting upkeep data from one keeper
* registry version to another
*/
-contract UpkeepTranscoder is UpkeepTranscoderInterface, TypeAndVersionInterface {
+contract UpkeepTranscoder is UpkeepTranscoderInterface, ITypeAndVersion {
error InvalidTranscoding();
/**
diff --git a/contracts/src/v0.8/automation/dev/MercuryRegistry.sol b/contracts/src/v0.8/automation/dev/MercuryRegistry.sol
index 247301a7438..9035f0af927 100644
--- a/contracts/src/v0.8/automation/dev/MercuryRegistry.sol
+++ b/contracts/src/v0.8/automation/dev/MercuryRegistry.sol
@@ -3,7 +3,7 @@ pragma solidity 0.8.19;
import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol";
import {AutomationCompatibleInterface} from "../interfaces/AutomationCompatibleInterface.sol";
import {StreamsLookupCompatibleInterface} from "../interfaces/StreamsLookupCompatibleInterface.sol";
-import {ChainSpecificUtil} from "../../ChainSpecificUtil.sol";
+import {ChainSpecificUtil} from "../../shared/util/ChainSpecificUtil.sol";
/*--------------------------------------------------------------------------------------------------------------------+
| Mercury + Automation |
diff --git a/contracts/src/v0.8/tests/MockArbGasInfo.sol b/contracts/src/v0.8/automation/mocks/MockArbGasInfo.sol
similarity index 100%
rename from contracts/src/v0.8/tests/MockArbGasInfo.sol
rename to contracts/src/v0.8/automation/mocks/MockArbGasInfo.sol
diff --git a/contracts/src/v0.8/mocks/MockArbSys.sol b/contracts/src/v0.8/automation/mocks/MockArbSys.sol
similarity index 100%
rename from contracts/src/v0.8/mocks/MockArbSys.sol
rename to contracts/src/v0.8/automation/mocks/MockArbSys.sol
diff --git a/contracts/src/v0.8/tests/MockGasBoundCaller.sol b/contracts/src/v0.8/automation/mocks/MockGasBoundCaller.sol
similarity index 100%
rename from contracts/src/v0.8/tests/MockGasBoundCaller.sol
rename to contracts/src/v0.8/automation/mocks/MockGasBoundCaller.sol
diff --git a/contracts/src/v0.8/tests/MockZKSyncSystemContext.sol b/contracts/src/v0.8/automation/mocks/MockZKSyncSystemContext.sol
similarity index 100%
rename from contracts/src/v0.8/tests/MockZKSyncSystemContext.sol
rename to contracts/src/v0.8/automation/mocks/MockZKSyncSystemContext.sol
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 e0d15daab6c..f1086e7bfa4 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
@@ -6,7 +6,7 @@ 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 {MockV3Aggregator} from "../../../shared/mocks/MockV3Aggregator.sol";
import {AutomationForwarderLogic} from "../../AutomationForwarderLogic.sol";
import {UpkeepTranscoder5_0 as Transcoder} from "../../v2_3/UpkeepTranscoder5_0.sol";
import {AutomationRegistry2_3} from "../../v2_3/AutomationRegistry2_3.sol";
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
index cde05ab3a22..dde8f5b3867 100644
--- 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
@@ -6,7 +6,7 @@ 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 {MockV3Aggregator} from "../../../shared/mocks/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";
@@ -21,8 +21,8 @@ import {IERC20Metadata as IERC20} from "../../../vendor/openzeppelin-solidity/v4
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";
+import {MockGasBoundCaller} from "../../mocks/MockGasBoundCaller.sol";
+import {MockZKSyncSystemContext} from "../../mocks/MockZKSyncSystemContext.sol";
/**
* @title BaseTest provides basic test setup procedures and dependencies for use by other
diff --git a/contracts/src/v0.8/tests/AutomationConsumerBenchmark.sol b/contracts/src/v0.8/automation/testhelpers/AutomationConsumerBenchmark.sol
similarity index 100%
rename from contracts/src/v0.8/tests/AutomationConsumerBenchmark.sol
rename to contracts/src/v0.8/automation/testhelpers/AutomationConsumerBenchmark.sol
diff --git a/contracts/src/v0.8/tests/CronReceiver.sol b/contracts/src/v0.8/automation/testhelpers/CronReceiver.sol
similarity index 100%
rename from contracts/src/v0.8/tests/CronReceiver.sol
rename to contracts/src/v0.8/automation/testhelpers/CronReceiver.sol
diff --git a/contracts/src/v0.8/tests/ERC20BalanceMonitorExposed.sol b/contracts/src/v0.8/automation/testhelpers/ERC20BalanceMonitorExposed.sol
similarity index 89%
rename from contracts/src/v0.8/tests/ERC20BalanceMonitorExposed.sol
rename to contracts/src/v0.8/automation/testhelpers/ERC20BalanceMonitorExposed.sol
index a29ba36eeb4..748cf1cb727 100644
--- a/contracts/src/v0.8/tests/ERC20BalanceMonitorExposed.sol
+++ b/contracts/src/v0.8/automation/testhelpers/ERC20BalanceMonitorExposed.sol
@@ -2,7 +2,7 @@
pragma solidity 0.8.6;
-import "../automation/upkeeps/ERC20BalanceMonitor.sol";
+import "../upkeeps/ERC20BalanceMonitor.sol";
contract ERC20BalanceMonitorExposed is ERC20BalanceMonitor {
constructor(
diff --git a/contracts/src/v0.8/tests/EthBalanceMonitorExposed.sol b/contracts/src/v0.8/automation/testhelpers/EthBalanceMonitorExposed.sol
similarity index 88%
rename from contracts/src/v0.8/tests/EthBalanceMonitorExposed.sol
rename to contracts/src/v0.8/automation/testhelpers/EthBalanceMonitorExposed.sol
index 74cc682df23..f27c9621c39 100644
--- a/contracts/src/v0.8/tests/EthBalanceMonitorExposed.sol
+++ b/contracts/src/v0.8/automation/testhelpers/EthBalanceMonitorExposed.sol
@@ -2,7 +2,7 @@
pragma solidity 0.8.6;
-import "../automation/upkeeps/EthBalanceMonitor.sol";
+import "../upkeeps/EthBalanceMonitor.sol";
contract EthBalanceMonitorExposed is EthBalanceMonitor {
constructor(
diff --git a/contracts/src/v0.8/tests/KeeperCompatibleTestHelper.sol b/contracts/src/v0.8/automation/testhelpers/KeeperCompatibleTestHelper.sol
similarity index 88%
rename from contracts/src/v0.8/tests/KeeperCompatibleTestHelper.sol
rename to contracts/src/v0.8/automation/testhelpers/KeeperCompatibleTestHelper.sol
index 2e931c4fb4d..3c71dc2f848 100644
--- a/contracts/src/v0.8/tests/KeeperCompatibleTestHelper.sol
+++ b/contracts/src/v0.8/automation/testhelpers/KeeperCompatibleTestHelper.sol
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
-import "../automation/KeeperCompatible.sol";
+import "../KeeperCompatible.sol";
contract KeeperCompatibleTestHelper is KeeperCompatible {
function checkUpkeep(bytes calldata) external override returns (bool, bytes memory) {}
diff --git a/contracts/src/v0.8/tests/MockOVMGasPriceOracle.sol b/contracts/src/v0.8/automation/testhelpers/MockOVMGasPriceOracle.sol
similarity index 100%
rename from contracts/src/v0.8/tests/MockOVMGasPriceOracle.sol
rename to contracts/src/v0.8/automation/testhelpers/MockOVMGasPriceOracle.sol
diff --git a/contracts/src/v0.8/tests/ReceiveEmitter.sol b/contracts/src/v0.8/automation/testhelpers/ReceiveEmitter.sol
similarity index 100%
rename from contracts/src/v0.8/tests/ReceiveEmitter.sol
rename to contracts/src/v0.8/automation/testhelpers/ReceiveEmitter.sol
diff --git a/contracts/src/v0.8/tests/ReceiveFallbackEmitter.sol b/contracts/src/v0.8/automation/testhelpers/ReceiveFallbackEmitter.sol
similarity index 100%
rename from contracts/src/v0.8/tests/ReceiveFallbackEmitter.sol
rename to contracts/src/v0.8/automation/testhelpers/ReceiveFallbackEmitter.sol
diff --git a/contracts/src/v0.8/tests/ReceiveReverter.sol b/contracts/src/v0.8/automation/testhelpers/ReceiveReverter.sol
similarity index 100%
rename from contracts/src/v0.8/tests/ReceiveReverter.sol
rename to contracts/src/v0.8/automation/testhelpers/ReceiveReverter.sol
diff --git a/contracts/src/v0.8/tests/StreamsLookupUpkeep.sol b/contracts/src/v0.8/automation/testhelpers/StreamsLookupUpkeep.sol
similarity index 95%
rename from contracts/src/v0.8/tests/StreamsLookupUpkeep.sol
rename to contracts/src/v0.8/automation/testhelpers/StreamsLookupUpkeep.sol
index dec93d5b1f7..aaf35b5c595 100644
--- a/contracts/src/v0.8/tests/StreamsLookupUpkeep.sol
+++ b/contracts/src/v0.8/automation/testhelpers/StreamsLookupUpkeep.sol
@@ -1,8 +1,8 @@
pragma solidity 0.8.16;
-import "../automation/interfaces/AutomationCompatibleInterface.sol";
-import "../automation/interfaces/StreamsLookupCompatibleInterface.sol";
-import {ArbSys} from "../vendor/@arbitrum/nitro-contracts/src/precompiles/ArbSys.sol";
+import "../interfaces/AutomationCompatibleInterface.sol";
+import "../interfaces/StreamsLookupCompatibleInterface.sol";
+import {ArbSys} from "../../vendor/@arbitrum/nitro-contracts/src/precompiles/ArbSys.sol";
interface IVerifierProxy {
/**
diff --git a/contracts/src/v0.8/tests/VerifiableLoadBase.sol b/contracts/src/v0.8/automation/testhelpers/VerifiableLoadBase.sol
similarity index 98%
rename from contracts/src/v0.8/tests/VerifiableLoadBase.sol
rename to contracts/src/v0.8/automation/testhelpers/VerifiableLoadBase.sol
index 86ebf8b8c7c..1aa181dd1d3 100644
--- a/contracts/src/v0.8/tests/VerifiableLoadBase.sol
+++ b/contracts/src/v0.8/automation/testhelpers/VerifiableLoadBase.sol
@@ -1,11 +1,11 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;
-import "../vendor/openzeppelin-solidity/v4.7.3/contracts/utils/structs/EnumerableSet.sol";
-import {IKeeperRegistryMaster, IAutomationV21PlusCommon} from "../automation/interfaces/v2_1/IKeeperRegistryMaster.sol";
-import {ArbSys} from "../vendor/@arbitrum/nitro-contracts/src/precompiles/ArbSys.sol";
-import "../automation/v2_1/AutomationRegistrar2_1.sol";
-import {LogTriggerConfig} from "../automation/v2_1/AutomationUtils2_1.sol";
+import "../../vendor/openzeppelin-solidity/v4.7.3/contracts/utils/structs/EnumerableSet.sol";
+import {IKeeperRegistryMaster, IAutomationV21PlusCommon} from "../interfaces/v2_1/IKeeperRegistryMaster.sol";
+import {ArbSys} from "../../vendor/@arbitrum/nitro-contracts/src/precompiles/ArbSys.sol";
+import "../v2_1/AutomationRegistrar2_1.sol";
+import {LogTriggerConfig} from "../v2_1/AutomationUtils2_1.sol";
abstract contract VerifiableLoadBase is ConfirmedOwner {
error IndexOutOfRange();
diff --git a/contracts/src/v0.8/tests/VerifiableLoadLogTriggerUpkeep.sol b/contracts/src/v0.8/automation/testhelpers/VerifiableLoadLogTriggerUpkeep.sol
similarity index 97%
rename from contracts/src/v0.8/tests/VerifiableLoadLogTriggerUpkeep.sol
rename to contracts/src/v0.8/automation/testhelpers/VerifiableLoadLogTriggerUpkeep.sol
index 39b95bb0ae5..400ddd0c966 100644
--- a/contracts/src/v0.8/tests/VerifiableLoadLogTriggerUpkeep.sol
+++ b/contracts/src/v0.8/automation/testhelpers/VerifiableLoadLogTriggerUpkeep.sol
@@ -2,8 +2,8 @@
pragma solidity 0.8.16;
import "./VerifiableLoadBase.sol";
-import "../automation/interfaces/ILogAutomation.sol";
-import "../automation/interfaces/StreamsLookupCompatibleInterface.sol";
+import "../interfaces/ILogAutomation.sol";
+import "../interfaces/StreamsLookupCompatibleInterface.sol";
contract VerifiableLoadLogTriggerUpkeep is VerifiableLoadBase, StreamsLookupCompatibleInterface, ILogAutomation {
bool public useMercury;
diff --git a/contracts/src/v0.8/tests/VerifiableLoadStreamsLookupUpkeep.sol b/contracts/src/v0.8/automation/testhelpers/VerifiableLoadStreamsLookupUpkeep.sol
similarity index 97%
rename from contracts/src/v0.8/tests/VerifiableLoadStreamsLookupUpkeep.sol
rename to contracts/src/v0.8/automation/testhelpers/VerifiableLoadStreamsLookupUpkeep.sol
index c74aec1a790..97be9ebc81a 100644
--- a/contracts/src/v0.8/tests/VerifiableLoadStreamsLookupUpkeep.sol
+++ b/contracts/src/v0.8/automation/testhelpers/VerifiableLoadStreamsLookupUpkeep.sol
@@ -2,7 +2,7 @@
pragma solidity 0.8.16;
import "./VerifiableLoadBase.sol";
-import "../automation/interfaces/StreamsLookupCompatibleInterface.sol";
+import "../interfaces/StreamsLookupCompatibleInterface.sol";
contract VerifiableLoadStreamsLookupUpkeep is VerifiableLoadBase, StreamsLookupCompatibleInterface {
constructor(AutomationRegistrar2_1 _registrar, bool _useArb) VerifiableLoadBase(_registrar, _useArb) {}
diff --git a/contracts/src/v0.8/tests/VerifiableLoadUpkeep.sol b/contracts/src/v0.8/automation/testhelpers/VerifiableLoadUpkeep.sol
similarity index 100%
rename from contracts/src/v0.8/tests/VerifiableLoadUpkeep.sol
rename to contracts/src/v0.8/automation/testhelpers/VerifiableLoadUpkeep.sol
diff --git a/contracts/src/v0.8/automation/v1_2/KeeperRegistrar1_2.sol b/contracts/src/v0.8/automation/v1_2/KeeperRegistrar1_2.sol
index f455d56f17a..d2b6e560487 100644
--- a/contracts/src/v0.8/automation/v1_2/KeeperRegistrar1_2.sol
+++ b/contracts/src/v0.8/automation/v1_2/KeeperRegistrar1_2.sol
@@ -2,7 +2,7 @@
pragma solidity 0.8.6;
import "../interfaces/v1_2/KeeperRegistryInterface1_2.sol";
-import "../../interfaces/TypeAndVersionInterface.sol";
+import "../../shared/interfaces/ITypeAndVersion.sol";
import "../../shared/interfaces/LinkTokenInterface.sol";
import "../../shared/access/ConfirmedOwner.sol";
import "../../shared/interfaces/IERC677Receiver.sol";
@@ -17,7 +17,7 @@ import "../../shared/interfaces/IERC677Receiver.sol";
* The idea is to have same interface(functions,events) for UI or anyone using this contract irrespective of auto approve being enabled or not.
* they can just listen to `RegistrationRequested` & `RegistrationApproved` events and know the status on registrations.
*/
-contract KeeperRegistrar is TypeAndVersionInterface, ConfirmedOwner, IERC677Receiver {
+contract KeeperRegistrar is ITypeAndVersion, ConfirmedOwner, IERC677Receiver {
/**
* DISABLED: No auto approvals, all new upkeeps should be approved manually.
* ENABLED_SENDER_ALLOWLIST: Auto approvals for allowed senders subject to max allowed. Manual for rest.
diff --git a/contracts/src/v0.8/automation/v1_2/KeeperRegistry1_2.sol b/contracts/src/v0.8/automation/v1_2/KeeperRegistry1_2.sol
index 2fa1ee6188b..5e1c8dacd48 100644
--- a/contracts/src/v0.8/automation/v1_2/KeeperRegistry1_2.sol
+++ b/contracts/src/v0.8/automation/v1_2/KeeperRegistry1_2.sol
@@ -6,7 +6,7 @@ import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "../KeeperBase.sol";
-import "../../interfaces/TypeAndVersionInterface.sol";
+import "../../shared/interfaces/ITypeAndVersion.sol";
import "../../shared/interfaces/AggregatorV3Interface.sol";
import "../interfaces/KeeperCompatibleInterface.sol";
import "../interfaces/v1_2/KeeperRegistryInterface1_2.sol";
@@ -31,7 +31,7 @@ struct Upkeep {
* contracts. Clients must support the Upkeep interface.
*/
contract KeeperRegistry1_2 is
- TypeAndVersionInterface,
+ ITypeAndVersion,
ConfirmedOwner,
KeeperBase,
ReentrancyGuard,
diff --git a/contracts/src/v0.8/automation/v1_3/KeeperRegistry1_3.sol b/contracts/src/v0.8/automation/v1_3/KeeperRegistry1_3.sol
index dbef8d77d19..2d56443822b 100644
--- a/contracts/src/v0.8/automation/v1_3/KeeperRegistry1_3.sol
+++ b/contracts/src/v0.8/automation/v1_3/KeeperRegistry1_3.sol
@@ -8,7 +8,7 @@ import "./KeeperRegistryBase1_3.sol";
import "./KeeperRegistryLogic1_3.sol";
import {AutomationRegistryExecutableInterface, State} from "../interfaces/v1_3/AutomationRegistryInterface1_3.sol";
import "../interfaces/MigratableKeeperRegistryInterface.sol";
-import "../../interfaces/TypeAndVersionInterface.sol";
+import "../../shared/interfaces/ITypeAndVersion.sol";
import "../../shared/interfaces/IERC677Receiver.sol";
/**
@@ -18,7 +18,7 @@ import "../../shared/interfaces/IERC677Receiver.sol";
contract KeeperRegistry1_3 is
KeeperRegistryBase1_3,
Proxy,
- TypeAndVersionInterface,
+ ITypeAndVersion,
AutomationRegistryExecutableInterface,
MigratableKeeperRegistryInterface,
IERC677Receiver
diff --git a/contracts/src/v0.8/automation/v2_0/KeeperRegistrar2_0.sol b/contracts/src/v0.8/automation/v2_0/KeeperRegistrar2_0.sol
index c1b7e45b859..78cc06a8b20 100644
--- a/contracts/src/v0.8/automation/v2_0/KeeperRegistrar2_0.sol
+++ b/contracts/src/v0.8/automation/v2_0/KeeperRegistrar2_0.sol
@@ -3,7 +3,7 @@ pragma solidity 0.8.6;
import "../../shared/interfaces/LinkTokenInterface.sol";
import "../interfaces/v2_0/AutomationRegistryInterface2_0.sol";
-import "../../interfaces/TypeAndVersionInterface.sol";
+import "../../shared/interfaces/ITypeAndVersion.sol";
import "../../shared/access/ConfirmedOwner.sol";
import "../../shared/interfaces/IERC677Receiver.sol";
@@ -17,7 +17,7 @@ import "../../shared/interfaces/IERC677Receiver.sol";
* The idea is to have same interface(functions,events) for UI or anyone using this contract irrespective of auto approve being enabled or not.
* they can just listen to `RegistrationRequested` & `RegistrationApproved` events and know the status on registrations.
*/
-contract KeeperRegistrar2_0 is TypeAndVersionInterface, ConfirmedOwner, IERC677Receiver {
+contract KeeperRegistrar2_0 is ITypeAndVersion, ConfirmedOwner, IERC677Receiver {
/**
* DISABLED: No auto approvals, all new upkeeps should be approved manually.
* ENABLED_SENDER_ALLOWLIST: Auto approvals for allowed senders subject to max allowed. Manual for rest.
diff --git a/contracts/src/v0.8/automation/v2_0/UpkeepTranscoder3_0.sol b/contracts/src/v0.8/automation/v2_0/UpkeepTranscoder3_0.sol
index 0a56f209cc8..df8368de691 100644
--- a/contracts/src/v0.8/automation/v2_0/UpkeepTranscoder3_0.sol
+++ b/contracts/src/v0.8/automation/v2_0/UpkeepTranscoder3_0.sol
@@ -3,7 +3,7 @@
pragma solidity 0.8.6;
import "../../automation/interfaces/UpkeepTranscoderInterface.sol";
-import "../../interfaces/TypeAndVersionInterface.sol";
+import "../../shared/interfaces/ITypeAndVersion.sol";
import {Upkeep as UpkeepV1} from "../../automation/v1_2/KeeperRegistry1_2.sol";
import {Upkeep as UpkeepV2} from "../../automation/v1_3/KeeperRegistryBase1_3.sol";
import {Upkeep as UpkeepV3} from "../../automation/v2_0/KeeperRegistryBase2_0.sol";
@@ -13,7 +13,7 @@ import "../../automation/UpkeepFormat.sol";
* @notice UpkeepTranscoder 3_0 allows converting upkeep data from previous keeper registry versions 1.2 and 1.3 to
* registry 2.0
*/
-contract UpkeepTranscoder3_0 is UpkeepTranscoderInterface, TypeAndVersionInterface {
+contract UpkeepTranscoder3_0 is UpkeepTranscoderInterface, ITypeAndVersion {
error InvalidTranscoding();
/**
diff --git a/contracts/src/v0.8/automation/v2_1/AutomationRegistrar2_1.sol b/contracts/src/v0.8/automation/v2_1/AutomationRegistrar2_1.sol
index 407dda2414e..503f16bbe4c 100644
--- a/contracts/src/v0.8/automation/v2_1/AutomationRegistrar2_1.sol
+++ b/contracts/src/v0.8/automation/v2_1/AutomationRegistrar2_1.sol
@@ -3,7 +3,7 @@ pragma solidity 0.8.16;
import {LinkTokenInterface} from "../../shared/interfaces/LinkTokenInterface.sol";
import {IKeeperRegistryMaster} from "../interfaces/v2_1/IKeeperRegistryMaster.sol";
-import {TypeAndVersionInterface} from "../../interfaces/TypeAndVersionInterface.sol";
+import {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol";
import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol";
import {IERC677Receiver} from "../../shared/interfaces/IERC677Receiver.sol";
@@ -17,7 +17,7 @@ import {IERC677Receiver} from "../../shared/interfaces/IERC677Receiver.sol";
* The idea is to have same interface(functions,events) for UI or anyone using this contract irrespective of auto approve being enabled or not.
* they can just listen to `RegistrationRequested` & `RegistrationApproved` events and know the status on registrations.
*/
-contract AutomationRegistrar2_1 is TypeAndVersionInterface, ConfirmedOwner, IERC677Receiver {
+contract AutomationRegistrar2_1 is ITypeAndVersion, ConfirmedOwner, IERC677Receiver {
/**
* DISABLED: No auto approvals, all new upkeeps should be approved manually.
* ENABLED_SENDER_ALLOWLIST: Auto approvals for allowed senders subject to max allowed. Manual for rest.
diff --git a/contracts/src/v0.8/automation/v2_1/UpkeepTranscoder4_0.sol b/contracts/src/v0.8/automation/v2_1/UpkeepTranscoder4_0.sol
index 53b681d4cc1..41f50de0932 100644
--- a/contracts/src/v0.8/automation/v2_1/UpkeepTranscoder4_0.sol
+++ b/contracts/src/v0.8/automation/v2_1/UpkeepTranscoder4_0.sol
@@ -3,7 +3,7 @@
pragma solidity 0.8.16;
import {UpkeepTranscoderInterfaceV2} from "../interfaces/UpkeepTranscoderInterfaceV2.sol";
-import {TypeAndVersionInterface} from "../../interfaces/TypeAndVersionInterface.sol";
+import {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol";
import {KeeperRegistryBase2_1 as R21} from "./KeeperRegistryBase2_1.sol";
import {IAutomationForwarder} from "../interfaces/IAutomationForwarder.sol";
@@ -52,7 +52,7 @@ struct UpkeepV20 {
* @notice UpkeepTranscoder allows converting upkeep data from previous keeper registry versions 1.2, 1.3, and
* 2.0 to registry 2.1
*/
-contract UpkeepTranscoder4_0 is UpkeepTranscoderInterfaceV2, TypeAndVersionInterface {
+contract UpkeepTranscoder4_0 is UpkeepTranscoderInterfaceV2, ITypeAndVersion {
error InvalidTranscoding();
/**
diff --git a/contracts/src/v0.8/automation/v2_3/AutomationRegistrar2_3.sol b/contracts/src/v0.8/automation/v2_3/AutomationRegistrar2_3.sol
index 2effb8d4d2f..251611cfb04 100644
--- a/contracts/src/v0.8/automation/v2_3/AutomationRegistrar2_3.sol
+++ b/contracts/src/v0.8/automation/v2_3/AutomationRegistrar2_3.sol
@@ -3,7 +3,7 @@ pragma solidity 0.8.19;
import {LinkTokenInterface} from "../../shared/interfaces/LinkTokenInterface.sol";
import {IAutomationRegistryMaster2_3} from "../interfaces/v2_3/IAutomationRegistryMaster2_3.sol";
-import {TypeAndVersionInterface} from "../../interfaces/TypeAndVersionInterface.sol";
+import {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol";
import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol";
import {IERC677Receiver} from "../../shared/interfaces/IERC677Receiver.sol";
import {IERC20Metadata as IERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/extensions/IERC20Metadata.sol";
@@ -21,7 +21,7 @@ import {SafeERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/tok
* The idea is to have same interface(functions,events) for UI or anyone using this contract irrespective of auto approve being enabled or not.
* they can just listen to `RegistrationRequested` & `RegistrationApproved` events and know the status on registrations.
*/
-contract AutomationRegistrar2_3 is TypeAndVersionInterface, ConfirmedOwner, IERC677Receiver {
+contract AutomationRegistrar2_3 is ITypeAndVersion, ConfirmedOwner, IERC677Receiver {
using SafeERC20 for IERC20;
/**
diff --git a/contracts/src/v0.8/automation/v2_3/UpkeepTranscoder5_0.sol b/contracts/src/v0.8/automation/v2_3/UpkeepTranscoder5_0.sol
index 32530c71257..e0312588ed9 100644
--- a/contracts/src/v0.8/automation/v2_3/UpkeepTranscoder5_0.sol
+++ b/contracts/src/v0.8/automation/v2_3/UpkeepTranscoder5_0.sol
@@ -3,7 +3,7 @@
pragma solidity 0.8.19;
import {UpkeepTranscoderInterfaceV2} from "../interfaces/UpkeepTranscoderInterfaceV2.sol";
-import {TypeAndVersionInterface} from "../../interfaces/TypeAndVersionInterface.sol";
+import {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol";
enum RegistryVersion {
V12,
@@ -17,7 +17,7 @@ enum RegistryVersion {
* @notice UpkeepTranscoder is a contract that allows converting upkeep data from previous registry versions to newer versions
* @dev it currently only supports 2.3 -> 2.3 migrations
*/
-contract UpkeepTranscoder5_0 is UpkeepTranscoderInterfaceV2, TypeAndVersionInterface {
+contract UpkeepTranscoder5_0 is UpkeepTranscoderInterfaceV2, ITypeAndVersion {
error InvalidTranscoding();
string public constant override typeAndVersion = "UpkeepTranscoder 5.0.0";
diff --git a/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.getTokenPrice.t.sol b/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.getTokenPrice.t.sol
index a06e4cbebf8..a0f0a1076d8 100644
--- a/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.getTokenPrice.t.sol
+++ b/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.getTokenPrice.t.sol
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
-import {MockV3Aggregator} from "../../../tests/MockV3Aggregator.sol";
+import {MockV3Aggregator} from "../../../shared/mocks/MockV3Aggregator.sol";
import {FeeQuoter} from "../../FeeQuoter.sol";
import {Internal} from "../../libraries/Internal.sol";
import {FeeQuoterSetup} from "./FeeQuoterSetup.t.sol";
diff --git a/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.getValidatedTokenPrice.t.sol b/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.getValidatedTokenPrice.t.sol
index d43cc5a6799..b5603a61306 100644
--- a/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.getValidatedTokenPrice.t.sol
+++ b/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.getValidatedTokenPrice.t.sol
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
-import {MockV3Aggregator} from "../../../tests/MockV3Aggregator.sol";
+import {MockV3Aggregator} from "../../../shared/mocks/MockV3Aggregator.sol";
import {FeeQuoter} from "../../FeeQuoter.sol";
import {Internal} from "../../libraries/Internal.sol";
import {FeeQuoterSetup} from "./FeeQuoterSetup.t.sol";
diff --git a/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoterSetup.t.sol b/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoterSetup.t.sol
index 7864d4080a2..e001ccd47cf 100644
--- a/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoterSetup.t.sol
+++ b/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoterSetup.t.sol
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
-import {MockV3Aggregator} from "../../../tests/MockV3Aggregator.sol";
+import {MockV3Aggregator} from "../../../shared/mocks/MockV3Aggregator.sol";
import {FeeQuoter} from "../../FeeQuoter.sol";
import {Client} from "../../libraries/Client.sol";
import {Internal} from "../../libraries/Internal.sol";
diff --git a/contracts/src/v0.8/functions/tests/v1_X/Setup.t.sol b/contracts/src/v0.8/functions/tests/v1_X/Setup.t.sol
index 444fc18fe81..f0069231e87 100644
--- a/contracts/src/v0.8/functions/tests/v1_X/Setup.t.sol
+++ b/contracts/src/v0.8/functions/tests/v1_X/Setup.t.sol
@@ -7,10 +7,10 @@ import {FunctionsRouterHarness, FunctionsRouter} from "./testhelpers/FunctionsRo
import {FunctionsCoordinatorHarness} from "./testhelpers/FunctionsCoordinatorHarness.sol";
import {FunctionsBilling} from "../../dev/v1_X/FunctionsBilling.sol";
import {FunctionsResponse} from "../../dev/v1_X/libraries/FunctionsResponse.sol";
-import {MockV3Aggregator} from "../../../tests/MockV3Aggregator.sol";
+import {MockV3Aggregator} from "../../../shared/mocks/MockV3Aggregator.sol";
import {TermsOfServiceAllowList} from "../../dev/v1_X/accessControl/TermsOfServiceAllowList.sol";
import {TermsOfServiceAllowListConfig} from "../../dev/v1_X/accessControl/interfaces/ITermsOfServiceAllowList.sol";
-import {MockLinkToken} from "../../../mocks/MockLinkToken.sol";
+import {MockLinkToken} from "./testhelpers/MockLinkToken.sol";
import {FunctionsBillingConfig} from "../../dev/v1_X/interfaces/IFunctionsBilling.sol";
import "forge-std/Vm.sol";
diff --git a/contracts/src/v0.8/mocks/MockLinkToken.sol b/contracts/src/v0.8/functions/tests/v1_X/testhelpers/MockLinkToken.sol
similarity index 94%
rename from contracts/src/v0.8/mocks/MockLinkToken.sol
rename to contracts/src/v0.8/functions/tests/v1_X/testhelpers/MockLinkToken.sol
index a68f1b1d341..37ab5f50d56 100644
--- a/contracts/src/v0.8/mocks/MockLinkToken.sol
+++ b/contracts/src/v0.8/functions/tests/v1_X/testhelpers/MockLinkToken.sol
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
-import {IERC677Receiver} from "../shared/interfaces/IERC677Receiver.sol";
+import {IERC677Receiver} from "../../../../shared/interfaces/IERC677Receiver.sol";
contract MockLinkToken {
uint256 private constant TOTAL_SUPPLY = 1_000_000_000 * 1e18;
diff --git a/contracts/src/v0.8/interfaces/FeedRegistryInterface.sol b/contracts/src/v0.8/interfaces/FeedRegistryInterface.sol
deleted file mode 100644
index 6e353a79263..00000000000
--- a/contracts/src/v0.8/interfaces/FeedRegistryInterface.sol
+++ /dev/null
@@ -1,124 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.0;
-pragma abicoder v2;
-
-import {AggregatorV2V3Interface} from "../shared/interfaces/AggregatorV2V3Interface.sol";
-
-// solhint-disable-next-line interface-starts-with-i
-interface FeedRegistryInterface {
- struct Phase {
- uint16 phaseId;
- uint80 startingAggregatorRoundId;
- uint80 endingAggregatorRoundId;
- }
-
- event FeedProposed(
- address indexed asset,
- address indexed denomination,
- address indexed proposedAggregator,
- address currentAggregator,
- address sender
- );
- event FeedConfirmed(
- address indexed asset,
- address indexed denomination,
- address indexed latestAggregator,
- address previousAggregator,
- uint16 nextPhaseId,
- address sender
- );
-
- // V3 AggregatorV3Interface
-
- function decimals(address base, address quote) external view returns (uint8);
-
- function description(address base, address quote) external view returns (string memory);
-
- function version(address base, address quote) external view returns (uint256);
-
- function latestRoundData(
- address base,
- address quote
- ) external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
-
- function getRoundData(
- address base,
- address quote,
- uint80 _roundId
- ) external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
-
- // V2 AggregatorInterface
-
- function latestAnswer(address base, address quote) external view returns (int256 answer);
-
- function latestTimestamp(address base, address quote) external view returns (uint256 timestamp);
-
- function latestRound(address base, address quote) external view returns (uint256 roundId);
-
- function getAnswer(address base, address quote, uint256 roundId) external view returns (int256 answer);
-
- function getTimestamp(address base, address quote, uint256 roundId) external view returns (uint256 timestamp);
-
- // Registry getters
-
- function getFeed(address base, address quote) external view returns (AggregatorV2V3Interface aggregator);
-
- function getPhaseFeed(
- address base,
- address quote,
- uint16 phaseId
- ) external view returns (AggregatorV2V3Interface aggregator);
-
- function isFeedEnabled(address aggregator) external view returns (bool);
-
- function getPhase(address base, address quote, uint16 phaseId) external view returns (Phase memory phase);
-
- // Round helpers
-
- function getRoundFeed(
- address base,
- address quote,
- uint80 roundId
- ) external view returns (AggregatorV2V3Interface aggregator);
-
- function getPhaseRange(
- address base,
- address quote,
- uint16 phaseId
- ) external view returns (uint80 startingRoundId, uint80 endingRoundId);
-
- function getPreviousRoundId(
- address base,
- address quote,
- uint80 roundId
- ) external view returns (uint80 previousRoundId);
-
- function getNextRoundId(address base, address quote, uint80 roundId) external view returns (uint80 nextRoundId);
-
- // Feed management
-
- function proposeFeed(address base, address quote, address aggregator) external;
-
- function confirmFeed(address base, address quote, address aggregator) external;
-
- // Proposed aggregator
-
- function getProposedFeed(
- address base,
- address quote
- ) external view returns (AggregatorV2V3Interface proposedAggregator);
-
- function proposedGetRoundData(
- address base,
- address quote,
- uint80 roundId
- ) external view returns (uint80 id, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
-
- function proposedLatestRoundData(
- address base,
- address quote
- ) external view returns (uint80 id, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
-
- // Phases
- function getCurrentPhaseId(address base, address quote) external view returns (uint16 currentPhaseId);
-}
diff --git a/contracts/src/v0.8/interfaces/FlagsInterface.sol b/contracts/src/v0.8/interfaces/FlagsInterface.sol
deleted file mode 100644
index beb2b581e3f..00000000000
--- a/contracts/src/v0.8/interfaces/FlagsInterface.sol
+++ /dev/null
@@ -1,17 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.0;
-
-// solhint-disable-next-line interface-starts-with-i
-interface FlagsInterface {
- function getFlag(address) external view returns (bool);
-
- function getFlags(address[] calldata) external view returns (bool[] memory);
-
- function raiseFlag(address) external;
-
- function raiseFlags(address[] calldata) external;
-
- function lowerFlags(address[] calldata) external;
-
- function setRaisingAccessController(address) external;
-}
diff --git a/contracts/src/v0.8/interfaces/PoRAddressList.sol b/contracts/src/v0.8/interfaces/PoRAddressList.sol
deleted file mode 100644
index af06e29a456..00000000000
--- a/contracts/src/v0.8/interfaces/PoRAddressList.sol
+++ /dev/null
@@ -1,29 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.0;
-
-/**
- * @title Chainlink Proof-of-Reserve address list interface.
- * @notice This interface enables Chainlink nodes to get the list addresses to be used in a PoR feed. A single
- * contract that implements this interface can only store an address list for a single PoR feed.
- * @dev All functions in this interface are expected to be called off-chain, so gas usage is not a big concern.
- * This makes it possible to store addresses in optimized data types and convert them to human-readable strings
- * in `getPoRAddressList()`.
- */
-// solhint-disable-next-line interface-starts-with-i
-interface PoRAddressList {
- /// @notice Get total number of addresses in the list.
- function getPoRAddressListLength() external view returns (uint256);
-
- /**
- * @notice Get a batch of human-readable addresses from the address list. The requested batch size can be greater
- * than the actual address list size, in which the full address list will be returned.
- * @dev Due to limitations of gas usage in off-chain calls, we need to support fetching the addresses in batches.
- * EVM addresses need to be converted to human-readable strings. The address strings need to be in the same format
- * that would be used when querying the balance of that address.
- * @param startIndex The index of the first address in the batch.
- * @param endIndex The index of the last address in the batch. If `endIndex > getPoRAddressListLength()-1`,
- * endIndex need to default to `getPoRAddressListLength()-1`.
- * @return Array of addresses as strings.
- */
- function getPoRAddressList(uint256 startIndex, uint256 endIndex) external view returns (string[] memory);
-}
diff --git a/contracts/src/v0.8/interfaces/TypeAndVersionInterface.sol b/contracts/src/v0.8/interfaces/TypeAndVersionInterface.sol
deleted file mode 100644
index 786f2750acf..00000000000
--- a/contracts/src/v0.8/interfaces/TypeAndVersionInterface.sol
+++ /dev/null
@@ -1,6 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.0;
-
-abstract contract TypeAndVersionInterface {
- function typeAndVersion() external pure virtual returns (string memory);
-}
diff --git a/contracts/src/v0.8/tests/FeedConsumer.sol b/contracts/src/v0.8/l2ep/test/FeedConsumer.sol
similarity index 92%
rename from contracts/src/v0.8/tests/FeedConsumer.sol
rename to contracts/src/v0.8/l2ep/test/FeedConsumer.sol
index c9fc62357a6..f83781b5ac1 100644
--- a/contracts/src/v0.8/tests/FeedConsumer.sol
+++ b/contracts/src/v0.8/l2ep/test/FeedConsumer.sol
@@ -1,9 +1,10 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
-import {AggregatorV2V3Interface} from "../shared/interfaces/AggregatorV2V3Interface.sol";
+import {AggregatorV2V3Interface} from "../../shared/interfaces/AggregatorV2V3Interface.sol";
contract FeedConsumer {
+ // solhint-disable-next-line
AggregatorV2V3Interface public immutable AGGREGATOR;
constructor(address feedAddress) {
diff --git a/contracts/src/v0.8/tests/Greeter.sol b/contracts/src/v0.8/l2ep/test/Greeter.sol
similarity index 82%
rename from contracts/src/v0.8/tests/Greeter.sol
rename to contracts/src/v0.8/l2ep/test/Greeter.sol
index 88ccca560de..313c7c5e3b0 100644
--- a/contracts/src/v0.8/tests/Greeter.sol
+++ b/contracts/src/v0.8/l2ep/test/Greeter.sol
@@ -1,7 +1,8 @@
pragma solidity ^0.8.0;
-import "../shared/access/ConfirmedOwner.sol";
+import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol";
+// solhint-disable
contract Greeter is ConfirmedOwner {
string public greeting;
diff --git a/contracts/src/v0.8/tests/MockArbitrumInbox.sol b/contracts/src/v0.8/l2ep/test/mocks/MockArbitrumInbox.sol
similarity index 94%
rename from contracts/src/v0.8/tests/MockArbitrumInbox.sol
rename to contracts/src/v0.8/l2ep/test/mocks/MockArbitrumInbox.sol
index 445a361b309..3ec76338b8c 100644
--- a/contracts/src/v0.8/tests/MockArbitrumInbox.sol
+++ b/contracts/src/v0.8/l2ep/test/mocks/MockArbitrumInbox.sol
@@ -1,8 +1,8 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
-import {IInbox} from "../vendor/arb-bridge-eth/v0.8.0-custom/contracts/bridge/interfaces/IInbox.sol";
-import {IBridge} from "../vendor/arb-bridge-eth/v0.8.0-custom/contracts/bridge/interfaces/IBridge.sol";
+import {IInbox} from "../../../vendor/arb-bridge-eth/v0.8.0-custom/contracts/bridge/interfaces/IInbox.sol";
+import {IBridge} from "../../../vendor/arb-bridge-eth/v0.8.0-custom/contracts/bridge/interfaces/IBridge.sol";
contract MockArbitrumInbox is IInbox {
event RetryableTicketNoRefundAliasRewriteCreated(
diff --git a/contracts/src/v0.8/tests/MockOptimismL1CrossDomainMessenger.sol b/contracts/src/v0.8/l2ep/test/mocks/MockOptimismL1CrossDomainMessenger.sol
similarity index 100%
rename from contracts/src/v0.8/tests/MockOptimismL1CrossDomainMessenger.sol
rename to contracts/src/v0.8/l2ep/test/mocks/MockOptimismL1CrossDomainMessenger.sol
diff --git a/contracts/src/v0.8/tests/MockOptimismL2CrossDomainMessenger.sol b/contracts/src/v0.8/l2ep/test/mocks/MockOptimismL2CrossDomainMessenger.sol
similarity index 100%
rename from contracts/src/v0.8/tests/MockOptimismL2CrossDomainMessenger.sol
rename to contracts/src/v0.8/l2ep/test/mocks/MockOptimismL2CrossDomainMessenger.sol
diff --git a/contracts/src/v0.8/l2ep/test/v1_0_0/L2EPTest.t.sol b/contracts/src/v0.8/l2ep/test/v1_0_0/L2EPTest.t.sol
index 93640f4bcb4..0bd377a7cbf 100644
--- a/contracts/src/v0.8/l2ep/test/v1_0_0/L2EPTest.t.sol
+++ b/contracts/src/v0.8/l2ep/test/v1_0_0/L2EPTest.t.sol
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
-import {Greeter} from "../../../tests/Greeter.sol";
+import {Greeter} from "../Greeter.sol";
import {MultiSend} from "../../../vendor/MultiSend.sol";
import {Test} from "forge-std/Test.sol";
diff --git a/contracts/src/v0.8/l2ep/test/v1_0_0/arbitrum/ArbitrumCrossDomainForwarder.t.sol b/contracts/src/v0.8/l2ep/test/v1_0_0/arbitrum/ArbitrumCrossDomainForwarder.t.sol
index e0a76a2b37a..62f7cd5651e 100644
--- a/contracts/src/v0.8/l2ep/test/v1_0_0/arbitrum/ArbitrumCrossDomainForwarder.t.sol
+++ b/contracts/src/v0.8/l2ep/test/v1_0_0/arbitrum/ArbitrumCrossDomainForwarder.t.sol
@@ -2,7 +2,7 @@
pragma solidity 0.8.24;
import {ArbitrumCrossDomainForwarder} from "../../../arbitrum/ArbitrumCrossDomainForwarder.sol";
-import {Greeter} from "../../../../tests/Greeter.sol";
+import {Greeter} from "../../Greeter.sol";
import {L2EPTest} from "../L2EPTest.t.sol";
contract ArbitrumCrossDomainForwarderTest is L2EPTest {
diff --git a/contracts/src/v0.8/l2ep/test/v1_0_0/arbitrum/ArbitrumCrossDomainGovernor.t.sol b/contracts/src/v0.8/l2ep/test/v1_0_0/arbitrum/ArbitrumCrossDomainGovernor.t.sol
index 746da3d1cef..45f67d52ccd 100644
--- a/contracts/src/v0.8/l2ep/test/v1_0_0/arbitrum/ArbitrumCrossDomainGovernor.t.sol
+++ b/contracts/src/v0.8/l2ep/test/v1_0_0/arbitrum/ArbitrumCrossDomainGovernor.t.sol
@@ -2,7 +2,7 @@
pragma solidity 0.8.24;
import {ArbitrumCrossDomainGovernor} from "../../../arbitrum/ArbitrumCrossDomainGovernor.sol";
-import {Greeter} from "../../../../tests/Greeter.sol";
+import {Greeter} from "../../Greeter.sol";
import {L2EPTest} from "../L2EPTest.t.sol";
import {MultiSend} from "../../../../vendor/MultiSend.sol";
diff --git a/contracts/src/v0.8/l2ep/test/v1_0_0/arbitrum/ArbitrumSequencerUptimeFeed.t.sol b/contracts/src/v0.8/l2ep/test/v1_0_0/arbitrum/ArbitrumSequencerUptimeFeed.t.sol
index deaa81977b7..1474b680ec2 100644
--- a/contracts/src/v0.8/l2ep/test/v1_0_0/arbitrum/ArbitrumSequencerUptimeFeed.t.sol
+++ b/contracts/src/v0.8/l2ep/test/v1_0_0/arbitrum/ArbitrumSequencerUptimeFeed.t.sol
@@ -4,7 +4,7 @@ pragma solidity 0.8.24;
import {SimpleWriteAccessController} from "../../../../shared/access/SimpleWriteAccessController.sol";
import {ArbitrumSequencerUptimeFeed} from "../../../arbitrum/ArbitrumSequencerUptimeFeed.sol";
import {MockAggregatorV2V3} from "../../mocks/MockAggregatorV2V3.sol";
-import {FeedConsumer} from "../../../../tests/FeedConsumer.sol";
+import {FeedConsumer} from "../../FeedConsumer.sol";
import {Flags} from "../../../Flags.sol";
import {L2EPTest} from "../L2EPTest.t.sol";
diff --git a/contracts/src/v0.8/l2ep/test/v1_0_0/arbitrum/ArbitrumValidator.t.sol b/contracts/src/v0.8/l2ep/test/v1_0_0/arbitrum/ArbitrumValidator.t.sol
index 95278e644b1..7497ae198e2 100644
--- a/contracts/src/v0.8/l2ep/test/v1_0_0/arbitrum/ArbitrumValidator.t.sol
+++ b/contracts/src/v0.8/l2ep/test/v1_0_0/arbitrum/ArbitrumValidator.t.sol
@@ -6,7 +6,7 @@ import {AccessControllerInterface} from "../../../../shared/interfaces/AccessCon
import {SimpleWriteAccessController} from "../../../../shared/access/SimpleWriteAccessController.sol";
import {ArbitrumSequencerUptimeFeed} from "../../../arbitrum/ArbitrumSequencerUptimeFeed.sol";
import {ArbitrumValidator} from "../../../arbitrum/ArbitrumValidator.sol";
-import {MockArbitrumInbox} from "../../../../tests/MockArbitrumInbox.sol";
+import {MockArbitrumInbox} from "../../mocks/MockArbitrumInbox.sol";
import {MockAggregatorV2V3} from "../../mocks/MockAggregatorV2V3.sol";
import {L2EPTest} from "../L2EPTest.t.sol";
diff --git a/contracts/src/v0.8/l2ep/test/v1_0_0/optimism/OptimismCrossDomainForwarder.t.sol b/contracts/src/v0.8/l2ep/test/v1_0_0/optimism/OptimismCrossDomainForwarder.t.sol
index 28d70fa35a5..5562b413e3b 100644
--- a/contracts/src/v0.8/l2ep/test/v1_0_0/optimism/OptimismCrossDomainForwarder.t.sol
+++ b/contracts/src/v0.8/l2ep/test/v1_0_0/optimism/OptimismCrossDomainForwarder.t.sol
@@ -3,7 +3,7 @@ pragma solidity 0.8.24;
import {OptimismCrossDomainForwarder} from "../../../optimism/OptimismCrossDomainForwarder.sol";
import {MockOVMCrossDomainMessenger} from "../../mocks/optimism/MockOVMCrossDomainMessenger.sol";
-import {Greeter} from "../../../../tests/Greeter.sol";
+import {Greeter} from "../../Greeter.sol";
import {L2EPTest} from "../L2EPTest.t.sol";
contract OptimismCrossDomainForwarderTest is L2EPTest {
diff --git a/contracts/src/v0.8/l2ep/test/v1_0_0/optimism/OptimismCrossDomainGovernor.t.sol b/contracts/src/v0.8/l2ep/test/v1_0_0/optimism/OptimismCrossDomainGovernor.t.sol
index 57f79124512..3328a89b89d 100644
--- a/contracts/src/v0.8/l2ep/test/v1_0_0/optimism/OptimismCrossDomainGovernor.t.sol
+++ b/contracts/src/v0.8/l2ep/test/v1_0_0/optimism/OptimismCrossDomainGovernor.t.sol
@@ -3,7 +3,7 @@ pragma solidity 0.8.24;
import {OptimismCrossDomainGovernor} from "../../../optimism/OptimismCrossDomainGovernor.sol";
import {MockOVMCrossDomainMessenger} from "../../mocks/optimism/MockOVMCrossDomainMessenger.sol";
-import {Greeter} from "../../../../tests/Greeter.sol";
+import {Greeter} from "../../Greeter.sol";
import {L2EPTest} from "../L2EPTest.t.sol";
import {MultiSend} from "../../../../vendor/MultiSend.sol";
diff --git a/contracts/src/v0.8/l2ep/test/v1_0_0/optimism/OptimismSequencerUptimeFeed.t.sol b/contracts/src/v0.8/l2ep/test/v1_0_0/optimism/OptimismSequencerUptimeFeed.t.sol
index 34010c313e8..393da70d79a 100644
--- a/contracts/src/v0.8/l2ep/test/v1_0_0/optimism/OptimismSequencerUptimeFeed.t.sol
+++ b/contracts/src/v0.8/l2ep/test/v1_0_0/optimism/OptimismSequencerUptimeFeed.t.sol
@@ -1,8 +1,8 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
-import {MockOptimismL1CrossDomainMessenger} from "../../../../tests/MockOptimismL1CrossDomainMessenger.sol";
-import {MockOptimismL2CrossDomainMessenger} from "../../../../tests/MockOptimismL2CrossDomainMessenger.sol";
+import {MockOptimismL1CrossDomainMessenger} from "../../mocks/MockOptimismL1CrossDomainMessenger.sol";
+import {MockOptimismL2CrossDomainMessenger} from "../../mocks/MockOptimismL2CrossDomainMessenger.sol";
import {OptimismSequencerUptimeFeed} from "../../../optimism/OptimismSequencerUptimeFeed.sol";
import {BaseSequencerUptimeFeed} from "../../../base/BaseSequencerUptimeFeed.sol";
import {L2EPTest} from "../L2EPTest.t.sol";
diff --git a/contracts/src/v0.8/l2ep/test/v1_0_0/optimism/OptimismValidator.t.sol b/contracts/src/v0.8/l2ep/test/v1_0_0/optimism/OptimismValidator.t.sol
index 48ff1f7778d..6fb00e708c5 100644
--- a/contracts/src/v0.8/l2ep/test/v1_0_0/optimism/OptimismValidator.t.sol
+++ b/contracts/src/v0.8/l2ep/test/v1_0_0/optimism/OptimismValidator.t.sol
@@ -3,8 +3,8 @@ pragma solidity 0.8.24;
import {ISequencerUptimeFeed} from "../../../interfaces/ISequencerUptimeFeed.sol";
-import {MockOptimismL1CrossDomainMessenger} from "../../../../tests/MockOptimismL1CrossDomainMessenger.sol";
-import {MockOptimismL2CrossDomainMessenger} from "../../../../tests/MockOptimismL2CrossDomainMessenger.sol";
+import {MockOptimismL1CrossDomainMessenger} from "../../mocks/MockOptimismL1CrossDomainMessenger.sol";
+import {MockOptimismL2CrossDomainMessenger} from "../../mocks/MockOptimismL2CrossDomainMessenger.sol";
import {OptimismSequencerUptimeFeed} from "../../../optimism/OptimismSequencerUptimeFeed.sol";
import {OptimismValidator} from "../../../optimism/OptimismValidator.sol";
import {L2EPTest} from "../L2EPTest.t.sol";
diff --git a/contracts/src/v0.8/l2ep/test/v1_0_0/scroll/ScrollCrossDomainForwarder.t.sol b/contracts/src/v0.8/l2ep/test/v1_0_0/scroll/ScrollCrossDomainForwarder.t.sol
index 0025c6b9937..d28df02e975 100644
--- a/contracts/src/v0.8/l2ep/test/v1_0_0/scroll/ScrollCrossDomainForwarder.t.sol
+++ b/contracts/src/v0.8/l2ep/test/v1_0_0/scroll/ScrollCrossDomainForwarder.t.sol
@@ -3,7 +3,7 @@ pragma solidity 0.8.24;
import {MockScrollCrossDomainMessenger} from "../../mocks/scroll/MockScrollCrossDomainMessenger.sol";
import {ScrollCrossDomainForwarder} from "../../../scroll/ScrollCrossDomainForwarder.sol";
-import {Greeter} from "../../../../tests/Greeter.sol";
+import {Greeter} from "../../Greeter.sol";
import {L2EPTest} from "../L2EPTest.t.sol";
contract ScrollCrossDomainForwarderTest is L2EPTest {
diff --git a/contracts/src/v0.8/l2ep/test/v1_0_0/scroll/ScrollCrossDomainGovernor.t.sol b/contracts/src/v0.8/l2ep/test/v1_0_0/scroll/ScrollCrossDomainGovernor.t.sol
index a2523e5feb6..544923f49f5 100644
--- a/contracts/src/v0.8/l2ep/test/v1_0_0/scroll/ScrollCrossDomainGovernor.t.sol
+++ b/contracts/src/v0.8/l2ep/test/v1_0_0/scroll/ScrollCrossDomainGovernor.t.sol
@@ -3,7 +3,7 @@ pragma solidity 0.8.24;
import {MockScrollCrossDomainMessenger} from "../../mocks/scroll/MockScrollCrossDomainMessenger.sol";
import {ScrollCrossDomainGovernor} from "../../../scroll/ScrollCrossDomainGovernor.sol";
-import {Greeter} from "../../../../tests/Greeter.sol";
+import {Greeter} from "../../Greeter.sol";
import {L2EPTest} from "../L2EPTest.t.sol";
import {MultiSend} from "../../../../vendor/MultiSend.sol";
diff --git a/contracts/src/v0.8/l2ep/test/v1_0_0/shared/BaseSequencerUptimeFeed.t.sol b/contracts/src/v0.8/l2ep/test/v1_0_0/shared/BaseSequencerUptimeFeed.t.sol
index 20553e33bab..367aa00a620 100644
--- a/contracts/src/v0.8/l2ep/test/v1_0_0/shared/BaseSequencerUptimeFeed.t.sol
+++ b/contracts/src/v0.8/l2ep/test/v1_0_0/shared/BaseSequencerUptimeFeed.t.sol
@@ -5,7 +5,7 @@ import {Vm} from "forge-std/Test.sol";
import {AddressAliasHelper} from "../../../../vendor/arb-bridge-eth/v0.8.0-custom/contracts/libraries/AddressAliasHelper.sol";
import {BaseSequencerUptimeFeed} from "../../../base/BaseSequencerUptimeFeed.sol";
import {MockBaseSequencerUptimeFeed} from "../../../test/mocks/MockBaseSequencerUptimeFeed.sol";
-import {FeedConsumer} from "../../../../tests/FeedConsumer.sol";
+import {FeedConsumer} from "../../FeedConsumer.sol";
import {L2EPTest} from "../L2EPTest.t.sol";
contract BaseSequencerUptimeFeed_Setup is L2EPTest {
diff --git a/contracts/src/v0.8/llo-feeds/v0.3.0/FeeManager.sol b/contracts/src/v0.8/llo-feeds/v0.3.0/FeeManager.sol
index 44f550e3253..71f2f50fcb8 100644
--- a/contracts/src/v0.8/llo-feeds/v0.3.0/FeeManager.sol
+++ b/contracts/src/v0.8/llo-feeds/v0.3.0/FeeManager.sol
@@ -3,7 +3,7 @@ pragma solidity 0.8.19;
import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol";
import {IFeeManager} from "./interfaces/IFeeManager.sol";
-import {TypeAndVersionInterface} from "../../interfaces/TypeAndVersionInterface.sol";
+import {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.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";
@@ -19,7 +19,7 @@ import {IVerifierFeeManager} from "./interfaces/IVerifierFeeManager.sol";
* @author Austin Born
* @notice This contract is used for the handling of fees required for users verifying reports.
*/
-contract FeeManager is IFeeManager, ConfirmedOwner, TypeAndVersionInterface {
+contract FeeManager is IFeeManager, ConfirmedOwner, ITypeAndVersion {
using SafeERC20 for IERC20;
/// @notice list of subscribers and their discounts subscriberDiscounts[subscriber][feedId][token]
@@ -158,7 +158,7 @@ contract FeeManager is IFeeManager, ConfirmedOwner, TypeAndVersionInterface {
_;
}
- /// @inheritdoc TypeAndVersionInterface
+ /// @inheritdoc ITypeAndVersion
function typeAndVersion() external pure override returns (string memory) {
return "FeeManager 2.0.0";
}
diff --git a/contracts/src/v0.8/llo-feeds/v0.3.0/RewardManager.sol b/contracts/src/v0.8/llo-feeds/v0.3.0/RewardManager.sol
index 49fef51c569..9e9a58857c7 100644
--- a/contracts/src/v0.8/llo-feeds/v0.3.0/RewardManager.sol
+++ b/contracts/src/v0.8/llo-feeds/v0.3.0/RewardManager.sol
@@ -4,7 +4,7 @@ pragma solidity 0.8.19;
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 {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol";
import {Common} from "../libraries/Common.sol";
import {SafeERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol";
@@ -14,7 +14,7 @@ import {SafeERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/tok
* @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 RewardManager is IRewardManager, ConfirmedOwner, TypeAndVersionInterface {
+contract RewardManager is IRewardManager, ConfirmedOwner, ITypeAndVersion {
using SafeERC20 for IERC20;
// @dev The mapping of total fees collected for a particular pot: s_totalRewardRecipientFees[poolId]
@@ -73,7 +73,7 @@ contract RewardManager is IRewardManager, ConfirmedOwner, TypeAndVersionInterfac
i_linkAddress = linkAddress;
}
- // @inheritdoc TypeAndVersionInterface
+ // @inheritdoc ITypeAndVersion
function typeAndVersion() external pure override returns (string memory) {
return "RewardManager 1.1.0";
}
diff --git a/contracts/src/v0.8/llo-feeds/v0.3.0/Verifier.sol b/contracts/src/v0.8/llo-feeds/v0.3.0/Verifier.sol
index fe5742108a5..ce4fe974bd9 100644
--- a/contracts/src/v0.8/llo-feeds/v0.3.0/Verifier.sol
+++ b/contracts/src/v0.8/llo-feeds/v0.3.0/Verifier.sol
@@ -4,7 +4,7 @@ pragma solidity 0.8.19;
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 {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol";
import {IERC165} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol";
import {Common} from "../libraries/Common.sol";
@@ -18,7 +18,7 @@ uint256 constant MAX_NUM_ORACLES = 31;
* a feed. The verifier contract is used to verify that such reports have
* been signed by the correct signers.
**/
-contract Verifier is IVerifier, ConfirmedOwner, TypeAndVersionInterface {
+contract Verifier is IVerifier, ConfirmedOwner, ITypeAndVersion {
// The first byte of the mask can be 0, because we only ever have 31 oracles
uint256 internal constant ORACLE_MASK = 0x0001010101010101010101010101010101010101010101010101010101010101;
@@ -193,7 +193,7 @@ contract Verifier is IVerifier, ConfirmedOwner, TypeAndVersionInterface {
return interfaceId == this.verify.selector;
}
- /// @inheritdoc TypeAndVersionInterface
+ /// @inheritdoc ITypeAndVersion
function typeAndVersion() external pure override returns (string memory) {
return "Verifier 1.2.0";
}
diff --git a/contracts/src/v0.8/llo-feeds/v0.3.0/VerifierProxy.sol b/contracts/src/v0.8/llo-feeds/v0.3.0/VerifierProxy.sol
index c06312dd7be..e66b937f153 100644
--- a/contracts/src/v0.8/llo-feeds/v0.3.0/VerifierProxy.sol
+++ b/contracts/src/v0.8/llo-feeds/v0.3.0/VerifierProxy.sol
@@ -4,7 +4,7 @@ pragma solidity 0.8.19;
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 {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.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";
@@ -15,7 +15,7 @@ import {Common} from "../libraries/Common.sol";
* on a chain. It is responsible for taking in a verification request and routing
* it to the correct verifier contract.
*/
-contract VerifierProxy is IVerifierProxy, ConfirmedOwner, TypeAndVersionInterface {
+contract VerifierProxy is IVerifierProxy, ConfirmedOwner, ITypeAndVersion {
/// @notice This event is emitted whenever a new verifier contract is set
/// @param oldConfigDigest The config digest that was previously the latest config
/// digest of the verifier contract at the verifier address.
@@ -115,7 +115,7 @@ contract VerifierProxy is IVerifierProxy, ConfirmedOwner, TypeAndVersionInterfac
_;
}
- /// @inheritdoc TypeAndVersionInterface
+ /// @inheritdoc ITypeAndVersion
function typeAndVersion() external pure override returns (string memory) {
return "VerifierProxy 2.0.0";
}
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
index 08ac1d45f58..eb35cdc7956 100644
--- a/contracts/src/v0.8/llo-feeds/v0.4.0/DestinationFeeManager.sol
+++ b/contracts/src/v0.8/llo-feeds/v0.4.0/DestinationFeeManager.sol
@@ -2,7 +2,7 @@
pragma solidity 0.8.19;
import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol";
-import {TypeAndVersionInterface} from "../../interfaces/TypeAndVersionInterface.sol";
+import {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.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";
@@ -23,7 +23,7 @@ contract DestinationFeeManager is
IDestinationFeeManager,
IDestinationVerifierFeeManager,
ConfirmedOwner,
- TypeAndVersionInterface
+ ITypeAndVersion
{
using SafeERC20 for IERC20;
@@ -164,7 +164,7 @@ contract DestinationFeeManager is
_;
}
- /// @inheritdoc TypeAndVersionInterface
+ /// @inheritdoc ITypeAndVersion
function typeAndVersion() external pure override returns (string memory) {
return "DestinationFeeManager 0.4.0";
}
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
index 4b4c1f50efd..9f66a423cb6 100644
--- a/contracts/src/v0.8/llo-feeds/v0.4.0/DestinationRewardManager.sol
+++ b/contracts/src/v0.8/llo-feeds/v0.4.0/DestinationRewardManager.sol
@@ -4,7 +4,7 @@ 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 {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol";
import {Common} from "../libraries/Common.sol";
import {SafeERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol";
@@ -14,7 +14,7 @@ import {SafeERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/tok
* @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 {
+contract DestinationRewardManager is IDestinationRewardManager, ConfirmedOwner, ITypeAndVersion {
using SafeERC20 for IERC20;
// @dev The mapping of total fees collected for a particular pot: s_totalRewardRecipientFees[poolId]
@@ -73,7 +73,7 @@ contract DestinationRewardManager is IDestinationRewardManager, ConfirmedOwner,
i_linkAddress = linkAddress;
}
- // @inheritdoc TypeAndVersionInterface
+ // @inheritdoc ITypeAndVersion
function typeAndVersion() external pure override returns (string memory) {
return "DestinationRewardManager 0.4.0";
}
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
index 8ab0f6acc23..545a0d60727 100644
--- a/contracts/src/v0.8/llo-feeds/v0.4.0/DestinationVerifier.sol
+++ b/contracts/src/v0.8/llo-feeds/v0.4.0/DestinationVerifier.sol
@@ -3,7 +3,7 @@ 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 {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.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";
@@ -23,7 +23,7 @@ contract DestinationVerifier is
IDestinationVerifier,
IDestinationVerifierProxyVerifier,
ConfirmedOwner,
- TypeAndVersionInterface
+ ITypeAndVersion
{
/// @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;
@@ -436,7 +436,7 @@ contract DestinationVerifier is
interfaceId == type(IDestinationVerifierProxyVerifier).interfaceId;
}
- /// @inheritdoc TypeAndVersionInterface
+ /// @inheritdoc ITypeAndVersion
function typeAndVersion() external pure override returns (string memory) {
return "DestinationVerifier 0.4.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
index 6790883ba31..6a16dc20cb3 100644
--- a/contracts/src/v0.8/llo-feeds/v0.4.0/DestinationVerifierProxy.sol
+++ b/contracts/src/v0.8/llo-feeds/v0.4.0/DestinationVerifierProxy.sol
@@ -2,7 +2,7 @@
pragma solidity 0.8.19;
import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol";
-import {TypeAndVersionInterface} from "../../interfaces/TypeAndVersionInterface.sol";
+import {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol";
import {IERC165} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol";
import {IDestinationVerifierProxy} from "./interfaces/IDestinationVerifierProxy.sol";
import {IDestinationVerifierProxyVerifier} from "./interfaces/IDestinationVerifierProxyVerifier.sol";
@@ -12,7 +12,7 @@ import {IDestinationVerifierProxyVerifier} from "./interfaces/IDestinationVerifi
* @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 {
+contract DestinationVerifierProxy is IDestinationVerifierProxy, ConfirmedOwner, ITypeAndVersion {
/// @notice The active verifier for this proxy
IDestinationVerifierProxyVerifier private s_verifier;
@@ -24,7 +24,7 @@ contract DestinationVerifierProxy is IDestinationVerifierProxy, ConfirmedOwner,
constructor() ConfirmedOwner(msg.sender) {}
- /// @inheritdoc TypeAndVersionInterface
+ /// @inheritdoc ITypeAndVersion
function typeAndVersion() external pure override returns (string memory) {
return "DestinationVerifierProxy 0.4.0";
}
diff --git a/contracts/src/v0.8/llo-feeds/v0.5.0/configuration/ChannelConfigStore.sol b/contracts/src/v0.8/llo-feeds/v0.5.0/configuration/ChannelConfigStore.sol
index f5e5040bb8f..465292d9e0c 100644
--- a/contracts/src/v0.8/llo-feeds/v0.5.0/configuration/ChannelConfigStore.sol
+++ b/contracts/src/v0.8/llo-feeds/v0.5.0/configuration/ChannelConfigStore.sol
@@ -3,9 +3,9 @@ pragma solidity ^0.8.19;
import {ConfirmedOwner} from "../../../shared/access/ConfirmedOwner.sol";
import {IChannelConfigStore} from "./interfaces/IChannelConfigStore.sol";
-import {TypeAndVersionInterface} from "../../../interfaces/TypeAndVersionInterface.sol";
+import {ITypeAndVersion} from "../../../shared/interfaces/ITypeAndVersion.sol";
-contract ChannelConfigStore is ConfirmedOwner, IChannelConfigStore, TypeAndVersionInterface {
+contract ChannelConfigStore is ConfirmedOwner, IChannelConfigStore, ITypeAndVersion {
event NewChannelDefinition(uint256 indexed donId, uint32 version, string url, bytes32 sha);
constructor() ConfirmedOwner(msg.sender) {}
diff --git a/contracts/src/v0.8/llo-feeds/v0.5.0/configuration/Configurator.sol b/contracts/src/v0.8/llo-feeds/v0.5.0/configuration/Configurator.sol
index c946b3e2508..9b72f3d4fec 100644
--- a/contracts/src/v0.8/llo-feeds/v0.5.0/configuration/Configurator.sol
+++ b/contracts/src/v0.8/llo-feeds/v0.5.0/configuration/Configurator.sol
@@ -2,7 +2,7 @@
pragma solidity 0.8.19;
import {ConfirmedOwner} from "../../../shared/access/ConfirmedOwner.sol";
-import {TypeAndVersionInterface} from "../../../interfaces/TypeAndVersionInterface.sol";
+import {ITypeAndVersion} from "../../../shared/interfaces/ITypeAndVersion.sol";
import {IERC165} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol";
import {IConfigurator} from "./interfaces/IConfigurator.sol";
@@ -18,7 +18,7 @@ uint256 constant MIN_SUPPORTED_ONCHAIN_CONFIG_VERSION = 1;
* @notice This contract is intended to be deployed on the source chain and acts as a OCR3 configurator for LLO/Mercury
**/
-contract Configurator is IConfigurator, ConfirmedOwner, TypeAndVersionInterface, IERC165 {
+contract Configurator is IConfigurator, ConfirmedOwner, ITypeAndVersion, IERC165 {
/// @notice This error is thrown whenever trying to set a config
/// with a fault tolerance of 0
error FaultToleranceMustBePositive();
@@ -334,7 +334,7 @@ contract Configurator is IConfigurator, ConfirmedOwner, TypeAndVersionInterface,
return interfaceId == type(IConfigurator).interfaceId;
}
- /// @inheritdoc TypeAndVersionInterface
+ /// @inheritdoc ITypeAndVersion
function typeAndVersion() external pure override returns (string memory) {
return "Configurator 0.5.0";
}
diff --git a/contracts/src/v0.8/mocks/MockAggregatorValidator.sol b/contracts/src/v0.8/mocks/MockAggregatorValidator.sol
deleted file mode 100644
index bdc935cd231..00000000000
--- a/contracts/src/v0.8/mocks/MockAggregatorValidator.sol
+++ /dev/null
@@ -1,30 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.0;
-
-import "../shared/interfaces/AggregatorValidatorInterface.sol";
-
-contract MockAggregatorValidator is AggregatorValidatorInterface {
- uint8 immutable id;
-
- constructor(uint8 id_) {
- id = id_;
- }
-
- event ValidateCalled(
- uint8 id,
- uint256 previousRoundId,
- int256 previousAnswer,
- uint256 currentRoundId,
- int256 currentAnswer
- );
-
- function validate(
- uint256 previousRoundId,
- int256 previousAnswer,
- uint256 currentRoundId,
- int256 currentAnswer
- ) external override returns (bool) {
- emit ValidateCalled(id, previousRoundId, previousAnswer, currentRoundId, currentAnswer);
- return true;
- }
-}
diff --git a/contracts/src/v0.8/mocks/MockOffchainAggregator.sol b/contracts/src/v0.8/mocks/MockOffchainAggregator.sol
deleted file mode 100644
index 5366bbee0b0..00000000000
--- a/contracts/src/v0.8/mocks/MockOffchainAggregator.sol
+++ /dev/null
@@ -1,14 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity 0.8.6;
-
-contract MockOffchainAggregator {
- event RoundIdUpdated(uint80 roundId);
-
- uint80 public roundId;
-
- function requestNewRound() external returns (uint80) {
- roundId++;
- emit RoundIdUpdated(roundId);
- return roundId;
- }
-}
diff --git a/contracts/src/v0.8/Chainlink.sol b/contracts/src/v0.8/operatorforwarder/Chainlink.sol
similarity index 96%
rename from contracts/src/v0.8/Chainlink.sol
rename to contracts/src/v0.8/operatorforwarder/Chainlink.sol
index e511cfc8085..f3ee84cb11e 100644
--- a/contracts/src/v0.8/Chainlink.sol
+++ b/contracts/src/v0.8/operatorforwarder/Chainlink.sol
@@ -1,8 +1,8 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
-import {CBORChainlink} from "./vendor/CBORChainlink.sol";
-import {BufferChainlink} from "./vendor/BufferChainlink.sol";
+import {CBORChainlink} from "../vendor/CBORChainlink.sol";
+import {BufferChainlink} from "../vendor/BufferChainlink.sol";
/**
* @title Library for common Chainlink functions
diff --git a/contracts/src/v0.8/ChainlinkClient.sol b/contracts/src/v0.8/operatorforwarder/ChainlinkClient.sol
similarity index 98%
rename from contracts/src/v0.8/ChainlinkClient.sol
rename to contracts/src/v0.8/operatorforwarder/ChainlinkClient.sol
index 1d8640a27b2..c619683cbb1 100644
--- a/contracts/src/v0.8/ChainlinkClient.sol
+++ b/contracts/src/v0.8/operatorforwarder/ChainlinkClient.sol
@@ -3,11 +3,11 @@ pragma solidity ^0.8.0;
import {Chainlink} from "./Chainlink.sol";
import {ENSInterface} from "./interfaces/ENSInterface.sol";
-import {LinkTokenInterface} from "./shared/interfaces/LinkTokenInterface.sol";
+import {LinkTokenInterface} from "../shared/interfaces/LinkTokenInterface.sol";
import {ChainlinkRequestInterface} from "./interfaces/ChainlinkRequestInterface.sol";
import {OperatorInterface} from "./interfaces/OperatorInterface.sol";
import {PointerInterface} from "./interfaces/PointerInterface.sol";
-import {ENSResolver as ENSResolver_Chainlink} from "./vendor/ENSResolver.sol";
+import {ENSResolver as ENSResolver_Chainlink} from "../vendor/ENSResolver.sol";
/**
* @title The ChainlinkClient contract
diff --git a/contracts/src/v0.8/operatorforwarder/Operator.sol b/contracts/src/v0.8/operatorforwarder/Operator.sol
index 64882e43cda..ff22558a098 100644
--- a/contracts/src/v0.8/operatorforwarder/Operator.sol
+++ b/contracts/src/v0.8/operatorforwarder/Operator.sol
@@ -6,10 +6,10 @@ import {LinkTokenReceiver} from "./LinkTokenReceiver.sol";
import {ConfirmedOwner} from "../shared/access/ConfirmedOwner.sol";
import {LinkTokenInterface} from "../shared/interfaces/LinkTokenInterface.sol";
import {IAuthorizedReceiver} from "./interfaces/IAuthorizedReceiver.sol";
-import {OperatorInterface} from "../interfaces/OperatorInterface.sol";
+import {OperatorInterface} from "./interfaces/OperatorInterface.sol";
import {IOwnable} from "../shared/interfaces/IOwnable.sol";
import {IWithdrawal} from "./interfaces/IWithdrawal.sol";
-import {OracleInterface} from "../interfaces/OracleInterface.sol";
+import {OracleInterface} from "./interfaces/OracleInterface.sol";
import {SafeCast} from "../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/math/SafeCast.sol";
// @title The Chainlink Operator contract
diff --git a/contracts/src/v0.8/interfaces/ChainlinkRequestInterface.sol b/contracts/src/v0.8/operatorforwarder/interfaces/ChainlinkRequestInterface.sol
similarity index 100%
rename from contracts/src/v0.8/interfaces/ChainlinkRequestInterface.sol
rename to contracts/src/v0.8/operatorforwarder/interfaces/ChainlinkRequestInterface.sol
diff --git a/contracts/src/v0.8/interfaces/ENSInterface.sol b/contracts/src/v0.8/operatorforwarder/interfaces/ENSInterface.sol
similarity index 100%
rename from contracts/src/v0.8/interfaces/ENSInterface.sol
rename to contracts/src/v0.8/operatorforwarder/interfaces/ENSInterface.sol
diff --git a/contracts/src/v0.8/interfaces/OperatorInterface.sol b/contracts/src/v0.8/operatorforwarder/interfaces/OperatorInterface.sol
similarity index 100%
rename from contracts/src/v0.8/interfaces/OperatorInterface.sol
rename to contracts/src/v0.8/operatorforwarder/interfaces/OperatorInterface.sol
diff --git a/contracts/src/v0.8/interfaces/OracleInterface.sol b/contracts/src/v0.8/operatorforwarder/interfaces/OracleInterface.sol
similarity index 100%
rename from contracts/src/v0.8/interfaces/OracleInterface.sol
rename to contracts/src/v0.8/operatorforwarder/interfaces/OracleInterface.sol
diff --git a/contracts/src/v0.8/interfaces/PointerInterface.sol b/contracts/src/v0.8/operatorforwarder/interfaces/PointerInterface.sol
similarity index 100%
rename from contracts/src/v0.8/interfaces/PointerInterface.sol
rename to contracts/src/v0.8/operatorforwarder/interfaces/PointerInterface.sol
diff --git a/contracts/src/v0.8/tests/Broken.sol b/contracts/src/v0.8/operatorforwarder/test/Broken.sol
similarity index 95%
rename from contracts/src/v0.8/tests/Broken.sol
rename to contracts/src/v0.8/operatorforwarder/test/Broken.sol
index 21fa9b014e9..6edfbd88d51 100644
--- a/contracts/src/v0.8/tests/Broken.sol
+++ b/contracts/src/v0.8/operatorforwarder/test/Broken.sol
@@ -1,6 +1,7 @@
pragma solidity ^0.8.0;
// Broken is a contract to aid debugging and testing reverting calls during development.
+// solhint-disable
contract Broken {
error Unauthorized(string reason, int256 reason2);
diff --git a/contracts/src/v0.8/operatorforwarder/test/testhelpers/ChainlinkClientHelper.sol b/contracts/src/v0.8/operatorforwarder/test/testhelpers/ChainlinkClientHelper.sol
index 9b6ba6bb432..1efd93114d9 100644
--- a/contracts/src/v0.8/operatorforwarder/test/testhelpers/ChainlinkClientHelper.sol
+++ b/contracts/src/v0.8/operatorforwarder/test/testhelpers/ChainlinkClientHelper.sol
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
-import {ChainlinkClient} from "../../../ChainlinkClient.sol";
+import {ChainlinkClient} from "../../ChainlinkClient.sol";
contract ChainlinkClientHelper is ChainlinkClient {
bytes4 public constant FULFILL_SELECTOR = this.fulfill.selector;
diff --git a/contracts/src/v0.8/operatorforwarder/test/testhelpers/Chainlinked.sol b/contracts/src/v0.8/operatorforwarder/test/testhelpers/Chainlinked.sol
index dba5d407623..67fda6452cd 100644
--- a/contracts/src/v0.8/operatorforwarder/test/testhelpers/Chainlinked.sol
+++ b/contracts/src/v0.8/operatorforwarder/test/testhelpers/Chainlinked.sol
@@ -1,6 +1,6 @@
pragma solidity ^0.8.0;
-import {ChainlinkClient, Chainlink} from "../../../ChainlinkClient.sol";
+import {ChainlinkClient, Chainlink} from "../../ChainlinkClient.sol";
/**
* @title The Chainlinked contract
diff --git a/contracts/src/v0.8/operatorforwarder/test/testhelpers/Consumer.sol b/contracts/src/v0.8/operatorforwarder/test/testhelpers/Consumer.sol
index 3ec32dd6a29..b422081084e 100644
--- a/contracts/src/v0.8/operatorforwarder/test/testhelpers/Consumer.sol
+++ b/contracts/src/v0.8/operatorforwarder/test/testhelpers/Consumer.sol
@@ -1,8 +1,8 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
-import {ChainlinkClient, ChainlinkRequestInterface, LinkTokenInterface} from "../../../ChainlinkClient.sol";
-import {Chainlink} from "../../../Chainlink.sol";
+import {ChainlinkClient, ChainlinkRequestInterface, LinkTokenInterface} from "../../ChainlinkClient.sol";
+import {Chainlink} from "../../Chainlink.sol";
contract Consumer is ChainlinkClient {
using Chainlink for Chainlink.Request;
diff --git a/contracts/src/v0.8/operatorforwarder/test/testhelpers/EmptyOracle.sol b/contracts/src/v0.8/operatorforwarder/test/testhelpers/EmptyOracle.sol
index f278791d2bb..6a4c281995a 100644
--- a/contracts/src/v0.8/operatorforwarder/test/testhelpers/EmptyOracle.sol
+++ b/contracts/src/v0.8/operatorforwarder/test/testhelpers/EmptyOracle.sol
@@ -1,8 +1,8 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
-import {ChainlinkRequestInterface} from "../../../interfaces/ChainlinkRequestInterface.sol";
-import {OracleInterface} from "../../../interfaces/OracleInterface.sol";
+import {ChainlinkRequestInterface} from "../../interfaces/ChainlinkRequestInterface.sol";
+import {OracleInterface} from "../../interfaces/OracleInterface.sol";
/* solhint-disable no-empty-blocks */
contract EmptyOracle is ChainlinkRequestInterface, OracleInterface {
diff --git a/contracts/src/v0.8/operatorforwarder/test/testhelpers/GasGuzzlingConsumer.sol b/contracts/src/v0.8/operatorforwarder/test/testhelpers/GasGuzzlingConsumer.sol
index 029102018b0..040eeec394e 100644
--- a/contracts/src/v0.8/operatorforwarder/test/testhelpers/GasGuzzlingConsumer.sol
+++ b/contracts/src/v0.8/operatorforwarder/test/testhelpers/GasGuzzlingConsumer.sol
@@ -2,7 +2,7 @@
pragma solidity ^0.8.0;
import {Consumer} from "./Consumer.sol";
-import {Chainlink} from "../../../Chainlink.sol";
+import {Chainlink} from "../../Chainlink.sol";
contract GasGuzzlingConsumer is Consumer {
using Chainlink for Chainlink.Request;
diff --git a/contracts/src/v0.8/operatorforwarder/test/testhelpers/MaliciousMultiWordConsumer.sol b/contracts/src/v0.8/operatorforwarder/test/testhelpers/MaliciousMultiWordConsumer.sol
index 93af16f64fd..ad65927b40b 100644
--- a/contracts/src/v0.8/operatorforwarder/test/testhelpers/MaliciousMultiWordConsumer.sol
+++ b/contracts/src/v0.8/operatorforwarder/test/testhelpers/MaliciousMultiWordConsumer.sol
@@ -1,8 +1,8 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
-import {ChainlinkClient} from "../../../ChainlinkClient.sol";
-import {Chainlink} from "../../../Chainlink.sol";
+import {ChainlinkClient} from "../../ChainlinkClient.sol";
+import {Chainlink} from "../../Chainlink.sol";
contract MaliciousMultiWordConsumer is ChainlinkClient {
uint256 private constant ORACLE_PAYMENT = 1 ether;
diff --git a/contracts/src/v0.8/operatorforwarder/test/testhelpers/MaliciousRequester.sol b/contracts/src/v0.8/operatorforwarder/test/testhelpers/MaliciousRequester.sol
index c01c8a60bb7..8864d8fdffb 100644
--- a/contracts/src/v0.8/operatorforwarder/test/testhelpers/MaliciousRequester.sol
+++ b/contracts/src/v0.8/operatorforwarder/test/testhelpers/MaliciousRequester.sol
@@ -2,7 +2,7 @@ pragma solidity ^0.8.0;
import {MaliciousChainlink} from "./MaliciousChainlink.sol";
import {MaliciousChainlinked, Chainlink} from "./MaliciousChainlinked.sol";
-import {ChainlinkRequestInterface} from "../../../interfaces/ChainlinkRequestInterface.sol";
+import {ChainlinkRequestInterface} from "../../interfaces/ChainlinkRequestInterface.sol";
contract MaliciousRequester is MaliciousChainlinked {
uint256 private constant ORACLE_PAYMENT = 1 ether;
diff --git a/contracts/src/v0.8/operatorforwarder/test/testhelpers/MultiWordConsumer.sol b/contracts/src/v0.8/operatorforwarder/test/testhelpers/MultiWordConsumer.sol
index b3fdfcb813a..50420807cf9 100644
--- a/contracts/src/v0.8/operatorforwarder/test/testhelpers/MultiWordConsumer.sol
+++ b/contracts/src/v0.8/operatorforwarder/test/testhelpers/MultiWordConsumer.sol
@@ -1,7 +1,7 @@
pragma solidity ^0.8.0;
-import {ChainlinkClient, ChainlinkRequestInterface, LinkTokenInterface} from "../../../ChainlinkClient.sol";
-import {Chainlink} from "../../../Chainlink.sol";
+import {ChainlinkClient, ChainlinkRequestInterface, LinkTokenInterface} from "../../ChainlinkClient.sol";
+import {Chainlink} from "../../Chainlink.sol";
contract MultiWordConsumer is ChainlinkClient {
using Chainlink for Chainlink.Request;
diff --git a/contracts/src/v0.8/tests/MockV3Aggregator.sol b/contracts/src/v0.8/shared/mocks/MockV3Aggregator.sol
similarity index 95%
rename from contracts/src/v0.8/tests/MockV3Aggregator.sol
rename to contracts/src/v0.8/shared/mocks/MockV3Aggregator.sol
index 9822d23e853..a405b7f6bef 100644
--- a/contracts/src/v0.8/tests/MockV3Aggregator.sol
+++ b/contracts/src/v0.8/shared/mocks/MockV3Aggregator.sol
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
-import "../shared/interfaces/AggregatorV2V3Interface.sol";
+import {AggregatorV2V3Interface} from "../interfaces/AggregatorV2V3Interface.sol";
/**
* @title MockV3Aggregator
@@ -11,6 +11,7 @@ import "../shared/interfaces/AggregatorV2V3Interface.sol";
* aggregator contract, but how the aggregator got
* its answer is unimportant
*/
+// solhint-disable
contract MockV3Aggregator is AggregatorV2V3Interface {
uint256 public constant override version = 0;
diff --git a/contracts/src/v0.8/tests/LogEmitter.sol b/contracts/src/v0.8/shared/test/helpers/LogEmitter.sol
similarity index 97%
rename from contracts/src/v0.8/tests/LogEmitter.sol
rename to contracts/src/v0.8/shared/test/helpers/LogEmitter.sol
index 37306cc2bc5..4bf9e9e5674 100644
--- a/contracts/src/v0.8/tests/LogEmitter.sol
+++ b/contracts/src/v0.8/shared/test/helpers/LogEmitter.sol
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
+// solhint-disable
contract LogEmitter {
event Log1(uint256);
event Log2(uint256 indexed);
diff --git a/contracts/src/v0.8/tests/VRFLogEmitter.sol b/contracts/src/v0.8/shared/test/helpers/VRFLogEmitter.sol
similarity index 100%
rename from contracts/src/v0.8/tests/VRFLogEmitter.sol
rename to contracts/src/v0.8/shared/test/helpers/VRFLogEmitter.sol
diff --git a/contracts/src/v0.8/ChainSpecificUtil.sol b/contracts/src/v0.8/shared/util/ChainSpecificUtil.sol
similarity index 95%
rename from contracts/src/v0.8/ChainSpecificUtil.sol
rename to contracts/src/v0.8/shared/util/ChainSpecificUtil.sol
index c5052cd9b25..d541f5f8486 100644
--- a/contracts/src/v0.8/ChainSpecificUtil.sol
+++ b/contracts/src/v0.8/shared/util/ChainSpecificUtil.sol
@@ -1,9 +1,9 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
-import {ArbSys} from "./vendor/@arbitrum/nitro-contracts/src/precompiles/ArbSys.sol";
-import {ArbGasInfo} from "./vendor/@arbitrum/nitro-contracts/src/precompiles/ArbGasInfo.sol";
-import {OVM_GasPriceOracle} from "./vendor/@eth-optimism/contracts/v0.8.9/contracts/L2/predeploys/OVM_GasPriceOracle.sol";
+import {ArbSys} from "../../vendor/@arbitrum/nitro-contracts/src/precompiles/ArbSys.sol";
+import {ArbGasInfo} from "../../vendor/@arbitrum/nitro-contracts/src/precompiles/ArbGasInfo.sol";
+import {OVM_GasPriceOracle} from "../../vendor/@eth-optimism/contracts/v0.8.9/contracts/L2/predeploys/OVM_GasPriceOracle.sol";
/// @dev A library that abstracts out opcodes that behave differently across chains.
/// @dev The methods below return values that are pertinent to the given chain.
diff --git a/contracts/src/v0.8/tests/ChainlinkClientTestHelper.sol b/contracts/src/v0.8/tests/ChainlinkClientTestHelper.sol
deleted file mode 100644
index a344138a17d..00000000000
--- a/contracts/src/v0.8/tests/ChainlinkClientTestHelper.sol
+++ /dev/null
@@ -1,83 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.0;
-
-import "../ChainlinkClient.sol";
-
-contract ChainlinkClientTestHelper is ChainlinkClient {
- constructor(address _link, address _oracle) {
- _setChainlinkToken(_link);
- _setChainlinkOracle(_oracle);
- }
-
- event Request(bytes32 id, address callbackAddress, bytes4 callbackfunctionSelector, bytes data);
- event LinkAmount(uint256 amount);
-
- function publicNewRequest(bytes32 _id, address _address, bytes memory _fulfillmentSignature) public {
- Chainlink.Request memory req = _buildChainlinkRequest(_id, _address, bytes4(keccak256(_fulfillmentSignature)));
- emit Request(req.id, req.callbackAddress, req.callbackFunctionId, req.buf.buf);
- }
-
- function publicRequest(bytes32 _id, address _address, bytes memory _fulfillmentSignature, uint256 _wei) public {
- Chainlink.Request memory req = _buildChainlinkRequest(_id, _address, bytes4(keccak256(_fulfillmentSignature)));
- _sendChainlinkRequest(req, _wei);
- }
-
- function publicRequestRunTo(
- address _oracle,
- bytes32 _id,
- address _address,
- bytes memory _fulfillmentSignature,
- uint256 _wei
- ) public {
- Chainlink.Request memory run = _buildChainlinkRequest(_id, _address, bytes4(keccak256(_fulfillmentSignature)));
- _sendChainlinkRequestTo(_oracle, run, _wei);
- }
-
- function publicRequestOracleData(bytes32 _id, bytes memory _fulfillmentSignature, uint256 _wei) public {
- Chainlink.Request memory req = _buildOperatorRequest(_id, bytes4(keccak256(_fulfillmentSignature)));
- _sendOperatorRequest(req, _wei);
- }
-
- function publicRequestOracleDataFrom(
- address _oracle,
- bytes32 _id,
- bytes memory _fulfillmentSignature,
- uint256 _wei
- ) public {
- Chainlink.Request memory run = _buildOperatorRequest(_id, bytes4(keccak256(_fulfillmentSignature)));
- _sendOperatorRequestTo(_oracle, run, _wei);
- }
-
- function publicCancelRequest(
- bytes32 _requestId,
- uint256 _payment,
- bytes4 _callbackFunctionId,
- uint256 _expiration
- ) public {
- _cancelChainlinkRequest(_requestId, _payment, _callbackFunctionId, _expiration);
- }
-
- function publicChainlinkToken() public view returns (address) {
- return _chainlinkTokenAddress();
- }
-
- function publicFulfillChainlinkRequest(bytes32 _requestId, bytes32) public {
- fulfillRequest(_requestId, bytes32(0));
- }
-
- function fulfillRequest(bytes32 _requestId, bytes32) public {
- _validateChainlinkCallback(_requestId);
- }
-
- function publicLINK(uint256 _amount) public {
- emit LinkAmount(LINK_DIVISIBILITY * _amount);
- }
-
- function publicOracleAddress() public view returns (address) {
- return _chainlinkOracleAddress();
- }
-
- function publicAddExternalRequest(address _oracle, bytes32 _requestId) public {
- _addChainlinkExternalRequest(_oracle, _requestId);
- }
-}
diff --git a/contracts/src/v0.8/tests/ChainlinkTestHelper.sol b/contracts/src/v0.8/tests/ChainlinkTestHelper.sol
deleted file mode 100644
index d42f30c374d..00000000000
--- a/contracts/src/v0.8/tests/ChainlinkTestHelper.sol
+++ /dev/null
@@ -1,57 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.0;
-
-import "../Chainlink.sol";
-import "../vendor/CBORChainlink.sol";
-import "../vendor/BufferChainlink.sol";
-
-contract ChainlinkTestHelper {
- using Chainlink for Chainlink.Request;
- using CBORChainlink for BufferChainlink.buffer;
-
- Chainlink.Request private req;
-
- event RequestData(bytes payload);
-
- function closeEvent() public {
- emit RequestData(req.buf.buf);
- }
-
- function setBuffer(bytes memory data) public {
- Chainlink.Request memory r2 = req;
- r2._setBuffer(data);
- req = r2;
- }
-
- function add(string memory _key, string memory _value) public {
- Chainlink.Request memory r2 = req;
- r2._add(_key, _value);
- req = r2;
- }
-
- function addBytes(string memory _key, bytes memory _value) public {
- Chainlink.Request memory r2 = req;
- r2._addBytes(_key, _value);
- req = r2;
- }
-
- function addInt(string memory _key, int256 _value) public {
- Chainlink.Request memory r2 = req;
- r2._addInt(_key, _value);
- req = r2;
- }
-
- function addUint(string memory _key, uint256 _value) public {
- Chainlink.Request memory r2 = req;
- r2._addUint(_key, _value);
- req = r2;
- }
-
- // Temporarily have method receive bytes32[] memory until experimental
- // string[] memory can be invoked from truffle tests.
- function addStringArray(string memory _key, string[] memory _values) public {
- Chainlink.Request memory r2 = req;
- r2._addStringArray(_key, _values);
- req = r2;
- }
-}
diff --git a/contracts/src/v0.8/tests/Counter.sol b/contracts/src/v0.8/tests/Counter.sol
deleted file mode 100644
index 1ceb7891490..00000000000
--- a/contracts/src/v0.8/tests/Counter.sol
+++ /dev/null
@@ -1,26 +0,0 @@
-// SPDX-License-Identifier: MIT
-
-pragma solidity ^0.8.0;
-
-contract Counter {
- error AlwaysRevert();
-
- uint256 public count = 0;
-
- function increment() public returns (uint256) {
- count += 1;
- return count;
- }
-
- function reset() public {
- count = 0;
- }
-
- function alwaysRevert() public pure {
- revert AlwaysRevert();
- }
-
- function alwaysRevertWithString() public pure {
- revert("always revert");
- }
-}
diff --git a/contracts/src/v0.8/tests/FlagsTestHelper.sol b/contracts/src/v0.8/tests/FlagsTestHelper.sol
deleted file mode 100644
index 3e35cae8911..00000000000
--- a/contracts/src/v0.8/tests/FlagsTestHelper.sol
+++ /dev/null
@@ -1,20 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.0;
-
-import "../Flags.sol";
-
-contract FlagsTestHelper {
- Flags public flags;
-
- constructor(address flagsContract) {
- flags = Flags(flagsContract);
- }
-
- function getFlag(address subject) external view returns (bool) {
- return flags.getFlag(subject);
- }
-
- function getFlags(address[] calldata subjects) external view returns (bool[] memory) {
- return flags.getFlags(subjects);
- }
-}
diff --git a/contracts/src/v0.8/tests/MockETHLINKAggregator.sol b/contracts/src/v0.8/tests/MockETHLINKAggregator.sol
deleted file mode 100644
index d685aac7314..00000000000
--- a/contracts/src/v0.8/tests/MockETHLINKAggregator.sol
+++ /dev/null
@@ -1,44 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.0;
-
-import "../shared/interfaces/AggregatorV3Interface.sol";
-
-contract MockETHLINKAggregator is AggregatorV3Interface {
- int256 public answer;
-
- constructor(int256 _answer) public {
- answer = _answer;
- }
-
- function decimals() external view override returns (uint8) {
- return 18;
- }
-
- function description() external view override returns (string memory) {
- return "MockETHLINKAggregator";
- }
-
- function version() external view override returns (uint256) {
- return 1;
- }
-
- function getRoundData(
- uint80 _roundId
- )
- external
- view
- override
- returns (uint80 roundId, int256 ans, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)
- {
- return (1, answer, block.timestamp, block.timestamp, 1);
- }
-
- function latestRoundData()
- external
- view
- override
- returns (uint80 roundId, int256 ans, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)
- {
- return (1, answer, block.timestamp, block.timestamp, 1);
- }
-}
diff --git a/contracts/src/v0.8/vrf/BatchBlockhashStore.sol b/contracts/src/v0.8/vrf/BatchBlockhashStore.sol
index cf29f148a54..4ed6f28d381 100644
--- a/contracts/src/v0.8/vrf/BatchBlockhashStore.sol
+++ b/contracts/src/v0.8/vrf/BatchBlockhashStore.sol
@@ -2,7 +2,7 @@
// solhint-disable-next-line one-contract-per-file
pragma solidity 0.8.19;
-import {ChainSpecificUtil} from "../ChainSpecificUtil.sol";
+import {ChainSpecificUtil} from "../shared/util/ChainSpecificUtil.sol";
/**
* @title BatchBlockhashStore
diff --git a/contracts/src/v0.8/ChainSpecificUtil_v0_8_6.sol b/contracts/src/v0.8/vrf/ChainSpecificUtil_v0_8_6.sol
similarity index 96%
rename from contracts/src/v0.8/ChainSpecificUtil_v0_8_6.sol
rename to contracts/src/v0.8/vrf/ChainSpecificUtil_v0_8_6.sol
index 0379dc86ca0..eabc061e3f5 100644
--- a/contracts/src/v0.8/ChainSpecificUtil_v0_8_6.sol
+++ b/contracts/src/v0.8/vrf/ChainSpecificUtil_v0_8_6.sol
@@ -1,9 +1,9 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.6;
-import {ArbSys} from "./vendor/@arbitrum/nitro-contracts/src/precompiles/ArbSys.sol";
-import {ArbGasInfo} from "./vendor/@arbitrum/nitro-contracts/src/precompiles/ArbGasInfo.sol";
-import {OVM_GasPriceOracle} from "./vendor/@eth-optimism/contracts/v0.8.6/contracts/L2/predeploys/OVM_GasPriceOracle.sol";
+import {ArbSys} from "../vendor/@arbitrum/nitro-contracts/src/precompiles/ArbSys.sol";
+import {ArbGasInfo} from "../vendor/@arbitrum/nitro-contracts/src/precompiles/ArbGasInfo.sol";
+import {OVM_GasPriceOracle} from "../vendor/@eth-optimism/contracts/v0.8.6/contracts/L2/predeploys/OVM_GasPriceOracle.sol";
/// @dev A library that abstracts out opcodes that behave differently across chains.
/// @dev The methods below return values that are pertinent to the given chain.
diff --git a/contracts/src/v0.8/vrf/VRFCoordinatorV2.sol b/contracts/src/v0.8/vrf/VRFCoordinatorV2.sol
index 717826a3b95..ab0eecd6c45 100644
--- a/contracts/src/v0.8/vrf/VRFCoordinatorV2.sol
+++ b/contracts/src/v0.8/vrf/VRFCoordinatorV2.sol
@@ -5,13 +5,13 @@ import {LinkTokenInterface} from "../shared/interfaces/LinkTokenInterface.sol";
import {BlockhashStoreInterface} from "./interfaces/BlockhashStoreInterface.sol";
import {AggregatorV3Interface} from "../shared/interfaces/AggregatorV3Interface.sol";
import {VRFCoordinatorV2Interface} from "./interfaces/VRFCoordinatorV2Interface.sol";
-import {TypeAndVersionInterface} from "../interfaces/TypeAndVersionInterface.sol";
+import {ITypeAndVersion} from "../shared/interfaces/ITypeAndVersion.sol";
import {IERC677Receiver} from "../shared/interfaces/IERC677Receiver.sol";
import {VRF} from "./VRF.sol";
import {ConfirmedOwner} from "../shared/access/ConfirmedOwner.sol";
import {VRFConsumerBaseV2} from "./VRFConsumerBaseV2.sol";
-import {ChainSpecificUtil} from "../ChainSpecificUtil_v0_8_6.sol";
-contract VRFCoordinatorV2 is VRF, ConfirmedOwner, TypeAndVersionInterface, VRFCoordinatorV2Interface, IERC677Receiver {
+import {ChainSpecificUtil} from "./ChainSpecificUtil_v0_8_6.sol";
+contract VRFCoordinatorV2 is VRF, ConfirmedOwner, ITypeAndVersion, VRFCoordinatorV2Interface, IERC677Receiver {
// solhint-disable-next-line chainlink-solidity/prefix-immutable-variables-with-i
LinkTokenInterface public immutable LINK;
// solhint-disable-next-line chainlink-solidity/prefix-immutable-variables-with-i
diff --git a/contracts/src/v0.8/vrf/VRFV2Wrapper.sol b/contracts/src/v0.8/vrf/VRFV2Wrapper.sol
index a656ef071f1..584136e3beb 100644
--- a/contracts/src/v0.8/vrf/VRFV2Wrapper.sol
+++ b/contracts/src/v0.8/vrf/VRFV2Wrapper.sol
@@ -3,20 +3,20 @@
pragma solidity ^0.8.6;
import {ConfirmedOwner} from "../shared/access/ConfirmedOwner.sol";
-import {TypeAndVersionInterface} from "../interfaces/TypeAndVersionInterface.sol";
+import {ITypeAndVersion} from "../shared/interfaces/ITypeAndVersion.sol";
import {VRFConsumerBaseV2} from "./VRFConsumerBaseV2.sol";
import {LinkTokenInterface} from "../shared/interfaces/LinkTokenInterface.sol";
import {AggregatorV3Interface} from "../shared/interfaces/AggregatorV3Interface.sol";
import {VRFCoordinatorV2Interface} from "./interfaces/VRFCoordinatorV2Interface.sol";
import {VRFV2WrapperInterface} from "./interfaces/VRFV2WrapperInterface.sol";
import {VRFV2WrapperConsumerBase} from "./VRFV2WrapperConsumerBase.sol";
-import {ChainSpecificUtil} from "../ChainSpecificUtil_v0_8_6.sol";
+import {ChainSpecificUtil} from "./ChainSpecificUtil_v0_8_6.sol";
/**
* @notice A wrapper for VRFCoordinatorV2 that provides an interface better suited to one-off
* @notice requests for randomness.
*/
-contract VRFV2Wrapper is ConfirmedOwner, TypeAndVersionInterface, VRFConsumerBaseV2, VRFV2WrapperInterface {
+contract VRFV2Wrapper is ConfirmedOwner, ITypeAndVersion, VRFConsumerBaseV2, VRFV2WrapperInterface {
event WrapperFulfillmentFailed(uint256 indexed requestId, address indexed consumer);
// solhint-disable-next-line chainlink-solidity/prefix-immutable-variables-with-i
diff --git a/contracts/src/v0.8/vrf/dev/BlockhashStore.sol b/contracts/src/v0.8/vrf/dev/BlockhashStore.sol
index 0bef7aeada5..8889060922b 100644
--- a/contracts/src/v0.8/vrf/dev/BlockhashStore.sol
+++ b/contracts/src/v0.8/vrf/dev/BlockhashStore.sol
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
-import {ChainSpecificUtil} from "../../ChainSpecificUtil.sol";
+import {ChainSpecificUtil} from "../../shared/util/ChainSpecificUtil.sol";
/**
* @title BlockhashStore
diff --git a/contracts/src/v0.8/vrf/dev/TrustedBlockhashStore.sol b/contracts/src/v0.8/vrf/dev/TrustedBlockhashStore.sol
index b3b77c8095d..b6a770168e5 100644
--- a/contracts/src/v0.8/vrf/dev/TrustedBlockhashStore.sol
+++ b/contracts/src/v0.8/vrf/dev/TrustedBlockhashStore.sol
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
-import {ChainSpecificUtil} from "../../ChainSpecificUtil.sol";
+import {ChainSpecificUtil} from "../../shared/util/ChainSpecificUtil.sol";
import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol";
import {BlockhashStore} from "./BlockhashStore.sol";
diff --git a/contracts/src/v0.8/vrf/dev/VRFV2PlusWrapper.sol b/contracts/src/v0.8/vrf/dev/VRFV2PlusWrapper.sol
index 40fd8a90612..fced5822642 100644
--- a/contracts/src/v0.8/vrf/dev/VRFV2PlusWrapper.sol
+++ b/contracts/src/v0.8/vrf/dev/VRFV2PlusWrapper.sol
@@ -2,7 +2,7 @@
pragma solidity 0.8.19;
import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol";
-import {TypeAndVersionInterface} from "../../interfaces/TypeAndVersionInterface.sol";
+import {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol";
import {VRFConsumerBaseV2Plus} from "./VRFConsumerBaseV2Plus.sol";
import {LinkTokenInterface} from "../../shared/interfaces/LinkTokenInterface.sol";
import {AggregatorV3Interface} from "../../shared/interfaces/AggregatorV3Interface.sol";
@@ -15,7 +15,7 @@ import {VRFV2PlusWrapperConsumerBase} from "./VRFV2PlusWrapperConsumerBase.sol";
* @notice requests for randomness.
*/
// solhint-disable-next-line max-states-count
-contract VRFV2PlusWrapper is ConfirmedOwner, TypeAndVersionInterface, VRFConsumerBaseV2Plus, IVRFV2PlusWrapper {
+contract VRFV2PlusWrapper is ConfirmedOwner, ITypeAndVersion, VRFConsumerBaseV2Plus, IVRFV2PlusWrapper {
event WrapperFulfillmentFailed(uint256 indexed requestId, address indexed consumer);
// upper bound limit for premium percentages to make sure fee calculations don't overflow
diff --git a/contracts/src/v0.8/vrf/dev/testhelpers/VRFCoordinatorTestV2_5.sol b/contracts/src/v0.8/vrf/dev/testhelpers/VRFCoordinatorTestV2_5.sol
index 2e9c4a2da75..62dfddbee8d 100644
--- a/contracts/src/v0.8/vrf/dev/testhelpers/VRFCoordinatorTestV2_5.sol
+++ b/contracts/src/v0.8/vrf/dev/testhelpers/VRFCoordinatorTestV2_5.sol
@@ -5,7 +5,7 @@ import {BlockhashStoreInterface} from "../../interfaces/BlockhashStoreInterface.
import {VRFOld} from "./VRFOld.sol";
import {VRFTypes} from "../../VRFTypes.sol";
import {VRFConsumerBaseV2Plus, IVRFMigratableConsumerV2Plus} from "../VRFConsumerBaseV2Plus.sol";
-import {ChainSpecificUtil} from "../../../ChainSpecificUtil.sol";
+import {ChainSpecificUtil} from "../../../shared/util/ChainSpecificUtil.sol";
import {SubscriptionAPI} from "../SubscriptionAPI.sol";
import {VRFV2PlusClient} from "../libraries/VRFV2PlusClient.sol";
import {IVRFCoordinatorV2PlusMigration} from "../interfaces/IVRFCoordinatorV2PlusMigration.sol";
diff --git a/contracts/src/v0.8/vrf/dev/testhelpers/VRFCoordinatorV2PlusUpgradedVersion.sol b/contracts/src/v0.8/vrf/dev/testhelpers/VRFCoordinatorV2PlusUpgradedVersion.sol
index af5c56bde6c..c16a498fcb7 100644
--- a/contracts/src/v0.8/vrf/dev/testhelpers/VRFCoordinatorV2PlusUpgradedVersion.sol
+++ b/contracts/src/v0.8/vrf/dev/testhelpers/VRFCoordinatorV2PlusUpgradedVersion.sol
@@ -7,7 +7,7 @@ import {IVRFCoordinatorV2Plus, IVRFSubscriptionV2Plus} from "../interfaces/IVRFC
import {VRF} from "../../../vrf/VRF.sol";
import {VRFTypes} from "../../VRFTypes.sol";
import {VRFConsumerBaseV2Plus, IVRFMigratableConsumerV2Plus} from "../VRFConsumerBaseV2Plus.sol";
-import {ChainSpecificUtil} from "../../../ChainSpecificUtil.sol";
+import {ChainSpecificUtil} from "../../../shared/util/ChainSpecificUtil.sol";
import {SubscriptionAPI} from "../SubscriptionAPI.sol";
import {VRFV2PlusClient} from "../libraries/VRFV2PlusClient.sol";
import {IVRFCoordinatorV2PlusMigration} from "../interfaces/IVRFCoordinatorV2PlusMigration.sol";
diff --git a/contracts/src/v0.8/vrf/dev/testhelpers/VRFV2PlusLoadTestWithMetrics.sol b/contracts/src/v0.8/vrf/dev/testhelpers/VRFV2PlusLoadTestWithMetrics.sol
index 87e70f60e35..f70c0331cd1 100644
--- a/contracts/src/v0.8/vrf/dev/testhelpers/VRFV2PlusLoadTestWithMetrics.sol
+++ b/contracts/src/v0.8/vrf/dev/testhelpers/VRFV2PlusLoadTestWithMetrics.sol
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
-import {ChainSpecificUtil} from "../../../ChainSpecificUtil.sol";
+import {ChainSpecificUtil} from "../../../shared/util/ChainSpecificUtil.sol";
import {VRFConsumerBaseV2Plus} from "../VRFConsumerBaseV2Plus.sol";
import {VRFV2PlusClient} from "../libraries/VRFV2PlusClient.sol";
diff --git a/contracts/src/v0.8/vrf/dev/testhelpers/VRFV2PlusWrapperLoadTestConsumer.sol b/contracts/src/v0.8/vrf/dev/testhelpers/VRFV2PlusWrapperLoadTestConsumer.sol
index 6935723d931..ae76fed365a 100644
--- a/contracts/src/v0.8/vrf/dev/testhelpers/VRFV2PlusWrapperLoadTestConsumer.sol
+++ b/contracts/src/v0.8/vrf/dev/testhelpers/VRFV2PlusWrapperLoadTestConsumer.sol
@@ -3,7 +3,7 @@ pragma solidity ^0.8.6;
import {VRFV2PlusWrapperConsumerBase} from "../VRFV2PlusWrapperConsumerBase.sol";
import {ConfirmedOwner} from "../../../shared/access/ConfirmedOwner.sol";
-import {ChainSpecificUtil} from "../../../ChainSpecificUtil.sol";
+import {ChainSpecificUtil} from "../../../shared/util/ChainSpecificUtil.sol";
import {VRFV2PlusClient} from "../libraries/VRFV2PlusClient.sol";
contract VRFV2PlusWrapperLoadTestConsumer is VRFV2PlusWrapperConsumerBase, ConfirmedOwner {
diff --git a/contracts/src/v0.8/vrf/test/ChainSpecificUtil.t.sol b/contracts/src/v0.8/vrf/test/ChainSpecificUtil.t.sol
index efeb9027462..3e81dd2d3c9 100644
--- a/contracts/src/v0.8/vrf/test/ChainSpecificUtil.t.sol
+++ b/contracts/src/v0.8/vrf/test/ChainSpecificUtil.t.sol
@@ -1,7 +1,7 @@
pragma solidity 0.8.6;
import "./BaseTest.t.sol";
-import {ChainSpecificUtil} from "../../ChainSpecificUtil_v0_8_6.sol";
+import {ChainSpecificUtil} from "../ChainSpecificUtil_v0_8_6.sol";
import {ArbSys} from "../../vendor/@arbitrum/nitro-contracts/src/precompiles/ArbSys.sol";
import {ArbGasInfo} from "../../vendor/@arbitrum/nitro-contracts/src/precompiles/ArbGasInfo.sol";
diff --git a/contracts/src/v0.8/vrf/test/FixtureVRFCoordinatorV2_5.t.sol b/contracts/src/v0.8/vrf/test/FixtureVRFCoordinatorV2_5.t.sol
index c1c2c7eb27c..3574143f6c5 100644
--- a/contracts/src/v0.8/vrf/test/FixtureVRFCoordinatorV2_5.t.sol
+++ b/contracts/src/v0.8/vrf/test/FixtureVRFCoordinatorV2_5.t.sol
@@ -8,8 +8,8 @@ import {BlockhashStore} from "../dev/BlockhashStore.sol";
import {VRFV2PlusClient} from "../dev/libraries/VRFV2PlusClient.sol";
import {ExposedVRFCoordinatorV2_5} from "../dev/testhelpers/ExposedVRFCoordinatorV2_5.sol";
import {VRFV2PlusConsumerExample} from "../dev/testhelpers/VRFV2PlusConsumerExample.sol";
-import {MockLinkToken} from "../../mocks/MockLinkToken.sol";
-import {MockV3Aggregator} from "../../tests/MockV3Aggregator.sol";
+import {MockLinkToken} from "../../functions/tests/v1_X/testhelpers/MockLinkToken.sol";
+import {MockV3Aggregator} from "../../shared/mocks/MockV3Aggregator.sol";
import "./BaseTest.t.sol";
contract FixtureVRFCoordinatorV2_5 is BaseTest, VRF {
diff --git a/contracts/src/v0.8/vrf/test/VRFCoordinatorV2Mock.t.sol b/contracts/src/v0.8/vrf/test/VRFCoordinatorV2Mock.t.sol
index 1716118b765..c0c0a2a2f52 100644
--- a/contracts/src/v0.8/vrf/test/VRFCoordinatorV2Mock.t.sol
+++ b/contracts/src/v0.8/vrf/test/VRFCoordinatorV2Mock.t.sol
@@ -2,8 +2,8 @@ pragma solidity 0.8.6;
import "./BaseTest.t.sol";
import {VRF} from "../VRF.sol";
-import {MockLinkToken} from "../../mocks/MockLinkToken.sol";
-import {MockV3Aggregator} from "../../tests/MockV3Aggregator.sol";
+import {MockLinkToken} from "../../functions/tests/v1_X/testhelpers/MockLinkToken.sol";
+import {MockV3Aggregator} from "../../shared/mocks/MockV3Aggregator.sol";
import {VRFCoordinatorV2Mock} from "../mocks/VRFCoordinatorV2Mock.sol";
import {VRFConsumerV2} from "../testhelpers/VRFConsumerV2.sol";
diff --git a/contracts/src/v0.8/vrf/test/VRFCoordinatorV2Plus_Migration.t.sol b/contracts/src/v0.8/vrf/test/VRFCoordinatorV2Plus_Migration.t.sol
index ad239592d41..2d12f5ec82e 100644
--- a/contracts/src/v0.8/vrf/test/VRFCoordinatorV2Plus_Migration.t.sol
+++ b/contracts/src/v0.8/vrf/test/VRFCoordinatorV2Plus_Migration.t.sol
@@ -6,8 +6,8 @@ import {ExposedVRFCoordinatorV2_5} from "../dev/testhelpers/ExposedVRFCoordinato
import {VRFCoordinatorV2_5} from "../dev/VRFCoordinatorV2_5.sol";
import {SubscriptionAPI} from "../dev/SubscriptionAPI.sol";
import {VRFV2PlusConsumerExample} from "../dev/testhelpers/VRFV2PlusConsumerExample.sol";
-import {MockLinkToken} from "../../mocks/MockLinkToken.sol";
-import {MockV3Aggregator} from "../../tests/MockV3Aggregator.sol";
+import {MockLinkToken} from "../../functions/tests/v1_X/testhelpers/MockLinkToken.sol";
+import {MockV3Aggregator} from "../../shared/mocks/MockV3Aggregator.sol";
import {VRFV2PlusMaliciousMigrator} from "../dev/testhelpers/VRFV2PlusMaliciousMigrator.sol";
contract VRFCoordinatorV2Plus_Migration is BaseTest {
diff --git a/contracts/src/v0.8/vrf/test/VRFCoordinatorV2_5Mock.t.sol b/contracts/src/v0.8/vrf/test/VRFCoordinatorV2_5Mock.t.sol
index 75c763c88cb..d379ab9679d 100644
--- a/contracts/src/v0.8/vrf/test/VRFCoordinatorV2_5Mock.t.sol
+++ b/contracts/src/v0.8/vrf/test/VRFCoordinatorV2_5Mock.t.sol
@@ -5,7 +5,7 @@ import {VRFV2PlusClient} from "../dev/libraries/VRFV2PlusClient.sol";
import {SubscriptionAPI} from "../dev/SubscriptionAPI.sol";
import {VRFCoordinatorV2_5Mock} from "../mocks/VRFCoordinatorV2_5Mock.sol";
import {VRFConsumerV2Plus} from "../testhelpers/VRFConsumerV2Plus.sol";
-import {MockLinkToken} from "../../mocks/MockLinkToken.sol";
+import {MockLinkToken} from "../../functions/tests/v1_X/testhelpers/MockLinkToken.sol";
contract VRFCoordinatorV2_5MockTest is BaseTest {
MockLinkToken internal s_linkToken;
diff --git a/contracts/src/v0.8/vrf/test/VRFCoordinatorV2_5_Arbitrum.t.sol b/contracts/src/v0.8/vrf/test/VRFCoordinatorV2_5_Arbitrum.t.sol
index 8e47b800ee5..a6c2c88d016 100644
--- a/contracts/src/v0.8/vrf/test/VRFCoordinatorV2_5_Arbitrum.t.sol
+++ b/contracts/src/v0.8/vrf/test/VRFCoordinatorV2_5_Arbitrum.t.sol
@@ -1,8 +1,8 @@
pragma solidity 0.8.19;
import "./BaseTest.t.sol";
-import {MockLinkToken} from "../../mocks/MockLinkToken.sol";
-import {MockV3Aggregator} from "../../tests/MockV3Aggregator.sol";
+import {MockLinkToken} from "../../functions/tests/v1_X/testhelpers/MockLinkToken.sol";
+import {MockV3Aggregator} from "../../shared/mocks/MockV3Aggregator.sol";
import {ExposedVRFCoordinatorV2_5_Arbitrum} from "../dev/testhelpers/ExposedVRFCoordinatorV2_5_Arbitrum.sol";
import {BlockhashStore} from "../dev/BlockhashStore.sol";
import {ArbGasInfo} from "../../vendor/@arbitrum/nitro-contracts/src/precompiles/ArbGasInfo.sol";
diff --git a/contracts/src/v0.8/vrf/test/VRFCoordinatorV2_5_Optimism.t.sol b/contracts/src/v0.8/vrf/test/VRFCoordinatorV2_5_Optimism.t.sol
index b54dbbaaa04..0ebec3b1c56 100644
--- a/contracts/src/v0.8/vrf/test/VRFCoordinatorV2_5_Optimism.t.sol
+++ b/contracts/src/v0.8/vrf/test/VRFCoordinatorV2_5_Optimism.t.sol
@@ -1,8 +1,8 @@
pragma solidity 0.8.19;
import "./BaseTest.t.sol";
-import {MockLinkToken} from "../../mocks/MockLinkToken.sol";
-import {MockV3Aggregator} from "../../tests/MockV3Aggregator.sol";
+import {MockLinkToken} from "../../functions/tests/v1_X/testhelpers/MockLinkToken.sol";
+import {MockV3Aggregator} from "../../shared/mocks/MockV3Aggregator.sol";
import {ExposedVRFCoordinatorV2_5_Optimism} from "../dev/testhelpers/ExposedVRFCoordinatorV2_5_Optimism.sol";
import {OptimismL1Fees} from "../dev/OptimismL1Fees.sol";
import {BlockhashStore} from "../dev/BlockhashStore.sol";
diff --git a/contracts/src/v0.8/vrf/test/VRFV2Plus.t.sol b/contracts/src/v0.8/vrf/test/VRFV2Plus.t.sol
index dd3f54b580a..5d8366b5c7f 100644
--- a/contracts/src/v0.8/vrf/test/VRFV2Plus.t.sol
+++ b/contracts/src/v0.8/vrf/test/VRFV2Plus.t.sol
@@ -2,8 +2,8 @@ pragma solidity 0.8.19;
import "./BaseTest.t.sol";
import {VRF} from "../VRF.sol";
-import {MockLinkToken} from "../../mocks/MockLinkToken.sol";
-import {MockV3Aggregator} from "../../tests/MockV3Aggregator.sol";
+import {MockLinkToken} from "../../functions/tests/v1_X/testhelpers/MockLinkToken.sol";
+import {MockV3Aggregator} from "../../shared/mocks/MockV3Aggregator.sol";
import {ExposedVRFCoordinatorV2_5} from "../dev/testhelpers/ExposedVRFCoordinatorV2_5.sol";
import {VRFCoordinatorV2_5} from "../dev/VRFCoordinatorV2_5.sol";
import {SubscriptionAPI} from "../dev/SubscriptionAPI.sol";
diff --git a/contracts/src/v0.8/vrf/test/VRFV2PlusSubscriptionAPI.t.sol b/contracts/src/v0.8/vrf/test/VRFV2PlusSubscriptionAPI.t.sol
index 4fbb44ea717..4e89c0ec5f7 100644
--- a/contracts/src/v0.8/vrf/test/VRFV2PlusSubscriptionAPI.t.sol
+++ b/contracts/src/v0.8/vrf/test/VRFV2PlusSubscriptionAPI.t.sol
@@ -4,8 +4,8 @@ import "./BaseTest.t.sol";
import {ExposedVRFCoordinatorV2_5} from "../dev/testhelpers/ExposedVRFCoordinatorV2_5.sol";
import {VRFV2PlusLoadTestWithMetrics} from "../dev/testhelpers/VRFV2PlusLoadTestWithMetrics.sol";
import {SubscriptionAPI} from "../dev/SubscriptionAPI.sol";
-import {MockLinkToken} from "../../mocks/MockLinkToken.sol";
-import {MockV3Aggregator} from "../../tests/MockV3Aggregator.sol";
+import {MockLinkToken} from "../../functions/tests/v1_X/testhelpers/MockLinkToken.sol";
+import {MockV3Aggregator} from "../../shared/mocks/MockV3Aggregator.sol";
import "@openzeppelin/contracts/utils/Strings.sol"; // for Strings.toString
import {VmSafe} from "forge-std/Vm.sol";
diff --git a/contracts/src/v0.8/vrf/test/VRFV2PlusWrapper.t.sol b/contracts/src/v0.8/vrf/test/VRFV2PlusWrapper.t.sol
index 4b3a893fe1f..45e2131ce7a 100644
--- a/contracts/src/v0.8/vrf/test/VRFV2PlusWrapper.t.sol
+++ b/contracts/src/v0.8/vrf/test/VRFV2PlusWrapper.t.sol
@@ -2,8 +2,8 @@
pragma solidity 0.8.19;
import {BaseTest} from "./BaseTest.t.sol";
-import {MockLinkToken} from "../../mocks/MockLinkToken.sol";
-import {MockV3Aggregator} from "../../tests/MockV3Aggregator.sol";
+import {MockLinkToken} from "../../functions/tests/v1_X/testhelpers/MockLinkToken.sol";
+import {MockV3Aggregator} from "../../shared/mocks/MockV3Aggregator.sol";
import {ExposedVRFCoordinatorV2_5} from "../dev/testhelpers/ExposedVRFCoordinatorV2_5.sol";
import {SubscriptionAPI} from "../dev/SubscriptionAPI.sol";
import {VRFV2PlusWrapperConsumerExample} from "../dev/testhelpers/VRFV2PlusWrapperConsumerExample.sol";
diff --git a/contracts/src/v0.8/vrf/test/VRFV2PlusWrapper_Arbitrum.t.sol b/contracts/src/v0.8/vrf/test/VRFV2PlusWrapper_Arbitrum.t.sol
index 96f14847c41..f88dd15f2d5 100644
--- a/contracts/src/v0.8/vrf/test/VRFV2PlusWrapper_Arbitrum.t.sol
+++ b/contracts/src/v0.8/vrf/test/VRFV2PlusWrapper_Arbitrum.t.sol
@@ -2,8 +2,8 @@
pragma solidity 0.8.19;
import {BaseTest} from "./BaseTest.t.sol";
-import {MockLinkToken} from "../../mocks/MockLinkToken.sol";
-import {MockV3Aggregator} from "../../tests/MockV3Aggregator.sol";
+import {MockLinkToken} from "../../functions/tests/v1_X/testhelpers/MockLinkToken.sol";
+import {MockV3Aggregator} from "../../shared/mocks/MockV3Aggregator.sol";
import {ExposedVRFCoordinatorV2_5_Arbitrum} from "../dev/testhelpers/ExposedVRFCoordinatorV2_5_Arbitrum.sol";
import {VRFV2PlusWrapper_Arbitrum} from "../dev/VRFV2PlusWrapper_Arbitrum.sol";
import {ArbGasInfo} from "../../vendor/@arbitrum/nitro-contracts/src/precompiles/ArbGasInfo.sol";
diff --git a/contracts/src/v0.8/vrf/test/VRFV2PlusWrapper_Migration.t.sol b/contracts/src/v0.8/vrf/test/VRFV2PlusWrapper_Migration.t.sol
index ba77686088e..26cc5a213ec 100644
--- a/contracts/src/v0.8/vrf/test/VRFV2PlusWrapper_Migration.t.sol
+++ b/contracts/src/v0.8/vrf/test/VRFV2PlusWrapper_Migration.t.sol
@@ -2,8 +2,8 @@
pragma solidity 0.8.19;
import {BaseTest} from "./BaseTest.t.sol";
-import {MockLinkToken} from "../../mocks/MockLinkToken.sol";
-import {MockV3Aggregator} from "../../tests/MockV3Aggregator.sol";
+import {MockLinkToken} from "../../functions/tests/v1_X/testhelpers/MockLinkToken.sol";
+import {MockV3Aggregator} from "../../shared/mocks/MockV3Aggregator.sol";
import {ExposedVRFCoordinatorV2_5} from "../dev/testhelpers/ExposedVRFCoordinatorV2_5.sol";
import {VRFCoordinatorV2Plus_V2Example} from "../dev/testhelpers/VRFCoordinatorV2Plus_V2Example.sol";
import {VRFV2PlusWrapperConsumerExample} from "../dev/testhelpers/VRFV2PlusWrapperConsumerExample.sol";
diff --git a/contracts/src/v0.8/vrf/test/VRFV2PlusWrapper_Optimism.t.sol b/contracts/src/v0.8/vrf/test/VRFV2PlusWrapper_Optimism.t.sol
index a8a97a57f0e..de56a9a7e2b 100644
--- a/contracts/src/v0.8/vrf/test/VRFV2PlusWrapper_Optimism.t.sol
+++ b/contracts/src/v0.8/vrf/test/VRFV2PlusWrapper_Optimism.t.sol
@@ -2,8 +2,8 @@
pragma solidity 0.8.19;
import {BaseTest} from "./BaseTest.t.sol";
-import {MockLinkToken} from "../../mocks/MockLinkToken.sol";
-import {MockV3Aggregator} from "../../tests/MockV3Aggregator.sol";
+import {MockLinkToken} from "../../functions/tests/v1_X/testhelpers/MockLinkToken.sol";
+import {MockV3Aggregator} from "../../shared/mocks/MockV3Aggregator.sol";
import {ExposedVRFCoordinatorV2_5_Optimism} from "../dev/testhelpers/ExposedVRFCoordinatorV2_5_Optimism.sol";
import {VRFV2PlusWrapper_Optimism} from "../dev/VRFV2PlusWrapper_Optimism.sol";
import {OptimismL1Fees} from "../dev/OptimismL1Fees.sol";
diff --git a/contracts/src/v0.8/vrf/testhelpers/ChainSpecificUtilHelper.sol b/contracts/src/v0.8/vrf/testhelpers/ChainSpecificUtilHelper.sol
index 16a157e3547..96a088a652e 100644
--- a/contracts/src/v0.8/vrf/testhelpers/ChainSpecificUtilHelper.sol
+++ b/contracts/src/v0.8/vrf/testhelpers/ChainSpecificUtilHelper.sol
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
-import {ChainSpecificUtil} from "../../ChainSpecificUtil_v0_8_6.sol";
+import {ChainSpecificUtil} from "../ChainSpecificUtil_v0_8_6.sol";
/// @dev A helper contract that exposes ChainSpecificUtil methods for testing
contract ChainSpecificUtilHelper {
diff --git a/contracts/src/v0.8/vrf/testhelpers/VRFCoordinatorTestV2.sol b/contracts/src/v0.8/vrf/testhelpers/VRFCoordinatorTestV2.sol
index 5774b770750..5c42a4070dc 100644
--- a/contracts/src/v0.8/vrf/testhelpers/VRFCoordinatorTestV2.sol
+++ b/contracts/src/v0.8/vrf/testhelpers/VRFCoordinatorTestV2.sol
@@ -5,19 +5,13 @@ import {LinkTokenInterface} from "../../shared/interfaces/LinkTokenInterface.sol
import {BlockhashStoreInterface} from "../interfaces/BlockhashStoreInterface.sol";
import {AggregatorV3Interface} from "../../shared/interfaces/AggregatorV3Interface.sol";
import {VRFCoordinatorV2Interface} from "../interfaces/VRFCoordinatorV2Interface.sol";
-import {TypeAndVersionInterface} from "../../interfaces/TypeAndVersionInterface.sol";
+import {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol";
import {IERC677Receiver} from "../../shared/interfaces/IERC677Receiver.sol";
import {VRF} from "../VRF.sol";
import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol";
import {VRFConsumerBaseV2} from "../VRFConsumerBaseV2.sol";
-contract VRFCoordinatorTestV2 is
- VRF,
- ConfirmedOwner,
- TypeAndVersionInterface,
- VRFCoordinatorV2Interface,
- IERC677Receiver
-{
+contract VRFCoordinatorTestV2 is VRF, ConfirmedOwner, ITypeAndVersion, VRFCoordinatorV2Interface, IERC677Receiver {
LinkTokenInterface public immutable LINK;
AggregatorV3Interface public immutable LINK_ETH_FEED;
BlockhashStoreInterface public immutable BLOCKHASH_STORE;
diff --git a/contracts/src/v0.8/vrf/testhelpers/VRFV2LoadTestWithMetrics.sol b/contracts/src/v0.8/vrf/testhelpers/VRFV2LoadTestWithMetrics.sol
index b4d0104acee..3e9e7bfc47a 100644
--- a/contracts/src/v0.8/vrf/testhelpers/VRFV2LoadTestWithMetrics.sol
+++ b/contracts/src/v0.8/vrf/testhelpers/VRFV2LoadTestWithMetrics.sol
@@ -3,7 +3,7 @@ pragma solidity ^0.8.0;
import {VRFCoordinatorV2Interface} from "../interfaces/VRFCoordinatorV2Interface.sol";
import {VRFConsumerBaseV2} from "../VRFConsumerBaseV2.sol";
-import {ChainSpecificUtil} from "../../ChainSpecificUtil_v0_8_6.sol";
+import {ChainSpecificUtil} from "../ChainSpecificUtil_v0_8_6.sol";
import {LinkTokenInterface} from "../../shared/interfaces/LinkTokenInterface.sol";
/**
diff --git a/contracts/src/v0.8/vrf/testhelpers/VRFV2OwnerTestConsumer.sol b/contracts/src/v0.8/vrf/testhelpers/VRFV2OwnerTestConsumer.sol
index 8f1b275397c..c0c1c659fe1 100644
--- a/contracts/src/v0.8/vrf/testhelpers/VRFV2OwnerTestConsumer.sol
+++ b/contracts/src/v0.8/vrf/testhelpers/VRFV2OwnerTestConsumer.sol
@@ -4,7 +4,7 @@ pragma solidity ^0.8.0;
import {VRFCoordinatorV2Interface} from "../interfaces/VRFCoordinatorV2Interface.sol";
import {VRFConsumerBaseV2} from "../VRFConsumerBaseV2.sol";
import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol";
-import {ChainSpecificUtil} from "../../ChainSpecificUtil_v0_8_6.sol";
+import {ChainSpecificUtil} from "../ChainSpecificUtil_v0_8_6.sol";
import {LinkTokenInterface} from "../../shared/interfaces/LinkTokenInterface.sol";
contract VRFV2OwnerTestConsumer is VRFConsumerBaseV2, ConfirmedOwner {
diff --git a/contracts/src/v0.8/vrf/testhelpers/VRFV2WrapperLoadTestConsumer.sol b/contracts/src/v0.8/vrf/testhelpers/VRFV2WrapperLoadTestConsumer.sol
index 3da8f17469a..9501a74b220 100644
--- a/contracts/src/v0.8/vrf/testhelpers/VRFV2WrapperLoadTestConsumer.sol
+++ b/contracts/src/v0.8/vrf/testhelpers/VRFV2WrapperLoadTestConsumer.sol
@@ -3,7 +3,7 @@ pragma solidity ^0.8.6;
import {VRFV2WrapperConsumerBase} from "../VRFV2WrapperConsumerBase.sol";
import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol";
-import {ChainSpecificUtil} from "../../ChainSpecificUtil_v0_8_6.sol";
+import {ChainSpecificUtil} from "../ChainSpecificUtil_v0_8_6.sol";
import {VRFV2WrapperInterface} from "../interfaces/VRFV2WrapperInterface.sol";
contract VRFV2WrapperLoadTestConsumer is VRFV2WrapperConsumerBase, ConfirmedOwner {
diff --git a/contracts/test/v0.8/Chainlink.test.ts b/contracts/test/v0.8/Chainlink.test.ts
deleted file mode 100644
index 30063ca1024..00000000000
--- a/contracts/test/v0.8/Chainlink.test.ts
+++ /dev/null
@@ -1,182 +0,0 @@
-import { ethers } from 'hardhat'
-import { publicAbi, decodeDietCBOR, hexToBuf } from '../test-helpers/helpers'
-import { assert } from 'chai'
-import { Contract, ContractFactory, providers, Signer } from 'ethers'
-import { Roles, getUsers } from '../test-helpers/setup'
-import { makeDebug } from '../test-helpers/debug'
-
-const debug = makeDebug('ChainlinkTestHelper')
-let concreteChainlinkFactory: ContractFactory
-
-let roles: Roles
-
-before(async () => {
- roles = (await getUsers()).roles
- concreteChainlinkFactory = await ethers.getContractFactory(
- 'src/v0.8/tests/ChainlinkTestHelper.sol:ChainlinkTestHelper',
- roles.defaultAccount,
- )
-})
-
-describe('ChainlinkTestHelper', () => {
- let ccl: Contract
- let defaultAccount: Signer
-
- beforeEach(async () => {
- defaultAccount = roles.defaultAccount
- ccl = await concreteChainlinkFactory.connect(defaultAccount).deploy()
- })
-
- it('has a limited public interface [ @skip-coverage ]', () => {
- publicAbi(ccl, [
- 'add',
- 'addBytes',
- 'addInt',
- 'addStringArray',
- 'addUint',
- 'closeEvent',
- 'setBuffer',
- ])
- })
-
- async function parseCCLEvent(tx: providers.TransactionResponse) {
- const receipt = await tx.wait()
- const data = receipt.logs?.[0].data
- const d = debug.extend('parseCCLEvent')
- d('data %s', data)
- return ethers.utils.defaultAbiCoder.decode(['bytes'], data ?? '')
- }
-
- describe('#close', () => {
- it('handles empty payloads', async () => {
- const tx = await ccl.closeEvent()
- const [payload] = await parseCCLEvent(tx)
- const decoded = await decodeDietCBOR(payload)
- assert.deepEqual(decoded, {})
- })
- })
-
- describe('#setBuffer', () => {
- it('emits the buffer', async () => {
- await ccl.setBuffer('0xA161616162')
- const tx = await ccl.closeEvent()
- const [payload] = await parseCCLEvent(tx)
- const decoded = await decodeDietCBOR(payload)
- assert.deepEqual(decoded, { a: 'b' })
- })
- })
-
- describe('#add', () => {
- it('stores and logs keys and values', async () => {
- await ccl.add('first', 'word!!')
- const tx = await ccl.closeEvent()
- const [payload] = await parseCCLEvent(tx)
- const decoded = await decodeDietCBOR(payload)
- assert.deepEqual(decoded, { first: 'word!!' })
- })
-
- it('handles two entries', async () => {
- await ccl.add('first', 'uno')
- await ccl.add('second', 'dos')
- const tx = await ccl.closeEvent()
- const [payload] = await parseCCLEvent(tx)
- const decoded = await decodeDietCBOR(payload)
-
- assert.deepEqual(decoded, {
- first: 'uno',
- second: 'dos',
- })
- })
- })
-
- describe('#addBytes', () => {
- it('stores and logs keys and values', async () => {
- await ccl.addBytes('first', '0xaabbccddeeff')
- const tx = await ccl.closeEvent()
- const [payload] = await parseCCLEvent(tx)
- const decoded = await decodeDietCBOR(payload)
- const expected = hexToBuf('0xaabbccddeeff')
- assert.deepEqual(decoded, { first: expected })
- })
-
- it('handles two entries', async () => {
- await ccl.addBytes('first', '0x756E6F')
- await ccl.addBytes('second', '0x646F73')
- const tx = await ccl.closeEvent()
- const [payload] = await parseCCLEvent(tx)
- const decoded = await decodeDietCBOR(payload)
-
- const expectedFirst = hexToBuf('0x756E6F')
- const expectedSecond = hexToBuf('0x646F73')
- assert.deepEqual(decoded, {
- first: expectedFirst,
- second: expectedSecond,
- })
- })
-
- it('handles strings', async () => {
- await ccl.addBytes('first', ethers.utils.toUtf8Bytes('apple'))
- const tx = await ccl.closeEvent()
- const [payload] = await parseCCLEvent(tx)
- const decoded = await decodeDietCBOR(payload)
- const expected = ethers.utils.toUtf8Bytes('apple')
- assert.deepEqual(decoded, { first: expected })
- })
- })
-
- describe('#addInt', () => {
- it('stores and logs keys and values', async () => {
- await ccl.addInt('first', 1)
- const tx = await ccl.closeEvent()
- const [payload] = await parseCCLEvent(tx)
- const decoded = await decodeDietCBOR(payload)
- assert.deepEqual(decoded, { first: 1 })
- })
-
- it('handles two entries', async () => {
- await ccl.addInt('first', 1)
- await ccl.addInt('second', 2)
- const tx = await ccl.closeEvent()
- const [payload] = await parseCCLEvent(tx)
- const decoded = await decodeDietCBOR(payload)
-
- assert.deepEqual(decoded, {
- first: 1,
- second: 2,
- })
- })
- })
-
- describe('#addUint', () => {
- it('stores and logs keys and values', async () => {
- await ccl.addUint('first', 1)
- const tx = await ccl.closeEvent()
- const [payload] = await parseCCLEvent(tx)
- const decoded = await decodeDietCBOR(payload)
- assert.deepEqual(decoded, { first: 1 })
- })
-
- it('handles two entries', async () => {
- await ccl.addUint('first', 1)
- await ccl.addUint('second', 2)
- const tx = await ccl.closeEvent()
- const [payload] = await parseCCLEvent(tx)
- const decoded = await decodeDietCBOR(payload)
-
- assert.deepEqual(decoded, {
- first: 1,
- second: 2,
- })
- })
- })
-
- describe('#addStringArray', () => {
- it('stores and logs keys and values', async () => {
- await ccl.addStringArray('word', ['seinfeld', '"4"', 'LIFE'])
- const tx = await ccl.closeEvent()
- const [payload] = await parseCCLEvent(tx)
- const decoded = await decodeDietCBOR(payload)
- assert.deepEqual(decoded, { word: ['seinfeld', '"4"', 'LIFE'] })
- })
- })
-})
diff --git a/contracts/test/v0.8/ChainlinkClient.test.ts b/contracts/test/v0.8/ChainlinkClient.test.ts
deleted file mode 100644
index c5691211c1a..00000000000
--- a/contracts/test/v0.8/ChainlinkClient.test.ts
+++ /dev/null
@@ -1,452 +0,0 @@
-import { ethers } from 'hardhat'
-import { assert } from 'chai'
-import { Contract, ContractFactory } from 'ethers'
-import { getUsers, Roles } from '../test-helpers/setup'
-import {
- convertFufillParams,
- decodeCCRequest,
- decodeRunRequest,
- RunRequest,
-} from '../test-helpers/oracle'
-import { decodeDietCBOR } from '../test-helpers/helpers'
-import { evmRevert } from '../test-helpers/matchers'
-
-let concreteChainlinkClientFactory: ContractFactory
-let emptyOracleFactory: ContractFactory
-let getterSetterFactory: ContractFactory
-let operatorFactory: ContractFactory
-let linkTokenFactory: ContractFactory
-
-let roles: Roles
-
-before(async () => {
- roles = (await getUsers()).roles
-
- concreteChainlinkClientFactory = await ethers.getContractFactory(
- 'src/v0.8/tests/ChainlinkClientTestHelper.sol:ChainlinkClientTestHelper',
- roles.defaultAccount,
- )
- emptyOracleFactory = await ethers.getContractFactory(
- 'src/v0.8/operatorforwarder/test/testhelpers/EmptyOracle.sol:EmptyOracle',
- roles.defaultAccount,
- )
- getterSetterFactory = await ethers.getContractFactory(
- 'src/v0.8/operatorforwarder/test/testhelpers/GetterSetter.sol:GetterSetter',
- roles.defaultAccount,
- )
- operatorFactory = await ethers.getContractFactory(
- 'src/v0.8/operatorforwarder/Operator.sol:Operator',
- roles.defaultAccount,
- )
- linkTokenFactory = await ethers.getContractFactory(
- 'src/v0.8/shared/test/helpers/LinkTokenTestHelper.sol:LinkTokenTestHelper',
- roles.defaultAccount,
- )
-})
-
-describe('ChainlinkClientTestHelper', () => {
- const specId =
- '0x4c7b7ffb66b344fbaa64995af81e355a00000000000000000000000000000000'
- let cc: Contract
- let gs: Contract
- let oc: Contract
- let newoc: Contract
- let link: Contract
-
- beforeEach(async () => {
- link = await linkTokenFactory.connect(roles.defaultAccount).deploy()
- oc = await operatorFactory
- .connect(roles.defaultAccount)
- .deploy(link.address, await roles.defaultAccount.getAddress())
- newoc = await operatorFactory
- .connect(roles.defaultAccount)
- .deploy(link.address, await roles.defaultAccount.getAddress())
- gs = await getterSetterFactory.connect(roles.defaultAccount).deploy()
- cc = await concreteChainlinkClientFactory
- .connect(roles.defaultAccount)
- .deploy(link.address, oc.address)
- })
-
- describe('#newRequest', () => {
- it('forwards the information to the oracle contract through the link token', async () => {
- const tx = await cc.publicNewRequest(
- specId,
- gs.address,
- ethers.utils.toUtf8Bytes('requestedBytes32(bytes32,bytes32)'),
- )
- const receipt = await tx.wait()
-
- assert.equal(1, receipt.logs?.length)
- const [jId, cbAddr, cbFId, cborData] = receipt.logs
- ? decodeCCRequest(receipt.logs[0])
- : []
- const params = decodeDietCBOR(cborData ?? '')
-
- assert.equal(specId, jId)
- assert.equal(gs.address, cbAddr)
- assert.equal('0xed53e511', cbFId)
- assert.deepEqual({}, params)
- })
- })
-
- describe('#chainlinkRequest(Request)', () => {
- it('emits an event from the contract showing the run ID', async () => {
- const tx = await cc.publicRequest(
- specId,
- cc.address,
- ethers.utils.toUtf8Bytes('fulfillRequest(bytes32,bytes32)'),
- 0,
- )
-
- const { events, logs } = await tx.wait()
-
- assert.equal(4, events?.length)
-
- assert.equal(logs?.[0].address, cc.address)
- assert.equal(events?.[0].event, 'ChainlinkRequested')
- })
- })
-
- describe('#chainlinkRequestTo(Request)', () => {
- it('emits an event from the contract showing the run ID', async () => {
- const tx = await cc.publicRequestRunTo(
- newoc.address,
- specId,
- cc.address,
- ethers.utils.toUtf8Bytes('fulfillRequest(bytes32,bytes32)'),
- 0,
- )
- const { events } = await tx.wait()
-
- assert.equal(4, events?.length)
- assert.equal(events?.[0].event, 'ChainlinkRequested')
- })
-
- it('emits an event on the target oracle contract', async () => {
- const tx = await cc.publicRequestRunTo(
- newoc.address,
- specId,
- cc.address,
- ethers.utils.toUtf8Bytes('fulfillRequest(bytes32,bytes32)'),
- 0,
- )
- const { logs } = await tx.wait()
- const event = logs && newoc.interface.parseLog(logs[3])
-
- assert.equal(4, logs?.length)
- assert.equal(event?.name, 'OracleRequest')
- })
-
- it('does not modify the stored oracle address', async () => {
- await cc.publicRequestRunTo(
- newoc.address,
- specId,
- cc.address,
- ethers.utils.toUtf8Bytes('fulfillRequest(bytes32,bytes32)'),
- 0,
- )
-
- const actualOracleAddress = await cc.publicOracleAddress()
- assert.equal(oc.address, actualOracleAddress)
- })
- })
-
- describe('#requestOracleData', () => {
- it('emits an event from the contract showing the run ID', async () => {
- const tx = await cc.publicRequestOracleData(
- specId,
- ethers.utils.toUtf8Bytes('fulfillRequest(bytes32,bytes32)'),
- 0,
- )
-
- const { events, logs } = await tx.wait()
-
- assert.equal(4, events?.length)
-
- assert.equal(logs?.[0].address, cc.address)
- assert.equal(events?.[0].event, 'ChainlinkRequested')
- })
- })
-
- describe('#requestOracleDataFrom', () => {
- it('emits an event from the contract showing the run ID', async () => {
- const tx = await cc.publicRequestOracleDataFrom(
- newoc.address,
- specId,
- ethers.utils.toUtf8Bytes('fulfillRequest(bytes32,bytes32)'),
- 0,
- )
- const { events } = await tx.wait()
-
- assert.equal(4, events?.length)
- assert.equal(events?.[0].event, 'ChainlinkRequested')
- })
-
- it('emits an event on the target oracle contract', async () => {
- const tx = await cc.publicRequestOracleDataFrom(
- newoc.address,
- specId,
- ethers.utils.toUtf8Bytes('fulfillRequest(bytes32,bytes32)'),
- 0,
- )
- const { logs } = await tx.wait()
- const event = logs && newoc.interface.parseLog(logs[3])
-
- assert.equal(4, logs?.length)
- assert.equal(event?.name, 'OracleRequest')
- })
-
- it('does not modify the stored oracle address', async () => {
- await cc.publicRequestOracleDataFrom(
- newoc.address,
- specId,
- ethers.utils.toUtf8Bytes('fulfillRequest(bytes32,bytes32)'),
- 0,
- )
-
- const actualOracleAddress = await cc.publicOracleAddress()
- assert.equal(oc.address, actualOracleAddress)
- })
- })
-
- describe('#cancelChainlinkRequest', () => {
- let requestId: string
- // a concrete chainlink attached to an empty oracle
- let ecc: Contract
-
- beforeEach(async () => {
- const emptyOracle = await emptyOracleFactory
- .connect(roles.defaultAccount)
- .deploy()
- ecc = await concreteChainlinkClientFactory
- .connect(roles.defaultAccount)
- .deploy(link.address, emptyOracle.address)
-
- const tx = await ecc.publicRequest(
- specId,
- ecc.address,
- ethers.utils.toUtf8Bytes('fulfillRequest(bytes32,bytes32)'),
- 0,
- )
- const { events } = await tx.wait()
- requestId = (events?.[0]?.args as any).id
- })
-
- it('emits an event from the contract showing the run was cancelled', async () => {
- const tx = await ecc.publicCancelRequest(
- requestId,
- 0,
- ethers.utils.hexZeroPad('0x', 4),
- 0,
- )
- const { events } = await tx.wait()
-
- assert.equal(1, events?.length)
- assert.equal(events?.[0].event, 'ChainlinkCancelled')
- assert.equal(requestId, (events?.[0].args as any).id)
- })
-
- it('throws if given a bogus event ID', async () => {
- await evmRevert(
- ecc.publicCancelRequest(
- ethers.utils.formatBytes32String('bogusId'),
- 0,
- ethers.utils.hexZeroPad('0x', 4),
- 0,
- ),
- )
- })
- })
-
- describe('#recordChainlinkFulfillment(modifier)', () => {
- let request: RunRequest
-
- beforeEach(async () => {
- await oc.setAuthorizedSenders([await roles.defaultAccount.getAddress()])
- const tx = await cc.publicRequest(
- specId,
- cc.address,
- ethers.utils.toUtf8Bytes('fulfillRequest(bytes32,bytes32)'),
- 0,
- )
- const { logs } = await tx.wait()
-
- request = decodeRunRequest(logs?.[3])
- })
-
- it('emits an event marking the request fulfilled', async () => {
- const tx = await oc
- .connect(roles.defaultAccount)
- .fulfillOracleRequest(
- ...convertFufillParams(
- request,
- ethers.utils.formatBytes32String('hi mom!'),
- ),
- )
- const { logs } = await tx.wait()
-
- const event = logs && cc.interface.parseLog(logs[1])
-
- assert.equal(2, logs?.length)
- assert.equal(event?.name, 'ChainlinkFulfilled')
- assert.equal(request.requestId, event?.args.id)
- })
-
- it('should only allow one fulfillment per id', async () => {
- await oc
- .connect(roles.defaultAccount)
- .fulfillOracleRequest(
- ...convertFufillParams(
- request,
- ethers.utils.formatBytes32String('hi mom!'),
- ),
- )
-
- await evmRevert(
- oc
- .connect(roles.defaultAccount)
- .fulfillOracleRequest(
- ...convertFufillParams(
- request,
- ethers.utils.formatBytes32String('hi mom!'),
- ),
- ),
- 'Must have a valid requestId',
- )
- })
-
- it('should only allow the oracle to fulfill the request', async () => {
- await evmRevert(
- oc
- .connect(roles.stranger)
- .fulfillOracleRequest(
- ...convertFufillParams(
- request,
- ethers.utils.formatBytes32String('hi mom!'),
- ),
- ),
- 'Not authorized sender',
- )
- })
- })
-
- describe('#fulfillChainlinkRequest(function)', () => {
- let request: RunRequest
-
- beforeEach(async () => {
- await oc.setAuthorizedSenders([await roles.defaultAccount.getAddress()])
- const tx = await cc.publicRequest(
- specId,
- cc.address,
- ethers.utils.toUtf8Bytes(
- 'publicFulfillChainlinkRequest(bytes32,bytes32)',
- ),
- 0,
- )
- const { logs } = await tx.wait()
-
- request = decodeRunRequest(logs?.[3])
- })
-
- it('emits an event marking the request fulfilled', async () => {
- await oc.setAuthorizedSenders([await roles.defaultAccount.getAddress()])
- const tx = await oc
- .connect(roles.defaultAccount)
- .fulfillOracleRequest(
- ...convertFufillParams(
- request,
- ethers.utils.formatBytes32String('hi mom!'),
- ),
- )
-
- const { logs } = await tx.wait()
- const event = logs && cc.interface.parseLog(logs[1])
-
- assert.equal(2, logs?.length)
- assert.equal(event?.name, 'ChainlinkFulfilled')
- assert.equal(request.requestId, event?.args?.id)
- })
-
- it('should only allow one fulfillment per id', async () => {
- await oc
- .connect(roles.defaultAccount)
- .fulfillOracleRequest(
- ...convertFufillParams(
- request,
- ethers.utils.formatBytes32String('hi mom!'),
- ),
- )
-
- await evmRevert(
- oc
- .connect(roles.defaultAccount)
- .fulfillOracleRequest(
- ...convertFufillParams(
- request,
- ethers.utils.formatBytes32String('hi mom!'),
- ),
- ),
- 'Must have a valid requestId',
- )
- })
-
- it('should only allow the oracle to fulfill the request', async () => {
- await evmRevert(
- oc
- .connect(roles.stranger)
- .fulfillOracleRequest(
- ...convertFufillParams(
- request,
- ethers.utils.formatBytes32String('hi mom!'),
- ),
- ),
- 'Not authorized sender',
- )
- })
- })
-
- describe('#chainlinkToken', () => {
- it('returns the Link Token address', async () => {
- const addr = await cc.publicChainlinkToken()
- assert.equal(addr, link.address)
- })
- })
-
- describe('#addExternalRequest', () => {
- let mock: Contract
- let request: RunRequest
-
- beforeEach(async () => {
- mock = await concreteChainlinkClientFactory
- .connect(roles.defaultAccount)
- .deploy(link.address, oc.address)
-
- const tx = await cc.publicRequest(
- specId,
- mock.address,
- ethers.utils.toUtf8Bytes('fulfillRequest(bytes32,bytes32)'),
- 0,
- )
- const receipt = await tx.wait()
-
- request = decodeRunRequest(receipt.logs?.[3])
- await mock.publicAddExternalRequest(oc.address, request.requestId)
- })
-
- it('allows the external request to be fulfilled', async () => {
- await oc.setAuthorizedSenders([await roles.defaultAccount.getAddress()])
- await oc.fulfillOracleRequest(
- ...convertFufillParams(
- request,
- ethers.utils.formatBytes32String('hi mom!'),
- ),
- )
- })
-
- it('does not allow the same requestId to be used', async () => {
- await evmRevert(
- cc.publicAddExternalRequest(newoc.address, request.requestId),
- )
- })
- })
-})
diff --git a/contracts/test/v0.8/Flags.test.ts b/contracts/test/v0.8/Flags.test.ts
deleted file mode 100644
index eff0912c9e1..00000000000
--- a/contracts/test/v0.8/Flags.test.ts
+++ /dev/null
@@ -1,405 +0,0 @@
-import { ethers } from 'hardhat'
-import { publicAbi } from '../test-helpers/helpers'
-import { assert, expect } from 'chai'
-import { Contract, ContractFactory } from 'ethers'
-import { Personas, getUsers } from '../test-helpers/setup'
-
-let personas: Personas
-
-let controllerFactory: ContractFactory
-let flagsFactory: ContractFactory
-let consumerFactory: ContractFactory
-
-let controller: Contract
-let flags: Contract
-let consumer: Contract
-
-before(async () => {
- personas = (await getUsers()).personas
- controllerFactory = await ethers.getContractFactory(
- 'src/v0.8/shared/access/SimpleWriteAccessController.sol:SimpleWriteAccessController',
- personas.Nelly,
- )
- consumerFactory = await ethers.getContractFactory(
- 'src/v0.8/tests/FlagsTestHelper.sol:FlagsTestHelper',
- personas.Nelly,
- )
- flagsFactory = await ethers.getContractFactory(
- 'src/v0.8/Flags.sol:Flags',
- personas.Nelly,
- )
-})
-
-describe('Flags', () => {
- beforeEach(async () => {
- controller = await controllerFactory.deploy()
- flags = await flagsFactory.deploy(controller.address)
- await flags.disableAccessCheck()
- consumer = await consumerFactory.deploy(flags.address)
- })
-
- it('has a limited public interface [ @skip-coverage ]', async () => {
- publicAbi(flags, [
- 'getFlag',
- 'getFlags',
- 'lowerFlags',
- 'raiseFlag',
- 'raiseFlags',
- 'raisingAccessController',
- 'setRaisingAccessController',
- // Ownable methods:
- 'acceptOwnership',
- 'owner',
- 'transferOwnership',
- // AccessControl methods:
- 'addAccess',
- 'disableAccessCheck',
- 'enableAccessCheck',
- 'removeAccess',
- 'checkEnabled',
- 'hasAccess',
- ])
- })
-
- describe('#raiseFlag', () => {
- describe('when called by the owner', () => {
- it('updates the warning flag', async () => {
- assert.equal(false, await flags.getFlag(consumer.address))
-
- await flags.connect(personas.Nelly).raiseFlag(consumer.address)
-
- assert.equal(true, await flags.getFlag(consumer.address))
- })
-
- it('emits an event log', async () => {
- await expect(flags.connect(personas.Nelly).raiseFlag(consumer.address))
- .to.emit(flags, 'FlagRaised')
- .withArgs(consumer.address)
- })
-
- describe('if a flag has already been raised', () => {
- beforeEach(async () => {
- await flags.connect(personas.Nelly).raiseFlag(consumer.address)
- })
-
- it('emits an event log', async () => {
- const tx = await flags
- .connect(personas.Nelly)
- .raiseFlag(consumer.address)
- const receipt = await tx.wait()
- assert.equal(0, receipt.events?.length)
- })
- })
- })
-
- describe('when called by an enabled setter', () => {
- beforeEach(async () => {
- await controller
- .connect(personas.Nelly)
- .addAccess(await personas.Neil.getAddress())
- })
-
- it('sets the flags', async () => {
- await flags.connect(personas.Neil).raiseFlag(consumer.address),
- assert.equal(true, await flags.getFlag(consumer.address))
- })
- })
-
- describe('when called by a non-enabled setter', () => {
- it('reverts', async () => {
- await expect(
- flags.connect(personas.Neil).raiseFlag(consumer.address),
- ).to.be.revertedWith('Not allowed to raise flags')
- })
- })
-
- describe('when called when there is no raisingAccessController', () => {
- beforeEach(async () => {
- await expect(
- flags
- .connect(personas.Nelly)
- .setRaisingAccessController(
- '0x0000000000000000000000000000000000000000',
- ),
- ).to.emit(flags, 'RaisingAccessControllerUpdated')
- assert.equal(
- '0x0000000000000000000000000000000000000000',
- await flags.raisingAccessController(),
- )
- })
-
- it('succeeds for the owner', async () => {
- await flags.connect(personas.Nelly).raiseFlag(consumer.address)
- assert.equal(true, await flags.getFlag(consumer.address))
- })
-
- it('reverts for non-owner', async () => {
- await expect(flags.connect(personas.Neil).raiseFlag(consumer.address))
- .to.be.reverted
- })
- })
- })
-
- describe('#raiseFlags', () => {
- describe('when called by the owner', () => {
- it('updates the warning flag', async () => {
- assert.equal(false, await flags.getFlag(consumer.address))
-
- await flags.connect(personas.Nelly).raiseFlags([consumer.address])
-
- assert.equal(true, await flags.getFlag(consumer.address))
- })
-
- it('emits an event log', async () => {
- await expect(
- flags.connect(personas.Nelly).raiseFlags([consumer.address]),
- )
- .to.emit(flags, 'FlagRaised')
- .withArgs(consumer.address)
- })
-
- describe('if a flag has already been raised', () => {
- beforeEach(async () => {
- await flags.connect(personas.Nelly).raiseFlags([consumer.address])
- })
-
- it('emits an event log', async () => {
- const tx = await flags
- .connect(personas.Nelly)
- .raiseFlags([consumer.address])
- const receipt = await tx.wait()
- assert.equal(0, receipt.events?.length)
- })
- })
- })
-
- describe('when called by an enabled setter', () => {
- beforeEach(async () => {
- await controller
- .connect(personas.Nelly)
- .addAccess(await personas.Neil.getAddress())
- })
-
- it('sets the flags', async () => {
- await flags.connect(personas.Neil).raiseFlags([consumer.address]),
- assert.equal(true, await flags.getFlag(consumer.address))
- })
- })
-
- describe('when called by a non-enabled setter', () => {
- it('reverts', async () => {
- await expect(
- flags.connect(personas.Neil).raiseFlags([consumer.address]),
- ).to.be.revertedWith('Not allowed to raise flags')
- })
- })
-
- describe('when called when there is no raisingAccessController', () => {
- beforeEach(async () => {
- await expect(
- flags
- .connect(personas.Nelly)
- .setRaisingAccessController(
- '0x0000000000000000000000000000000000000000',
- ),
- ).to.emit(flags, 'RaisingAccessControllerUpdated')
-
- assert.equal(
- '0x0000000000000000000000000000000000000000',
- await flags.raisingAccessController(),
- )
- })
-
- it('succeeds for the owner', async () => {
- await flags.connect(personas.Nelly).raiseFlags([consumer.address])
- assert.equal(true, await flags.getFlag(consumer.address))
- })
-
- it('reverts for non-owners', async () => {
- await expect(
- flags.connect(personas.Neil).raiseFlags([consumer.address]),
- ).to.be.reverted
- })
- })
- })
-
- describe('#lowerFlags', () => {
- beforeEach(async () => {
- await flags.connect(personas.Nelly).raiseFlags([consumer.address])
- })
-
- describe('when called by the owner', () => {
- it('updates the warning flag', async () => {
- assert.equal(true, await flags.getFlag(consumer.address))
-
- await flags.connect(personas.Nelly).lowerFlags([consumer.address])
-
- assert.equal(false, await flags.getFlag(consumer.address))
- })
-
- it('emits an event log', async () => {
- await expect(
- flags.connect(personas.Nelly).lowerFlags([consumer.address]),
- )
- .to.emit(flags, 'FlagLowered')
- .withArgs(consumer.address)
- })
-
- describe('if a flag has already been raised', () => {
- beforeEach(async () => {
- await flags.connect(personas.Nelly).lowerFlags([consumer.address])
- })
-
- it('emits an event log', async () => {
- const tx = await flags
- .connect(personas.Nelly)
- .lowerFlags([consumer.address])
- const receipt = await tx.wait()
- assert.equal(0, receipt.events?.length)
- })
- })
- })
-
- describe('when called by a non-owner', () => {
- it('reverts', async () => {
- await expect(
- flags.connect(personas.Neil).lowerFlags([consumer.address]),
- ).to.be.revertedWith('Only callable by owner')
- })
- })
- })
-
- describe('#getFlag', () => {
- describe('if the access control is turned on', () => {
- beforeEach(async () => {
- await flags.connect(personas.Nelly).enableAccessCheck()
- })
-
- it('reverts', async () => {
- await expect(consumer.getFlag(consumer.address)).to.be.revertedWith(
- 'No access',
- )
- })
-
- describe('if access is granted to the address', () => {
- beforeEach(async () => {
- await flags.connect(personas.Nelly).addAccess(consumer.address)
- })
-
- it('does not revert', async () => {
- await consumer.getFlag(consumer.address)
- })
- })
- })
-
- describe('if the access control is turned off', () => {
- beforeEach(async () => {
- await flags.connect(personas.Nelly).disableAccessCheck()
- })
-
- it('does not revert', async () => {
- await consumer.getFlag(consumer.address)
- })
-
- describe('if access is granted to the address', () => {
- beforeEach(async () => {
- await flags.connect(personas.Nelly).addAccess(consumer.address)
- })
-
- it('does not revert', async () => {
- await consumer.getFlag(consumer.address)
- })
- })
- })
- })
-
- describe('#getFlags', () => {
- beforeEach(async () => {
- await flags.connect(personas.Nelly).disableAccessCheck()
- await flags
- .connect(personas.Nelly)
- .raiseFlags([
- await personas.Neil.getAddress(),
- await personas.Norbert.getAddress(),
- ])
- })
-
- it('respects the access controls of #getFlag', async () => {
- await flags.connect(personas.Nelly).enableAccessCheck()
-
- await expect(consumer.getFlag(consumer.address)).to.be.revertedWith(
- 'No access',
- )
-
- await flags.connect(personas.Nelly).addAccess(consumer.address)
-
- await consumer.getFlag(consumer.address)
- })
-
- it('returns the flags in the order they are requested', async () => {
- const response = await consumer.getFlags([
- await personas.Nelly.getAddress(),
- await personas.Neil.getAddress(),
- await personas.Ned.getAddress(),
- await personas.Norbert.getAddress(),
- ])
-
- assert.deepEqual([false, true, false, true], response)
- })
- })
-
- describe('#setRaisingAccessController', () => {
- let controller2: Contract
-
- beforeEach(async () => {
- controller2 = await controllerFactory.connect(personas.Nelly).deploy()
- await controller2.connect(personas.Nelly).enableAccessCheck()
- })
-
- it('updates access control rules', async () => {
- const neilAddress = await personas.Neil.getAddress()
- await controller.connect(personas.Nelly).addAccess(neilAddress)
- await flags.connect(personas.Neil).raiseFlags([consumer.address]) // doesn't raise
-
- await flags
- .connect(personas.Nelly)
- .setRaisingAccessController(controller2.address)
-
- await expect(
- flags.connect(personas.Neil).raiseFlags([consumer.address]),
- ).to.be.revertedWith('Not allowed to raise flags')
- })
-
- it('emits a log announcing the change', async () => {
- await expect(
- flags
- .connect(personas.Nelly)
- .setRaisingAccessController(controller2.address),
- )
- .to.emit(flags, 'RaisingAccessControllerUpdated')
- .withArgs(controller.address, controller2.address)
- })
-
- it('does not emit a log when there is no change', async () => {
- await flags
- .connect(personas.Nelly)
- .setRaisingAccessController(controller2.address)
-
- await expect(
- flags
- .connect(personas.Nelly)
- .setRaisingAccessController(controller2.address),
- ).to.not.emit(flags, 'RaisingAccessControllerUpdated')
- })
-
- describe('when called by a non-owner', () => {
- it('reverts', async () => {
- await expect(
- flags
- .connect(personas.Neil)
- .setRaisingAccessController(controller2.address),
- ).to.be.revertedWith('Only callable by owner')
- })
- })
- })
-})
diff --git a/contracts/test/v0.8/HeartbeatRequester.test.ts b/contracts/test/v0.8/HeartbeatRequester.test.ts
deleted file mode 100644
index bb58192337d..00000000000
--- a/contracts/test/v0.8/HeartbeatRequester.test.ts
+++ /dev/null
@@ -1,142 +0,0 @@
-import { getUsers, Personas } from '../test-helpers/setup'
-import { ethers } from 'hardhat'
-import { Signer } from 'ethers'
-import {
- HeartbeatRequester,
- MockAggregatorProxy,
- MockOffchainAggregator,
-} from '../../typechain'
-import { HeartbeatRequester__factory as HeartbeatRequesterFactory } from '../../typechain/factories/HeartbeatRequester__factory'
-import { MockAggregatorProxy__factory as MockAggregatorProxyFactory } from '../../typechain/factories/MockAggregatorProxy__factory'
-import { MockOffchainAggregator__factory as MockOffchainAggregatorFactory } from '../../typechain/factories/MockOffchainAggregator__factory'
-import { assert, expect } from 'chai'
-
-let personas: Personas
-let owner: Signer
-let caller1: Signer
-let proxy1: Signer
-let proxy2: Signer
-let aggregator: MockOffchainAggregator
-let aggregatorFactory: MockOffchainAggregatorFactory
-let aggregatorProxy: MockAggregatorProxy
-let aggregatorProxyFactory: MockAggregatorProxyFactory
-let requester: HeartbeatRequester
-let requesterFactory: HeartbeatRequesterFactory
-
-describe('HeartbeatRequester', () => {
- beforeEach(async () => {
- personas = (await getUsers()).personas
- owner = personas.Default
- caller1 = personas.Carol
- proxy1 = personas.Nelly
- proxy2 = personas.Eddy
-
- // deploy heartbeat requester
- requesterFactory = await ethers.getContractFactory('HeartbeatRequester')
- requester = await requesterFactory.connect(owner).deploy()
- await requester.deployed()
- })
-
- describe('#permitHeartbeat', () => {
- it('adds a heartbeat and emits an event', async () => {
- const callerAddress = await caller1.getAddress()
- const proxyAddress1 = await proxy1.getAddress()
- const proxyAddress2 = await proxy2.getAddress()
- const tx1 = await requester
- .connect(owner)
- .permitHeartbeat(callerAddress, proxyAddress1)
- await expect(tx1)
- .to.emit(requester, 'HeartbeatPermitted')
- .withArgs(callerAddress, proxyAddress1, ethers.constants.AddressZero)
-
- const tx2 = await requester
- .connect(owner)
- .permitHeartbeat(callerAddress, proxyAddress2)
- await expect(tx2)
- .to.emit(requester, 'HeartbeatPermitted')
- .withArgs(callerAddress, proxyAddress2, proxyAddress1)
- })
-
- it('reverts when not called by its owner', async () => {
- const callerAddress = await caller1.getAddress()
- const proxyAddress = await proxy1.getAddress()
- await expect(
- requester.connect(caller1).permitHeartbeat(callerAddress, proxyAddress),
- ).to.be.revertedWith('Only callable by owner')
- })
- })
-
- describe('#removeHeartbeat', () => {
- it('removes a heartbeat and emits an event', async () => {
- const callerAddress = await caller1.getAddress()
- const proxyAddress = await proxy1.getAddress()
- const tx1 = await requester
- .connect(owner)
- .permitHeartbeat(callerAddress, proxyAddress)
- await expect(tx1)
- .to.emit(requester, 'HeartbeatPermitted')
- .withArgs(callerAddress, proxyAddress, ethers.constants.AddressZero)
-
- const tx2 = await requester.connect(owner).removeHeartbeat(callerAddress)
- await expect(tx2)
- .to.emit(requester, 'HeartbeatRemoved')
- .withArgs(callerAddress, proxyAddress)
- })
-
- it('reverts when not called by its owner', async () => {
- await expect(
- requester.connect(caller1).removeHeartbeat(await caller1.getAddress()),
- ).to.be.revertedWith('Only callable by owner')
- })
- })
-
- describe('#getAggregatorAndRequestHeartbeat', () => {
- it('reverts if caller and proxy combination is not allowed', async () => {
- const callerAddress = await caller1.getAddress()
- const proxyAddress = await proxy1.getAddress()
- await requester
- .connect(owner)
- .permitHeartbeat(callerAddress, proxyAddress)
-
- await expect(
- requester
- .connect(caller1)
- .getAggregatorAndRequestHeartbeat(await owner.getAddress()),
- ).to.be.revertedWithCustomError(requester, 'HeartbeatNotPermitted')
- })
-
- it('calls corresponding aggregator to request a new round', async () => {
- aggregatorFactory = await ethers.getContractFactory(
- 'MockOffchainAggregator',
- )
- aggregator = await aggregatorFactory.connect(owner).deploy()
- await aggregator.deployed()
-
- aggregatorProxyFactory = await ethers.getContractFactory(
- 'MockAggregatorProxy',
- )
- aggregatorProxy = await aggregatorProxyFactory
- .connect(owner)
- .deploy(aggregator.address)
- await aggregatorProxy.deployed()
-
- await requester
- .connect(owner)
- .permitHeartbeat(await caller1.getAddress(), aggregatorProxy.address)
-
- const tx1 = await requester
- .connect(caller1)
- .getAggregatorAndRequestHeartbeat(aggregatorProxy.address)
-
- await expect(tx1).to.emit(aggregator, 'RoundIdUpdated').withArgs(1)
- assert.equal((await aggregator.roundId()).toNumber(), 1)
-
- const tx2 = await requester
- .connect(caller1)
- .getAggregatorAndRequestHeartbeat(aggregatorProxy.address)
-
- await expect(tx2).to.emit(aggregator, 'RoundIdUpdated').withArgs(2)
- assert.equal((await aggregator.roundId()).toNumber(), 2)
- })
- })
-})
diff --git a/contracts/test/v0.8/PermissionedForwardProxy.test.ts b/contracts/test/v0.8/PermissionedForwardProxy.test.ts
deleted file mode 100644
index 12ce63cd9b4..00000000000
--- a/contracts/test/v0.8/PermissionedForwardProxy.test.ts
+++ /dev/null
@@ -1,176 +0,0 @@
-import { ethers } from 'hardhat'
-import { publicAbi } from '../test-helpers/helpers'
-import { assert, expect } from 'chai'
-import { Contract, ContractFactory } from 'ethers'
-import { getUsers, Personas } from '../test-helpers/setup'
-
-const PERMISSION_NOT_SET = 'PermissionNotSet'
-
-let personas: Personas
-
-let controllerFactory: ContractFactory
-let counterFactory: ContractFactory
-let controller: Contract
-let counter: Contract
-
-before(async () => {
- personas = (await getUsers()).personas
- controllerFactory = await ethers.getContractFactory(
- 'src/v0.8/PermissionedForwardProxy.sol:PermissionedForwardProxy',
- personas.Carol,
- )
- counterFactory = await ethers.getContractFactory(
- 'src/v0.8/tests/Counter.sol:Counter',
- personas.Carol,
- )
-})
-
-describe('PermissionedForwardProxy', () => {
- beforeEach(async () => {
- controller = await controllerFactory.connect(personas.Carol).deploy()
- counter = await counterFactory.connect(personas.Carol).deploy()
- })
-
- it('has a limited public interface [ @skip-coverage ]', async () => {
- publicAbi(controller, [
- 'forward',
- 'setPermission',
- 'removePermission',
- 'getPermission',
- // Owned
- 'acceptOwnership',
- 'owner',
- 'transferOwnership',
- ])
- })
-
- describe('#setPermission', () => {
- describe('when called by a non-owner', () => {
- it('reverts', async () => {
- await expect(
- controller
- .connect(personas.Eddy)
- .setPermission(
- await personas.Carol.getAddress(),
- await personas.Eddy.getAddress(),
- ),
- ).to.be.revertedWith('Only callable by owner')
- })
- })
-
- describe('when called by the owner', () => {
- it('adds the permission to the proxy', async () => {
- const tx = await controller
- .connect(personas.Carol)
- .setPermission(
- await personas.Carol.getAddress(),
- await personas.Eddy.getAddress(),
- )
- const receipt = await tx.wait()
- const eventLog = receipt?.events
-
- assert.equal(eventLog?.length, 1)
- assert.equal(eventLog?.[0].event, 'PermissionSet')
- assert.equal(eventLog?.[0].args?.[0], await personas.Carol.getAddress())
- assert.equal(eventLog?.[0].args?.[1], await personas.Eddy.getAddress())
-
- expect(
- await controller.getPermission(await personas.Carol.getAddress()),
- ).to.be.equal(await personas.Eddy.getAddress())
- })
- })
- })
-
- describe('#removePermission', () => {
- beforeEach(async () => {
- // Add permission before testing
- await controller
- .connect(personas.Carol)
- .setPermission(
- await personas.Carol.getAddress(),
- await personas.Eddy.getAddress(),
- )
- })
-
- describe('when called by a non-owner', () => {
- it('reverts', async () => {
- await expect(
- controller
- .connect(personas.Eddy)
- .removePermission(await personas.Carol.getAddress()),
- ).to.be.revertedWith('Only callable by owner')
- })
- })
-
- describe('when called by the owner', () => {
- it('removes the permission to the proxy', async () => {
- const tx = await controller
- .connect(personas.Carol)
- .removePermission(await personas.Carol.getAddress())
-
- const receipt = await tx.wait()
- const eventLog = receipt?.events
-
- assert.equal(eventLog?.length, 1)
- assert.equal(eventLog?.[0].event, 'PermissionRemoved')
- assert.equal(eventLog?.[0].args?.[0], await personas.Carol.getAddress())
-
- expect(
- await controller.getPermission(await personas.Carol.getAddress()),
- ).to.be.equal(ethers.constants.AddressZero)
- })
- })
- })
-
- describe('#forward', () => {
- describe('when permission does not exist', () => {
- it('reverts', async () => {
- await expect(
- controller
- .connect(personas.Carol)
- .forward(await personas.Eddy.getAddress(), '0x'),
- ).to.be.revertedWithCustomError(controller, PERMISSION_NOT_SET)
- })
- })
-
- describe('when permission exists', () => {
- beforeEach(async () => {
- // Add permission before testing
- await controller
- .connect(personas.Carol)
- .setPermission(await personas.Carol.getAddress(), counter.address)
- })
-
- it('calls target successfully', async () => {
- await controller
- .connect(personas.Carol)
- .forward(
- counter.address,
- counter.interface.encodeFunctionData('increment'),
- )
-
- expect(await counter.count()).to.be.equal(1)
- })
-
- it('reverts when target reverts and bubbles up error', async () => {
- await expect(
- controller
- .connect(personas.Carol)
- .forward(
- counter.address,
- counter.interface.encodeFunctionData('alwaysRevertWithString'),
- ),
- ).to.be.revertedWith('always revert') // Revert strings should be bubbled up
-
- await expect(
- controller
- .connect(personas.Carol)
- .forward(
- counter.address,
- counter.interface.encodeFunctionData('alwaysRevert'),
- ),
- ).to.be.reverted // Javascript VM not able to parse custom errors defined on another contract
- })
- })
- })
-})
diff --git a/contracts/test/v0.8/ValidatorProxy.test.ts b/contracts/test/v0.8/ValidatorProxy.test.ts
deleted file mode 100644
index 2d274245de4..00000000000
--- a/contracts/test/v0.8/ValidatorProxy.test.ts
+++ /dev/null
@@ -1,403 +0,0 @@
-import { ethers } from 'hardhat'
-import { publicAbi } from '../test-helpers/helpers'
-import { assert, expect } from 'chai'
-import { Signer, Contract, constants } from 'ethers'
-import { Users, getUsers } from '../test-helpers/setup'
-
-let users: Users
-
-let owner: Signer
-let ownerAddress: string
-let aggregator: Signer
-let aggregatorAddress: string
-let validator: Signer
-let validatorAddress: string
-let validatorProxy: Contract
-
-before(async () => {
- users = await getUsers()
- owner = users.personas.Default
- aggregator = users.contracts.contract1
- validator = users.contracts.contract2
- ownerAddress = await owner.getAddress()
- aggregatorAddress = await aggregator.getAddress()
- validatorAddress = await validator.getAddress()
-})
-
-describe('ValidatorProxy', () => {
- beforeEach(async () => {
- const vpf = await ethers.getContractFactory(
- 'src/v0.8/ValidatorProxy.sol:ValidatorProxy',
- owner,
- )
- validatorProxy = await vpf.deploy(aggregatorAddress, validatorAddress)
- validatorProxy = await validatorProxy.deployed()
- })
-
- it('has a limited public interface [ @skip-coverage ]', async () => {
- publicAbi(validatorProxy, [
- // ConfirmedOwner functions
- 'acceptOwnership',
- 'owner',
- 'transferOwnership',
- // ValidatorProxy functions
- 'validate',
- 'proposeNewAggregator',
- 'upgradeAggregator',
- 'getAggregators',
- 'proposeNewValidator',
- 'upgradeValidator',
- 'getValidators',
- 'typeAndVersion',
- ])
- })
-
- describe('#constructor', () => {
- it('should set the aggregator addresses correctly', async () => {
- const response = await validatorProxy.getAggregators()
- assert.equal(response.current, aggregatorAddress)
- assert.equal(response.hasProposal, false)
- assert.equal(response.proposed, constants.AddressZero)
- })
-
- it('should set the validator addresses conrrectly', async () => {
- const response = await validatorProxy.getValidators()
- assert.equal(response.current, validatorAddress)
- assert.equal(response.hasProposal, false)
- assert.equal(response.proposed, constants.AddressZero)
- })
-
- it('should set the owner correctly', async () => {
- const response = await validatorProxy.owner()
- assert.equal(response, ownerAddress)
- })
- })
-
- describe('#proposeNewAggregator', () => {
- let newAggregator: Signer
- let newAggregatorAddress: string
- beforeEach(async () => {
- newAggregator = users.contracts.contract3
- newAggregatorAddress = await newAggregator.getAddress()
- })
-
- describe('failure', () => {
- it('should only be called by the owner', async () => {
- const stranger = users.contracts.contract4
- await expect(
- validatorProxy
- .connect(stranger)
- .proposeNewAggregator(newAggregatorAddress),
- ).to.be.revertedWith('Only callable by owner')
- })
-
- it('should revert if no change in proposal', async () => {
- await validatorProxy.proposeNewAggregator(newAggregatorAddress)
- await expect(
- validatorProxy.proposeNewAggregator(newAggregatorAddress),
- ).to.be.revertedWith('Invalid proposal')
- })
-
- it('should revert if the proposal is the same as the current', async () => {
- await expect(
- validatorProxy.proposeNewAggregator(aggregatorAddress),
- ).to.be.revertedWith('Invalid proposal')
- })
- })
-
- describe('success', () => {
- it('should emit an event', async () => {
- await expect(validatorProxy.proposeNewAggregator(newAggregatorAddress))
- .to.emit(validatorProxy, 'AggregatorProposed')
- .withArgs(newAggregatorAddress)
- })
-
- it('should set the correct address and hasProposal is true', async () => {
- await validatorProxy.proposeNewAggregator(newAggregatorAddress)
- const response = await validatorProxy.getAggregators()
- assert.equal(response.current, aggregatorAddress)
- assert.equal(response.hasProposal, true)
- assert.equal(response.proposed, newAggregatorAddress)
- })
-
- it('should set a zero address and hasProposal is false', async () => {
- await validatorProxy.proposeNewAggregator(newAggregatorAddress)
- await validatorProxy.proposeNewAggregator(constants.AddressZero)
- const response = await validatorProxy.getAggregators()
- assert.equal(response.current, aggregatorAddress)
- assert.equal(response.hasProposal, false)
- assert.equal(response.proposed, constants.AddressZero)
- })
- })
- })
-
- describe('#upgradeAggregator', () => {
- describe('failure', () => {
- it('should only be called by the owner', async () => {
- const stranger = users.contracts.contract4
- await expect(
- validatorProxy.connect(stranger).upgradeAggregator(),
- ).to.be.revertedWith('Only callable by owner')
- })
-
- it('should revert if there is no proposal', async () => {
- await expect(validatorProxy.upgradeAggregator()).to.be.revertedWith(
- 'No proposal',
- )
- })
- })
-
- describe('success', () => {
- let newAggregator: Signer
- let newAggregatorAddress: string
- beforeEach(async () => {
- newAggregator = users.contracts.contract3
- newAggregatorAddress = await newAggregator.getAddress()
- await validatorProxy.proposeNewAggregator(newAggregatorAddress)
- })
-
- it('should emit an event', async () => {
- await expect(validatorProxy.upgradeAggregator())
- .to.emit(validatorProxy, 'AggregatorUpgraded')
- .withArgs(aggregatorAddress, newAggregatorAddress)
- })
-
- it('should upgrade the addresses', async () => {
- await validatorProxy.upgradeAggregator()
- const response = await validatorProxy.getAggregators()
- assert.equal(response.current, newAggregatorAddress)
- assert.equal(response.hasProposal, false)
- assert.equal(response.proposed, constants.AddressZero)
- })
- })
- })
-
- describe('#proposeNewValidator', () => {
- let newValidator: Signer
- let newValidatorAddress: string
-
- beforeEach(async () => {
- newValidator = users.contracts.contract3
- newValidatorAddress = await newValidator.getAddress()
- })
-
- describe('failure', () => {
- it('should only be called by the owner', async () => {
- const stranger = users.contracts.contract4
- await expect(
- validatorProxy
- .connect(stranger)
- .proposeNewAggregator(newValidatorAddress),
- ).to.be.revertedWith('Only callable by owner')
- })
-
- it('should revert if no change in proposal', async () => {
- await validatorProxy.proposeNewValidator(newValidatorAddress)
- await expect(
- validatorProxy.proposeNewValidator(newValidatorAddress),
- ).to.be.revertedWith('Invalid proposal')
- })
-
- it('should revert if the proposal is the same as the current', async () => {
- await expect(
- validatorProxy.proposeNewValidator(validatorAddress),
- ).to.be.revertedWith('Invalid proposal')
- })
- })
-
- describe('success', () => {
- it('should emit an event', async () => {
- await expect(validatorProxy.proposeNewValidator(newValidatorAddress))
- .to.emit(validatorProxy, 'ValidatorProposed')
- .withArgs(newValidatorAddress)
- })
-
- it('should set the correct address and hasProposal is true', async () => {
- await validatorProxy.proposeNewValidator(newValidatorAddress)
- const response = await validatorProxy.getValidators()
- assert.equal(response.current, validatorAddress)
- assert.equal(response.hasProposal, true)
- assert.equal(response.proposed, newValidatorAddress)
- })
-
- it('should set a zero address and hasProposal is false', async () => {
- await validatorProxy.proposeNewValidator(newValidatorAddress)
- await validatorProxy.proposeNewValidator(constants.AddressZero)
- const response = await validatorProxy.getValidators()
- assert.equal(response.current, validatorAddress)
- assert.equal(response.hasProposal, false)
- assert.equal(response.proposed, constants.AddressZero)
- })
- })
- })
-
- describe('#upgradeValidator', () => {
- describe('failure', () => {
- it('should only be called by the owner', async () => {
- const stranger = users.contracts.contract4
- await expect(
- validatorProxy.connect(stranger).upgradeValidator(),
- ).to.be.revertedWith('Only callable by owner')
- })
-
- it('should revert if there is no proposal', async () => {
- await expect(validatorProxy.upgradeValidator()).to.be.revertedWith(
- 'No proposal',
- )
- })
- })
-
- describe('success', () => {
- let newValidator: Signer
- let newValidatorAddress: string
- beforeEach(async () => {
- newValidator = users.contracts.contract3
- newValidatorAddress = await newValidator.getAddress()
- await validatorProxy.proposeNewValidator(newValidatorAddress)
- })
-
- it('should emit an event', async () => {
- await expect(validatorProxy.upgradeValidator())
- .to.emit(validatorProxy, 'ValidatorUpgraded')
- .withArgs(validatorAddress, newValidatorAddress)
- })
-
- it('should upgrade the addresses', async () => {
- await validatorProxy.upgradeValidator()
- const response = await validatorProxy.getValidators()
- assert.equal(response.current, newValidatorAddress)
- assert.equal(response.hasProposal, false)
- assert.equal(response.proposed, constants.AddressZero)
- })
- })
- })
-
- describe('#validate', () => {
- describe('failure', () => {
- it('reverts when not called by aggregator or proposed aggregator', async () => {
- const stranger = users.contracts.contract5
- await expect(
- validatorProxy.connect(stranger).validate(99, 88, 77, 66),
- ).to.be.revertedWith('Not a configured aggregator')
- })
-
- it('reverts when there is no validator set', async () => {
- const vpf = await ethers.getContractFactory(
- 'src/v0.8/ValidatorProxy.sol:ValidatorProxy',
- owner,
- )
- validatorProxy = await vpf.deploy(
- aggregatorAddress,
- constants.AddressZero,
- )
- await validatorProxy.deployed()
- await expect(
- validatorProxy.connect(aggregator).validate(99, 88, 77, 66),
- ).to.be.revertedWith('No validator set')
- })
- })
-
- describe('success', () => {
- describe('from the aggregator', () => {
- let mockValidator1: Contract
- beforeEach(async () => {
- const mvf = await ethers.getContractFactory(
- 'src/v0.8/mocks/MockAggregatorValidator.sol:MockAggregatorValidator',
- owner,
- )
- mockValidator1 = await mvf.deploy(1)
- mockValidator1 = await mockValidator1.deployed()
- const vpf = await ethers.getContractFactory(
- 'src/v0.8/ValidatorProxy.sol:ValidatorProxy',
- owner,
- )
- validatorProxy = await vpf.deploy(
- aggregatorAddress,
- mockValidator1.address,
- )
- validatorProxy = await validatorProxy.deployed()
- })
-
- describe('for a single validator', () => {
- it('calls validate on the validator', async () => {
- await expect(
- validatorProxy.connect(aggregator).validate(200, 300, 400, 500),
- )
- .to.emit(mockValidator1, 'ValidateCalled')
- .withArgs(1, 200, 300, 400, 500)
- })
-
- it('uses a specific amount of gas [ @skip-coverage ]', async () => {
- const resp = await validatorProxy
- .connect(aggregator)
- .validate(200, 300, 400, 500)
- const receipt = await resp.wait()
- assert.equal(receipt.gasUsed.toString(), '32373')
- })
- })
-
- describe('for a validator and a proposed validator', () => {
- let mockValidator2: Contract
-
- beforeEach(async () => {
- const mvf = await ethers.getContractFactory(
- 'src/v0.8/mocks/MockAggregatorValidator.sol:MockAggregatorValidator',
- owner,
- )
- mockValidator2 = await mvf.deploy(2)
- mockValidator2 = await mockValidator2.deployed()
- await validatorProxy.proposeNewValidator(mockValidator2.address)
- })
-
- it('calls validate on the validator', async () => {
- await expect(
- validatorProxy
- .connect(aggregator)
- .validate(2000, 3000, 4000, 5000),
- )
- .to.emit(mockValidator1, 'ValidateCalled')
- .withArgs(1, 2000, 3000, 4000, 5000)
- })
-
- it('also calls validate on the proposed validator', async () => {
- await expect(
- validatorProxy
- .connect(aggregator)
- .validate(2000, 3000, 4000, 5000),
- )
- .to.emit(mockValidator2, 'ValidateCalled')
- .withArgs(2, 2000, 3000, 4000, 5000)
- })
-
- it('uses a specific amount of gas [ @skip-coverage ]', async () => {
- const resp = await validatorProxy
- .connect(aggregator)
- .validate(2000, 3000, 4000, 5000)
- const receipt = await resp.wait()
- assert.equal(receipt.gasUsed.toString(), '40429')
- })
- })
- })
-
- describe('from the proposed aggregator', () => {
- let newAggregator: Signer
- let newAggregatorAddress: string
- beforeEach(async () => {
- newAggregator = users.contracts.contract3
- newAggregatorAddress = await newAggregator.getAddress()
- await validatorProxy
- .connect(owner)
- .proposeNewAggregator(newAggregatorAddress)
- })
-
- it('emits an event', async () => {
- await expect(
- validatorProxy.connect(newAggregator).validate(555, 666, 777, 888),
- )
- .to.emit(validatorProxy, 'ProposedAggregatorValidateCall')
- .withArgs(newAggregatorAddress, 555, 666, 777, 888)
- })
- })
- })
- })
-})
diff --git a/contracts/test/v0.8/automation/AutomationGasAnalysis.test.ts b/contracts/test/v0.8/automation/AutomationGasAnalysis.test.ts
index c2e08f4cd81..f393a5de1c2 100644
--- a/contracts/test/v0.8/automation/AutomationGasAnalysis.test.ts
+++ b/contracts/test/v0.8/automation/AutomationGasAnalysis.test.ts
@@ -69,7 +69,7 @@ describeMaybe('Automation Gas Analysis', () => {
const getFact = ethers.getContractFactory
const linkTokenFactory = await getFact('LinkToken')
const mockV3AggregatorFactory = await getFact(
- 'src/v0.8/tests/MockV3Aggregator.sol:MockV3Aggregator',
+ 'src/v0.8/shared/mocks/MockV3Aggregator.sol:MockV3Aggregator',
)
const upkeepMockFactory = await getFact('UpkeepMock')
const registry12Factory = await getFact('KeeperRegistry1_2')
diff --git a/contracts/test/v0.8/automation/AutomationRegistrar2_1.test.ts b/contracts/test/v0.8/automation/AutomationRegistrar2_1.test.ts
index a096ee4f481..6d3d591acb0 100644
--- a/contracts/test/v0.8/automation/AutomationRegistrar2_1.test.ts
+++ b/contracts/test/v0.8/automation/AutomationRegistrar2_1.test.ts
@@ -41,7 +41,7 @@ describe('AutomationRegistrar2_1 - Frozen [ @skip-coverage ]', () => {
// 'src/v0.8/shared/test/helpers/LinkTokenTestHelper.sol:LinkTokenTestHelper',
// )
// mockV3AggregatorFactory = (await ethers.getContractFactory(
-// 'src/v0.8/tests/MockV3Aggregator.sol:MockV3Aggregator',
+// 'src/v0.8/shared/mocks/MockV3Aggregator.sol:MockV3Aggregator',
// )) as unknown as MockV3AggregatorFactory
// upkeepMockFactory = await ethers.getContractFactory('UpkeepMock')
// })
diff --git a/contracts/test/v0.8/automation/AutomationRegistrar2_3.test.ts b/contracts/test/v0.8/automation/AutomationRegistrar2_3.test.ts
index 31712e1380b..e98218ec214 100644
--- a/contracts/test/v0.8/automation/AutomationRegistrar2_3.test.ts
+++ b/contracts/test/v0.8/automation/AutomationRegistrar2_3.test.ts
@@ -44,7 +44,7 @@ before(async () => {
'src/v0.8/shared/test/helpers/LinkTokenTestHelper.sol:LinkTokenTestHelper',
)
mockV3AggregatorFactory = (await ethers.getContractFactory(
- 'src/v0.8/tests/MockV3Aggregator.sol:MockV3Aggregator',
+ 'src/v0.8/shared/mocks/MockV3Aggregator.sol:MockV3Aggregator',
)) as unknown as MockV3AggregatorFactory
upkeepMockFactory = await ethers.getContractFactory('UpkeepMock')
})
diff --git a/contracts/test/v0.8/automation/AutomationRegistry2_2.test.ts b/contracts/test/v0.8/automation/AutomationRegistry2_2.test.ts
index 6b220f2f7cb..593ac08a5e7 100644
--- a/contracts/test/v0.8/automation/AutomationRegistry2_2.test.ts
+++ b/contracts/test/v0.8/automation/AutomationRegistry2_2.test.ts
@@ -419,7 +419,7 @@ describe('AutomationRegistry2_2', () => {
)
// need full path because there are two contracts with name MockV3Aggregator
mockV3AggregatorFactory = (await ethers.getContractFactory(
- 'src/v0.8/tests/MockV3Aggregator.sol:MockV3Aggregator',
+ 'src/v0.8/shared/mocks/MockV3Aggregator.sol:MockV3Aggregator',
)) as unknown as MockV3AggregatorFactory
mockArbGasInfoFactory = await ethers.getContractFactory('MockArbGasInfo')
mockOVMGasPriceOracleFactory = await ethers.getContractFactory(
diff --git a/contracts/test/v0.8/automation/AutomationRegistry2_3.test.ts b/contracts/test/v0.8/automation/AutomationRegistry2_3.test.ts
index f3c2d9bb984..48ec8469f9a 100644
--- a/contracts/test/v0.8/automation/AutomationRegistry2_3.test.ts
+++ b/contracts/test/v0.8/automation/AutomationRegistry2_3.test.ts
@@ -431,7 +431,7 @@ describe('AutomationRegistry2_3', () => {
)
// need full path because there are two contracts with name MockV3Aggregator
mockV3AggregatorFactory = (await ethers.getContractFactory(
- 'src/v0.8/tests/MockV3Aggregator.sol:MockV3Aggregator',
+ 'src/v0.8/shared/mocks/MockV3Aggregator.sol:MockV3Aggregator',
)) as unknown as MockV3AggregatorFactory
mockArbGasInfoFactory = await ethers.getContractFactory('MockArbGasInfo')
mockOVMGasPriceOracleFactory = await ethers.getContractFactory(
diff --git a/contracts/test/v0.8/automation/KeeperCompatible.test.ts b/contracts/test/v0.8/automation/KeeperCompatible.test.ts
index 13d1d0deff5..17c83790811 100644
--- a/contracts/test/v0.8/automation/KeeperCompatible.test.ts
+++ b/contracts/test/v0.8/automation/KeeperCompatible.test.ts
@@ -10,7 +10,7 @@ describe('KeeperCompatible', () => {
before(async () => {
const factory = await ethers.getContractFactory(
- `src/v0.${version}/tests/KeeperCompatibleTestHelper.sol:KeeperCompatibleTestHelper`,
+ `src/v0.${version}/automation/testhelpers/KeeperCompatibleTestHelper.sol:KeeperCompatibleTestHelper`,
)
contract = await factory.deploy()
})
diff --git a/contracts/test/v0.8/automation/UpkeepTranscoder3_0.test.ts b/contracts/test/v0.8/automation/UpkeepTranscoder3_0.test.ts
index d58cfd377f7..7fd811d8226 100644
--- a/contracts/test/v0.8/automation/UpkeepTranscoder3_0.test.ts
+++ b/contracts/test/v0.8/automation/UpkeepTranscoder3_0.test.ts
@@ -132,7 +132,7 @@ before(async () => {
)
// need full path because there are two contracts with name MockV3Aggregator
mockV3AggregatorFactory = (await ethers.getContractFactory(
- 'src/v0.8/tests/MockV3Aggregator.sol:MockV3Aggregator',
+ 'src/v0.8/shared/mocks/MockV3Aggregator.sol:MockV3Aggregator',
)) as unknown as MockV3AggregatorFactory
upkeepMockFactory = await ethers.getContractFactory('UpkeepMock')
diff --git a/contracts/test/v0.8/automation/UpkeepTranscoder4_0.test.ts b/contracts/test/v0.8/automation/UpkeepTranscoder4_0.test.ts
index 392a1cb5966..b49dfb1d5b4 100644
--- a/contracts/test/v0.8/automation/UpkeepTranscoder4_0.test.ts
+++ b/contracts/test/v0.8/automation/UpkeepTranscoder4_0.test.ts
@@ -335,7 +335,7 @@ const setup = async () => {
linkToken = await linkTokenFactory.connect(owner).deploy()
// need full path because there are two contracts with name MockV3Aggregator
const mockV3AggregatorFactory = (await ethers.getContractFactory(
- 'src/v0.8/tests/MockV3Aggregator.sol:MockV3Aggregator',
+ 'src/v0.8/shared/mocks/MockV3Aggregator.sol:MockV3Aggregator',
)) as unknown as MockV3AggregatorFactory
gasPriceFeed = await mockV3AggregatorFactory.connect(owner).deploy(0, gasWei)
diff --git a/contracts/test/v0.8/automation/ZKSyncAutomationRegistry2_3.test.ts b/contracts/test/v0.8/automation/ZKSyncAutomationRegistry2_3.test.ts
index 95210cf6444..ffbde4464b9 100644
--- a/contracts/test/v0.8/automation/ZKSyncAutomationRegistry2_3.test.ts
+++ b/contracts/test/v0.8/automation/ZKSyncAutomationRegistry2_3.test.ts
@@ -416,7 +416,7 @@ describe('ZKSyncAutomationRegistry2_3', () => {
)
// need full path because there are two contracts with name MockV3Aggregator
mockV3AggregatorFactory = (await ethers.getContractFactory(
- 'src/v0.8/tests/MockV3Aggregator.sol:MockV3Aggregator',
+ 'src/v0.8/shared/mocks/MockV3Aggregator.sol:MockV3Aggregator',
)) as unknown as MockV3AggregatorFactory
mockZKSyncSystemContextFactory = await ethers.getContractFactory(
'MockZKSyncSystemContext',
diff --git a/contracts/test/v0.8/operatorforwarder/AuthorizedForwarder.test.ts b/contracts/test/v0.8/operatorforwarder/AuthorizedForwarder.test.ts
index d4e1918c976..6530a2f3c4e 100644
--- a/contracts/test/v0.8/operatorforwarder/AuthorizedForwarder.test.ts
+++ b/contracts/test/v0.8/operatorforwarder/AuthorizedForwarder.test.ts
@@ -22,7 +22,7 @@ before(async () => {
roles.defaultAccount,
)
brokenFactory = await ethers.getContractFactory(
- 'src/v0.8/tests/Broken.sol:Broken',
+ 'src/v0.8/operatorforwarder/test/Broken.sol:Broken',
roles.defaultAccount,
)
forwarderFactory = await ethers.getContractFactory(
diff --git a/core/chains/evm/logpoller/helper_test.go b/core/chains/evm/logpoller/helper_test.go
index b8d849d7d83..6a5959c5586 100644
--- a/core/chains/evm/logpoller/helper_test.go
+++ b/core/chains/evm/logpoller/helper_test.go
@@ -25,7 +25,7 @@ import (
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker"
"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/generated/log_emitter"
+ "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/log_emitter"
"github.com/smartcontractkit/chainlink/v2/core/internal/testutils"
"github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest"
)
diff --git a/core/chains/evm/logpoller/log_poller_internal_test.go b/core/chains/evm/logpoller/log_poller_internal_test.go
index 620bbf14f41..757c5d4193c 100644
--- a/core/chains/evm/logpoller/log_poller_internal_test.go
+++ b/core/chains/evm/logpoller/log_poller_internal_test.go
@@ -32,7 +32,7 @@ import (
evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks"
evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils"
- "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/log_emitter"
+ "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/log_emitter"
"github.com/smartcontractkit/chainlink/v2/core/internal/testutils"
"github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest"
)
diff --git a/core/chains/evm/logpoller/log_poller_test.go b/core/chains/evm/logpoller/log_poller_test.go
index df688cd5e5c..3a1eb7b186f 100644
--- a/core/chains/evm/logpoller/log_poller_test.go
+++ b/core/chains/evm/logpoller/log_poller_test.go
@@ -38,7 +38,7 @@ import (
evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils"
ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big"
- "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/log_emitter"
+ "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/log_emitter"
"github.com/smartcontractkit/chainlink/v2/core/internal/testutils"
"github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest"
"github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest"
diff --git a/core/gethwrappers/abigen_test.go b/core/gethwrappers/abigen_test.go
index 5874bf0b57c..21858f67ee4 100644
--- a/core/gethwrappers/abigen_test.go
+++ b/core/gethwrappers/abigen_test.go
@@ -8,7 +8,7 @@ import (
"github.com/ethereum/go-ethereum/ethclient/simulated"
"github.com/stretchr/testify/require"
- "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/log_emitter"
+ "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/log_emitter"
"github.com/smartcontractkit/chainlink/v2/core/internal/testutils"
)
diff --git a/core/gethwrappers/generated/type_and_version_interface_wrapper/type_and_version_interface_wrapper.go b/core/gethwrappers/generated/type_and_version_interface_wrapper/type_and_version_interface_wrapper.go
deleted file mode 100644
index bf907b0354b..00000000000
--- a/core/gethwrappers/generated/type_and_version_interface_wrapper/type_and_version_interface_wrapper.go
+++ /dev/null
@@ -1,183 +0,0 @@
-// Code generated - DO NOT EDIT.
-// This file is a generated binding and any manual changes will be lost.
-
-package type_and_version_interface_wrapper
-
-import (
- "errors"
- "math/big"
- "strings"
-
- ethereum "github.com/ethereum/go-ethereum"
- "github.com/ethereum/go-ethereum/accounts/abi"
- "github.com/ethereum/go-ethereum/accounts/abi/bind"
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/event"
-)
-
-var (
- _ = errors.New
- _ = big.NewInt
- _ = strings.NewReader
- _ = ethereum.NotFound
- _ = bind.Bind
- _ = common.Big1
- _ = types.BloomLookup
- _ = event.NewSubscription
- _ = abi.ConvertType
-)
-
-var TypeAndVersionInterfaceMetaData = &bind.MetaData{
- ABI: "[{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"pure\",\"type\":\"function\"}]",
-}
-
-var TypeAndVersionInterfaceABI = TypeAndVersionInterfaceMetaData.ABI
-
-type TypeAndVersionInterface struct {
- address common.Address
- abi abi.ABI
- TypeAndVersionInterfaceCaller
- TypeAndVersionInterfaceTransactor
- TypeAndVersionInterfaceFilterer
-}
-
-type TypeAndVersionInterfaceCaller struct {
- contract *bind.BoundContract
-}
-
-type TypeAndVersionInterfaceTransactor struct {
- contract *bind.BoundContract
-}
-
-type TypeAndVersionInterfaceFilterer struct {
- contract *bind.BoundContract
-}
-
-type TypeAndVersionInterfaceSession struct {
- Contract *TypeAndVersionInterface
- CallOpts bind.CallOpts
- TransactOpts bind.TransactOpts
-}
-
-type TypeAndVersionInterfaceCallerSession struct {
- Contract *TypeAndVersionInterfaceCaller
- CallOpts bind.CallOpts
-}
-
-type TypeAndVersionInterfaceTransactorSession struct {
- Contract *TypeAndVersionInterfaceTransactor
- TransactOpts bind.TransactOpts
-}
-
-type TypeAndVersionInterfaceRaw struct {
- Contract *TypeAndVersionInterface
-}
-
-type TypeAndVersionInterfaceCallerRaw struct {
- Contract *TypeAndVersionInterfaceCaller
-}
-
-type TypeAndVersionInterfaceTransactorRaw struct {
- Contract *TypeAndVersionInterfaceTransactor
-}
-
-func NewTypeAndVersionInterface(address common.Address, backend bind.ContractBackend) (*TypeAndVersionInterface, error) {
- abi, err := abi.JSON(strings.NewReader(TypeAndVersionInterfaceABI))
- if err != nil {
- return nil, err
- }
- contract, err := bindTypeAndVersionInterface(address, backend, backend, backend)
- if err != nil {
- return nil, err
- }
- return &TypeAndVersionInterface{address: address, abi: abi, TypeAndVersionInterfaceCaller: TypeAndVersionInterfaceCaller{contract: contract}, TypeAndVersionInterfaceTransactor: TypeAndVersionInterfaceTransactor{contract: contract}, TypeAndVersionInterfaceFilterer: TypeAndVersionInterfaceFilterer{contract: contract}}, nil
-}
-
-func NewTypeAndVersionInterfaceCaller(address common.Address, caller bind.ContractCaller) (*TypeAndVersionInterfaceCaller, error) {
- contract, err := bindTypeAndVersionInterface(address, caller, nil, nil)
- if err != nil {
- return nil, err
- }
- return &TypeAndVersionInterfaceCaller{contract: contract}, nil
-}
-
-func NewTypeAndVersionInterfaceTransactor(address common.Address, transactor bind.ContractTransactor) (*TypeAndVersionInterfaceTransactor, error) {
- contract, err := bindTypeAndVersionInterface(address, nil, transactor, nil)
- if err != nil {
- return nil, err
- }
- return &TypeAndVersionInterfaceTransactor{contract: contract}, nil
-}
-
-func NewTypeAndVersionInterfaceFilterer(address common.Address, filterer bind.ContractFilterer) (*TypeAndVersionInterfaceFilterer, error) {
- contract, err := bindTypeAndVersionInterface(address, nil, nil, filterer)
- if err != nil {
- return nil, err
- }
- return &TypeAndVersionInterfaceFilterer{contract: contract}, nil
-}
-
-func bindTypeAndVersionInterface(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) {
- parsed, err := TypeAndVersionInterfaceMetaData.GetAbi()
- if err != nil {
- return nil, err
- }
- return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil
-}
-
-func (_TypeAndVersionInterface *TypeAndVersionInterfaceRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
- return _TypeAndVersionInterface.Contract.TypeAndVersionInterfaceCaller.contract.Call(opts, result, method, params...)
-}
-
-func (_TypeAndVersionInterface *TypeAndVersionInterfaceRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
- return _TypeAndVersionInterface.Contract.TypeAndVersionInterfaceTransactor.contract.Transfer(opts)
-}
-
-func (_TypeAndVersionInterface *TypeAndVersionInterfaceRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
- return _TypeAndVersionInterface.Contract.TypeAndVersionInterfaceTransactor.contract.Transact(opts, method, params...)
-}
-
-func (_TypeAndVersionInterface *TypeAndVersionInterfaceCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
- return _TypeAndVersionInterface.Contract.contract.Call(opts, result, method, params...)
-}
-
-func (_TypeAndVersionInterface *TypeAndVersionInterfaceTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
- return _TypeAndVersionInterface.Contract.contract.Transfer(opts)
-}
-
-func (_TypeAndVersionInterface *TypeAndVersionInterfaceTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
- return _TypeAndVersionInterface.Contract.contract.Transact(opts, method, params...)
-}
-
-func (_TypeAndVersionInterface *TypeAndVersionInterfaceCaller) TypeAndVersion(opts *bind.CallOpts) (string, error) {
- var out []interface{}
- err := _TypeAndVersionInterface.contract.Call(opts, &out, "typeAndVersion")
-
- if err != nil {
- return *new(string), err
- }
-
- out0 := *abi.ConvertType(out[0], new(string)).(*string)
-
- return out0, err
-
-}
-
-func (_TypeAndVersionInterface *TypeAndVersionInterfaceSession) TypeAndVersion() (string, error) {
- return _TypeAndVersionInterface.Contract.TypeAndVersion(&_TypeAndVersionInterface.CallOpts)
-}
-
-func (_TypeAndVersionInterface *TypeAndVersionInterfaceCallerSession) TypeAndVersion() (string, error) {
- return _TypeAndVersionInterface.Contract.TypeAndVersion(&_TypeAndVersionInterface.CallOpts)
-}
-
-func (_TypeAndVersionInterface *TypeAndVersionInterface) Address() common.Address {
- return _TypeAndVersionInterface.address
-}
-
-type TypeAndVersionInterfaceInterface interface {
- TypeAndVersion(opts *bind.CallOpts) (string, 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 20b5bfbdbad..b10ad89f930 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
@@ -49,7 +49,6 @@ keeper_registry_wrapper1_3: ../../contracts/solc/v0.8.6/KeeperRegistry1_3/Keeper
keeper_registry_wrapper2_0: ../../contracts/solc/v0.8.6/KeeperRegistry2_0/KeeperRegistry2_0.abi ../../contracts/solc/v0.8.6/KeeperRegistry2_0/KeeperRegistry2_0.bin c32dea7d5ef66b7c58ddc84ddf69aa44df1b3ae8601fbc271c95be4ff5853056
keeper_registry_wrapper_2_1: ../../contracts/solc/v0.8.16/KeeperRegistry2_1/KeeperRegistry2_1.abi ../../contracts/solc/v0.8.16/KeeperRegistry2_1/KeeperRegistry2_1.bin 11d36cb9eab0e136a2c3224709f7df17711756a126127e8c82326ce0a2e2b4f4
keepers_vrf_consumer: ../../contracts/solc/v0.8.6/KeepersVRFConsumer/KeepersVRFConsumer.abi ../../contracts/solc/v0.8.6/KeepersVRFConsumer/KeepersVRFConsumer.bin fa75572e689c9e84705c63e8dbe1b7b8aa1a8fe82d66356c4873d024bb9166e8
-log_emitter: ../../contracts/solc/v0.8.19/LogEmitter/LogEmitter.abi ../../contracts/solc/v0.8.19/LogEmitter/LogEmitter.bin 4b129ab93432c95ff9143f0631323e189887668889e0b36ccccf18a571e41ccf
log_triggered_streams_lookup_wrapper: ../../contracts/solc/v0.8.16/LogTriggeredStreamsLookup/LogTriggeredStreamsLookup.abi ../../contracts/solc/v0.8.16/LogTriggeredStreamsLookup/LogTriggeredStreamsLookup.bin 920fff3b662909f12ed11b47d168036ffa74ad52070a94e2fa26cdad5e428b4e
log_upkeep_counter_wrapper: ../../contracts/solc/v0.8.6/LogUpkeepCounter/LogUpkeepCounter.abi ../../contracts/solc/v0.8.6/LogUpkeepCounter/LogUpkeepCounter.bin 5482033d55eddb653bf580de0cc950db89a329091e085ac4122583df4a9777cd
mock_aggregator_proxy: ../../contracts/solc/v0.8.6/MockAggregatorProxy/MockAggregatorProxy.abi ../../contracts/solc/v0.8.6/MockAggregatorProxy/MockAggregatorProxy.bin b16c108f3dd384c342ddff5e94da7c0a8d39d1be5e3d8f2cf61ecc7f0e50ff42
@@ -67,7 +66,6 @@ solidity_vrf_v08_verifier_wrapper: ../../contracts/solc/v0.8.6/VRFTestHelper/VRF
streams_lookup_compatible_interface: ../../contracts/solc/v0.8.16/StreamsLookupCompatibleInterface/StreamsLookupCompatibleInterface.abi ../../contracts/solc/v0.8.16/StreamsLookupCompatibleInterface/StreamsLookupCompatibleInterface.bin 2861f553fb4731e89126b13319462df674727005a51982d1e617e2c2e44fa422
streams_lookup_upkeep_wrapper: ../../contracts/solc/v0.8.16/StreamsLookupUpkeep/StreamsLookupUpkeep.abi ../../contracts/solc/v0.8.16/StreamsLookupUpkeep/StreamsLookupUpkeep.bin 37e3a61091cc2a156539dd4aaff987e07577118aa02e97931a647df55705465e
trusted_blockhash_store: ../../contracts/solc/v0.8.19/TrustedBlockhashStore/TrustedBlockhashStore.abi ../../contracts/solc/v0.8.19/TrustedBlockhashStore/TrustedBlockhashStore.bin 1570663ef6feabf8660a93e85d2427ad8e7dabcfa5b418d308c62132451c5662
-type_and_version_interface_wrapper: ../../contracts/solc/v0.8.6/KeeperRegistry1_2/TypeAndVersionInterface.abi ../../contracts/solc/v0.8.6/KeeperRegistry1_2/TypeAndVersionInterface.bin bc9c3a6e73e3ebd5b58754df0deeb3b33f4bb404d5709bb904aed51d32f4b45e
upkeep_counter_wrapper: ../../contracts/solc/v0.8.16/UpkeepCounter/UpkeepCounter.abi ../../contracts/solc/v0.8.16/UpkeepCounter/UpkeepCounter.bin cef953186d12ac802e54d17c897d01605b60bbe0ce2df3b4cf2c31c5c3168b35
upkeep_perform_counter_restrictive_wrapper: ../../contracts/solc/v0.8.16/UpkeepPerformCounterRestrictive/UpkeepPerformCounterRestrictive.abi ../../contracts/solc/v0.8.16/UpkeepPerformCounterRestrictive/UpkeepPerformCounterRestrictive.bin 20955b21acceb58355fa287b29194a73edf5937067ba7140667301017cb2b24c
upkeep_transcoder: ../../contracts/solc/v0.8.6/UpkeepTranscoder/UpkeepTranscoder.abi ../../contracts/solc/v0.8.6/UpkeepTranscoder/UpkeepTranscoder.bin 336c92a981597be26508455f81a908a0784a817b129a59686c5b2c4afcba730a
@@ -90,7 +88,6 @@ vrf_external_sub_owner_example: ../../contracts/solc/v0.8.6/VRFExternalSubOwnerE
vrf_load_test_external_sub_owner: ../../contracts/solc/v0.8.6/VRFLoadTestExternalSubOwner/VRFLoadTestExternalSubOwner.abi ../../contracts/solc/v0.8.6/VRFLoadTestExternalSubOwner/VRFLoadTestExternalSubOwner.bin 2097faa70265e420036cc8a3efb1f1e0836ad2d7323b295b9a26a125dbbe6c7d
vrf_load_test_ownerless_consumer: ../../contracts/solc/v0.8.6/VRFLoadTestOwnerlessConsumer/VRFLoadTestOwnerlessConsumer.abi ../../contracts/solc/v0.8.6/VRFLoadTestOwnerlessConsumer/VRFLoadTestOwnerlessConsumer.bin 74f914843cbc70b9c3079c3e1c709382ce415225e8bb40113e7ac018bfcb0f5c
vrf_load_test_with_metrics: ../../contracts/solc/v0.8.6/VRFV2LoadTestWithMetrics/VRFV2LoadTestWithMetrics.abi ../../contracts/solc/v0.8.6/VRFV2LoadTestWithMetrics/VRFV2LoadTestWithMetrics.bin c9621c52d216a090ff6bbe942f1b75d2bce8658a27323c3789e5e14b523277ee
-vrf_log_emitter: ../../contracts/solc/v0.8.19/VRFLogEmitter/VRFLogEmitter.abi ../../contracts/solc/v0.8.19/VRFLogEmitter/VRFLogEmitter.bin 15f491d445ac4d0c712d1cbe4e5054c759b080bf20de7d54bfe2a82cde4dcf06
vrf_malicious_consumer_v2: ../../contracts/solc/v0.8.6/VRFMaliciousConsumerV2/VRFMaliciousConsumerV2.abi ../../contracts/solc/v0.8.6/VRFMaliciousConsumerV2/VRFMaliciousConsumerV2.bin 9755fa8ffc7f5f0b337d5d413d77b0c9f6cd6f68c31727d49acdf9d4a51bc522
vrf_malicious_consumer_v2_plus: ../../contracts/solc/v0.8.19/VRFMaliciousConsumerV2Plus/VRFMaliciousConsumerV2Plus.abi ../../contracts/solc/v0.8.19/VRFMaliciousConsumerV2Plus/VRFMaliciousConsumerV2Plus.bin f6bf81658d3472bb705d28dc4a837097ec93d78c3f786efaa9cd040ada9d3319
vrf_mock_ethlink_aggregator: ../../contracts/solc/v0.8.6/VRFMockETHLINKAggregator/VRFMockETHLINKAggregator.abi ../../contracts/solc/v0.8.6/VRFMockETHLINKAggregator/VRFMockETHLINKAggregator.bin 3657f8c552147eb55d7538fa7d8012c1a983d8c5184610de60600834a72e006b
diff --git a/core/gethwrappers/go_generate.go b/core/gethwrappers/go_generate.go
index 1fee016fe8b..ab610f01d67 100644
--- a/core/gethwrappers/go_generate.go
+++ b/core/gethwrappers/go_generate.go
@@ -17,7 +17,6 @@ package gethwrappers
//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/KeeperRegistrar1_2/KeeperRegistrar.abi ../../contracts/solc/v0.8.6/KeeperRegistrar1_2/KeeperRegistrar.bin KeeperRegistrar keeper_registrar_wrapper1_2
//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/KeeperRegistrar1_2Mock/KeeperRegistrar1_2Mock.abi ../../contracts/solc/v0.8.6/KeeperRegistrar1_2Mock/KeeperRegistrar1_2Mock.bin KeeperRegistrarMock keeper_registrar_wrapper1_2_mock
//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/KeeperRegistry1_2/KeeperRegistry1_2.abi ../../contracts/solc/v0.8.6/KeeperRegistry1_2/KeeperRegistry1_2.bin KeeperRegistry keeper_registry_wrapper1_2
-//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/KeeperRegistry1_2/TypeAndVersionInterface.abi ../../contracts/solc/v0.8.6/KeeperRegistry1_2/TypeAndVersionInterface.bin TypeAndVersionInterface type_and_version_interface_wrapper
//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/KeeperRegistryCheckUpkeepGasUsageWrapper1_2/KeeperRegistryCheckUpkeepGasUsageWrapper1_2.abi ../../contracts/solc/v0.8.6/KeeperRegistryCheckUpkeepGasUsageWrapper1_2/KeeperRegistryCheckUpkeepGasUsageWrapper1_2.bin KeeperRegistryCheckUpkeepGasUsageWrapper gas_wrapper
//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/KeeperRegistryCheckUpkeepGasUsageWrapper1_2Mock/KeeperRegistryCheckUpkeepGasUsageWrapper1_2Mock.abi ../../contracts/solc/v0.8.6/KeeperRegistryCheckUpkeepGasUsageWrapper1_2Mock/KeeperRegistryCheckUpkeepGasUsageWrapper1_2Mock.bin KeeperRegistryCheckUpkeepGasUsageWrapperMock gas_wrapper_mock
//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/KeeperRegistry1_3/KeeperRegistry1_3.abi ../../contracts/solc/v0.8.6/KeeperRegistry1_3/KeeperRegistry1_3.bin KeeperRegistry keeper_registry_wrapper1_3
diff --git a/core/gethwrappers/go_generate_logpoller.go b/core/gethwrappers/go_generate_logpoller.go
deleted file mode 100644
index b28b8205830..00000000000
--- a/core/gethwrappers/go_generate_logpoller.go
+++ /dev/null
@@ -1,7 +0,0 @@
-// Package gethwrappers provides tools for wrapping solidity contracts with
-// golang packages, using abigen.
-package gethwrappers
-
-// Log tester
-//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.19/LogEmitter/LogEmitter.abi ../../contracts/solc/v0.8.19/LogEmitter/LogEmitter.bin LogEmitter log_emitter
-//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.19/VRFLogEmitter/VRFLogEmitter.abi ../../contracts/solc/v0.8.19/VRFLogEmitter/VRFLogEmitter.bin VRFLogEmitter vrf_log_emitter
diff --git a/core/gethwrappers/generated/log_emitter/log_emitter.go b/core/gethwrappers/shared/generated/log_emitter/log_emitter.go
similarity index 93%
rename from core/gethwrappers/generated/log_emitter/log_emitter.go
rename to core/gethwrappers/shared/generated/log_emitter/log_emitter.go
index 24fef257af3..6ae06d7f08d 100644
--- a/core/gethwrappers/generated/log_emitter/log_emitter.go
+++ b/core/gethwrappers/shared/generated/log_emitter/log_emitter.go
@@ -31,7 +31,7 @@ var (
)
var LogEmitterMetaData = &bind.MetaData{
- ABI: "[{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"Log1\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"Log2\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"name\":\"Log3\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"Log4\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"uint256[]\",\"name\":\"v\",\"type\":\"uint256[]\"}],\"name\":\"EmitLog1\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[]\",\"name\":\"v\",\"type\":\"uint256[]\"}],\"name\":\"EmitLog2\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string[]\",\"name\":\"v\",\"type\":\"string[]\"}],\"name\":\"EmitLog3\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"v\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"w\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"c\",\"type\":\"uint256\"}],\"name\":\"EmitLog4\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]",
+ ABI: "[{\"type\":\"function\",\"name\":\"EmitLog1\",\"inputs\":[{\"name\":\"v\",\"type\":\"uint256[]\",\"internalType\":\"uint256[]\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"EmitLog2\",\"inputs\":[{\"name\":\"v\",\"type\":\"uint256[]\",\"internalType\":\"uint256[]\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"EmitLog3\",\"inputs\":[{\"name\":\"v\",\"type\":\"string[]\",\"internalType\":\"string[]\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"EmitLog4\",\"inputs\":[{\"name\":\"v\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"w\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"c\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"event\",\"name\":\"Log1\",\"inputs\":[{\"name\":\"\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Log2\",\"inputs\":[{\"name\":\"\",\"type\":\"uint256\",\"indexed\":true,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Log3\",\"inputs\":[{\"name\":\"\",\"type\":\"string\",\"indexed\":false,\"internalType\":\"string\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Log4\",\"inputs\":[{\"name\":\"\",\"type\":\"uint256\",\"indexed\":true,\"internalType\":\"uint256\"},{\"name\":\"\",\"type\":\"uint256\",\"indexed\":true,\"internalType\":\"uint256\"}],\"anonymous\":false}]",
Bin: "0x608060405234801561001057600080fd5b506105c5806100206000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c8063696933c914610051578063b4b12d9814610066578063bc253bc014610079578063d9c21f461461008c575b600080fd5b61006461005f3660046102d7565b61009f565b005b61006461007436600461036d565b610113565b6100646100873660046102d7565b610163565b61006461009a366004610399565b6101c7565b60005b815181101561010f577f46692c0e59ca9cd1ad8f984a9d11715ec83424398b7eed4e05c8ce84662415a88282815181106100de576100de6104be565b60200260200101516040516100f591815260200190565b60405180910390a180610107816104ed565b9150506100a2565b5050565b60005b8181101561015d57604051839085907fba21d5b63d64546cb4ab29e370a8972bf26f78cb0c395391b4f451699fdfdc5d90600090a380610155816104ed565b915050610116565b50505050565b60005b815181101561010f57818181518110610181576101816104be565b60200260200101517f624fb00c2ce79f34cb543884c3af64816dce0f4cec3d32661959e49d488a7a9360405160405180910390a2806101bf816104ed565b915050610166565b60005b815181101561010f577fb94ec34dfe32a8a7170992a093976368d1e63decf8f0bc0b38a8eb89cc9f95cf828281518110610206576102066104be565b602002602001015160405161021b919061054c565b60405180910390a18061022d816104ed565b9150506101ca565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156102ab576102ab610235565b604052919050565b600067ffffffffffffffff8211156102cd576102cd610235565b5060051b60200190565b600060208083850312156102ea57600080fd5b823567ffffffffffffffff81111561030157600080fd5b8301601f8101851361031257600080fd5b8035610325610320826102b3565b610264565b81815260059190911b8201830190838101908783111561034457600080fd5b928401925b8284101561036257833582529284019290840190610349565b979650505050505050565b60008060006060848603121561038257600080fd5b505081359360208301359350604090920135919050565b600060208083850312156103ac57600080fd5b823567ffffffffffffffff808211156103c457600080fd5b8185019150601f86818401126103d957600080fd5b82356103e7610320826102b3565b81815260059190911b8401850190858101908983111561040657600080fd5b8686015b838110156104b0578035868111156104225760008081fd5b8701603f81018c136104345760008081fd5b8881013560408882111561044a5761044a610235565b6104798b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08a85011601610264565b8281528e8284860101111561048e5760008081fd5b828285018d83013760009281018c01929092525084525091870191870161040a565b509998505050505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203610545577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b5060010190565b600060208083528351808285015260005b818110156105795785810183015185820160400152820161055d565b5060006040828601015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f830116850101925050509291505056fea164736f6c6343000813000a",
}
diff --git a/core/gethwrappers/shared/generated/type_and_version/type_and_version.go b/core/gethwrappers/shared/generated/type_and_version/type_and_version.go
new file mode 100644
index 00000000000..a4a518d9ea2
--- /dev/null
+++ b/core/gethwrappers/shared/generated/type_and_version/type_and_version.go
@@ -0,0 +1,183 @@
+// Code generated - DO NOT EDIT.
+// This file is a generated binding and any manual changes will be lost.
+
+package type_and_version
+
+import (
+ "errors"
+ "math/big"
+ "strings"
+
+ ethereum "github.com/ethereum/go-ethereum"
+ "github.com/ethereum/go-ethereum/accounts/abi"
+ "github.com/ethereum/go-ethereum/accounts/abi/bind"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/event"
+)
+
+var (
+ _ = errors.New
+ _ = big.NewInt
+ _ = strings.NewReader
+ _ = ethereum.NotFound
+ _ = bind.Bind
+ _ = common.Big1
+ _ = types.BloomLookup
+ _ = event.NewSubscription
+ _ = abi.ConvertType
+)
+
+var ITypeAndVersionMetaData = &bind.MetaData{
+ ABI: "[{\"type\":\"function\",\"name\":\"typeAndVersion\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"string\",\"internalType\":\"string\"}],\"stateMutability\":\"pure\"}]",
+}
+
+var ITypeAndVersionABI = ITypeAndVersionMetaData.ABI
+
+type ITypeAndVersion struct {
+ address common.Address
+ abi abi.ABI
+ ITypeAndVersionCaller
+ ITypeAndVersionTransactor
+ ITypeAndVersionFilterer
+}
+
+type ITypeAndVersionCaller struct {
+ contract *bind.BoundContract
+}
+
+type ITypeAndVersionTransactor struct {
+ contract *bind.BoundContract
+}
+
+type ITypeAndVersionFilterer struct {
+ contract *bind.BoundContract
+}
+
+type ITypeAndVersionSession struct {
+ Contract *ITypeAndVersion
+ CallOpts bind.CallOpts
+ TransactOpts bind.TransactOpts
+}
+
+type ITypeAndVersionCallerSession struct {
+ Contract *ITypeAndVersionCaller
+ CallOpts bind.CallOpts
+}
+
+type ITypeAndVersionTransactorSession struct {
+ Contract *ITypeAndVersionTransactor
+ TransactOpts bind.TransactOpts
+}
+
+type ITypeAndVersionRaw struct {
+ Contract *ITypeAndVersion
+}
+
+type ITypeAndVersionCallerRaw struct {
+ Contract *ITypeAndVersionCaller
+}
+
+type ITypeAndVersionTransactorRaw struct {
+ Contract *ITypeAndVersionTransactor
+}
+
+func NewITypeAndVersion(address common.Address, backend bind.ContractBackend) (*ITypeAndVersion, error) {
+ abi, err := abi.JSON(strings.NewReader(ITypeAndVersionABI))
+ if err != nil {
+ return nil, err
+ }
+ contract, err := bindITypeAndVersion(address, backend, backend, backend)
+ if err != nil {
+ return nil, err
+ }
+ return &ITypeAndVersion{address: address, abi: abi, ITypeAndVersionCaller: ITypeAndVersionCaller{contract: contract}, ITypeAndVersionTransactor: ITypeAndVersionTransactor{contract: contract}, ITypeAndVersionFilterer: ITypeAndVersionFilterer{contract: contract}}, nil
+}
+
+func NewITypeAndVersionCaller(address common.Address, caller bind.ContractCaller) (*ITypeAndVersionCaller, error) {
+ contract, err := bindITypeAndVersion(address, caller, nil, nil)
+ if err != nil {
+ return nil, err
+ }
+ return &ITypeAndVersionCaller{contract: contract}, nil
+}
+
+func NewITypeAndVersionTransactor(address common.Address, transactor bind.ContractTransactor) (*ITypeAndVersionTransactor, error) {
+ contract, err := bindITypeAndVersion(address, nil, transactor, nil)
+ if err != nil {
+ return nil, err
+ }
+ return &ITypeAndVersionTransactor{contract: contract}, nil
+}
+
+func NewITypeAndVersionFilterer(address common.Address, filterer bind.ContractFilterer) (*ITypeAndVersionFilterer, error) {
+ contract, err := bindITypeAndVersion(address, nil, nil, filterer)
+ if err != nil {
+ return nil, err
+ }
+ return &ITypeAndVersionFilterer{contract: contract}, nil
+}
+
+func bindITypeAndVersion(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) {
+ parsed, err := ITypeAndVersionMetaData.GetAbi()
+ if err != nil {
+ return nil, err
+ }
+ return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil
+}
+
+func (_ITypeAndVersion *ITypeAndVersionRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
+ return _ITypeAndVersion.Contract.ITypeAndVersionCaller.contract.Call(opts, result, method, params...)
+}
+
+func (_ITypeAndVersion *ITypeAndVersionRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
+ return _ITypeAndVersion.Contract.ITypeAndVersionTransactor.contract.Transfer(opts)
+}
+
+func (_ITypeAndVersion *ITypeAndVersionRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
+ return _ITypeAndVersion.Contract.ITypeAndVersionTransactor.contract.Transact(opts, method, params...)
+}
+
+func (_ITypeAndVersion *ITypeAndVersionCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
+ return _ITypeAndVersion.Contract.contract.Call(opts, result, method, params...)
+}
+
+func (_ITypeAndVersion *ITypeAndVersionTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
+ return _ITypeAndVersion.Contract.contract.Transfer(opts)
+}
+
+func (_ITypeAndVersion *ITypeAndVersionTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
+ return _ITypeAndVersion.Contract.contract.Transact(opts, method, params...)
+}
+
+func (_ITypeAndVersion *ITypeAndVersionCaller) TypeAndVersion(opts *bind.CallOpts) (string, error) {
+ var out []interface{}
+ err := _ITypeAndVersion.contract.Call(opts, &out, "typeAndVersion")
+
+ if err != nil {
+ return *new(string), err
+ }
+
+ out0 := *abi.ConvertType(out[0], new(string)).(*string)
+
+ return out0, err
+
+}
+
+func (_ITypeAndVersion *ITypeAndVersionSession) TypeAndVersion() (string, error) {
+ return _ITypeAndVersion.Contract.TypeAndVersion(&_ITypeAndVersion.CallOpts)
+}
+
+func (_ITypeAndVersion *ITypeAndVersionCallerSession) TypeAndVersion() (string, error) {
+ return _ITypeAndVersion.Contract.TypeAndVersion(&_ITypeAndVersion.CallOpts)
+}
+
+func (_ITypeAndVersion *ITypeAndVersion) Address() common.Address {
+ return _ITypeAndVersion.address
+}
+
+type ITypeAndVersionInterface interface {
+ TypeAndVersion(opts *bind.CallOpts) (string, error)
+
+ Address() common.Address
+}
diff --git a/core/gethwrappers/generated/vrf_log_emitter/vrf_log_emitter.go b/core/gethwrappers/shared/generated/vrf_log_emitter/vrf_log_emitter.go
similarity index 88%
rename from core/gethwrappers/generated/vrf_log_emitter/vrf_log_emitter.go
rename to core/gethwrappers/shared/generated/vrf_log_emitter/vrf_log_emitter.go
index 2cdeaa6c3a8..db6fae033a8 100644
--- a/core/gethwrappers/generated/vrf_log_emitter/vrf_log_emitter.go
+++ b/core/gethwrappers/shared/generated/vrf_log_emitter/vrf_log_emitter.go
@@ -31,7 +31,7 @@ var (
)
var VRFLogEmitterMetaData = &bind.MetaData{
- ABI: "[{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"requestId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"outputSeed\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"payment\",\"type\":\"uint96\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"}],\"name\":\"RandomWordsFulfilled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"keyHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"requestId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"preSeed\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"minimumRequestConfirmations\",\"type\":\"uint16\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"numWords\",\"type\":\"uint32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RandomWordsRequested\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"requestId\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"outputSeed\",\"type\":\"uint256\"},{\"internalType\":\"uint96\",\"name\":\"payment\",\"type\":\"uint96\"},{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"}],\"name\":\"emitRandomWordsFulfilled\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"keyHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"requestId\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"preSeed\",\"type\":\"uint256\"},{\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"internalType\":\"uint16\",\"name\":\"minimumRequestConfirmations\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"numWords\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"emitRandomWordsRequested\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]",
+ ABI: "[{\"type\":\"function\",\"name\":\"emitRandomWordsFulfilled\",\"inputs\":[{\"name\":\"requestId\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"outputSeed\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"payment\",\"type\":\"uint96\",\"internalType\":\"uint96\"},{\"name\":\"success\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"emitRandomWordsRequested\",\"inputs\":[{\"name\":\"keyHash\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"requestId\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"preSeed\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"subId\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"minimumRequestConfirmations\",\"type\":\"uint16\",\"internalType\":\"uint16\"},{\"name\":\"callbackGasLimit\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"numWords\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"sender\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"event\",\"name\":\"RandomWordsFulfilled\",\"inputs\":[{\"name\":\"requestId\",\"type\":\"uint256\",\"indexed\":true,\"internalType\":\"uint256\"},{\"name\":\"outputSeed\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"},{\"name\":\"payment\",\"type\":\"uint96\",\"indexed\":false,\"internalType\":\"uint96\"},{\"name\":\"success\",\"type\":\"bool\",\"indexed\":false,\"internalType\":\"bool\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"RandomWordsRequested\",\"inputs\":[{\"name\":\"keyHash\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"bytes32\"},{\"name\":\"requestId\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"},{\"name\":\"preSeed\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"},{\"name\":\"subId\",\"type\":\"uint64\",\"indexed\":true,\"internalType\":\"uint64\"},{\"name\":\"minimumRequestConfirmations\",\"type\":\"uint16\",\"indexed\":false,\"internalType\":\"uint16\"},{\"name\":\"callbackGasLimit\",\"type\":\"uint32\",\"indexed\":false,\"internalType\":\"uint32\"},{\"name\":\"numWords\",\"type\":\"uint32\",\"indexed\":false,\"internalType\":\"uint32\"},{\"name\":\"sender\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false}]",
Bin: "0x608060405234801561001057600080fd5b5061027f806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063ca920adb1461003b578063fe62d3e914610050575b600080fd5b61004e61004936600461015b565b610063565b005b61004e61005e366004610212565b6100eb565b604080518881526020810188905261ffff86168183015263ffffffff858116606083015284166080820152905173ffffffffffffffffffffffffffffffffffffffff83169167ffffffffffffffff8816918b917f63373d1c4696214b898952999c9aaec57dac1ee2723cec59bea6888f489a9772919081900360a00190a45050505050505050565b604080518481526bffffffffffffffffffffffff8416602082015282151581830152905185917f7dffc5ae5ee4e2e4df1651cf6ad329a73cebdb728f37ea0187b9b17e036756e4919081900360600190a250505050565b803563ffffffff8116811461015657600080fd5b919050565b600080600080600080600080610100898b03121561017857600080fd5b883597506020890135965060408901359550606089013567ffffffffffffffff811681146101a557600080fd5b9450608089013561ffff811681146101bc57600080fd5b93506101ca60a08a01610142565b92506101d860c08a01610142565b915060e089013573ffffffffffffffffffffffffffffffffffffffff8116811461020157600080fd5b809150509295985092959890939650565b6000806000806080858703121561022857600080fd5b843593506020850135925060408501356bffffffffffffffffffffffff8116811461025257600080fd5b91506060850135801515811461026757600080fd5b93969295509093505056fea164736f6c6343000813000a",
}
diff --git a/core/gethwrappers/shared/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/shared/generation/generated-wrapper-dependency-versions-do-not-edit.txt
index 1c333b653ef..9b7ba5f8832 100644
--- a/core/gethwrappers/shared/generation/generated-wrapper-dependency-versions-do-not-edit.txt
+++ b/core/gethwrappers/shared/generation/generated-wrapper-dependency-versions-do-not-edit.txt
@@ -5,6 +5,9 @@ burn_mint_erc677: ../../../contracts/solc/shared/BurnMintERC677/BurnMintERC677.s
chain_reader_tester: ../../../contracts/solc/shared/ChainReaderTester/ChainReaderTester.sol/ChainReaderTester.abi.json ../../../contracts/solc/shared/ChainReaderTester/ChainReaderTester.sol/ChainReaderTester.bin 876c55e8d2556dc9cc953c786ae72b0430cb2c992f84573a2aae9680068f293d
erc20: ../../../contracts/solc/vendor/ERC20/ERC20.sol/ERC20.abi.json ../../../contracts/solc/vendor/ERC20/ERC20.sol/ERC20.bin 9a5e3f7ec9fea385eeba374d184d6b83784304f537a90f6b81827c732d0b37c4
link_token: ../../../contracts/solc/shared/LinkToken/LinkToken.sol/LinkToken.abi.json ../../../contracts/solc/shared/LinkToken/LinkToken.sol/LinkToken.bin 9d1c648233822b70b03bf4fdb1af4cffaead8f1391dd149a79b3072defbd0c62
-mock_v3_aggregator_contract: ../../../contracts/solc/tests/MockV3Aggregator/MockV3Aggregator.sol/MockV3Aggregator.abi.json ../../../contracts/solc/tests/MockV3Aggregator/MockV3Aggregator.sol/MockV3Aggregator.bin 76796e0faffb2981d49082d94f2f2c9ec87d8ad960b022993d0681f9c81a832d
+log_emitter: ../../../contracts/solc/shared/LogEmitter/LogEmitter.sol/LogEmitter.abi.json ../../../contracts/solc/shared/LogEmitter/LogEmitter.sol/LogEmitter.bin f884ed34204f82dcd1ea8f20db1b24d410bf23ab2687d56968d2c670e98277dd
+mock_v3_aggregator_contract: ../../../contracts/solc/shared/MockV3Aggregator/MockV3Aggregator.sol/MockV3Aggregator.abi.json ../../../contracts/solc/shared/MockV3Aggregator/MockV3Aggregator.sol/MockV3Aggregator.bin 76796e0faffb2981d49082d94f2f2c9ec87d8ad960b022993d0681f9c81a832d
multicall3: ../../../contracts/solc/vendor/Multicall3/Multicall3.sol/Multicall3.abi.json ../../../contracts/solc/vendor/Multicall3/Multicall3.sol/Multicall3.bin 175cd8790a4c714790c3761c50b0e93694c71bb7f8897eb92150847e6d8a94f4
+type_and_version: ../../../contracts/solc/shared/ITypeAndVersion/ITypeAndVersion.sol/ITypeAndVersion.abi.json ../../../contracts/solc/shared/ITypeAndVersion/ITypeAndVersion.sol/ITypeAndVersion.bin 21f6da4daa754971a4fdafea90ec64a77a5f03e62f9a9639802726b22eaa380a
+vrf_log_emitter: ../../../contracts/solc/shared/VRFLogEmitter/VRFLogEmitter.sol/VRFLogEmitter.abi.json ../../../contracts/solc/shared/VRFLogEmitter/VRFLogEmitter.sol/VRFLogEmitter.bin 46788c9519425dd23befdea8e561ee454dcb559f6a8fe70f4a092805574218f6
werc20_mock: ../../../contracts/solc/shared/WERC20Mock/WERC20Mock.sol/WERC20Mock.abi.json ../../../contracts/solc/shared/WERC20Mock/WERC20Mock.sol/WERC20Mock.bin f5ba13fc99c248354508e3bab6cd0fb66607d3b7377f59a1e80b930e96ed4f48
diff --git a/core/gethwrappers/shared/go_generate.go b/core/gethwrappers/shared/go_generate.go
index 3ac9b8ac6e9..0881e1b31e3 100644
--- a/core/gethwrappers/shared/go_generate.go
+++ b/core/gethwrappers/shared/go_generate.go
@@ -8,7 +8,10 @@ package gethwrappers
//go:generate go run ../generation/wrap.go shared WERC20Mock werc20_mock
//go:generate go run ../generation/wrap.go shared ChainReaderTester chain_reader_tester
//go:generate go run ../generation/wrap.go shared AggregatorV3Interface aggregator_v3_interface
+//go:generate go run ../generation/wrap.go shared MockV3Aggregator mock_v3_aggregator_contract
+//go:generate go run ../generation/wrap.go shared LogEmitter log_emitter
+//go:generate go run ../generation/wrap.go shared VRFLogEmitter vrf_log_emitter
+//go:generate go run ../generation/wrap.go shared ITypeAndVersion type_and_version
//go:generate go run ../generation/wrap.go vendor ERC20 erc20
//go:generate go run ../generation/wrap.go vendor Multicall3 multicall3
-//go:generate go run ../generation/wrap.go tests MockV3Aggregator mock_v3_aggregator_contract
diff --git a/core/services/keeper/registry_interface.go b/core/services/keeper/registry_interface.go
index 04bcb8e257d..b37917cef60 100644
--- a/core/services/keeper/registry_interface.go
+++ b/core/services/keeper/registry_interface.go
@@ -16,7 +16,7 @@ import (
registry1_1 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/keeper_registry_wrapper1_1"
registry1_2 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/keeper_registry_wrapper1_2"
registry1_3 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/keeper_registry_wrapper1_3"
- type_and_version "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/type_and_version_interface_wrapper"
+ "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/type_and_version"
)
type RegistryVersion int32
@@ -61,14 +61,14 @@ type RegistryWrapper struct {
}
func NewRegistryWrapper(address evmtypes.EIP55Address, evmClient evmclient.Client) (*RegistryWrapper, error) {
- interface_wrapper, err := type_and_version.NewTypeAndVersionInterface(
+ interfaceWrapper, err := type_and_version.NewITypeAndVersion(
address.Address(),
evmClient,
)
if err != nil {
return nil, errors.Wrap(err, "unable to create type and interface wrapper")
}
- version, err := getRegistryVersion(interface_wrapper)
+ version, err := getRegistryVersion(interfaceWrapper)
if err != nil {
return nil, errors.Wrap(err, "unable to determine version of keeper registry contract")
}
@@ -105,7 +105,7 @@ func NewRegistryWrapper(address evmtypes.EIP55Address, evmClient evmclient.Clien
}, nil
}
-func getRegistryVersion(contract *type_and_version.TypeAndVersionInterface) (*RegistryVersion, error) {
+func getRegistryVersion(contract *type_and_version.ITypeAndVersion) (*RegistryVersion, error) {
typeAndVersion, err := contract.TypeAndVersion(nil)
if err != nil {
jsonErr := evmclient.ExtractRPCErrorOrNil(err)
diff --git a/core/services/ocr2/plugins/ccip/config/type_and_version.go b/core/services/ocr2/plugins/ccip/config/type_and_version.go
index fdfd892b087..9d5e1629c11 100644
--- a/core/services/ocr2/plugins/ccip/config/type_and_version.go
+++ b/core/services/ocr2/plugins/ccip/config/type_and_version.go
@@ -9,7 +9,7 @@ import (
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
- type_and_version "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/type_and_version_interface_wrapper"
+ "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/type_and_version"
)
type ContractType string
@@ -39,7 +39,7 @@ func VerifyTypeAndVersion(addr common.Address, client bind.ContractBackend, expe
}
func TypeAndVersion(addr common.Address, client bind.ContractBackend) (ContractType, semver.Version, error) {
- tv, err := type_and_version.NewTypeAndVersionInterface(addr, client)
+ tv, err := type_and_version.NewITypeAndVersion(addr, client)
if err != nil {
return "", semver.Version{}, err
}
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 32ec1b24ac9..6d5d000e1fe 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
@@ -13,7 +13,7 @@ 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/gethwrappers/shared/generated/type_and_version"
"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"
@@ -24,7 +24,7 @@ import (
)
var (
- typeAndVersionABI = abihelpers.MustParseABI(type_and_version.TypeAndVersionInterfaceABI)
+ typeAndVersionABI = abihelpers.MustParseABI(type_and_version.ITypeAndVersionABI)
)
type EVMTokenPoolBatchedReader struct {
diff --git a/core/services/relay/evm/capabilities/testutils/chain_reader.go b/core/services/relay/evm/capabilities/testutils/chain_reader.go
index 64fbf5fe720..07e0f3e05ac 100644
--- a/core/services/relay/evm/capabilities/testutils/chain_reader.go
+++ b/core/services/relay/evm/capabilities/testutils/chain_reader.go
@@ -14,7 +14,7 @@ import (
commontypes "github.com/smartcontractkit/chainlink-common/pkg/types"
commonvalues "github.com/smartcontractkit/chainlink-common/pkg/values"
"github.com/smartcontractkit/chainlink/v2/core/capabilities/triggers/logevent/logeventcap"
- "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/log_emitter"
+ "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/log_emitter"
coretestutils "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"
diff --git a/core/services/vrf/v2/listener_v2_log_listener_test.go b/core/services/vrf/v2/listener_v2_log_listener_test.go
index 06af4c83f19..a29449a7ebf 100644
--- a/core/services/vrf/v2/listener_v2_log_listener_test.go
+++ b/core/services/vrf/v2/listener_v2_log_listener_test.go
@@ -26,9 +26,9 @@ import (
evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types"
ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big"
evmmocks "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm/mocks"
- "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/log_emitter"
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2"
- "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_log_emitter"
+ "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/log_emitter"
+ "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/vrf_log_emitter"
"github.com/smartcontractkit/chainlink/v2/core/internal/testutils"
"github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest"
"github.com/smartcontractkit/chainlink/v2/core/logger"
diff --git a/integration-tests/ccip-tests/contracts/contract_deployer.go b/integration-tests/ccip-tests/contracts/contract_deployer.go
index 940f76e93b8..0aaec8f66a0 100644
--- a/integration-tests/ccip-tests/contracts/contract_deployer.go
+++ b/integration-tests/ccip-tests/contracts/contract_deployer.go
@@ -50,10 +50,10 @@ import (
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/usdc_token_pool_1_4_0"
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/weth9"
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/link_token_interface"
- type_and_version "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/type_and_version_interface_wrapper"
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/burn_mint_erc677"
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/erc20"
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/mock_v3_aggregator_contract"
+ "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/type_and_version"
"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/testhelpers"
@@ -1259,7 +1259,7 @@ func (e *CCIPContractsDeployer) NewMockAggregator(addr common.Address) (*MockAgg
}
func (e *CCIPContractsDeployer) TypeAndVersion(addr common.Address) (string, error) {
- tv, err := type_and_version.NewTypeAndVersionInterface(addr, wrappers.MustNewWrappedContractBackend(e.evmClient, nil))
+ tv, err := type_and_version.NewITypeAndVersion(addr, wrappers.MustNewWrappedContractBackend(e.evmClient, nil))
if err != nil {
return "", err
}
diff --git a/integration-tests/contracts/test_contracts.go b/integration-tests/contracts/test_contracts.go
index f8674e2136d..f6ea627ef39 100644
--- a/integration-tests/contracts/test_contracts.go
+++ b/integration-tests/contracts/test_contracts.go
@@ -11,7 +11,7 @@ import (
"github.com/smartcontractkit/chainlink-testing-framework/seth"
"github.com/smartcontractkit/chainlink/integration-tests/wrappers"
- le "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/log_emitter"
+ le "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/log_emitter"
)
type LogEmitterContract struct {
diff --git a/integration-tests/load/automationv2_1/automationv2_1_test.go b/integration-tests/load/automationv2_1/automationv2_1_test.go
index 823c1bd8825..8d8135d214f 100644
--- a/integration-tests/load/automationv2_1/automationv2_1_test.go
+++ b/integration-tests/load/automationv2_1/automationv2_1_test.go
@@ -47,8 +47,8 @@ import (
aconfig "github.com/smartcontractkit/chainlink/integration-tests/testconfig/automation"
"github.com/smartcontractkit/chainlink/integration-tests/testreporters"
ac "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_compatible_utils"
- "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/log_emitter"
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/simple_log_upkeep_counter_wrapper"
+ "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/log_emitter"
)
const (
diff --git a/integration-tests/load/automationv2_1/gun.go b/integration-tests/load/automationv2_1/gun.go
index aa61562741c..7e26d906456 100644
--- a/integration-tests/load/automationv2_1/gun.go
+++ b/integration-tests/load/automationv2_1/gun.go
@@ -10,7 +10,7 @@ import (
"github.com/smartcontractkit/chainlink-testing-framework/seth"
- "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/log_emitter"
+ "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/log_emitter"
"github.com/smartcontractkit/chainlink/integration-tests/contracts"
)
diff --git a/integration-tests/universal/log_poller/helpers.go b/integration-tests/universal/log_poller/helpers.go
index 0c127d576c0..c75bff6c0c2 100644
--- a/integration-tests/universal/log_poller/helpers.go
+++ b/integration-tests/universal/log_poller/helpers.go
@@ -45,7 +45,7 @@ import (
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller"
cltypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types"
ac "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_compatible_utils"
- le "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/log_emitter"
+ le "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/log_emitter"
core_logger "github.com/smartcontractkit/chainlink/v2/core/logger"
)
diff --git a/tools/ci/ccip_lcov_prune b/tools/ci/ccip_lcov_prune
index 9ec51e53536..fadb2cc410c 100755
--- a/tools/ci/ccip_lcov_prune
+++ b/tools/ci/ccip_lcov_prune
@@ -12,7 +12,6 @@ set -e
# BurnWithFromMintTokenPool is excluded because Forge doesn't seem to
# register coverage, even though it is 100% covered.
-
lcov --remove $1 -o $2 \
'*/ccip/test/*' \
'*/vendor/*' \
@@ -23,8 +22,6 @@ lcov --remove $1 -o $2 \
'src/v0.8/ccip/libraries/USDPriceWith18Decimals.sol' \
'src/v0.8/ccip/libraries/MerkleMultiProof.sol' \
'src/v0.8/ccip/libraries/Pool.sol' \
- 'src/v0.8/ConfirmedOwnerWithProposal.sol' \
- 'src/v0.8/tests/MockV3Aggregator.sol' \
'src/v0.8/ccip/applications/CCIPClientExample.sol' \
'src/v0.8/ccip/pools/BurnWithFromMintTokenPool.sol' \
'src/v0.8/ccip/rmn/RMNHome.sol' \
From ab46d04d983fe145fa490b43bb2fe685a1c08809 Mon Sep 17 00:00:00 2001
From: FelixFan1992
Date: Wed, 8 Jan 2025 11:05:31 -0500
Subject: [PATCH 12/91] DEVSVCS-1087: remove unused automation hardhat tests
(#15847)
* remove unused automation hardhat tests
* freeze contracts
* remove more tests
* update
---
.../action.yml | 4 +
.../automation/AutomationGasAnalysis.test.ts | 258 -
.../automation/AutomationRegistrar2_1.test.ts | 1022 ---
.../automation/AutomationRegistry2_2.test.ts | 5962 -----------------
.../test/v0.8/automation/CronUpkeep.test.ts | 576 --
.../v0.8/automation/CronUpkeepFactory.test.ts | 107 -
.../automation/ERC20BalanceMonitor.test.ts | 695 --
.../v0.8/automation/EthBalanceMonitor.test.ts | 663 --
.../IAutomationRegistryMaster2_2.test.ts | 117 -
.../LinkAvailableBalanceMonitor.test.ts | 1077 ---
.../automation/UpkeepBalanceMonitor.test.ts | 402 --
.../automation/UpkeepTranscoder3_0.test.ts | 576 --
.../automation/UpkeepTranscoder4_0.test.ts | 654 --
contracts/test/v0.8/automation/helpers.ts | 68 -
14 files changed, 4 insertions(+), 12177 deletions(-)
delete mode 100644 contracts/test/v0.8/automation/AutomationGasAnalysis.test.ts
delete mode 100644 contracts/test/v0.8/automation/AutomationRegistrar2_1.test.ts
delete mode 100644 contracts/test/v0.8/automation/AutomationRegistry2_2.test.ts
delete mode 100644 contracts/test/v0.8/automation/CronUpkeep.test.ts
delete mode 100644 contracts/test/v0.8/automation/CronUpkeepFactory.test.ts
delete mode 100644 contracts/test/v0.8/automation/ERC20BalanceMonitor.test.ts
delete mode 100644 contracts/test/v0.8/automation/EthBalanceMonitor.test.ts
delete mode 100644 contracts/test/v0.8/automation/IAutomationRegistryMaster2_2.test.ts
delete mode 100644 contracts/test/v0.8/automation/LinkAvailableBalanceMonitor.test.ts
delete mode 100644 contracts/test/v0.8/automation/UpkeepBalanceMonitor.test.ts
delete mode 100644 contracts/test/v0.8/automation/UpkeepTranscoder3_0.test.ts
delete mode 100644 contracts/test/v0.8/automation/UpkeepTranscoder4_0.test.ts
diff --git a/.github/actions/detect-solidity-readonly-file-changes/action.yml b/.github/actions/detect-solidity-readonly-file-changes/action.yml
index faca16d53f0..d0890a9f604 100644
--- a/.github/actions/detect-solidity-readonly-file-changes/action.yml
+++ b/.github/actions/detect-solidity-readonly-file-changes/action.yml
@@ -16,9 +16,13 @@ runs:
filters: |
read_only_sol:
- 'contracts/src/v0.8/interfaces/**/*'
+ - 'contracts/src/v0.8/automation/interfaces/**/*'
+ - 'contracts/src/v0.8/automation/upkeeps/**/*'
- 'contracts/src/v0.8/automation/v1_2/**/*'
- 'contracts/src/v0.8/automation/v1_3/**/*'
- 'contracts/src/v0.8/automation/v2_0/**/*'
+ - 'contracts/src/v0.8/automation/v2_1/**/*'
+ - 'contracts/src/v0.8/automation/v2_2/**/*'
- name: Fail if read-only files have changed
if: ${{ steps.changed_files.outputs.read_only_sol == 'true' }}
diff --git a/contracts/test/v0.8/automation/AutomationGasAnalysis.test.ts b/contracts/test/v0.8/automation/AutomationGasAnalysis.test.ts
deleted file mode 100644
index f393a5de1c2..00000000000
--- a/contracts/test/v0.8/automation/AutomationGasAnalysis.test.ts
+++ /dev/null
@@ -1,258 +0,0 @@
-import { ethers } from 'hardhat'
-import { BigNumber } from 'ethers'
-import { expect, assert } from 'chai'
-import { getUsers } from '../../test-helpers/setup'
-import { randomAddress, toWei } from '../../test-helpers/helpers'
-import { deployRegistry21 } from './helpers'
-
-// don't run these tests in CI
-const describeMaybe = process.env.CI ? describe.skip : describe
-
-// registry settings
-const f = 1
-const linkEth = BigNumber.from(300000000)
-const gasWei = BigNumber.from(100)
-const minUpkeepSpend = BigNumber.from('1000000000000000000')
-const paymentPremiumPPB = BigNumber.from(250000000)
-const flatFeeMicroLink = BigNumber.from(0)
-const blockCountPerTurn = 20
-const checkGasLimit = BigNumber.from(20000000)
-const fallbackGasPrice = BigNumber.from(200)
-const fallbackLinkPrice = BigNumber.from(200000000)
-const maxCheckDataSize = BigNumber.from(10000)
-const maxPerformDataSize = BigNumber.from(10000)
-const maxRevertDataSize = BigNumber.from(1000)
-const maxPerformGas = BigNumber.from(5000000)
-const stalenessSeconds = BigNumber.from(43820)
-const gasCeilingMultiplier = BigNumber.from(1)
-const signers = [
- randomAddress(),
- randomAddress(),
- randomAddress(),
- randomAddress(),
-]
-const transmitters = [
- randomAddress(),
- randomAddress(),
- randomAddress(),
- randomAddress(),
-]
-const transcoder = ethers.constants.AddressZero
-
-// registrar settings
-const triggerType = 0 // conditional
-const autoApproveType = 2 // auto-approve enabled
-const autoApproveMaxAllowed = 100 // auto-approve enabled
-
-// upkeep settings
-const name = 'test upkeep'
-const encryptedEmail = '0xabcd1234'
-const gasLimit = 100_000
-const checkData = '0xdeadbeef'
-const amount = toWei('5')
-const source = 5
-const triggerConfig = '0x'
-const offchainConfig = '0x'
-
-describeMaybe('Automation Gas Analysis', () => {
- it('Compares gas usage amongst registries / registrars', async () => {
- assert(
- Boolean(process.env.REPORT_GAS),
- 'this test must be run with REPORT_GAS=true',
- )
-
- const personas = (await getUsers()).personas
- const owner = personas.Default
- const ownerAddress = await owner.getAddress()
-
- // factories
- const getFact = ethers.getContractFactory
- const linkTokenFactory = await getFact('LinkToken')
- const mockV3AggregatorFactory = await getFact(
- 'src/v0.8/shared/mocks/MockV3Aggregator.sol:MockV3Aggregator',
- )
- const upkeepMockFactory = await getFact('UpkeepMock')
- const registry12Factory = await getFact('KeeperRegistry1_2')
- const registrar12Factory = await getFact('KeeperRegistrar')
- const registry20Factory = await getFact('KeeperRegistry2_0')
- const registryLogic20Factory = await getFact('KeeperRegistryLogic2_0')
- const registrar20Factory = await getFact('KeeperRegistrar2_0')
- const registrar21Factory = await getFact('AutomationRegistrar2_1')
- const forwarderLogicFactory = await getFact('AutomationForwarderLogic')
-
- // deploy dependancy contracts
- const linkToken = await linkTokenFactory.connect(owner).deploy()
- const gasPriceFeed = await mockV3AggregatorFactory
- .connect(owner)
- .deploy(0, gasWei)
- const linkEthFeed = await mockV3AggregatorFactory
- .connect(owner)
- .deploy(9, linkEth)
- const upkeep = await upkeepMockFactory.connect(owner).deploy()
-
- // deploy v1.2
- const registrar12 = await registrar12Factory.connect(owner).deploy(
- linkToken.address,
- autoApproveType,
- autoApproveMaxAllowed,
- ethers.constants.AddressZero, // set later
- minUpkeepSpend,
- )
- const registry12 = await registry12Factory
- .connect(owner)
- .deploy(linkToken.address, linkEthFeed.address, gasPriceFeed.address, {
- paymentPremiumPPB,
- flatFeeMicroLink,
- blockCountPerTurn,
- checkGasLimit,
- stalenessSeconds,
- gasCeilingMultiplier,
- minUpkeepSpend,
- maxPerformGas,
- fallbackGasPrice,
- fallbackLinkPrice,
- transcoder,
- registrar: registrar12.address,
- })
- await registrar12.setRegistrationConfig(
- autoApproveType,
- autoApproveMaxAllowed,
- registry12.address,
- minUpkeepSpend,
- )
-
- // deploy v2.0
- const registryLogic20 = await registryLogic20Factory
- .connect(owner)
- .deploy(0, linkToken.address, linkEthFeed.address, gasPriceFeed.address)
- const registry20 = await registry20Factory
- .connect(owner)
- .deploy(registryLogic20.address)
- const registrar20 = await registrar20Factory
- .connect(owner)
- .deploy(
- linkToken.address,
- autoApproveType,
- autoApproveMaxAllowed,
- registry20.address,
- minUpkeepSpend,
- )
- const config20 = {
- paymentPremiumPPB,
- flatFeeMicroLink,
- checkGasLimit,
- stalenessSeconds,
- gasCeilingMultiplier,
- minUpkeepSpend,
- maxCheckDataSize,
- maxPerformDataSize,
- maxPerformGas,
- fallbackGasPrice,
- fallbackLinkPrice,
- transcoder,
- registrar: registrar20.address,
- }
- const onchainConfig20 = ethers.utils.defaultAbiCoder.encode(
- [
- 'tuple(uint32 paymentPremiumPPB,uint32 flatFeeMicroLink,uint32 checkGasLimit,uint24 stalenessSeconds\
- ,uint16 gasCeilingMultiplier,uint96 minUpkeepSpend,uint32 maxPerformGas,uint32 maxCheckDataSize,\
- uint32 maxPerformDataSize,uint256 fallbackGasPrice,uint256 fallbackLinkPrice,address transcoder,\
- address registrar)',
- ],
- [config20],
- )
- await registry20
- .connect(owner)
- .setConfig(signers, transmitters, f, onchainConfig20, 1, '0x')
-
- // deploy v2.1
- const forwarderLogic = await forwarderLogicFactory.connect(owner).deploy()
- const registry21 = await deployRegistry21(
- owner,
- 0,
- linkToken.address,
- linkEthFeed.address,
- gasPriceFeed.address,
- forwarderLogic.address,
- )
- const registrar21 = await registrar21Factory
- .connect(owner)
- .deploy(linkToken.address, registry21.address, minUpkeepSpend, [
- {
- triggerType,
- autoApproveType,
- autoApproveMaxAllowed,
- },
- ])
- const onchainConfig21 = {
- paymentPremiumPPB,
- flatFeeMicroLink,
- checkGasLimit,
- stalenessSeconds,
- gasCeilingMultiplier,
- minUpkeepSpend,
- maxCheckDataSize,
- maxPerformDataSize,
- maxRevertDataSize,
- maxPerformGas,
- fallbackGasPrice,
- fallbackLinkPrice,
- transcoder,
- registrars: [registrar21.address],
- upkeepPrivilegeManager: randomAddress(),
- }
- await registry21
- .connect(owner)
- .setConfigTypeSafe(signers, transmitters, f, onchainConfig21, 1, '0x')
-
- // approve LINK
- await linkToken.connect(owner).approve(registrar20.address, amount)
- await linkToken.connect(owner).approve(registrar21.address, amount)
-
- const abiEncodedBytes = registrar12.interface.encodeFunctionData(
- 'register',
- [
- name,
- encryptedEmail,
- upkeep.address,
- gasLimit,
- ownerAddress,
- checkData,
- amount,
- source,
- ownerAddress,
- ],
- )
-
- let tx = await linkToken
- .connect(owner)
- .transferAndCall(registrar12.address, amount, abiEncodedBytes)
- await expect(tx).to.emit(registry12, 'UpkeepRegistered')
-
- tx = await registrar20.connect(owner).registerUpkeep({
- name,
- encryptedEmail,
- upkeepContract: upkeep.address,
- gasLimit,
- adminAddress: ownerAddress,
- checkData,
- amount,
- offchainConfig,
- })
- await expect(tx).to.emit(registry20, 'UpkeepRegistered')
-
- tx = await registrar21.connect(owner).registerUpkeep({
- name,
- encryptedEmail,
- upkeepContract: upkeep.address,
- gasLimit,
- adminAddress: ownerAddress,
- triggerType,
- checkData,
- amount,
- triggerConfig,
- offchainConfig,
- })
- await expect(tx).to.emit(registry21, 'UpkeepRegistered')
- })
-})
diff --git a/contracts/test/v0.8/automation/AutomationRegistrar2_1.test.ts b/contracts/test/v0.8/automation/AutomationRegistrar2_1.test.ts
deleted file mode 100644
index 6d3d591acb0..00000000000
--- a/contracts/test/v0.8/automation/AutomationRegistrar2_1.test.ts
+++ /dev/null
@@ -1,1022 +0,0 @@
-import { ethers } from 'hardhat'
-import { assert } from 'chai'
-import { AutomationRegistrar2_1__factory as AutomationRegistrarFactory } from '../../../typechain/factories/AutomationRegistrar2_1__factory'
-
-//////////////////////////////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////////////////////////////
-
-/*********************************** REGISTRAR v2.1 IS FROZEN ************************************/
-
-// As 2.1 is still actively being deployed, we keep the tests below.
-
-describe('AutomationRegistrar2_1 - Frozen [ @skip-coverage ]', () => {
- it('has not changed', () => {
- assert.equal(
- ethers.utils.id(AutomationRegistrarFactory.bytecode),
- '0x9633058bd81e8479f88baaee9bda533406295c80ccbc43d4509701001bbea6e3',
- 'KeeperRegistry bytecode has changed',
- )
- })
-})
-
-//////////////////////////////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////////////////////////////
-//
-// // copied from KeeperRegistryBase2_1.sol
-// enum Trigger {
-// CONDITION,
-// LOG,
-// }
-//
-// let linkTokenFactory: LinkTokenFactory
-// let mockV3AggregatorFactory: MockV3AggregatorFactory
-// let upkeepMockFactory: UpkeepMockFactory
-//
-// let personas: Personas
-//
-// before(async () => {
-// personas = (await getUsers()).personas
-//
-// linkTokenFactory = await ethers.getContractFactory(
-// 'src/v0.8/shared/test/helpers/LinkTokenTestHelper.sol:LinkTokenTestHelper',
-// )
-// mockV3AggregatorFactory = (await ethers.getContractFactory(
-// 'src/v0.8/shared/mocks/MockV3Aggregator.sol:MockV3Aggregator',
-// )) as unknown as MockV3AggregatorFactory
-// upkeepMockFactory = await ethers.getContractFactory('UpkeepMock')
-// })
-//
-// const errorMsgs = {
-// onlyOwner: 'revert Only callable by owner',
-// onlyAdmin: 'OnlyAdminOrOwner()',
-// hashPayload: 'HashMismatch()',
-// requestNotFound: 'RequestNotFound()',
-// }
-//
-// describe('AutomationRegistrar2_1', () => {
-// const upkeepName = 'SampleUpkeep'
-//
-// const linkEth = BigNumber.from(300000000)
-// const gasWei = BigNumber.from(100)
-// const performGas = BigNumber.from(100000)
-// const paymentPremiumPPB = BigNumber.from(250000000)
-// const flatFeeMicroLink = BigNumber.from(0)
-// const maxAllowedAutoApprove = 5
-// const trigger = '0xdeadbeef'
-// const offchainConfig = '0x01234567'
-//
-// const emptyBytes = '0x00'
-// const stalenessSeconds = BigNumber.from(43820)
-// const gasCeilingMultiplier = BigNumber.from(1)
-// const checkGasLimit = BigNumber.from(20000000)
-// const fallbackGasPrice = BigNumber.from(200)
-// const fallbackLinkPrice = BigNumber.from(200000000)
-// const maxCheckDataSize = BigNumber.from(10000)
-// const maxPerformDataSize = BigNumber.from(10000)
-// const maxRevertDataSize = BigNumber.from(1000)
-// const maxPerformGas = BigNumber.from(5000000)
-// const minUpkeepSpend = BigNumber.from('1000000000000000000')
-// const amount = BigNumber.from('5000000000000000000')
-// const amount1 = BigNumber.from('6000000000000000000')
-// const transcoder = ethers.constants.AddressZero
-// const upkeepManager = ethers.Wallet.createRandom().address
-//
-// // Enum values are not auto exported in ABI so have to manually declare
-// const autoApproveType_DISABLED = 0
-// const autoApproveType_ENABLED_SENDER_ALLOWLIST = 1
-// const autoApproveType_ENABLED_ALL = 2
-//
-// let owner: Signer
-// let admin: Signer
-// let someAddress: Signer
-// let registrarOwner: Signer
-// let stranger: Signer
-// let requestSender: Signer
-//
-// let linkToken: LinkToken
-// let linkEthFeed: MockV3Aggregator
-// let gasPriceFeed: MockV3Aggregator
-// let mock: UpkeepMock
-// let registry: IKeeperRegistry
-// let registrar: Registrar
-//
-// beforeEach(async () => {
-// owner = personas.Default
-// admin = personas.Neil
-// someAddress = personas.Ned
-// registrarOwner = personas.Nelly
-// stranger = personas.Nancy
-// requestSender = personas.Norbert
-//
-// linkToken = await linkTokenFactory.connect(owner).deploy()
-// gasPriceFeed = await mockV3AggregatorFactory
-// .connect(owner)
-// .deploy(0, gasWei)
-// linkEthFeed = await mockV3AggregatorFactory
-// .connect(owner)
-// .deploy(9, linkEth)
-//
-// registry = await deployRegistry21(
-// owner,
-// 0,
-// linkToken.address,
-// linkEthFeed.address,
-// gasPriceFeed.address,
-// )
-//
-// mock = await upkeepMockFactory.deploy()
-//
-// const registrarFactory = await ethers.getContractFactory(
-// 'AutomationRegistrar2_1',
-// )
-// registrar = await registrarFactory
-// .connect(registrarOwner)
-// .deploy(linkToken.address, registry.address, minUpkeepSpend, [
-// {
-// triggerType: Trigger.CONDITION,
-// autoApproveType: autoApproveType_DISABLED,
-// autoApproveMaxAllowed: 0,
-// },
-// {
-// triggerType: Trigger.LOG,
-// autoApproveType: autoApproveType_DISABLED,
-// autoApproveMaxAllowed: 0,
-// },
-// ])
-//
-// await linkToken
-// .connect(owner)
-// .transfer(await requestSender.getAddress(), toWei('1000'))
-//
-// const keepers = [
-// await personas.Carol.getAddress(),
-// await personas.Nancy.getAddress(),
-// await personas.Ned.getAddress(),
-// await personas.Neil.getAddress(),
-// ]
-// const onchainConfig = {
-// paymentPremiumPPB,
-// flatFeeMicroLink,
-// checkGasLimit,
-// stalenessSeconds,
-// gasCeilingMultiplier,
-// minUpkeepSpend,
-// maxCheckDataSize,
-// maxPerformDataSize,
-// maxRevertDataSize,
-// maxPerformGas,
-// fallbackGasPrice,
-// fallbackLinkPrice,
-// transcoder,
-// registrars: [registrar.address],
-// upkeepPrivilegeManager: upkeepManager,
-// }
-// await registry
-// .connect(owner)
-// .setConfigTypeSafe(keepers, keepers, 1, onchainConfig, 1, '0x')
-// })
-//
-// describe('#typeAndVersion', () => {
-// it('uses the correct type and version', async () => {
-// const typeAndVersion = await registrar.typeAndVersion()
-// assert.equal(typeAndVersion, 'AutomationRegistrar 2.1.0')
-// })
-// })
-//
-// describe('#register', () => {
-// it('reverts if not called by the LINK token', async () => {
-// await evmRevert(
-// registrar
-// .connect(someAddress)
-// .register(
-// upkeepName,
-// emptyBytes,
-// mock.address,
-// performGas,
-// await admin.getAddress(),
-// 0,
-// emptyBytes,
-// trigger,
-// offchainConfig,
-// amount,
-// await requestSender.getAddress(),
-// ),
-// 'OnlyLink()',
-// )
-// })
-//
-// it('reverts if the amount passed in data mismatches actual amount sent', async () => {
-// await registrar
-// .connect(registrarOwner)
-// .setTriggerConfig(
-// Trigger.CONDITION,
-// autoApproveType_ENABLED_ALL,
-// maxAllowedAutoApprove,
-// )
-//
-// const abiEncodedBytes = registrar.interface.encodeFunctionData(
-// 'register',
-// [
-// upkeepName,
-// emptyBytes,
-// mock.address,
-// performGas,
-// await admin.getAddress(),
-// 0,
-// emptyBytes,
-// trigger,
-// offchainConfig,
-// amount1,
-// await requestSender.getAddress(),
-// ],
-// )
-//
-// await evmRevert(
-// linkToken
-// .connect(requestSender)
-// .transferAndCall(registrar.address, amount, abiEncodedBytes),
-// 'AmountMismatch()',
-// )
-// })
-//
-// it('reverts if the sender passed in data mismatches actual sender', async () => {
-// const abiEncodedBytes = registrar.interface.encodeFunctionData(
-// 'register',
-// [
-// upkeepName,
-// emptyBytes,
-// mock.address,
-// performGas,
-// await admin.getAddress(),
-// 0,
-// emptyBytes,
-// trigger,
-// offchainConfig,
-// amount,
-// await admin.getAddress(), // Should have been requestSender.getAddress()
-// ],
-// )
-// await evmRevert(
-// linkToken
-// .connect(requestSender)
-// .transferAndCall(registrar.address, amount, abiEncodedBytes),
-// 'SenderMismatch()',
-// )
-// })
-//
-// it('reverts if the admin address is 0x0000...', async () => {
-// const abiEncodedBytes = registrar.interface.encodeFunctionData(
-// 'register',
-// [
-// upkeepName,
-// emptyBytes,
-// mock.address,
-// performGas,
-// '0x0000000000000000000000000000000000000000',
-// 0,
-// emptyBytes,
-// trigger,
-// offchainConfig,
-// amount,
-// await requestSender.getAddress(),
-// ],
-// )
-//
-// await evmRevert(
-// linkToken
-// .connect(requestSender)
-// .transferAndCall(registrar.address, amount, abiEncodedBytes),
-// 'RegistrationRequestFailed()',
-// )
-// })
-//
-// it('Auto Approve ON - registers an upkeep on KeeperRegistry instantly and emits both RegistrationRequested and RegistrationApproved events', async () => {
-// //set auto approve ON with high threshold limits
-// await registrar
-// .connect(registrarOwner)
-// .setTriggerConfig(
-// Trigger.CONDITION,
-// autoApproveType_ENABLED_ALL,
-// maxAllowedAutoApprove,
-// )
-//
-// //register with auto approve ON
-// const abiEncodedBytes = registrar.interface.encodeFunctionData(
-// 'register',
-// [
-// upkeepName,
-// emptyBytes,
-// mock.address,
-// performGas,
-// await admin.getAddress(),
-// 0,
-// emptyBytes,
-// trigger,
-// offchainConfig,
-// amount,
-// await requestSender.getAddress(),
-// ],
-// )
-// const tx = await linkToken
-// .connect(requestSender)
-// .transferAndCall(registrar.address, amount, abiEncodedBytes)
-//
-// const [id] = await registry.getActiveUpkeepIDs(0, 1)
-//
-// //confirm if a new upkeep has been registered and the details are the same as the one just registered
-// const newupkeep = await registry.getUpkeep(id)
-// assert.equal(newupkeep.target, mock.address)
-// assert.equal(newupkeep.admin, await admin.getAddress())
-// assert.equal(newupkeep.checkData, emptyBytes)
-// assert.equal(newupkeep.balance.toString(), amount.toString())
-// assert.equal(newupkeep.performGas, performGas.toNumber())
-// assert.equal(newupkeep.offchainConfig, offchainConfig)
-//
-// await expect(tx).to.emit(registrar, 'RegistrationRequested')
-// await expect(tx).to.emit(registrar, 'RegistrationApproved')
-// })
-//
-// it('Auto Approve OFF - does not registers an upkeep on KeeperRegistry, emits only RegistrationRequested event', async () => {
-// //get upkeep count before attempting registration
-// const beforeCount = (await registry.getState()).state.numUpkeeps
-//
-// //set auto approve OFF, threshold limits dont matter in this case
-// await registrar
-// .connect(registrarOwner)
-// .setTriggerConfig(
-// Trigger.CONDITION,
-// autoApproveType_DISABLED,
-// maxAllowedAutoApprove,
-// )
-//
-// //register with auto approve OFF
-// const abiEncodedBytes = registrar.interface.encodeFunctionData(
-// 'register',
-// [
-// upkeepName,
-// emptyBytes,
-// mock.address,
-// performGas,
-// await admin.getAddress(),
-// 0,
-// emptyBytes,
-// trigger,
-// offchainConfig,
-// amount,
-// await requestSender.getAddress(),
-// ],
-// )
-// const tx = await linkToken
-// .connect(requestSender)
-// .transferAndCall(registrar.address, amount, abiEncodedBytes)
-// const receipt = await tx.wait()
-//
-// //get upkeep count after attempting registration
-// const afterCount = (await registry.getState()).state.numUpkeeps
-// //confirm that a new upkeep has NOT been registered and upkeep count is still the same
-// assert.deepEqual(beforeCount, afterCount)
-//
-// //confirm that only RegistrationRequested event is emitted and RegistrationApproved event is not
-// await expect(tx).to.emit(registrar, 'RegistrationRequested')
-// await expect(tx).not.to.emit(registrar, 'RegistrationApproved')
-//
-// const hash = receipt.logs[2].topics[1]
-// const pendingRequest = await registrar.getPendingRequest(hash)
-// assert.equal(await admin.getAddress(), pendingRequest[0])
-// assert.ok(amount.eq(pendingRequest[1]))
-// })
-//
-// it('Auto Approve ON - Throttle max approvals - does not register an upkeep on KeeperRegistry beyond the max limit, emits only RegistrationRequested event after limit is hit', async () => {
-// assert.equal((await registry.getState()).state.numUpkeeps.toNumber(), 0)
-//
-// //set auto approve on, with max 1 allowed
-// await registrar
-// .connect(registrarOwner)
-// .setTriggerConfig(Trigger.CONDITION, autoApproveType_ENABLED_ALL, 1)
-//
-// //set auto approve on, with max 1 allowed
-// await registrar
-// .connect(registrarOwner)
-// .setTriggerConfig(Trigger.LOG, autoApproveType_ENABLED_ALL, 1)
-//
-// // register within threshold, new upkeep should be registered
-// let abiEncodedBytes = registrar.interface.encodeFunctionData('register', [
-// upkeepName,
-// emptyBytes,
-// mock.address,
-// performGas,
-// await admin.getAddress(),
-// 0,
-// emptyBytes,
-// trigger,
-// offchainConfig,
-// amount,
-// await requestSender.getAddress(),
-// ])
-// await linkToken
-// .connect(requestSender)
-// .transferAndCall(registrar.address, amount, abiEncodedBytes)
-// assert.equal((await registry.getState()).state.numUpkeeps.toNumber(), 1) // 0 -> 1
-//
-// // try registering another one, new upkeep should not be registered
-// abiEncodedBytes = registrar.interface.encodeFunctionData('register', [
-// upkeepName,
-// emptyBytes,
-// mock.address,
-// performGas.toNumber() + 1, // make unique hash
-// await admin.getAddress(),
-// 0,
-// emptyBytes,
-// trigger,
-// offchainConfig,
-// amount,
-// await requestSender.getAddress(),
-// ])
-// await linkToken
-// .connect(requestSender)
-// .transferAndCall(registrar.address, amount, abiEncodedBytes)
-// assert.equal((await registry.getState()).state.numUpkeeps.toNumber(), 1) // Still 1
-//
-// // register a second type of upkeep, different limit
-// abiEncodedBytes = registrar.interface.encodeFunctionData('register', [
-// upkeepName,
-// emptyBytes,
-// mock.address,
-// performGas,
-// await admin.getAddress(),
-// Trigger.LOG,
-// emptyBytes,
-// trigger,
-// offchainConfig,
-// amount,
-// await requestSender.getAddress(),
-// ])
-// await linkToken
-// .connect(requestSender)
-// .transferAndCall(registrar.address, amount, abiEncodedBytes)
-// assert.equal((await registry.getState()).state.numUpkeeps.toNumber(), 2) // 1 -> 2
-//
-// // Now set new max limit to 2. One more upkeep should get auto approved
-// await registrar
-// .connect(registrarOwner)
-// .setTriggerConfig(Trigger.CONDITION, autoApproveType_ENABLED_ALL, 2)
-//
-// abiEncodedBytes = registrar.interface.encodeFunctionData('register', [
-// upkeepName,
-// emptyBytes,
-// mock.address,
-// performGas.toNumber() + 2, // make unique hash
-// await admin.getAddress(),
-// 0,
-// emptyBytes,
-// trigger,
-// offchainConfig,
-// amount,
-// await requestSender.getAddress(),
-// ])
-// await linkToken
-// .connect(requestSender)
-// .transferAndCall(registrar.address, amount, abiEncodedBytes)
-// assert.equal((await registry.getState()).state.numUpkeeps.toNumber(), 3) // 2 -> 3
-//
-// // One more upkeep should not get registered
-// abiEncodedBytes = registrar.interface.encodeFunctionData('register', [
-// upkeepName,
-// emptyBytes,
-// mock.address,
-// performGas.toNumber() + 3, // make unique hash
-// await admin.getAddress(),
-// 0,
-// emptyBytes,
-// trigger,
-// offchainConfig,
-// amount,
-// await requestSender.getAddress(),
-// ])
-// await linkToken
-// .connect(requestSender)
-// .transferAndCall(registrar.address, amount, abiEncodedBytes)
-// assert.equal((await registry.getState()).state.numUpkeeps.toNumber(), 3) // Still 3
-// })
-//
-// it('Auto Approve Sender Allowlist - sender in allowlist - registers an upkeep on KeeperRegistry instantly and emits both RegistrationRequested and RegistrationApproved events', async () => {
-// const senderAddress = await requestSender.getAddress()
-//
-// //set auto approve to ENABLED_SENDER_ALLOWLIST type with high threshold limits
-// await registrar
-// .connect(registrarOwner)
-// .setTriggerConfig(
-// Trigger.CONDITION,
-// autoApproveType_ENABLED_SENDER_ALLOWLIST,
-// maxAllowedAutoApprove,
-// )
-//
-// // Add sender to allowlist
-// await registrar
-// .connect(registrarOwner)
-// .setAutoApproveAllowedSender(senderAddress, true)
-//
-// //register with auto approve ON
-// const abiEncodedBytes = registrar.interface.encodeFunctionData(
-// 'register',
-// [
-// upkeepName,
-// emptyBytes,
-// mock.address,
-// performGas,
-// await admin.getAddress(),
-// 0,
-// emptyBytes,
-// trigger,
-// offchainConfig,
-// amount,
-// await requestSender.getAddress(),
-// ],
-// )
-// const tx = await linkToken
-// .connect(requestSender)
-// .transferAndCall(registrar.address, amount, abiEncodedBytes)
-//
-// const [id] = await registry.getActiveUpkeepIDs(0, 1)
-//
-// //confirm if a new upkeep has been registered and the details are the same as the one just registered
-// const newupkeep = await registry.getUpkeep(id)
-// assert.equal(newupkeep.target, mock.address)
-// assert.equal(newupkeep.admin, await admin.getAddress())
-// assert.equal(newupkeep.checkData, emptyBytes)
-// assert.equal(newupkeep.balance.toString(), amount.toString())
-// assert.equal(newupkeep.performGas, performGas.toNumber())
-//
-// await expect(tx).to.emit(registrar, 'RegistrationRequested')
-// await expect(tx).to.emit(registrar, 'RegistrationApproved')
-// })
-//
-// it('Auto Approve Sender Allowlist - sender NOT in allowlist - does not registers an upkeep on KeeperRegistry, emits only RegistrationRequested event', async () => {
-// const beforeCount = (await registry.getState()).state.numUpkeeps
-// const senderAddress = await requestSender.getAddress()
-//
-// //set auto approve to ENABLED_SENDER_ALLOWLIST type with high threshold limits
-// await registrar
-// .connect(registrarOwner)
-// .setTriggerConfig(
-// Trigger.CONDITION,
-// autoApproveType_ENABLED_SENDER_ALLOWLIST,
-// maxAllowedAutoApprove,
-// )
-//
-// // Explicitly remove sender from allowlist
-// await registrar
-// .connect(registrarOwner)
-// .setAutoApproveAllowedSender(senderAddress, false)
-//
-// //register. auto approve shouldn't happen
-// const abiEncodedBytes = registrar.interface.encodeFunctionData(
-// 'register',
-// [
-// upkeepName,
-// emptyBytes,
-// mock.address,
-// performGas,
-// await admin.getAddress(),
-// 0,
-// emptyBytes,
-// trigger,
-// offchainConfig,
-// amount,
-// await requestSender.getAddress(),
-// ],
-// )
-// const tx = await linkToken
-// .connect(requestSender)
-// .transferAndCall(registrar.address, amount, abiEncodedBytes)
-// const receipt = await tx.wait()
-//
-// //get upkeep count after attempting registration
-// const afterCount = (await registry.getState()).state.numUpkeeps
-// //confirm that a new upkeep has NOT been registered and upkeep count is still the same
-// assert.deepEqual(beforeCount, afterCount)
-//
-// //confirm that only RegistrationRequested event is emitted and RegistrationApproved event is not
-// await expect(tx).to.emit(registrar, 'RegistrationRequested')
-// await expect(tx).not.to.emit(registrar, 'RegistrationApproved')
-//
-// const hash = receipt.logs[2].topics[1]
-// const pendingRequest = await registrar.getPendingRequest(hash)
-// assert.equal(await admin.getAddress(), pendingRequest[0])
-// assert.ok(amount.eq(pendingRequest[1]))
-// })
-// })
-//
-// describe('#registerUpkeep', () => {
-// it('reverts with empty message if amount sent is not available in LINK allowance', async () => {
-// await evmRevert(
-// registrar.connect(someAddress).registerUpkeep({
-// name: upkeepName,
-// upkeepContract: mock.address,
-// gasLimit: performGas,
-// adminAddress: await admin.getAddress(),
-// triggerType: 0,
-// checkData: emptyBytes,
-// triggerConfig: trigger,
-// offchainConfig: emptyBytes,
-// amount,
-// encryptedEmail: emptyBytes,
-// }),
-// '',
-// )
-// })
-//
-// it('reverts if the amount passed in data is less than configured minimum', async () => {
-// await registrar
-// .connect(registrarOwner)
-// .setTriggerConfig(
-// Trigger.CONDITION,
-// autoApproveType_ENABLED_ALL,
-// maxAllowedAutoApprove,
-// )
-//
-// // amt is one order of magnitude less than minUpkeepSpend
-// const amt = BigNumber.from('100000000000000000')
-//
-// await evmRevert(
-// registrar.connect(someAddress).registerUpkeep({
-// name: upkeepName,
-// upkeepContract: mock.address,
-// gasLimit: performGas,
-// adminAddress: await admin.getAddress(),
-// triggerType: 0,
-// checkData: emptyBytes,
-// triggerConfig: trigger,
-// offchainConfig: emptyBytes,
-// amount: amt,
-// encryptedEmail: emptyBytes,
-// }),
-// 'InsufficientPayment()',
-// )
-// })
-//
-// it('Auto Approve ON - registers an upkeep on KeeperRegistry instantly and emits both RegistrationRequested and RegistrationApproved events', async () => {
-// //set auto approve ON with high threshold limits
-// await registrar
-// .connect(registrarOwner)
-// .setTriggerConfig(
-// Trigger.CONDITION,
-// autoApproveType_ENABLED_ALL,
-// maxAllowedAutoApprove,
-// )
-//
-// await linkToken.connect(requestSender).approve(registrar.address, amount)
-//
-// const tx = await registrar.connect(requestSender).registerUpkeep({
-// name: upkeepName,
-// upkeepContract: mock.address,
-// gasLimit: performGas,
-// adminAddress: await admin.getAddress(),
-// triggerType: 0,
-// checkData: emptyBytes,
-// triggerConfig: trigger,
-// offchainConfig,
-// amount,
-// encryptedEmail: emptyBytes,
-// })
-// assert.equal((await registry.getState()).state.numUpkeeps.toNumber(), 1) // 0 -> 1
-//
-// //confirm if a new upkeep has been registered and the details are the same as the one just registered
-// const [id] = await registry.getActiveUpkeepIDs(0, 1)
-// const newupkeep = await registry.getUpkeep(id)
-// assert.equal(newupkeep.target, mock.address)
-// assert.equal(newupkeep.admin, await admin.getAddress())
-// assert.equal(newupkeep.checkData, emptyBytes)
-// assert.equal(newupkeep.balance.toString(), amount.toString())
-// assert.equal(newupkeep.performGas, performGas.toNumber())
-// assert.equal(newupkeep.offchainConfig, offchainConfig)
-//
-// await expect(tx).to.emit(registrar, 'RegistrationRequested')
-// await expect(tx).to.emit(registrar, 'RegistrationApproved')
-// })
-// })
-//
-// describe('#setAutoApproveAllowedSender', () => {
-// it('reverts if not called by the owner', async () => {
-// const tx = registrar
-// .connect(stranger)
-// .setAutoApproveAllowedSender(await admin.getAddress(), false)
-// await evmRevert(tx, 'Only callable by owner')
-// })
-//
-// it('sets the allowed status correctly and emits log', async () => {
-// const senderAddress = await stranger.getAddress()
-// let tx = await registrar
-// .connect(registrarOwner)
-// .setAutoApproveAllowedSender(senderAddress, true)
-// await expect(tx)
-// .to.emit(registrar, 'AutoApproveAllowedSenderSet')
-// .withArgs(senderAddress, true)
-//
-// let senderAllowedStatus = await registrar
-// .connect(owner)
-// .getAutoApproveAllowedSender(senderAddress)
-// assert.isTrue(senderAllowedStatus)
-//
-// tx = await registrar
-// .connect(registrarOwner)
-// .setAutoApproveAllowedSender(senderAddress, false)
-// await expect(tx)
-// .to.emit(registrar, 'AutoApproveAllowedSenderSet')
-// .withArgs(senderAddress, false)
-//
-// senderAllowedStatus = await registrar
-// .connect(owner)
-// .getAutoApproveAllowedSender(senderAddress)
-// assert.isFalse(senderAllowedStatus)
-// })
-// })
-//
-// describe('#setTriggerConfig', () => {
-// it('reverts if not called by the owner', async () => {
-// const tx = registrar
-// .connect(stranger)
-// .setTriggerConfig(Trigger.LOG, autoApproveType_ENABLED_ALL, 100)
-// await evmRevert(tx, 'Only callable by owner')
-// })
-//
-// it('changes the config', async () => {
-// const tx = await registrar
-// .connect(registrarOwner)
-// .setTriggerConfig(Trigger.LOG, autoApproveType_ENABLED_ALL, 100)
-// await registrar.getTriggerRegistrationDetails(Trigger.LOG)
-// await expect(tx)
-// .to.emit(registrar, 'TriggerConfigSet')
-// .withArgs(Trigger.LOG, autoApproveType_ENABLED_ALL, 100)
-// })
-// })
-//
-// describe('#approve', () => {
-// let hash: string
-//
-// beforeEach(async () => {
-// await registrar
-// .connect(registrarOwner)
-// .setTriggerConfig(
-// Trigger.CONDITION,
-// autoApproveType_DISABLED,
-// maxAllowedAutoApprove,
-// )
-//
-// //register with auto approve OFF
-// const abiEncodedBytes = registrar.interface.encodeFunctionData(
-// 'register',
-// [
-// upkeepName,
-// emptyBytes,
-// mock.address,
-// performGas,
-// await admin.getAddress(),
-// 0,
-// emptyBytes,
-// trigger,
-// offchainConfig,
-// amount,
-// await requestSender.getAddress(),
-// ],
-// )
-//
-// const tx = await linkToken
-// .connect(requestSender)
-// .transferAndCall(registrar.address, amount, abiEncodedBytes)
-// const receipt = await tx.wait()
-// hash = receipt.logs[2].topics[1]
-// })
-//
-// it('reverts if not called by the owner', async () => {
-// const tx = registrar
-// .connect(stranger)
-// .approve(
-// upkeepName,
-// mock.address,
-// performGas,
-// await admin.getAddress(),
-// 0,
-// emptyBytes,
-// trigger,
-// emptyBytes,
-// hash,
-// )
-// await evmRevert(tx, 'Only callable by owner')
-// })
-//
-// it('reverts if the hash does not exist', async () => {
-// const tx = registrar
-// .connect(registrarOwner)
-// .approve(
-// upkeepName,
-// mock.address,
-// performGas,
-// await admin.getAddress(),
-// 0,
-// emptyBytes,
-// trigger,
-// emptyBytes,
-// '0x000000000000000000000000322813fd9a801c5507c9de605d63cea4f2ce6c44',
-// )
-// await evmRevert(tx, errorMsgs.requestNotFound)
-// })
-//
-// it('reverts if any member of the payload changes', async () => {
-// let tx = registrar
-// .connect(registrarOwner)
-// .approve(
-// upkeepName,
-// ethers.Wallet.createRandom().address,
-// performGas,
-// await admin.getAddress(),
-// 0,
-// emptyBytes,
-// trigger,
-// emptyBytes,
-// hash,
-// )
-// await evmRevert(tx, errorMsgs.hashPayload)
-// tx = registrar
-// .connect(registrarOwner)
-// .approve(
-// upkeepName,
-// mock.address,
-// 10000,
-// await admin.getAddress(),
-// 0,
-// emptyBytes,
-// trigger,
-// emptyBytes,
-// hash,
-// )
-// await evmRevert(tx, errorMsgs.hashPayload)
-// tx = registrar
-// .connect(registrarOwner)
-// .approve(
-// upkeepName,
-// mock.address,
-// performGas,
-// ethers.Wallet.createRandom().address,
-// 0,
-// emptyBytes,
-// trigger,
-// emptyBytes,
-// hash,
-// )
-// await evmRevert(tx, errorMsgs.hashPayload)
-// tx = registrar
-// .connect(registrarOwner)
-// .approve(
-// upkeepName,
-// mock.address,
-// performGas,
-// await admin.getAddress(),
-// 0,
-// '0x1234',
-// trigger,
-// emptyBytes,
-// hash,
-// )
-// await evmRevert(tx, errorMsgs.hashPayload)
-// })
-//
-// it('approves an existing registration request', async () => {
-// const tx = await registrar
-// .connect(registrarOwner)
-// .approve(
-// upkeepName,
-// mock.address,
-// performGas,
-// await admin.getAddress(),
-// 0,
-// emptyBytes,
-// trigger,
-// offchainConfig,
-// hash,
-// )
-// await expect(tx).to.emit(registrar, 'RegistrationApproved')
-// })
-//
-// it('deletes the request afterwards / reverts if the request DNE', async () => {
-// await registrar
-// .connect(registrarOwner)
-// .approve(
-// upkeepName,
-// mock.address,
-// performGas,
-// await admin.getAddress(),
-// 0,
-// emptyBytes,
-// trigger,
-// offchainConfig,
-// hash,
-// )
-// const tx = registrar
-// .connect(registrarOwner)
-// .approve(
-// upkeepName,
-// mock.address,
-// performGas,
-// await admin.getAddress(),
-// 0,
-// emptyBytes,
-// trigger,
-// offchainConfig,
-// hash,
-// )
-// await evmRevert(tx, errorMsgs.requestNotFound)
-// })
-// })
-//
-// describe('#cancel', () => {
-// let hash: string
-//
-// beforeEach(async () => {
-// await registrar
-// .connect(registrarOwner)
-// .setTriggerConfig(
-// Trigger.CONDITION,
-// autoApproveType_DISABLED,
-// maxAllowedAutoApprove,
-// )
-//
-// //register with auto approve OFF
-// const abiEncodedBytes = registrar.interface.encodeFunctionData(
-// 'register',
-// [
-// upkeepName,
-// emptyBytes,
-// mock.address,
-// performGas,
-// await admin.getAddress(),
-// 0,
-// emptyBytes,
-// trigger,
-// offchainConfig,
-// amount,
-// await requestSender.getAddress(),
-// ],
-// )
-// const tx = await linkToken
-// .connect(requestSender)
-// .transferAndCall(registrar.address, amount, abiEncodedBytes)
-// const receipt = await tx.wait()
-// hash = receipt.logs[2].topics[1]
-// // submit duplicate request (increase balance)
-// await linkToken
-// .connect(requestSender)
-// .transferAndCall(registrar.address, amount, abiEncodedBytes)
-// })
-//
-// it('reverts if not called by the admin / owner', async () => {
-// const tx = registrar.connect(stranger).cancel(hash)
-// await evmRevert(tx, errorMsgs.onlyAdmin)
-// })
-//
-// it('reverts if the hash does not exist', async () => {
-// const tx = registrar
-// .connect(registrarOwner)
-// .cancel(
-// '0x000000000000000000000000322813fd9a801c5507c9de605d63cea4f2ce6c44',
-// )
-// await evmRevert(tx, errorMsgs.requestNotFound)
-// })
-//
-// it('refunds the total request balance to the admin address if owner cancels', async () => {
-// const before = await linkToken.balanceOf(await admin.getAddress())
-// const tx = await registrar.connect(registrarOwner).cancel(hash)
-// const after = await linkToken.balanceOf(await admin.getAddress())
-// assert.isTrue(after.sub(before).eq(amount.mul(BigNumber.from(2))))
-// await expect(tx).to.emit(registrar, 'RegistrationRejected')
-// })
-//
-// it('refunds the total request balance to the admin address if admin cancels', async () => {
-// const before = await linkToken.balanceOf(await admin.getAddress())
-// const tx = await registrar.connect(admin).cancel(hash)
-// const after = await linkToken.balanceOf(await admin.getAddress())
-// assert.isTrue(after.sub(before).eq(amount.mul(BigNumber.from(2))))
-// await expect(tx).to.emit(registrar, 'RegistrationRejected')
-// })
-//
-// it('deletes the request hash', async () => {
-// await registrar.connect(registrarOwner).cancel(hash)
-// let tx = registrar.connect(registrarOwner).cancel(hash)
-// await evmRevert(tx, errorMsgs.requestNotFound)
-// tx = registrar
-// .connect(registrarOwner)
-// .approve(
-// upkeepName,
-// mock.address,
-// performGas,
-// await admin.getAddress(),
-// 0,
-// emptyBytes,
-// trigger,
-// emptyBytes,
-// hash,
-// )
-// await evmRevert(tx, errorMsgs.requestNotFound)
-// })
-// })
-// })
diff --git a/contracts/test/v0.8/automation/AutomationRegistry2_2.test.ts b/contracts/test/v0.8/automation/AutomationRegistry2_2.test.ts
deleted file mode 100644
index 593ac08a5e7..00000000000
--- a/contracts/test/v0.8/automation/AutomationRegistry2_2.test.ts
+++ /dev/null
@@ -1,5962 +0,0 @@
-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 { MockArbGasInfo__factory as MockArbGasInfoFactory } from '../../../typechain/factories/MockArbGasInfo__factory'
-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 { 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'
-import { MockOVMGasPriceOracle } from '../../../typechain/MockOVMGasPriceOracle'
-import { StreamsLookupUpkeep } from '../../../typechain/StreamsLookupUpkeep'
-import { MockV3Aggregator } from '../../../typechain/MockV3Aggregator'
-import { UpkeepMock } from '../../../typechain/UpkeepMock'
-import { ChainModuleBase } from '../../../typechain/ChainModuleBase'
-import { ArbitrumModule } from '../../../typechain/ArbitrumModule'
-import { OptimismModuleV2 } from '../../../typechain/OptimismModuleV2'
-import { UpkeepTranscoder } from '../../../typechain/UpkeepTranscoder'
-import { IChainModule, UpkeepAutoFunder } from '../../../typechain'
-import {
- CancelledUpkeepReportEvent,
- IAutomationRegistryMaster as IAutomationRegistry,
- ReorgedUpkeepReportEvent,
- StaleUpkeepReportEvent,
- UpkeepPerformedEvent,
-} from '../../../typechain/IAutomationRegistryMaster'
-import {
- deployMockContract,
- MockContract,
-} from '@ethereum-waffle/mock-contract'
-import { deployRegistry22 } from './helpers'
-
-const describeMaybe = process.env.SKIP_SLOW ? describe.skip : describe
-const itMaybe = process.env.SKIP_SLOW ? it.skip : it
-
-// copied from AutomationRegistryInterface2_2.sol
-enum UpkeepFailureReason {
- NONE,
- UPKEEP_CANCELLED,
- UPKEEP_PAUSED,
- TARGET_CHECK_REVERTED,
- UPKEEP_NOT_NEEDED,
- PERFORM_DATA_EXCEEDS_LIMIT,
- INSUFFICIENT_BALANCE,
- CHECK_CALLBACK_REVERTED,
- REVERT_DATA_EXCEEDS_LIMIT,
- REGISTRY_PAUSED,
-}
-
-// copied from AutomationRegistryBase2_2.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]
-
-// -----------------------------------------------------------------------------------------------
-
-// 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(5000)
-// 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(5000)
-
-const linkEth = BigNumber.from(5000000000000000) // 1 Link = 0.005 Eth
-const gasWei = BigNumber.from(1000000000) // 1 gwei
-// -----------------------------------------------------------------------------------------------
-// test-wide configs for upkeeps
-const linkDivisibility = BigNumber.from('1000000000000000000')
-const performGas = BigNumber.from('1000000')
-const paymentPremiumBase = BigNumber.from('1000000000')
-const paymentPremiumPPB = BigNumber.from('250000000')
-const flatFeeMicroLink = BigNumber.from(0)
-
-const randomBytes = '0x1234abcd'
-const emptyBytes = '0x'
-const emptyBytes32 =
- '0x0000000000000000000000000000000000000000000000000000000000000000'
-
-const transmitGasOverhead = 1_000_000
-const checkGasOverhead = 500_000
-
-const stalenessSeconds = BigNumber.from(43820)
-const gasCeilingMultiplier = BigNumber.from(2)
-const checkGasLimit = BigNumber.from(10000000)
-const fallbackGasPrice = gasWei.mul(BigNumber.from('2'))
-const fallbackLinkPrice = linkEth.div(BigNumber.from('2'))
-const maxCheckDataSize = BigNumber.from(1000)
-const maxPerformDataSize = BigNumber.from(1000)
-const maxRevertDataSize = BigNumber.from(1000)
-const maxPerformGas = BigNumber.from(5000000)
-const minUpkeepSpend = BigNumber.from(0)
-const f = 1
-const offchainVersion = 1
-const offchainBytes = '0x'
-const zeroAddress = ethers.constants.AddressZero
-const epochAndRound5_1 =
- '0x0000000000000000000000000000000000000000000000000000000000000501'
-
-let logTriggerConfig: string
-
-// -----------------------------------------------------------------------------------------------
-
-// Smart contract factories
-let linkTokenFactory: ContractFactory
-let mockArbGasInfoFactory: MockArbGasInfoFactory
-let mockOVMGasPriceOracleFactory: MockOVMGasPriceOracleFactory
-let mockV3AggregatorFactory: MockV3AggregatorFactory
-let upkeepMockFactory: UpkeepMockFactory
-let upkeepAutoFunderFactory: UpkeepAutoFunderFactory
-let chainModuleBaseFactory: ChainModuleBaseFactory
-let arbitrumModuleFactory: ArbitrumModuleFactory
-let optimismModuleV2Factory: OptimismModuleV2Factory
-let streamsLookupUpkeepFactory: StreamsLookupUpkeepFactory
-let personas: Personas
-
-// contracts
-let linkToken: Contract
-let linkEthFeed: MockV3Aggregator
-let gasPriceFeed: MockV3Aggregator
-let registry: IAutomationRegistry // default registry, used for most tests
-let arbRegistry: IAutomationRegistry // arbitrum registry
-let opRegistry: IAutomationRegistry // optimism registry
-let mgRegistry: IAutomationRegistry // "migrate registry" used in migration tests
-let blankRegistry: IAutomationRegistry // used to test initial configurations
-let mockArbGasInfo: MockArbGasInfo
-let mockOVMGasPriceOracle: MockOVMGasPriceOracle
-let mock: UpkeepMock
-let autoFunderUpkeep: UpkeepAutoFunder
-let ltUpkeep: MockContract
-let transcoder: UpkeepTranscoder
-let chainModuleBase: ChainModuleBase
-let arbitrumModule: ArbitrumModule
-let optimismModule: OptimismModuleV2
-let streamsLookupUpkeep: StreamsLookupUpkeep
-let automationUtils: AutomationCompatibleUtils
-
-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' +
- automationUtils.interface.encodeFunctionData('_report', [report]).slice(10)
- )
-}
-
-type UpkeepData = {
- Id: BigNumberish
- performGas: BigNumberish
- performData: BytesLike
- trigger: BytesLike
-}
-
-const makeReport = (upkeeps: UpkeepData[]) => {
- const upkeepIds = upkeeps.map((u) => u.Id)
- const performGases = upkeeps.map((u) => u.performGas)
- const triggers = upkeeps.map((u) => u.trigger)
- const performDatas = upkeeps.map((u) => u.performData)
- return encodeReport({
- fastGasWei: gasWei,
- linkNative: linkEth,
- upkeepIds,
- gasLimits: performGases,
- triggers,
- performDatas,
- })
-}
-
-const makeLatestBlockReport = async (upkeepsIDs: BigNumberish[]) => {
- const latestBlock = await ethers.provider.getBlock('latest')
- const upkeeps: UpkeepData[] = []
- for (let i = 0; i < upkeepsIDs.length; i++) {
- upkeeps.push({
- Id: upkeepsIDs[i],
- performGas,
- trigger: encodeBlockTrigger({
- blockNum: latestBlock.number,
- blockHash: latestBlock.hash,
- }),
- performData: '0x',
- })
- }
- return makeReport(upkeeps)
-}
-
-const signReport = (
- reportContext: string[],
- report: any,
- signers: Wallet[],
-) => {
- const reportDigest = ethers.utils.keccak256(report)
- const packedArgs = ethers.utils.solidityPack(
- ['bytes32', 'bytes32[3]'],
- [reportDigest, reportContext],
- )
- const packedDigest = ethers.utils.keccak256(packedArgs)
-
- const signatures = []
- for (const signer of signers) {
- signatures.push(signer._signingKey().signDigest(packedDigest))
- }
- const vs = signatures.map((i) => '0' + (i.v - 27).toString(16)).join('')
- return {
- vs: '0x' + vs.padEnd(64, '0'),
- rs: signatures.map((i) => i.r),
- ss: signatures.map((i) => i.s),
- }
-}
-
-const parseUpkeepPerformedLogs = (receipt: ContractReceipt) => {
- const parsedLogs = []
- for (const rawLog of receipt.logs) {
- try {
- const log = registry.interface.parseLog(rawLog)
- if (
- log.name ==
- registry.interface.events[
- 'UpkeepPerformed(uint256,bool,uint96,uint256,uint256,bytes)'
- ].name
- ) {
- parsedLogs.push(log as unknown as UpkeepPerformedEvent)
- }
- } catch {
- continue
- }
- }
- return parsedLogs
-}
-
-const parseReorgedUpkeepReportLogs = (receipt: ContractReceipt) => {
- const parsedLogs = []
- for (const rawLog of receipt.logs) {
- try {
- const log = registry.interface.parseLog(rawLog)
- if (
- log.name ==
- registry.interface.events['ReorgedUpkeepReport(uint256,bytes)'].name
- ) {
- parsedLogs.push(log as unknown as ReorgedUpkeepReportEvent)
- }
- } catch {
- continue
- }
- }
- return parsedLogs
-}
-
-const parseStaleUpkeepReportLogs = (receipt: ContractReceipt) => {
- const parsedLogs = []
- for (const rawLog of receipt.logs) {
- try {
- const log = registry.interface.parseLog(rawLog)
- if (
- log.name ==
- registry.interface.events['StaleUpkeepReport(uint256,bytes)'].name
- ) {
- parsedLogs.push(log as unknown as StaleUpkeepReportEvent)
- }
- } catch {
- continue
- }
- }
- return parsedLogs
-}
-
-const 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('AutomationRegistry2_2', () => {
- let owner: Signer
- let keeper1: Signer
- let keeper2: Signer
- let keeper3: Signer
- let keeper4: Signer
- let keeper5: Signer
- let nonkeeper: Signer
- let signer1: Wallet
- let signer2: Wallet
- let signer3: Wallet
- let signer4: Wallet
- let signer5: Wallet
- let admin: Signer
- let payee1: Signer
- let payee2: Signer
- let payee3: Signer
- let payee4: Signer
- let payee5: Signer
-
- let upkeepId: BigNumber // conditional upkeep
- let afUpkeepId: BigNumber // auto funding upkeep
- let logUpkeepId: BigNumber // log trigger upkeepID
- let streamsLookupUpkeepId: BigNumber // streams lookup upkeep
- const numUpkeeps = 4 // see above
- let keeperAddresses: string[]
- let payees: string[]
- let signers: Wallet[]
- let signerAddresses: string[]
- let config: any
- let arbConfig: any
- let opConfig: any
- let baseConfig: Parameters
- let arbConfigParams: Parameters
- let opConfigParams: Parameters
- let upkeepManager: string
-
- before(async () => {
- personas = (await getUsers()).personas
-
- const convFactory = await ethers.getContractFactory(
- 'AutomationCompatibleUtils',
- )
- automationUtils = await convFactory.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/shared/mocks/MockV3Aggregator.sol:MockV3Aggregator',
- )) as unknown as MockV3AggregatorFactory
- mockArbGasInfoFactory = await ethers.getContractFactory('MockArbGasInfo')
- mockOVMGasPriceOracleFactory = await ethers.getContractFactory(
- 'MockOVMGasPriceOracle',
- )
- upkeepMockFactory = await ethers.getContractFactory('UpkeepMock')
- upkeepAutoFunderFactory =
- await ethers.getContractFactory('UpkeepAutoFunder')
- chainModuleBaseFactory = await ethers.getContractFactory('ChainModuleBase')
- arbitrumModuleFactory = await ethers.getContractFactory('ArbitrumModule')
- optimismModuleV2Factory =
- await ethers.getContractFactory('OptimismModuleV2')
- streamsLookupUpkeepFactory = await ethers.getContractFactory(
- 'StreamsLookupUpkeep',
- )
-
- owner = personas.Default
- keeper1 = personas.Carol
- keeper2 = personas.Eddy
- keeper3 = personas.Nancy
- keeper4 = personas.Norbert
- keeper5 = personas.Nick
- nonkeeper = personas.Ned
- admin = personas.Neil
- payee1 = personas.Nelly
- payee2 = personas.Norbert
- payee3 = personas.Nick
- payee4 = personas.Eddy
- payee5 = personas.Carol
- upkeepManager = await personas.Norbert.getAddress()
- // signers
- signer1 = new ethers.Wallet(
- '0x7777777000000000000000000000000000000000000000000000000000000001',
- )
- signer2 = new ethers.Wallet(
- '0x7777777000000000000000000000000000000000000000000000000000000002',
- )
- signer3 = new ethers.Wallet(
- '0x7777777000000000000000000000000000000000000000000000000000000003',
- )
- signer4 = new ethers.Wallet(
- '0x7777777000000000000000000000000000000000000000000000000000000004',
- )
- signer5 = new ethers.Wallet(
- '0x7777777000000000000000000000000000000000000000000000000000000005',
- )
-
- keeperAddresses = [
- await keeper1.getAddress(),
- await keeper2.getAddress(),
- await keeper3.getAddress(),
- await keeper4.getAddress(),
- await keeper5.getAddress(),
- ]
- payees = [
- await payee1.getAddress(),
- await payee2.getAddress(),
- await payee3.getAddress(),
- await payee4.getAddress(),
- await payee5.getAddress(),
- ]
- signers = [signer1, signer2, signer3, signer4, signer5]
-
- // We append 26 random addresses to keepers, payees and signers to get a system of 31 oracles
- // This allows f value of 1 - 10
- for (let i = 0; i < 26; i++) {
- keeperAddresses.push(randomAddress())
- payees.push(randomAddress())
- signers.push(ethers.Wallet.createRandom())
- }
- signerAddresses = []
- for (const signer of signers) {
- signerAddresses.push(await signer.getAddress())
- }
-
- logTriggerConfig =
- '0x' +
- automationUtils.interface
- .encodeFunctionData('_logTriggerConfig', [
- {
- contractAddress: randomAddress(),
- filterSelector: 0,
- topic0: ethers.utils.randomBytes(32),
- topic1: ethers.utils.randomBytes(32),
- topic2: ethers.utils.randomBytes(32),
- topic3: ethers.utils.randomBytes(32),
- },
- ])
- .slice(10)
- })
-
- // 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,
- l1CostWei?: BigNumber,
- ) => {
- l1CostWei = l1CostWei === undefined ? BigNumber.from(0) : l1CostWei
-
- const gasSpent = gasOverhead.add(BigNumber.from(upkeepGasSpent))
- const base = gasWei
- .mul(gasMultiplier)
- .mul(gasSpent)
- .mul(linkDivisibility)
- .div(linkEth)
- const l1Fee = l1CostWei.mul(linkDivisibility).div(linkEth)
- const gasPayment = base.add(l1Fee)
-
- const premium = gasWei
- .mul(gasMultiplier)
- .mul(upkeepGasSpent)
- .add(l1CostWei)
- .mul(linkDivisibility)
- .div(linkEth)
- .mul(premiumPPB)
- .div(paymentPremiumBase)
- .add(BigNumber.from(flatFee).mul('1000000000000'))
-
- return {
- total: gasPayment.add(premium),
- gasPayment,
- premium,
- }
- }
-
- const verifyMaxPayment = async (
- registry: IAutomationRegistry,
- chainModule: IChainModule,
- maxl1CostWeWithoutMultiplier?: BigNumber,
- ) => {
- type TestCase = {
- name: string
- multiplier: number
- gas: number
- premium: number
- flatFee: number
- }
-
- const tests: TestCase[] = [
- {
- name: 'no fees',
- multiplier: 1,
- gas: 100000,
- premium: 0,
- flatFee: 0,
- },
- {
- name: 'basic fees',
- multiplier: 1,
- gas: 100000,
- premium: 250000000,
- flatFee: 1000000,
- },
- {
- name: 'max fees',
- multiplier: 3,
- gas: 10000000,
- premium: 250000000,
- flatFee: 1000000,
- },
- ]
-
- const fPlusOne = BigNumber.from(f + 1)
- const chainModuleOverheads = await chainModule.getGasOverhead()
- const totalConditionalOverhead = registryConditionalOverhead
- .add(registryPerSignerGasOverhead.mul(fPlusOne))
- .add(
- registryPerPerformByteGasOverhead
- .add(chainModuleOverheads.chainModulePerByteOverhead)
- .mul(
- maxPerformDataSize
- .add(registryTransmitCalldataFixedBytesOverhead)
- .add(
- registryTransmitCalldataPerSignerBytesOverhead.mul(fPlusOne),
- ),
- ),
- )
- .add(chainModuleOverheads.chainModuleFixedOverhead)
-
- const totalLogOverhead = registryLogOverhead
- .add(registryPerSignerGasOverhead.mul(fPlusOne))
- .add(
- registryPerPerformByteGasOverhead
- .add(chainModuleOverheads.chainModulePerByteOverhead)
- .mul(
- maxPerformDataSize
- .add(registryTransmitCalldataFixedBytesOverhead)
- .add(
- registryTransmitCalldataPerSignerBytesOverhead.mul(fPlusOne),
- ),
- ),
- )
- .add(chainModuleOverheads.chainModuleFixedOverhead)
-
- for (const test of tests) {
- await registry.connect(owner).setConfigTypeSafe(
- signerAddresses,
- keeperAddresses,
- f,
- {
- paymentPremiumPPB: test.premium,
- flatFeeMicroLink: test.flatFee,
- checkGasLimit,
- stalenessSeconds,
- gasCeilingMultiplier: test.multiplier,
- minUpkeepSpend,
- maxCheckDataSize,
- maxPerformDataSize,
- maxRevertDataSize,
- maxPerformGas,
- fallbackGasPrice,
- fallbackLinkPrice,
- transcoder: transcoder.address,
- registrars: [],
- upkeepPrivilegeManager: upkeepManager,
- chainModule: chainModule.address,
- reorgProtectionEnabled: true,
- },
- offchainVersion,
- offchainBytes,
- )
-
- const conditionalPrice = await registry.getMaxPaymentForGas(
- Trigger.CONDITION,
- test.gas,
- )
- expect(conditionalPrice).to.equal(
- linkForGas(
- BigNumber.from(test.gas),
- totalConditionalOverhead,
- BigNumber.from(test.multiplier),
- BigNumber.from(test.premium),
- BigNumber.from(test.flatFee),
- maxl1CostWeWithoutMultiplier?.mul(BigNumber.from(test.multiplier)),
- ).total,
- )
-
- const logPrice = await registry.getMaxPaymentForGas(Trigger.LOG, test.gas)
- expect(logPrice).to.equal(
- linkForGas(
- BigNumber.from(test.gas),
- totalLogOverhead,
- BigNumber.from(test.multiplier),
- BigNumber.from(test.premium),
- BigNumber.from(test.flatFee),
- maxl1CostWeWithoutMultiplier?.mul(BigNumber.from(test.multiplier)),
- ).total,
- )
- }
- }
-
- const verifyConsistentAccounting = async (
- maxAllowedSpareChange: BigNumber,
- ) => {
- const expectedLinkBalance = (await registry.getState()).state
- .expectedLinkBalance
- const linkTokenBalance = await linkToken.balanceOf(registry.address)
- const upkeepIdBalance = (await registry.getUpkeep(upkeepId)).balance
- let totalKeeperBalance = BigNumber.from(0)
- for (let i = 0; i < keeperAddresses.length; i++) {
- totalKeeperBalance = totalKeeperBalance.add(
- (await registry.getTransmitterInfo(keeperAddresses[i])).balance,
- )
- }
- const ownerBalance = (await registry.getState()).state.ownerLinkBalance
- assert.isTrue(expectedLinkBalance.eq(linkTokenBalance))
- assert.isTrue(
- upkeepIdBalance
- .add(totalKeeperBalance)
- .add(ownerBalance)
- .lte(expectedLinkBalance),
- )
- assert.isTrue(
- expectedLinkBalance
- .sub(upkeepIdBalance)
- .sub(totalKeeperBalance)
- .sub(ownerBalance)
- .lte(maxAllowedSpareChange),
- )
- }
-
- interface GetTransmitTXOptions {
- numSigners?: number
- startingSignerIndex?: number
- gasLimit?: BigNumberish
- gasPrice?: BigNumberish
- performGas?: BigNumberish
- 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)
- linkEthFeed = await mockV3AggregatorFactory
- .connect(owner)
- .deploy(9, linkEth)
- const upkeepTranscoderFactory = await ethers.getContractFactory(
- 'UpkeepTranscoder4_0',
- )
- transcoder = await upkeepTranscoderFactory.connect(owner).deploy()
- mockArbGasInfo = await mockArbGasInfoFactory.connect(owner).deploy()
- mockOVMGasPriceOracle = await mockOVMGasPriceOracleFactory
- .connect(owner)
- .deploy()
- chainModuleBase = await chainModuleBaseFactory.connect(owner).deploy()
- arbitrumModule = await arbitrumModuleFactory.connect(owner).deploy()
- optimismModule = await optimismModuleV2Factory.connect(owner).deploy()
- streamsLookupUpkeep = await streamsLookupUpkeepFactory
- .connect(owner)
- .deploy(
- BigNumber.from('10000'),
- BigNumber.from('100'),
- false /* useArbBlock */,
- true /* staging */,
- false /* verify mercury response */,
- )
-
- const arbOracleCode = await ethers.provider.send('eth_getCode', [
- mockArbGasInfo.address,
- ])
- await ethers.provider.send('hardhat_setCode', [
- '0x000000000000000000000000000000000000006C',
- arbOracleCode,
- ])
-
- const optOracleCode = await ethers.provider.send('eth_getCode', [
- mockOVMGasPriceOracle.address,
- ])
- await ethers.provider.send('hardhat_setCode', [
- '0x420000000000000000000000000000000000000F',
- optOracleCode,
- ])
-
- const mockArbSys = await new MockArbSysFactory(owner).deploy()
- const arbSysCode = await ethers.provider.send('eth_getCode', [
- mockArbSys.address,
- ])
- await ethers.provider.send('hardhat_setCode', [
- '0x0000000000000000000000000000000000000064',
- arbSysCode,
- ])
-
- config = {
- paymentPremiumPPB,
- flatFeeMicroLink,
- checkGasLimit,
- stalenessSeconds,
- gasCeilingMultiplier,
- minUpkeepSpend,
- maxCheckDataSize,
- maxPerformDataSize,
- maxRevertDataSize,
- maxPerformGas,
- fallbackGasPrice,
- fallbackLinkPrice,
- transcoder: transcoder.address,
- registrars: [],
- upkeepPrivilegeManager: upkeepManager,
- chainModule: chainModuleBase.address,
- reorgProtectionEnabled: true,
- }
-
- arbConfig = { ...config }
- arbConfig.chainModule = arbitrumModule.address
- opConfig = { ...config }
- opConfig.chainModule = optimismModule.address
-
- baseConfig = [
- signerAddresses,
- keeperAddresses,
- f,
- config,
- offchainVersion,
- offchainBytes,
- ]
- arbConfigParams = [
- signerAddresses,
- keeperAddresses,
- f,
- arbConfig,
- offchainVersion,
- offchainBytes,
- ]
- opConfigParams = [
- signerAddresses,
- keeperAddresses,
- f,
- opConfig,
- offchainVersion,
- offchainBytes,
- ]
-
- registry = await deployRegistry22(
- owner,
- linkToken.address,
- linkEthFeed.address,
- gasPriceFeed.address,
- zeroAddress,
- )
-
- arbRegistry = await deployRegistry22(
- owner,
- linkToken.address,
- linkEthFeed.address,
- gasPriceFeed.address,
- zeroAddress,
- )
-
- opRegistry = await deployRegistry22(
- owner,
- linkToken.address,
- linkEthFeed.address,
- gasPriceFeed.address,
- zeroAddress,
- )
-
- mgRegistry = await deployRegistry22(
- owner,
- linkToken.address,
- linkEthFeed.address,
- gasPriceFeed.address,
- zeroAddress,
- )
-
- blankRegistry = await deployRegistry22(
- owner,
- linkToken.address,
- linkEthFeed.address,
- gasPriceFeed.address,
- zeroAddress,
- )
-
- 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)
- await arbRegistry.connect(owner).setConfigTypeSafe(...arbConfigParams)
- await opRegistry.connect(owner).setConfigTypeSafe(...opConfigParams)
- for (const reg of [registry, arbRegistry, opRegistry, 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(address,uint32,address,bytes,bytes)'
- ](mock.address, performGas, await admin.getAddress(), randomBytes, '0x')
- upkeepId = await getUpkeepID(tx)
-
- autoFunderUpkeep = await upkeepAutoFunderFactory
- .connect(owner)
- .deploy(linkToken.address, registry.address)
- tx = await registry
- .connect(owner)
- [
- 'registerUpkeep(address,uint32,address,bytes,bytes)'
- ](autoFunderUpkeep.address, performGas, autoFunderUpkeep.address, randomBytes, '0x')
- afUpkeepId = await getUpkeepID(tx)
-
- ltUpkeep = await deployMockContract(owner, ILogAutomationactory.abi)
- tx = await registry
- .connect(owner)
- [
- 'registerUpkeep(address,uint32,address,uint8,bytes,bytes,bytes)'
- ](ltUpkeep.address, performGas, await admin.getAddress(), Trigger.LOG, '0x', logTriggerConfig, emptyBytes)
- logUpkeepId = await getUpkeepID(tx)
-
- await autoFunderUpkeep.setUpkeepId(afUpkeepId)
- // Give enough funds for upkeep as well as to the upkeep contract
- await linkToken
- .connect(owner)
- .transfer(autoFunderUpkeep.address, toWei('1000'))
-
- tx = await registry
- .connect(owner)
- [
- 'registerUpkeep(address,uint32,address,bytes,bytes)'
- ](streamsLookupUpkeep.address, performGas, await admin.getAddress(), randomBytes, '0x')
- streamsLookupUpkeepId = await getUpkeepID(tx)
- }
-
- const getMultipleUpkeepsDeployedAndFunded = async (
- numPassingConditionalUpkeeps: number,
- numPassingLogUpkeeps: number,
- numFailingUpkeeps: number,
- ) => {
- const passingConditionalUpkeepIds = []
- const passingLogUpkeepIds = []
- const failingUpkeepIds = []
- for (let i = 0; i < numPassingConditionalUpkeeps; i++) {
- const mock = await upkeepMockFactory.deploy()
- await mock.setCanPerform(true)
- await mock.setPerformGasToBurn(BigNumber.from('0'))
- const tx = await registry
- .connect(owner)
- [
- 'registerUpkeep(address,uint32,address,bytes,bytes)'
- ](mock.address, performGas, await admin.getAddress(), randomBytes, '0x')
- const condUpkeepId = await getUpkeepID(tx)
- passingConditionalUpkeepIds.push(condUpkeepId)
-
- // Add funds to passing upkeeps
- await registry.connect(admin).addFunds(condUpkeepId, toWei('100'))
- }
- for (let i = 0; i < numPassingLogUpkeeps; i++) {
- const mock = await upkeepMockFactory.deploy()
- await mock.setCanPerform(true)
- await mock.setPerformGasToBurn(BigNumber.from('0'))
- const tx = await registry
- .connect(owner)
- [
- 'registerUpkeep(address,uint32,address,uint8,bytes,bytes,bytes)'
- ](mock.address, performGas, await admin.getAddress(), Trigger.LOG, '0x', logTriggerConfig, emptyBytes)
- const logUpkeepId = await getUpkeepID(tx)
- passingLogUpkeepIds.push(logUpkeepId)
-
- // Add funds to passing upkeeps
- await registry.connect(admin).addFunds(logUpkeepId, toWei('100'))
- }
- for (let i = 0; i < numFailingUpkeeps; i++) {
- const mock = await upkeepMockFactory.deploy()
- await mock.setCanPerform(true)
- await mock.setPerformGasToBurn(BigNumber.from('0'))
- const tx = await registry
- .connect(owner)
- [
- 'registerUpkeep(address,uint32,address,bytes,bytes)'
- ](mock.address, performGas, await admin.getAddress(), randomBytes, '0x')
- const failingUpkeepId = await getUpkeepID(tx)
- failingUpkeepIds.push(failingUpkeepId)
- }
- return {
- passingConditionalUpkeepIds,
- passingLogUpkeepIds,
- failingUpkeepIds,
- }
- }
-
- beforeEach(async () => {
- await loadFixture(setup)
- })
-
- describe('#transmit', () => {
- const fArray = [1, 5, 10]
-
- it('reverts when registry is paused', async () => {
- await registry.connect(owner).pause()
- await 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,
- linkNative: 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,
- )
-
- 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,
- )
- 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,
- )
- const tests: [string, BigNumber][] = [
- ['conditional', upkeepId],
- ['log-trigger', logUpkeepId],
- ]
- for (const [type, id] of tests) {
- const latestBlock = await ethers.provider.getBlock('latest')
-
- // Should fail when blockhash is empty
- let tx = await getTransmitTx(registry, keeper1, [id], {
- checkBlockNum: latestBlock.number + 100,
- checkBlockHash: emptyBytes32,
- })
- let receipt = await tx.wait()
- let reorgedUpkeepReportLogs = parseReorgedUpkeepReportLogs(receipt)
- // exactly 1 ReorgedUpkeepReportLogs log should be emitted
- assert.equal(
- reorgedUpkeepReportLogs.length,
- 1,
- `wrong log count for ${type} upkeep`,
- )
-
- // Should also fail when blockhash is not empty
- tx = await getTransmitTx(registry, keeper1, [id], {
- checkBlockNum: latestBlock.number + 100,
- checkBlockHash: latestBlock.hash,
- })
- receipt = await tx.wait()
- reorgedUpkeepReportLogs = parseReorgedUpkeepReportLogs(receipt)
- // exactly 1 ReorgedUpkeepReportLogs log should be emitted
- assert.equal(
- reorgedUpkeepReportLogs.length,
- 1,
- `wrong log count for ${type} upkeep`,
- )
- }
- })
-
- it('returns early when upkeep is cancelled and cancellation delay has gone', async () => {
- const latestBlockReport = await makeLatestBlockReport([upkeepId])
- await registry.connect(admin).cancelUpkeep(upkeepId)
-
- for (let i = 0; i < cancellationDelay; i++) {
- await ethers.provider.send('evm_mine', [])
- }
-
- const tx = await getTransmitTxWithReport(
- registry,
- keeper1,
- latestBlockReport,
- )
-
- const receipt = await tx.wait()
- const cancelledUpkeepReportLogs =
- parseCancelledUpkeepReportLogs(receipt)
- // exactly 1 CancelledUpkeepReport log should be emitted
- assert.equal(cancelledUpkeepReportLogs.length, 1)
- })
-
- it('does not revert if the target cannot execute', async () => {
- await mock.setCanPerform(false)
- const tx = await getTransmitTx(registry, keeper1, [upkeepId])
-
- const receipt = await tx.wait()
- const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt)
- // exactly 1 Upkeep Performed should be emitted
- assert.equal(upkeepPerformedLogs.length, 1)
- const upkeepPerformedLog = upkeepPerformedLogs[0]
-
- const success = upkeepPerformedLog.args.success
- assert.equal(success, false)
- })
-
- it('does not revert if the target runs out of gas', async () => {
- await mock.setCanPerform(false)
-
- const tx = await getTransmitTx(registry, keeper1, [upkeepId], {
- performGas: 10, // too little gas
- })
-
- const receipt = await tx.wait()
- const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt)
- // exactly 1 Upkeep Performed should be emitted
- assert.equal(upkeepPerformedLogs.length, 1)
- const upkeepPerformedLog = upkeepPerformedLogs[0]
-
- const success = upkeepPerformedLog.args.success
- assert.equal(success, false)
- })
-
- it('reverts if not enough gas supplied', async () => {
- await evmRevert(
- getTransmitTx(registry, keeper1, [upkeepId], {
- gasLimit: performGas,
- }),
- )
- })
-
- it('executes the data passed to the registry', async () => {
- await mock.setCanPerform(true)
-
- const tx = await getTransmitTx(registry, keeper1, [upkeepId], {
- 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 1x gasWei
- const gasPrice = gasWei.mul(BigNumber.from('1'))
- await mock.setCanPerform(true)
- const registryPremiumBefore = (await registry.getState()).state
- .totalPremium
- const tx = await getTransmitTx(registry, keeper1, [upkeepId], {
- gasPrice,
- })
- const receipt = await tx.wait()
- const registryPremiumAfter = (await registry.getState()).state
- .totalPremium
- const premium = registryPremiumAfter.sub(registryPremiumBefore)
-
- const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt)
- // exactly 1 Upkeep Performed should be emitted
- assert.equal(upkeepPerformedLogs.length, 1)
- const upkeepPerformedLog = upkeepPerformedLogs[0]
-
- const gasUsed = upkeepPerformedLog.args.gasUsed
- const gasOverhead = upkeepPerformedLog.args.gasOverhead
- const totalPayment = upkeepPerformedLog.args.totalPayment
-
- assert.equal(
- linkForGas(
- gasUsed,
- gasOverhead,
- BigNumber.from('1'), // Not the config multiplier, but the actual gas used
- paymentPremiumPPB,
- flatFeeMicroLink,
- ).total.toString(),
- totalPayment.toString(),
- )
-
- assert.equal(
- linkForGas(
- gasUsed,
- gasOverhead,
- BigNumber.from('1'), // Not the config multiplier, but the actual gas used
- paymentPremiumPPB,
- flatFeeMicroLink,
- ).premium.toString(),
- premium.toString(),
- )
- })
-
- it('only pays at a rate up to the gas ceiling [ @skip-coverage ]', async () => {
- // Actual multiplier is 2, but we set gasPrice to be 10x
- const gasPrice = gasWei.mul(BigNumber.from('10'))
- await mock.setCanPerform(true)
-
- const tx = await getTransmitTx(registry, keeper1, [upkeepId], {
- gasPrice,
- })
- const receipt = await tx.wait()
- const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt)
- // exactly 1 Upkeep Performed should be emitted
- assert.equal(upkeepPerformedLogs.length, 1)
- const upkeepPerformedLog = upkeepPerformedLogs[0]
-
- const gasUsed = upkeepPerformedLog.args.gasUsed
- const gasOverhead = upkeepPerformedLog.args.gasOverhead
- const totalPayment = upkeepPerformedLog.args.totalPayment
-
- assert.equal(
- linkForGas(
- gasUsed,
- gasOverhead,
- gasCeilingMultiplier, // Should be same with exisitng multiplier
- paymentPremiumPPB,
- flatFeeMicroLink,
- ).total.toString(),
- totalPayment.toString(),
- )
- })
-
- it('correctly accounts for l payment', async () => {
- await mock.setCanPerform(true)
- // Same as MockArbGasInfo.sol
- const l1CostWeiArb = BigNumber.from(1000000)
-
- let tx = await arbRegistry
- .connect(owner)
- [
- 'registerUpkeep(address,uint32,address,bytes,bytes)'
- ](mock.address, performGas, await admin.getAddress(), randomBytes, '0x')
- const testUpkeepId = await getUpkeepID(tx)
- await arbRegistry.connect(owner).addFunds(testUpkeepId, toWei('100'))
-
- // Do the thing
- tx = await getTransmitTx(
- arbRegistry,
- keeper1,
- [testUpkeepId],
-
- { gasPrice: gasWei.mul('5') }, // High gas price so that it gets capped
- )
- const receipt = await tx.wait()
- const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt)
- // exactly 1 Upkeep Performed should be emitted
- assert.equal(upkeepPerformedLogs.length, 1)
- const upkeepPerformedLog = upkeepPerformedLogs[0]
-
- const gasUsed = upkeepPerformedLog.args.gasUsed
- const gasOverhead = upkeepPerformedLog.args.gasOverhead
- const totalPayment = upkeepPerformedLog.args.totalPayment
-
- assert.equal(
- linkForGas(
- gasUsed,
- gasOverhead,
- gasCeilingMultiplier,
- paymentPremiumPPB,
- flatFeeMicroLink,
- l1CostWeiArb,
- ).total.toString(),
- totalPayment.toString(),
- )
- })
-
- itMaybe('can self fund', async () => {
- const maxPayment = await registry.getMaxPaymentForGas(
- Trigger.CONDITION,
- performGas,
- )
-
- // First set auto funding amount to 0 and verify that balance is deducted upon performUpkeep
- let initialBalance = toWei('100')
- await registry.connect(owner).addFunds(afUpkeepId, initialBalance)
- await autoFunderUpkeep.setAutoFundLink(0)
- await autoFunderUpkeep.setIsEligible(true)
- await getTransmitTx(registry, keeper1, [afUpkeepId])
-
- let postUpkeepBalance = (await registry.getUpkeep(afUpkeepId)).balance
- assert.isTrue(postUpkeepBalance.lt(initialBalance)) // Balance should be deducted
- assert.isTrue(postUpkeepBalance.gte(initialBalance.sub(maxPayment))) // Balance should not be deducted more than maxPayment
-
- // Now set auto funding amount to 100 wei and verify that the balance increases
- initialBalance = postUpkeepBalance
- const autoTopupAmount = toWei('100')
- await autoFunderUpkeep.setAutoFundLink(autoTopupAmount)
- await autoFunderUpkeep.setIsEligible(true)
- await getTransmitTx(registry, keeper1, [afUpkeepId])
-
- postUpkeepBalance = (await registry.getUpkeep(afUpkeepId)).balance
- // Balance should increase by autoTopupAmount and decrease by max maxPayment
- assert.isTrue(
- postUpkeepBalance.gte(
- initialBalance.add(autoTopupAmount).sub(maxPayment),
- ),
- )
- })
-
- it('can self cancel', async () => {
- await registry.connect(owner).addFunds(afUpkeepId, toWei('100'))
-
- await autoFunderUpkeep.setIsEligible(true)
- await autoFunderUpkeep.setShouldCancel(true)
-
- let registration = await registry.getUpkeep(afUpkeepId)
- const oldExpiration = registration.maxValidBlocknumber
-
- // Do the thing
- await getTransmitTx(registry, keeper1, [afUpkeepId])
-
- // Verify upkeep gets cancelled
- registration = await registry.getUpkeep(afUpkeepId)
- const newExpiration = registration.maxValidBlocknumber
- assert.isTrue(newExpiration.lt(oldExpiration))
- })
-
- it('reverts when configDigest mismatches', async () => {
- const report = await makeLatestBlockReport([upkeepId])
- const reportContext = [emptyBytes32, epochAndRound5_1, emptyBytes32] // wrong config digest
- const sigs = signReport(reportContext, report, signers.slice(0, f + 1))
- await 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,
- )
- const tx = await registry
- .connect(owner)
- ['registerUpkeep(address,uint32,address,bytes,bytes)'](
- mock.address,
- maxPerformGas, // max allowed gas
- await admin.getAddress(),
- randomBytes,
- '0x',
- )
- const testUpkeepId = await getUpkeepID(tx)
- await registry.connect(admin).addFunds(testUpkeepId, toWei('100'))
-
- let performData = '0x'
- for (let i = 0; i < maxPerformDataSize.toNumber(); i++) {
- performData += '11'
- } // max allowed performData
-
- await mock.setCanPerform(true)
- await mock.setPerformGasToBurn(maxPerformGas)
-
- await getTransmitTx(registry, keeper1, [testUpkeepId], {
- gasLimit: maxPerformGas.add(transmitGasOverhead),
- numSigners: 11,
- 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,
- )
- 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)
- }
- },
- )
-
- // skipping it for now as it is passing in local but failing in CI
- describe.skip('Gas benchmarking conditional upkeeps [ @skip-coverage ]', function () {
- const fs = [1, 10]
- fs.forEach(function (newF) {
- it(
- 'When f=' +
- newF +
- ' calculates gas overhead appropriately within a margin for different scenarios',
- async () => {
- // Perform the upkeep once to remove non-zero storage slots and have predictable gas measurement
- let tx = await getTransmitTx(registry, keeper1, [upkeepId])
- await tx.wait()
-
- // Different test scenarios
- let longBytes = '0x'
- for (let i = 0; i < maxPerformDataSize.toNumber(); i++) {
- longBytes += '11'
- }
- const upkeepSuccessArray = [true, false]
- const performGasArray = [5000, performGas]
- const performDataArray = ['0x', longBytes]
- const chainModuleOverheads =
- await chainModuleBase.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,
- )
- 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)
- const estimatedGasOverhead = registryConditionalOverhead
- .add(
- registryPerSignerGasOverhead.mul(
- BigNumber.from(newF + 1),
- ),
- )
- .add(
- registryPerPerformByteGasOverhead
- .add(chainModuleOverheads.chainModulePerByteOverhead)
- .mul(
- BigNumber.from(performData.length / 2 - 1)
- .add(registryTransmitCalldataFixedBytesOverhead)
- .add(
- registryTransmitCalldataPerSignerBytesOverhead.mul(
- BigNumber.from(newF + 1),
- ),
- ),
- ),
- )
- .add(chainModuleOverheads.chainModuleFixedOverhead)
-
- 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(),
- ' charged overhead: ',
- chargedGasOverhead.toString(),
- ' actual overhead: ',
- actualGasOverhead.toString(),
- ' calculation margin over gasUsed: ',
- chargedGasOverhead.sub(actualGasOverhead).toString(),
- ' estimation margin over gasUsed: ',
- estimatedGasOverhead.sub(actualGasOverhead).toString(),
- )
-
- // 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
- .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(),
- )
-
- // 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('Gas benchmarking log upkeeps [ @skip-coverage ]', function () {
- const fs = [1, 10]
- fs.forEach(function (newF) {
- it(
- 'When f=' +
- newF +
- ' calculates gas overhead appropriately within a margin',
- async () => {
- // Perform the upkeep once to remove non-zero storage slots and have predictable gas measurement
- let tx = await getTransmitTx(registry, keeper1, [logUpkeepId])
- await tx.wait()
- const performData = '0x'
- await mock.setCanPerform(true)
- await mock.setPerformGasToBurn(performGas)
- await registry.setConfigTypeSafe(
- signerAddresses,
- keeperAddresses,
- newF,
- config,
- offchainVersion,
- offchainBytes,
- )
- tx = await getTransmitTx(registry, keeper1, [logUpkeepId], {
- numSigners: newF + 1,
- 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 chainModuleBase.getGasOverhead()
-
- const upkeepGasUsed = upkeepPerformedLog.args.gasUsed
- const chargedGasOverhead = upkeepPerformedLog.args.gasOverhead
- const actualGasOverhead = receipt.gasUsed.sub(upkeepGasUsed)
- const estimatedGasOverhead = registryLogOverhead
- .add(registryPerSignerGasOverhead.mul(BigNumber.from(newF + 1)))
- .add(
- registryPerPerformByteGasOverhead
- .add(chainModuleOverheads.chainModulePerByteOverhead)
- .mul(
- BigNumber.from(performData.length / 2 - 1)
- .add(registryTransmitCalldataFixedBytesOverhead)
- .add(
- registryTransmitCalldataPerSignerBytesOverhead.mul(
- BigNumber.from(newF + 1),
- ),
- ),
- ),
- )
- .add(chainModuleOverheads.chainModuleFixedOverhead)
-
- 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(),
- )
-
- 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(),
- )
- },
- )
- })
- })
- })
- })
-
- describe('#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(address,uint32,address,bytes,bytes)'
- ](mock.address, performGas, await admin.getAddress(), randomBytes, '0x')
- const testUpkeepId = await getUpkeepID(tx)
- upkeepIds.push(testUpkeepId)
-
- // Add funds to passing upkeeps
- await registry.connect(owner).addFunds(testUpkeepId, toWei('10'))
-
- await mock.setCanPerform(true)
- await mock.setPerformGasToBurn(performGas)
-
- totalPerformGas = totalPerformGas.add(performGas)
- }
-
- // Should revert with no overhead added
- await evmRevert(
- getTransmitTx(registry, keeper1, upkeepIds, {
- gasLimit: totalPerformGas,
- }),
- )
- // Should not revert with overhead added
- await getTransmitTx(registry, keeper1, upkeepIds, {
- gasLimit: totalPerformGas.add(transmitGasOverhead),
- })
- })
-
- it('splits l2 payment among performed upkeeps according to perform data weight', async () => {
- const numUpkeeps = 7
- const upkeepIds: BigNumber[] = []
- const performDataSizes = [0, 10, 1000, 50, 33, 69, 420]
- const performDatas: string[] = []
- const upkeepCalldataWeights: BigNumber[] = []
- let totalCalldataWeight = BigNumber.from('0')
- // Same as MockArbGasInfo.sol
- const l1CostWeiArb = BigNumber.from(1000000)
-
- for (let i = 0; i < numUpkeeps; i++) {
- const mock = await upkeepMockFactory.deploy()
- const tx = await arbRegistry
- .connect(owner)
- [
- 'registerUpkeep(address,uint32,address,bytes,bytes)'
- ](mock.address, performGas, await admin.getAddress(), randomBytes, '0x')
- const testUpkeepId = await getUpkeepID(tx)
- upkeepIds.push(testUpkeepId)
-
- // Add funds to passing upkeeps
- await arbRegistry.connect(owner).addFunds(testUpkeepId, toWei('100'))
-
- // Generate performData
- let pd = '0x'
- for (let j = 0; j < performDataSizes[i]; j++) {
- pd += '11'
- }
- performDatas.push(pd)
- const w = BigNumber.from(performDataSizes[i])
- .add(registryTransmitCalldataFixedBytesOverhead)
- .add(
- registryTransmitCalldataPerSignerBytesOverhead.mul(
- BigNumber.from(f + 1),
- ),
- )
- upkeepCalldataWeights.push(w)
- totalCalldataWeight = totalCalldataWeight.add(w)
- }
-
- // Do the thing
- const tx = await getTransmitTx(arbRegistry, keeper1, upkeepIds, {
- gasPrice: gasWei.mul('5'), // High gas price so that it gets capped
- performDatas,
- })
-
- const receipt = await tx.wait()
- const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt)
- // exactly numPassingUpkeeps Upkeep Performed should be emitted
- assert.equal(upkeepPerformedLogs.length, numUpkeeps)
-
- for (let i = 0; i < numUpkeeps; i++) {
- const upkeepPerformedLog = upkeepPerformedLogs[i]
-
- const gasUsed = upkeepPerformedLog.args.gasUsed
- const gasOverhead = upkeepPerformedLog.args.gasOverhead
- const totalPayment = upkeepPerformedLog.args.totalPayment
-
- assert.equal(
- linkForGas(
- gasUsed,
- gasOverhead,
- gasCeilingMultiplier,
- paymentPremiumPPB,
- flatFeeMicroLink,
- l1CostWeiArb.mul(upkeepCalldataWeights[i]).div(totalCalldataWeight),
- ).total.toString(),
- totalPayment.toString(),
- )
- }
- })
- })
-
- describe('#recoverFunds', () => {
- const sent = toWei('7')
-
- beforeEach(async () => {
- await linkToken.connect(admin).approve(registry.address, toWei('100'))
- await linkToken
- .connect(owner)
- .transfer(await keeper1.getAddress(), toWei('1000'))
-
- // add funds to upkeep 1 and perform and withdraw some payment
- const tx = await registry
- .connect(owner)
- [
- 'registerUpkeep(address,uint32,address,bytes,bytes)'
- ](mock.address, performGas, await admin.getAddress(), emptyBytes, emptyBytes)
-
- const id1 = await getUpkeepID(tx)
- await registry.connect(admin).addFunds(id1, toWei('5'))
-
- await getTransmitTx(registry, keeper1, [id1])
- await getTransmitTx(registry, keeper2, [id1])
- await getTransmitTx(registry, keeper3, [id1])
-
- await registry
- .connect(payee1)
- .withdrawPayment(
- await keeper1.getAddress(),
- await nonkeeper.getAddress(),
- )
-
- // transfer funds directly to the registry
- await linkToken.connect(keeper1).transfer(registry.address, sent)
-
- // add funds to upkeep 2 and perform and withdraw some payment
- const tx2 = await registry
- .connect(owner)
- [
- 'registerUpkeep(address,uint32,address,bytes,bytes)'
- ](mock.address, performGas, await admin.getAddress(), emptyBytes, emptyBytes)
- const id2 = await getUpkeepID(tx2)
- await registry.connect(admin).addFunds(id2, toWei('5'))
-
- await getTransmitTx(registry, keeper1, [id2])
- await getTransmitTx(registry, keeper2, [id2])
- await getTransmitTx(registry, keeper3, [id2])
-
- await registry
- .connect(payee2)
- .withdrawPayment(
- await keeper2.getAddress(),
- await nonkeeper.getAddress(),
- )
-
- // transfer funds using onTokenTransfer
- const data = ethers.utils.defaultAbiCoder.encode(['uint256'], [id2])
- await linkToken
- .connect(owner)
- .transferAndCall(registry.address, toWei('1'), data)
-
- // withdraw some funds
- await registry.connect(owner).cancelUpkeep(id1)
- await registry
- .connect(admin)
- .withdrawFunds(id1, await nonkeeper.getAddress())
- })
-
- it('reverts if not called by owner', async () => {
- await evmRevert(
- registry.connect(keeper1).recoverFunds(),
- 'Only callable by owner',
- )
- })
-
- it('allows any funds that have been accidentally transfered to be moved', async () => {
- const balanceBefore = await linkToken.balanceOf(registry.address)
- const ownerBefore = await linkToken.balanceOf(await owner.getAddress())
-
- await registry.connect(owner).recoverFunds()
-
- const balanceAfter = await linkToken.balanceOf(registry.address)
- const ownerAfter = await linkToken.balanceOf(await owner.getAddress())
-
- assert.isTrue(balanceBefore.eq(balanceAfter.add(sent)))
- assert.isTrue(ownerAfter.eq(ownerBefore.add(sent)))
- })
- })
-
- describe('#getMinBalanceForUpkeep / #checkUpkeep / #transmit', () => {
- it('calculates the minimum balance appropriately', async () => {
- await mock.setCanCheck(true)
-
- const oneWei = BigNumber.from(1)
- const minBalance = await registry.getMinBalanceForUpkeep(upkeepId)
- const tooLow = minBalance.sub(oneWei)
-
- await registry.connect(admin).addFunds(upkeepId, tooLow)
- let checkUpkeepResult = await registry
- .connect(zeroAddress)
- .callStatic['checkUpkeep(uint256)'](upkeepId)
-
- assert.equal(checkUpkeepResult.upkeepNeeded, false)
- assert.equal(
- checkUpkeepResult.upkeepFailureReason,
- UpkeepFailureReason.INSUFFICIENT_BALANCE,
- )
-
- await registry.connect(admin).addFunds(upkeepId, oneWei)
- checkUpkeepResult = await registry
- .connect(zeroAddress)
- .callStatic['checkUpkeep(uint256)'](upkeepId)
- assert.equal(checkUpkeepResult.upkeepNeeded, true)
- })
-
- it('uses maxPerformData size in checkUpkeep but actual performDataSize in transmit', async () => {
- const tx = await registry
- .connect(owner)
- [
- 'registerUpkeep(address,uint32,address,bytes,bytes)'
- ](mock.address, performGas, await admin.getAddress(), randomBytes, '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(address,uint32,address,bytes,bytes)'
- ](mock.address, performGas, await admin.getAddress(), randomBytes, '0x')
- upkeepId2 = await getUpkeepID(tx)
-
- await registry.connect(admin).addFunds(upkeepId, toWei('100'))
- await registry.connect(admin).addFunds(upkeepId2, toWei('100'))
-
- // Do a perform so that upkeep is charged some amount
- await getTransmitTx(registry, keeper1, [upkeepId])
- await getTransmitTx(registry, keeper1, [upkeepId2])
- })
-
- it('reverts if called on a non existing ID', async () => {
- await 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)
- const payee = await payee1.getAddress()
- await registry.connect(admin).pauseUpkeep(upkeepId)
- await registry.connect(owner).cancelUpkeep(upkeepId)
- await expect(() =>
- registry.connect(admin).withdrawFunds(upkeepId, payee),
- ).to.changeTokenBalance(linkToken, payee1, balance)
- })
- })
-
- describe('after the registration is cancelled', () => {
- beforeEach(async () => {
- await registry.connect(owner).cancelUpkeep(upkeepId)
- await registry.connect(owner).cancelUpkeep(upkeepId2)
- })
-
- it('can be called successively on two upkeeps', async () => {
- await registry
- .connect(admin)
- .withdrawFunds(upkeepId, await payee1.getAddress())
- await registry
- .connect(admin)
- .withdrawFunds(upkeepId2, await payee1.getAddress())
- })
-
- it('moves the funds out and updates the balance and emits an event', async () => {
- const payee1Before = await linkToken.balanceOf(
- await payee1.getAddress(),
- )
- const registryBefore = await linkToken.balanceOf(registry.address)
-
- let registration = await registry.getUpkeep(upkeepId)
- const previousBalance = registration.balance
-
- const tx = await registry
- .connect(admin)
- .withdrawFunds(upkeepId, await payee1.getAddress())
- await expect(tx)
- .to.emit(registry, 'FundsWithdrawn')
- .withArgs(upkeepId, previousBalance, await payee1.getAddress())
-
- const payee1After = await linkToken.balanceOf(await payee1.getAddress())
- const registryAfter = await linkToken.balanceOf(registry.address)
-
- assert.isTrue(payee1Before.add(previousBalance).eq(payee1After))
- assert.isTrue(registryBefore.sub(previousBalance).eq(registryAfter))
-
- registration = await registry.getUpkeep(upkeepId)
- assert.equal(0, registration.balance.toNumber())
- })
- })
- })
-
- describe('#simulatePerformUpkeep', () => {
- it('reverts if called by non zero address', async () => {
- await 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)
-
- const simulatePerformResult = await registry
- .connect(zeroAddress)
- .callStatic.simulatePerformUpkeep(upkeepId, '0x')
-
- assert.equal(simulatePerformResult.success, true)
- // Full execute gas should be used, with some performGasBuffer(1000)
- assert.isTrue(
- simulatePerformResult.gasUsed.gt(
- performGas.sub(BigNumber.from('1000')),
- ),
- )
- })
- })
-
- describe('#checkUpkeep', () => {
- it('reverts if called by non zero address', async () => {
- await 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.linkNative.gt(BigNumber.from('0')))
- })
-
- it('returns false, error code, and no revert data if the target check revert data exceeds maxRevertDataSize', async () => {
- await mock.setShouldRevertCheck(true)
- let longRevertReason = ''
- for (let i = 0; i <= maxRevertDataSize.toNumber(); i++) {
- longRevertReason += 'x'
- }
- await mock.setCheckRevertReason(longRevertReason)
- const checkUpkeepResult = await registry
- .connect(zeroAddress)
- .callStatic['checkUpkeep(uint256)'](upkeepId)
- assert.equal(checkUpkeepResult.upkeepNeeded, false)
-
- assert.equal(checkUpkeepResult.performData, '0x')
- assert.equal(
- checkUpkeepResult.upkeepFailureReason,
- UpkeepFailureReason.REVERT_DATA_EXCEEDS_LIMIT,
- )
- assert.isTrue(checkUpkeepResult.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used
- expect(checkUpkeepResult.gasLimit).to.equal(performGas)
- })
-
- it('returns false and error code if the upkeep is not needed', async () => {
- await mock.setCanCheck(false)
- const checkUpkeepResult = await registry
- .connect(zeroAddress)
- .callStatic['checkUpkeep(uint256)'](upkeepId)
-
- assert.equal(checkUpkeepResult.upkeepNeeded, false)
- assert.equal(checkUpkeepResult.performData, '0x')
- assert.equal(
- checkUpkeepResult.upkeepFailureReason,
- UpkeepFailureReason.UPKEEP_NOT_NEEDED,
- )
- assert.isTrue(checkUpkeepResult.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used
- expect(checkUpkeepResult.gasLimit).to.equal(performGas)
- })
-
- it('returns false and error code if the performData exceeds limit', async () => {
- let longBytes = '0x'
- for (let i = 0; i < 5000; i++) {
- longBytes += '1'
- }
- await mock.setCanCheck(true)
- await mock.setPerformData(longBytes)
-
- const checkUpkeepResult = await registry
- .connect(zeroAddress)
- .callStatic['checkUpkeep(uint256)'](upkeepId)
-
- assert.equal(checkUpkeepResult.upkeepNeeded, false)
- assert.equal(checkUpkeepResult.performData, '0x')
- assert.equal(
- checkUpkeepResult.upkeepFailureReason,
- UpkeepFailureReason.PERFORM_DATA_EXCEEDS_LIMIT,
- )
- assert.isTrue(checkUpkeepResult.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used
- expect(checkUpkeepResult.gasLimit).to.equal(performGas)
- })
-
- it('returns true with gas used if the target can execute', async () => {
- await mock.setCanCheck(true)
- await mock.setPerformData(randomBytes)
-
- const latestBlock = await ethers.provider.getBlock('latest')
-
- const checkUpkeepResult = await registry
- .connect(zeroAddress)
- .callStatic['checkUpkeep(uint256)'](upkeepId, {
- blockTag: latestBlock.number,
- })
-
- assert.equal(checkUpkeepResult.upkeepNeeded, true)
- assert.equal(checkUpkeepResult.performData, randomBytes)
- assert.equal(
- checkUpkeepResult.upkeepFailureReason,
- UpkeepFailureReason.NONE,
- )
- assert.isTrue(checkUpkeepResult.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used
- expect(checkUpkeepResult.gasLimit).to.equal(performGas)
- assert.isTrue(checkUpkeepResult.fastGasWei.eq(gasWei))
- assert.isTrue(checkUpkeepResult.linkNative.eq(linkEth))
- })
-
- it('calls checkLog for log-trigger upkeeps', async () => {
- const log: Log = {
- index: 0,
- timestamp: 0,
- txHash: ethers.utils.randomBytes(32),
- blockNumber: 100,
- blockHash: ethers.utils.randomBytes(32),
- source: randomAddress(),
- topics: [ethers.utils.randomBytes(32), ethers.utils.randomBytes(32)],
- data: ethers.utils.randomBytes(1000),
- }
-
- await ltUpkeep.mock.checkLog.withArgs(log, '0x').returns(true, '0x1234')
-
- const checkData = encodeLog(log)
-
- const checkUpkeepResult = await registry
- .connect(zeroAddress)
- .callStatic['checkUpkeep(uint256,bytes)'](logUpkeepId, checkData)
-
- expect(checkUpkeepResult.upkeepNeeded).to.be.true
- expect(checkUpkeepResult.performData).to.equal('0x1234')
- })
-
- itMaybe(
- 'has a large enough gas overhead to cover upkeeps that use all their gas [ @skip-coverage ]',
- async () => {
- await mock.setCanCheck(true)
- await mock.setCheckGasToBurn(checkGasLimit)
- const gas = checkGasLimit.add(checkGasOverhead)
- const checkUpkeepResult = await registry
- .connect(zeroAddress)
- .callStatic['checkUpkeep(uint256)'](upkeepId, {
- gasLimit: gas,
- })
-
- assert.equal(checkUpkeepResult.upkeepNeeded, true)
- },
- )
- })
- })
-
- describe('#addFunds', () => {
- const amount = toWei('1')
-
- it('reverts if the registration does not exist', async () => {
- await evmRevertCustomError(
- registry.connect(keeper1).addFunds(upkeepId.add(1), amount),
- registry,
- 'UpkeepCancelled',
- )
- })
-
- it('adds to the balance of the registration', async () => {
- await registry.connect(admin).addFunds(upkeepId, amount)
- const registration = await registry.getUpkeep(upkeepId)
- assert.isTrue(amount.eq(registration.balance))
- })
-
- it('lets anyone add funds to an upkeep not just admin', async () => {
- await linkToken.connect(owner).transfer(await payee1.getAddress(), amount)
- await linkToken.connect(payee1).approve(registry.address, amount)
-
- await registry.connect(payee1).addFunds(upkeepId, amount)
- const registration = await registry.getUpkeep(upkeepId)
- assert.isTrue(amount.eq(registration.balance))
- })
-
- it('emits a log', async () => {
- const tx = await registry.connect(admin).addFunds(upkeepId, amount)
- await expect(tx)
- .to.emit(registry, 'FundsAdded')
- .withArgs(upkeepId, await admin.getAddress(), amount)
- })
-
- it('reverts if the upkeep is canceled', async () => {
- await registry.connect(admin).cancelUpkeep(upkeepId)
- await evmRevertCustomError(
- registry.connect(keeper1).addFunds(upkeepId, amount),
- registry,
- 'UpkeepCancelled',
- )
- })
- })
-
- 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
-
- beforeEach(async () => {
- const arbL1PriceinWei = BigNumber.from(1000) // Same as MockArbGasInfo.sol
- maxl1CostWeiArbWithoutMultiplier = arbL1PriceinWei.mul(
- maxPerformDataSize
- .add(registryTransmitCalldataFixedBytesOverhead)
- .add(
- registryTransmitCalldataPerSignerBytesOverhead.mul(
- BigNumber.from(f + 1),
- ),
- ),
- )
- maxl1CostWeiOptWithoutMultiplier = BigNumber.from(2000000) // Same as MockOVMGasPriceOracle.sol
- })
-
- itMaybe('calculates the max fee appropriately', async () => {
- await verifyMaxPayment(registry, chainModuleBase)
- })
-
- itMaybe('calculates the max fee appropriately for Arbitrum', async () => {
- await verifyMaxPayment(
- arbRegistry,
- arbitrumModule,
- maxl1CostWeiArbWithoutMultiplier,
- )
- })
-
- itMaybe('calculates the max fee appropriately for Optimism', async () => {
- await verifyMaxPayment(
- opRegistry,
- optimismModule,
- maxl1CostWeiOptWithoutMultiplier,
- )
- })
-
- it('uses the fallback gas price if the feed has issues', async () => {
- const chainModuleOverheads = await chainModuleBase.getGasOverhead()
- const expectedFallbackMaxPayment = linkForGas(
- performGas,
- registryConditionalOverhead
- .add(registryPerSignerGasOverhead.mul(f + 1))
- .add(
- maxPerformDataSize
- .add(registryTransmitCalldataFixedBytesOverhead)
- .add(
- registryTransmitCalldataPerSignerBytesOverhead.mul(
- BigNumber.from(f + 1),
- ),
- )
- .mul(
- registryPerPerformByteGasOverhead.add(
- chainModuleOverheads.chainModulePerByteOverhead,
- ),
- ),
- )
- .add(chainModuleOverheads.chainModuleFixedOverhead),
- gasCeilingMultiplier.mul('2'), // fallbackGasPrice is 2x gas price
- paymentPremiumPPB,
- flatFeeMicroLink,
- ).total
-
- // Stale feed
- let roundId = 99
- const answer = 100
- let updatedAt = 946684800 // New Years 2000 🥳
- let startedAt = 946684799
- await gasPriceFeed
- .connect(owner)
- .updateRoundData(roundId, answer, updatedAt, startedAt)
-
- assert.equal(
- expectedFallbackMaxPayment.toString(),
- (
- await registry.getMaxPaymentForGas(Trigger.CONDITION, performGas)
- ).toString(),
- )
-
- // Negative feed price
- roundId = 100
- updatedAt = now()
- startedAt = 946684799
- await gasPriceFeed
- .connect(owner)
- .updateRoundData(roundId, -100, updatedAt, startedAt)
-
- assert.equal(
- expectedFallbackMaxPayment.toString(),
- (
- await registry.getMaxPaymentForGas(Trigger.CONDITION, performGas)
- ).toString(),
- )
-
- // Zero feed price
- roundId = 101
- updatedAt = now()
- startedAt = 946684799
- await gasPriceFeed
- .connect(owner)
- .updateRoundData(roundId, 0, updatedAt, startedAt)
-
- assert.equal(
- expectedFallbackMaxPayment.toString(),
- (
- await registry.getMaxPaymentForGas(Trigger.CONDITION, performGas)
- ).toString(),
- )
- })
-
- it('uses the fallback link price if the feed has issues', async () => {
- const chainModuleOverheads = await chainModuleBase.getGasOverhead()
- const expectedFallbackMaxPayment = linkForGas(
- performGas,
- registryConditionalOverhead
- .add(registryPerSignerGasOverhead.mul(f + 1))
- .add(
- maxPerformDataSize
- .add(registryTransmitCalldataFixedBytesOverhead)
- .add(
- registryTransmitCalldataPerSignerBytesOverhead.mul(
- BigNumber.from(f + 1),
- ),
- )
- .mul(
- registryPerPerformByteGasOverhead.add(
- chainModuleOverheads.chainModulePerByteOverhead,
- ),
- ),
- )
- .add(chainModuleOverheads.chainModuleFixedOverhead),
- gasCeilingMultiplier.mul('2'), // fallbackLinkPrice is 1/2 link price, so multiply by 2
- paymentPremiumPPB,
- flatFeeMicroLink,
- ).total
-
- // Stale feed
- let roundId = 99
- const answer = 100
- let updatedAt = 946684800 // New Years 2000 🥳
- let startedAt = 946684799
- await linkEthFeed
- .connect(owner)
- .updateRoundData(roundId, answer, updatedAt, startedAt)
-
- assert.equal(
- expectedFallbackMaxPayment.toString(),
- (
- await registry.getMaxPaymentForGas(Trigger.CONDITION, performGas)
- ).toString(),
- )
-
- // Negative feed price
- roundId = 100
- updatedAt = now()
- startedAt = 946684799
- await linkEthFeed
- .connect(owner)
- .updateRoundData(roundId, -100, updatedAt, startedAt)
-
- assert.equal(
- expectedFallbackMaxPayment.toString(),
- (
- await registry.getMaxPaymentForGas(Trigger.CONDITION, performGas)
- ).toString(),
- )
-
- // Zero feed price
- roundId = 101
- updatedAt = now()
- startedAt = 946684799
- await linkEthFeed
- .connect(owner)
- .updateRoundData(roundId, 0, updatedAt, startedAt)
-
- assert.equal(
- expectedFallbackMaxPayment.toString(),
- (
- await registry.getMaxPaymentForGas(Trigger.CONDITION, performGas)
- ).toString(),
- )
- })
- })
-
- describe('#typeAndVersion', () => {
- it('uses the correct type and version', async () => {
- const typeAndVersion = await registry.typeAndVersion()
- assert.equal(typeAndVersion, 'AutomationRegistry 2.2.0')
- })
- })
-
- describe('#onTokenTransfer', () => {
- const amount = toWei('1')
-
- it('reverts if not called by the LINK token', async () => {
- const data = ethers.utils.defaultAbiCoder.encode(['uint256'], [upkeepId])
-
- await evmRevertCustomError(
- registry
- .connect(keeper1)
- .onTokenTransfer(await keeper1.getAddress(), amount, data),
- registry,
- 'OnlyCallableByLINKToken',
- )
- })
-
- it('reverts if not called with more or less than 32 bytes', async () => {
- const longData = ethers.utils.defaultAbiCoder.encode(
- ['uint256', 'uint256'],
- ['33', '34'],
- )
- const shortData = '0x12345678'
-
- await evmRevert(
- linkToken
- .connect(owner)
- .transferAndCall(registry.address, amount, longData),
- )
- await evmRevert(
- linkToken
- .connect(owner)
- .transferAndCall(registry.address, amount, shortData),
- )
- })
-
- it('reverts if the upkeep is canceled', async () => {
- await registry.connect(admin).cancelUpkeep(upkeepId)
- await evmRevertCustomError(
- registry.connect(keeper1).addFunds(upkeepId, amount),
- registry,
- 'UpkeepCancelled',
- )
- })
-
- it('updates the funds of the job id passed', async () => {
- const data = ethers.utils.defaultAbiCoder.encode(['uint256'], [upkeepId])
-
- const before = (await registry.getUpkeep(upkeepId)).balance
- await linkToken
- .connect(owner)
- .transferAndCall(registry.address, amount, data)
- const after = (await registry.getUpkeep(upkeepId)).balance
-
- assert.isTrue(before.add(amount).eq(after))
- })
- })
-
- describeMaybe('#setConfig - onchain', async () => {
- const payment = BigNumber.from(1)
- const flatFee = BigNumber.from(2)
- const maxGas = BigNumber.from(6)
- const staleness = BigNumber.from(4)
- const ceiling = BigNumber.from(5)
- const newMinUpkeepSpend = BigNumber.from(9)
- const newMaxCheckDataSize = BigNumber.from(10000)
- const newMaxPerformDataSize = BigNumber.from(10000)
- const newMaxRevertDataSize = BigNumber.from(10000)
- const newMaxPerformGas = BigNumber.from(10000000)
- const fbGasEth = BigNumber.from(7)
- const fbLinkEth = BigNumber.from(8)
- const newTranscoder = randomAddress()
- const newRegistrars = [randomAddress(), randomAddress()]
- const upkeepManager = randomAddress()
-
- const newConfig = {
- paymentPremiumPPB: payment,
- flatFeeMicroLink: flatFee,
- checkGasLimit: maxGas,
- stalenessSeconds: staleness,
- gasCeilingMultiplier: ceiling,
- minUpkeepSpend: newMinUpkeepSpend,
- maxCheckDataSize: newMaxCheckDataSize,
- maxPerformDataSize: newMaxPerformDataSize,
- maxRevertDataSize: newMaxRevertDataSize,
- maxPerformGas: newMaxPerformGas,
- fallbackGasPrice: fbGasEth,
- fallbackLinkPrice: fbLinkEth,
- transcoder: newTranscoder,
- registrars: newRegistrars,
- upkeepPrivilegeManager: upkeepManager,
- chainModule: chainModuleBase.address,
- reorgProtectionEnabled: true,
- }
-
- it('reverts when called by anyone but the proposed owner', async () => {
- await evmRevert(
- registry
- .connect(payee1)
- .setConfigTypeSafe(
- signerAddresses,
- keeperAddresses,
- f,
- newConfig,
- offchainVersion,
- offchainBytes,
- ),
- 'Only callable by owner',
- )
- })
-
- it('reverts if signers or transmitters are the zero address', async () => {
- await evmRevertCustomError(
- registry
- .connect(owner)
- .setConfigTypeSafe(
- [randomAddress(), randomAddress(), randomAddress(), zeroAddress],
- [
- randomAddress(),
- randomAddress(),
- randomAddress(),
- randomAddress(),
- ],
- f,
- newConfig,
- offchainVersion,
- offchainBytes,
- ),
- registry,
- 'InvalidSigner',
- )
-
- await evmRevertCustomError(
- registry
- .connect(owner)
- .setConfigTypeSafe(
- [
- randomAddress(),
- randomAddress(),
- randomAddress(),
- randomAddress(),
- ],
- [randomAddress(), randomAddress(), randomAddress(), zeroAddress],
- f,
- newConfig,
- offchainVersion,
- offchainBytes,
- ),
- registry,
- 'InvalidTransmitter',
- )
- })
-
- it('updates the onchainConfig and configDigest', async () => {
- const old = await registry.getState()
- const oldConfig = old.config
- const oldState = old.state
- assert.isTrue(paymentPremiumPPB.eq(oldConfig.paymentPremiumPPB))
- assert.isTrue(flatFeeMicroLink.eq(oldConfig.flatFeeMicroLink))
- assert.isTrue(stalenessSeconds.eq(oldConfig.stalenessSeconds))
- assert.isTrue(gasCeilingMultiplier.eq(oldConfig.gasCeilingMultiplier))
-
- await registry
- .connect(owner)
- .setConfigTypeSafe(
- signerAddresses,
- keeperAddresses,
- f,
- newConfig,
- offchainVersion,
- offchainBytes,
- )
-
- const updated = await registry.getState()
- const updatedConfig = updated.config
- const updatedState = updated.state
- assert.equal(updatedConfig.paymentPremiumPPB, payment.toNumber())
- assert.equal(updatedConfig.flatFeeMicroLink, flatFee.toNumber())
- assert.equal(updatedConfig.stalenessSeconds, staleness.toNumber())
- assert.equal(updatedConfig.gasCeilingMultiplier, ceiling.toNumber())
- assert.equal(
- updatedConfig.minUpkeepSpend.toString(),
- newMinUpkeepSpend.toString(),
- )
- assert.equal(
- updatedConfig.maxCheckDataSize,
- newMaxCheckDataSize.toNumber(),
- )
- assert.equal(
- updatedConfig.maxPerformDataSize,
- newMaxPerformDataSize.toNumber(),
- )
- assert.equal(
- updatedConfig.maxRevertDataSize,
- newMaxRevertDataSize.toNumber(),
- )
- assert.equal(updatedConfig.maxPerformGas, newMaxPerformGas.toNumber())
- assert.equal(updatedConfig.checkGasLimit, maxGas.toNumber())
- assert.equal(
- updatedConfig.fallbackGasPrice.toNumber(),
- fbGasEth.toNumber(),
- )
- assert.equal(
- updatedConfig.fallbackLinkPrice.toNumber(),
- fbLinkEth.toNumber(),
- )
- assert.equal(updatedState.latestEpoch, 0)
-
- assert(oldState.configCount + 1 == updatedState.configCount)
- assert(
- oldState.latestConfigBlockNumber !=
- updatedState.latestConfigBlockNumber,
- )
- assert(oldState.latestConfigDigest != updatedState.latestConfigDigest)
-
- assert.equal(updatedConfig.transcoder, newTranscoder)
- assert.deepEqual(updatedConfig.registrars, newRegistrars)
- assert.equal(updatedConfig.upkeepPrivilegeManager, upkeepManager)
- })
-
- it('maintains paused state when config is changed', async () => {
- await registry.pause()
- const old = await registry.getState()
- assert.isTrue(old.state.paused)
-
- await registry
- .connect(owner)
- .setConfigTypeSafe(
- signerAddresses,
- keeperAddresses,
- f,
- newConfig,
- offchainVersion,
- offchainBytes,
- )
-
- const updated = await registry.getState()
- assert.isTrue(updated.state.paused)
- })
-
- it('emits an event', async () => {
- const tx = await registry
- .connect(owner)
- .setConfigTypeSafe(
- signerAddresses,
- keeperAddresses,
- f,
- newConfig,
- offchainVersion,
- offchainBytes,
- )
- await expect(tx).to.emit(registry, 'ConfigSet')
- })
- })
-
- describe('#setConfig - offchain', () => {
- let newKeepers: string[]
-
- beforeEach(async () => {
- newKeepers = [
- await personas.Eddy.getAddress(),
- await personas.Nick.getAddress(),
- await personas.Neil.getAddress(),
- await personas.Carol.getAddress(),
- ]
- })
-
- it('reverts when called by anyone but the owner', async () => {
- await evmRevert(
- registry
- .connect(payee1)
- .setConfigTypeSafe(
- newKeepers,
- newKeepers,
- f,
- config,
- offchainVersion,
- offchainBytes,
- ),
- 'Only callable by owner',
- )
- })
-
- it('reverts if too many keeperAddresses set', async () => {
- for (let i = 0; i < 40; i++) {
- newKeepers.push(randomAddress())
- }
- await evmRevertCustomError(
- registry
- .connect(owner)
- .setConfigTypeSafe(
- newKeepers,
- newKeepers,
- f,
- config,
- offchainVersion,
- offchainBytes,
- ),
- registry,
- 'TooManyOracles',
- )
- })
-
- it('reverts if f=0', async () => {
- await evmRevertCustomError(
- registry
- .connect(owner)
- .setConfigTypeSafe(
- newKeepers,
- newKeepers,
- 0,
- config,
- offchainVersion,
- offchainBytes,
- ),
- registry,
- 'IncorrectNumberOfFaultyOracles',
- )
- })
-
- it('reverts if signers != transmitters length', async () => {
- const signers = [randomAddress()]
- await evmRevertCustomError(
- registry
- .connect(owner)
- .setConfigTypeSafe(
- signers,
- newKeepers,
- f,
- config,
- offchainVersion,
- offchainBytes,
- ),
- registry,
- 'IncorrectNumberOfSigners',
- )
- })
-
- it('reverts if signers <= 3f', async () => {
- newKeepers.pop()
- await evmRevertCustomError(
- registry
- .connect(owner)
- .setConfigTypeSafe(
- newKeepers,
- newKeepers,
- f,
- config,
- offchainVersion,
- offchainBytes,
- ),
- 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,
- ),
- 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,
- ),
- 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('#setPeerRegistryMigrationPermission() / #getPeerRegistryMigrationPermission()', () => {
- const peer = randomAddress()
- it('allows the owner to set the peer registries', async () => {
- let permission = await registry.getPeerRegistryMigrationPermission(peer)
- expect(permission).to.equal(0)
- await registry.setPeerRegistryMigrationPermission(peer, 1)
- permission = await registry.getPeerRegistryMigrationPermission(peer)
- expect(permission).to.equal(1)
- await registry.setPeerRegistryMigrationPermission(peer, 2)
- permission = await registry.getPeerRegistryMigrationPermission(peer)
- expect(permission).to.equal(2)
- await registry.setPeerRegistryMigrationPermission(peer, 0)
- permission = await registry.getPeerRegistryMigrationPermission(peer)
- expect(permission).to.equal(0)
- })
- it('reverts if passed an unsupported permission', async () => {
- await expect(
- registry.connect(admin).setPeerRegistryMigrationPermission(peer, 10),
- ).to.be.reverted
- })
- it('reverts if not called by the owner', async () => {
- await expect(
- registry.connect(admin).setPeerRegistryMigrationPermission(peer, 1),
- ).to.be.revertedWith('Only callable by owner')
- })
- })
-
- describe('#registerUpkeep', () => {
- it('reverts when registry is paused', async () => {
- await registry.connect(owner).pause()
- await evmRevertCustomError(
- registry
- .connect(owner)
- [
- 'registerUpkeep(address,uint32,address,bytes,bytes)'
- ](mock.address, performGas, await admin.getAddress(), emptyBytes, '0x'),
- registry,
- 'RegistryPaused',
- )
- })
-
- it('reverts if the target is not a contract', async () => {
- await evmRevertCustomError(
- registry
- .connect(owner)
- [
- 'registerUpkeep(address,uint32,address,bytes,bytes)'
- ](zeroAddress, performGas, await admin.getAddress(), emptyBytes, '0x'),
- registry,
- 'NotAContract',
- )
- })
-
- it('reverts if called by a non-owner', async () => {
- await evmRevertCustomError(
- registry
- .connect(keeper1)
- [
- 'registerUpkeep(address,uint32,address,bytes,bytes)'
- ](mock.address, performGas, await admin.getAddress(), emptyBytes, '0x'),
- registry,
- 'OnlyCallableByOwnerOrRegistrar',
- )
- })
-
- it('reverts if execute gas is too low', async () => {
- await evmRevertCustomError(
- registry
- .connect(owner)
- [
- 'registerUpkeep(address,uint32,address,bytes,bytes)'
- ](mock.address, 2299, await admin.getAddress(), emptyBytes, '0x'),
- registry,
- 'GasLimitOutsideRange',
- )
- })
-
- it('reverts if execute gas is too high', async () => {
- await evmRevertCustomError(
- registry
- .connect(owner)
- [
- 'registerUpkeep(address,uint32,address,bytes,bytes)'
- ](mock.address, 5000001, await admin.getAddress(), emptyBytes, '0x'),
- registry,
- 'GasLimitOutsideRange',
- )
- })
-
- it('reverts if checkData is too long', async () => {
- let longBytes = '0x'
- for (let i = 0; i < 10000; i++) {
- longBytes += '1'
- }
- await evmRevertCustomError(
- registry
- .connect(owner)
- [
- 'registerUpkeep(address,uint32,address,bytes,bytes)'
- ](mock.address, performGas, await admin.getAddress(), longBytes, '0x'),
- registry,
- 'CheckDataExceedsLimit',
- )
- })
-
- it('creates a record of the registration', async () => {
- const performGases = [100000, 500000]
- const checkDatas = [emptyBytes, '0x12']
-
- for (let jdx = 0; jdx < performGases.length; jdx++) {
- const performGas = performGases[jdx]
- for (let kdx = 0; kdx < checkDatas.length; kdx++) {
- const checkData = checkDatas[kdx]
- const tx = await registry
- .connect(owner)
- [
- 'registerUpkeep(address,uint32,address,bytes,bytes)'
- ](mock.address, performGas, await admin.getAddress(), checkData, '0x')
-
- //confirm the upkeep details and verify emitted events
- const testUpkeepId = await getUpkeepID(tx)
- await expect(tx)
- .to.emit(registry, 'UpkeepRegistered')
- .withArgs(testUpkeepId, performGas, await admin.getAddress())
-
- await expect(tx)
- .to.emit(registry, 'UpkeepCheckDataSet')
- .withArgs(testUpkeepId, checkData)
- await expect(tx)
- .to.emit(registry, 'UpkeepTriggerConfigSet')
- .withArgs(testUpkeepId, '0x')
-
- const registration = await registry.getUpkeep(testUpkeepId)
-
- assert.equal(mock.address, registration.target)
- assert.notEqual(
- ethers.constants.AddressZero,
- await registry.getForwarder(testUpkeepId),
- )
- assert.equal(
- performGas.toString(),
- registration.performGas.toString(),
- )
- assert.equal(await admin.getAddress(), registration.admin)
- assert.equal(0, registration.balance.toNumber())
- assert.equal(0, registration.amountSpent.toNumber())
- assert.equal(0, registration.lastPerformedBlockNumber)
- assert.equal(checkData, registration.checkData)
- assert.equal(registration.paused, false)
- assert.equal(registration.offchainConfig, '0x')
- assert(registration.maxValidBlocknumber.eq('0xffffffff'))
- }
- }
- })
- })
-
- describe('#pauseUpkeep', () => {
- it('reverts if the registration does not exist', async () => {
- await 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)
-
- const registration = await registry.getUpkeep(upkeepId)
- assert.equal(registration.paused, true)
- })
- })
-
- describe('#unpauseUpkeep', () => {
- it('reverts if the registration does not exist', async () => {
- await evmRevertCustomError(
- registry.connect(keeper1).unpauseUpkeep(upkeepId.add(1)),
- registry,
- 'OnlyCallableByAdmin',
- )
- })
-
- it('reverts if the upkeep is already canceled', async () => {
- await registry.connect(owner).cancelUpkeep(upkeepId)
-
- await evmRevertCustomError(
- registry.connect(admin).unpauseUpkeep(upkeepId),
- registry,
- 'UpkeepCancelled',
- )
- })
-
- it('marks the contract as paused', async () => {
- assert.isFalse((await registry.getState()).state.paused)
-
- await registry.connect(owner).pause()
-
- assert.isTrue((await registry.getState()).state.paused)
- })
-
- it('reverts if the upkeep is not paused', async () => {
- await evmRevertCustomError(
- registry.connect(admin).unpauseUpkeep(upkeepId),
- registry,
- 'OnlyPausedUpkeep',
- )
- })
-
- it('reverts if the caller is not the upkeep admin', async () => {
- await registry.connect(admin).pauseUpkeep(upkeepId)
-
- const registration = await registry.getUpkeep(upkeepId)
-
- assert.equal(registration.paused, true)
-
- await evmRevertCustomError(
- registry.connect(keeper1).unpauseUpkeep(upkeepId),
- registry,
- 'OnlyCallableByAdmin',
- )
- })
-
- it('unpauses the upkeep and emits an event', async () => {
- const originalCount = (await registry.getActiveUpkeepIDs(0, 0)).length
-
- await registry.connect(admin).pauseUpkeep(upkeepId)
-
- const tx = await registry.connect(admin).unpauseUpkeep(upkeepId)
-
- await expect(tx).to.emit(registry, 'UpkeepUnpaused').withArgs(upkeepId)
-
- const registration = await registry.getUpkeep(upkeepId)
- assert.equal(registration.paused, false)
-
- const upkeepIds = await registry.getActiveUpkeepIDs(0, 0)
- assert.equal(upkeepIds.length, originalCount)
- })
- })
-
- describe('#setUpkeepCheckData', () => {
- it('reverts if the registration does not exist', async () => {
- await 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(0, receipt.logs.length)
- })
- })
-
- describe('#acceptUpkeepAdmin', () => {
- beforeEach(async () => {
- // Start admin transfer to payee1
- await registry
- .connect(admin)
- .transferUpkeepAdmin(upkeepId, await payee1.getAddress())
- })
-
- it('reverts when not called by the proposed upkeep admin', async () => {
- await 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 owner', async () => {
- await evmRevert(
- registry.connect(keeper1).withdrawOwnerFunds(),
- 'Only callable by owner',
- )
- })
-
- itMaybe('withdraws the collected fees to owner', async () => {
- await registry.connect(admin).addFunds(upkeepId, toWei('100'))
- // Very high min spend, whole balance as cancellation fees
- const minUpkeepSpend = toWei('1000')
- await registry.connect(owner).setConfigTypeSafe(
- signerAddresses,
- keeperAddresses,
- f,
- {
- paymentPremiumPPB,
- flatFeeMicroLink,
- checkGasLimit,
- stalenessSeconds,
- gasCeilingMultiplier,
- minUpkeepSpend,
- maxCheckDataSize,
- maxPerformDataSize,
- maxRevertDataSize,
- maxPerformGas,
- fallbackGasPrice,
- fallbackLinkPrice,
- transcoder: transcoder.address,
- registrars: [],
- upkeepPrivilegeManager: upkeepManager,
- chainModule: chainModuleBase.address,
- reorgProtectionEnabled: true,
- },
- offchainVersion,
- offchainBytes,
- )
- const upkeepBalance = (await registry.getUpkeep(upkeepId)).balance
- const ownerBefore = await linkToken.balanceOf(await owner.getAddress())
-
- await registry.connect(owner).cancelUpkeep(upkeepId)
-
- // Transfered to owner balance on registry
- let ownerRegistryBalance = (await registry.getState()).state
- .ownerLinkBalance
- assert.isTrue(ownerRegistryBalance.eq(upkeepBalance))
-
- // Now withdraw
- await registry.connect(owner).withdrawOwnerFunds()
-
- ownerRegistryBalance = (await registry.getState()).state.ownerLinkBalance
- const ownerAfter = await linkToken.balanceOf(await owner.getAddress())
-
- // Owner registry balance should be changed to 0
- assert.isTrue(ownerRegistryBalance.eq(BigNumber.from('0')))
-
- // Owner should be credited with the balance
- assert.isTrue(ownerBefore.add(upkeepBalance).eq(ownerAfter))
- })
- })
-
- describe('#transferPayeeship', () => {
- it('reverts when called by anyone but the current payee', async () => {
- await 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(0, receipt.logs.length)
- })
- })
-
- describe('#acceptPayeeship', () => {
- beforeEach(async () => {
- await registry
- .connect(payee1)
- .transferPayeeship(
- await keeper1.getAddress(),
- await payee2.getAddress(),
- )
- })
-
- it('reverts when called by anyone but the proposed payee', async () => {
- await 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(address,uint32,address,bytes,bytes)'
- ](mock.address, performGas, await admin.getAddress(), emptyBytes, '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('#migrateUpkeeps() / #receiveUpkeeps()', async () => {
- context('when permissions are set', () => {
- beforeEach(async () => {
- await linkToken.connect(owner).approve(registry.address, toWei('100'))
- await registry.connect(owner).addFunds(upkeepId, toWei('100'))
- await registry.setPeerRegistryMigrationPermission(mgRegistry.address, 1)
- await mgRegistry.setPeerRegistryMigrationPermission(registry.address, 2)
- })
-
- it('migrates an upkeep', async () => {
- const offchainBytes = '0x987654abcd'
- await registry
- .connect(admin)
- .setUpkeepOffchainConfig(upkeepId, offchainBytes)
- const reg1Upkeep = await registry.getUpkeep(upkeepId)
- const forwarderAddress = await registry.getForwarder(upkeepId)
- expect(reg1Upkeep.balance).to.equal(toWei('100'))
- expect(reg1Upkeep.checkData).to.equal(randomBytes)
- expect(forwarderAddress).to.not.equal(ethers.constants.AddressZero)
- expect(reg1Upkeep.offchainConfig).to.equal(offchainBytes)
- expect((await registry.getState()).state.numUpkeeps).to.equal(
- numUpkeeps,
- )
- const forwarder = IAutomationForwarderFactory.connect(
- forwarderAddress,
- owner,
- )
- expect(await forwarder.getRegistry()).to.equal(registry.address)
- // Set an upkeep admin transfer in progress too
- await registry
- .connect(admin)
- .transferUpkeepAdmin(upkeepId, await payee1.getAddress())
-
- // migrate
- await registry
- .connect(admin)
- .migrateUpkeeps([upkeepId], mgRegistry.address)
- expect((await registry.getState()).state.numUpkeeps).to.equal(
- numUpkeeps - 1,
- )
- expect((await mgRegistry.getState()).state.numUpkeeps).to.equal(1)
- expect((await registry.getUpkeep(upkeepId)).balance).to.equal(0)
- expect((await registry.getUpkeep(upkeepId)).checkData).to.equal('0x')
- expect((await mgRegistry.getUpkeep(upkeepId)).balance).to.equal(
- toWei('100'),
- )
- expect(
- (await mgRegistry.getState()).state.expectedLinkBalance,
- ).to.equal(toWei('100'))
- expect((await mgRegistry.getUpkeep(upkeepId)).checkData).to.equal(
- randomBytes,
- )
- expect((await mgRegistry.getUpkeep(upkeepId)).offchainConfig).to.equal(
- offchainBytes,
- )
- expect(await mgRegistry.getForwarder(upkeepId)).to.equal(
- forwarderAddress,
- )
- // test that registry is updated on forwarder
- expect(await forwarder.getRegistry()).to.equal(mgRegistry.address)
- // migration will delete the upkeep and nullify admin transfer
- await expect(
- registry.connect(payee1).acceptUpkeepAdmin(upkeepId),
- ).to.be.revertedWithCustomError(registry, 'UpkeepCancelled')
- await expect(
- mgRegistry.connect(payee1).acceptUpkeepAdmin(upkeepId),
- ).to.be.revertedWithCustomError(
- mgRegistry,
- 'OnlyCallableByProposedAdmin',
- )
- })
-
- it('migrates a paused upkeep', async () => {
- expect((await registry.getUpkeep(upkeepId)).balance).to.equal(
- toWei('100'),
- )
- expect((await registry.getUpkeep(upkeepId)).checkData).to.equal(
- randomBytes,
- )
- expect((await registry.getState()).state.numUpkeeps).to.equal(
- numUpkeeps,
- )
- await registry.connect(admin).pauseUpkeep(upkeepId)
- // verify the upkeep is paused
- expect((await registry.getUpkeep(upkeepId)).paused).to.equal(true)
- // migrate
- await registry
- .connect(admin)
- .migrateUpkeeps([upkeepId], mgRegistry.address)
- expect((await registry.getState()).state.numUpkeeps).to.equal(
- numUpkeeps - 1,
- )
- expect((await mgRegistry.getState()).state.numUpkeeps).to.equal(1)
- expect((await registry.getUpkeep(upkeepId)).balance).to.equal(0)
- expect((await mgRegistry.getUpkeep(upkeepId)).balance).to.equal(
- toWei('100'),
- )
- expect((await registry.getUpkeep(upkeepId)).checkData).to.equal('0x')
- expect((await mgRegistry.getUpkeep(upkeepId)).checkData).to.equal(
- randomBytes,
- )
- expect(
- (await mgRegistry.getState()).state.expectedLinkBalance,
- ).to.equal(toWei('100'))
- // verify the upkeep is still paused after migration
- expect((await mgRegistry.getUpkeep(upkeepId)).paused).to.equal(true)
- })
-
- it('emits an event on both contracts', async () => {
- expect((await registry.getUpkeep(upkeepId)).balance).to.equal(
- toWei('100'),
- )
- expect((await registry.getUpkeep(upkeepId)).checkData).to.equal(
- randomBytes,
- )
- expect((await registry.getState()).state.numUpkeeps).to.equal(
- numUpkeeps,
- )
- const tx = registry
- .connect(admin)
- .migrateUpkeeps([upkeepId], mgRegistry.address)
- await expect(tx)
- .to.emit(registry, 'UpkeepMigrated')
- .withArgs(upkeepId, toWei('100'), mgRegistry.address)
- await expect(tx)
- .to.emit(mgRegistry, 'UpkeepReceived')
- .withArgs(upkeepId, toWei('100'), registry.address)
- })
-
- it('is only migratable by the admin', async () => {
- await expect(
- registry
- .connect(owner)
- .migrateUpkeeps([upkeepId], mgRegistry.address),
- ).to.be.revertedWithCustomError(registry, 'OnlyCallableByAdmin')
- await registry
- .connect(admin)
- .migrateUpkeeps([upkeepId], mgRegistry.address)
- })
- })
-
- context('when permissions are not set', () => {
- it('reverts', async () => {
- // no permissions
- await registry.setPeerRegistryMigrationPermission(mgRegistry.address, 0)
- await mgRegistry.setPeerRegistryMigrationPermission(registry.address, 0)
- await expect(registry.migrateUpkeeps([upkeepId], mgRegistry.address)).to
- .be.reverted
- // only outgoing permissions
- await registry.setPeerRegistryMigrationPermission(mgRegistry.address, 1)
- await mgRegistry.setPeerRegistryMigrationPermission(registry.address, 0)
- await expect(registry.migrateUpkeeps([upkeepId], mgRegistry.address)).to
- .be.reverted
- // only incoming permissions
- await registry.setPeerRegistryMigrationPermission(mgRegistry.address, 0)
- await mgRegistry.setPeerRegistryMigrationPermission(registry.address, 2)
- await expect(registry.migrateUpkeeps([upkeepId], mgRegistry.address)).to
- .be.reverted
- // permissions opposite direction
- await registry.setPeerRegistryMigrationPermission(mgRegistry.address, 2)
- await mgRegistry.setPeerRegistryMigrationPermission(registry.address, 1)
- await expect(registry.migrateUpkeeps([upkeepId], mgRegistry.address)).to
- .be.reverted
- })
- })
- })
-
- describe('#setPayees', () => {
- const IGNORE_ADDRESS = '0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF'
-
- it('reverts when not called by the owner', async () => {
- await evmRevert(
- registry.connect(keeper1).setPayees(payees),
- 'Only callable by owner',
- )
- })
-
- it('reverts with different numbers of payees than transmitters', async () => {
- await 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(true, ignored.active)
- })
-
- 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
- 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 gives to owner', async () => {
- const minUpkeepSpend = toWei('10')
-
- await registry.connect(owner).setConfigTypeSafe(
- signerAddresses,
- keeperAddresses,
- f,
- {
- paymentPremiumPPB,
- flatFeeMicroLink,
- checkGasLimit,
- stalenessSeconds,
- gasCeilingMultiplier,
- minUpkeepSpend,
- maxCheckDataSize,
- maxPerformDataSize,
- maxRevertDataSize,
- maxPerformGas,
- fallbackGasPrice,
- fallbackLinkPrice,
- transcoder: transcoder.address,
- registrars: [],
- upkeepPrivilegeManager: upkeepManager,
- chainModule: chainModuleBase.address,
- reorgProtectionEnabled: true,
- },
- offchainVersion,
- offchainBytes,
- )
-
- const payee1Before = await linkToken.balanceOf(
- await payee1.getAddress(),
- )
- const upkeepBefore = (await registry.getUpkeep(upkeepId)).balance
- const ownerBefore = (await registry.getState()).state.ownerLinkBalance
-
- const amountSpent = toWei('100').sub(upkeepBefore)
- const cancellationFee = minUpkeepSpend.sub(amountSpent)
-
- await registry.connect(admin).cancelUpkeep(upkeepId)
-
- const payee1After = await linkToken.balanceOf(
- await payee1.getAddress(),
- )
- const upkeepAfter = (await registry.getUpkeep(upkeepId)).balance
- const ownerAfter = (await registry.getState()).state.ownerLinkBalance
-
- // post upkeep balance should be previous balance minus cancellation fee
- assert.isTrue(upkeepBefore.sub(cancellationFee).eq(upkeepAfter))
- // payee balance should not change
- assert.isTrue(payee1Before.eq(payee1After))
- // owner should receive the cancellation fee
- assert.isTrue(ownerAfter.sub(ownerBefore).eq(cancellationFee))
- })
-
- it('deducts up to balance as cancellation fee', async () => {
- // Very high min spend, should deduct whole balance as cancellation fees
- const minUpkeepSpend = toWei('1000')
- await registry.connect(owner).setConfigTypeSafe(
- signerAddresses,
- keeperAddresses,
- f,
- {
- paymentPremiumPPB,
- flatFeeMicroLink,
- checkGasLimit,
- stalenessSeconds,
- gasCeilingMultiplier,
- minUpkeepSpend,
- maxCheckDataSize,
- maxPerformDataSize,
- maxRevertDataSize,
- maxPerformGas,
- fallbackGasPrice,
- fallbackLinkPrice,
- transcoder: transcoder.address,
- registrars: [],
- upkeepPrivilegeManager: upkeepManager,
- chainModule: chainModuleBase.address,
- reorgProtectionEnabled: true,
- },
- offchainVersion,
- offchainBytes,
- )
- const payee1Before = await linkToken.balanceOf(
- await payee1.getAddress(),
- )
- const upkeepBefore = (await registry.getUpkeep(upkeepId)).balance
- const ownerBefore = (await registry.getState()).state.ownerLinkBalance
-
- await registry.connect(admin).cancelUpkeep(upkeepId)
- const payee1After = await linkToken.balanceOf(
- await payee1.getAddress(),
- )
- const ownerAfter = (await registry.getState()).state.ownerLinkBalance
- const upkeepAfter = (await registry.getUpkeep(upkeepId)).balance
-
- // all upkeep balance is deducted for cancellation fee
- assert.equal(0, upkeepAfter.toNumber())
- // payee balance should not change
- assert.isTrue(payee1After.eq(payee1Before))
- // all upkeep balance is transferred to the owner
- assert.isTrue(ownerAfter.sub(ownerBefore).eq(upkeepBefore))
- })
-
- it('does not deduct cancellation fee if more than minUpkeepSpend is spent', async () => {
- // Very low min spend, already spent in one perform upkeep
- const minUpkeepSpend = BigNumber.from(420)
- await registry.connect(owner).setConfigTypeSafe(
- signerAddresses,
- keeperAddresses,
- f,
- {
- paymentPremiumPPB,
- flatFeeMicroLink,
- checkGasLimit,
- stalenessSeconds,
- gasCeilingMultiplier,
- minUpkeepSpend,
- maxCheckDataSize,
- maxPerformDataSize,
- maxRevertDataSize,
- maxPerformGas,
- fallbackGasPrice,
- fallbackLinkPrice,
- transcoder: transcoder.address,
- registrars: [],
- upkeepPrivilegeManager: upkeepManager,
- chainModule: chainModuleBase.address,
- reorgProtectionEnabled: true,
- },
- offchainVersion,
- offchainBytes,
- )
- const payee1Before = await linkToken.balanceOf(
- await payee1.getAddress(),
- )
- const upkeepBefore = (await registry.getUpkeep(upkeepId)).balance
- const ownerBefore = (await registry.getState()).state.ownerLinkBalance
-
- await registry.connect(admin).cancelUpkeep(upkeepId)
- const payee1After = await linkToken.balanceOf(
- await payee1.getAddress(),
- )
- const ownerAfter = (await registry.getState()).state.ownerLinkBalance
- const upkeepAfter = (await registry.getUpkeep(upkeepId)).balance
-
- // upkeep does not pay cancellation fee after cancellation because minimum upkeep spent is met
- assert.isTrue(upkeepBefore.eq(upkeepAfter))
- // owner balance does not change
- assert.isTrue(ownerAfter.eq(ownerBefore))
- // payee balance does not change
- assert.isTrue(payee1Before.eq(payee1After))
- })
- })
- })
- })
-
- describe('#withdrawPayment', () => {
- beforeEach(async () => {
- await linkToken.connect(owner).approve(registry.address, toWei('100'))
- await registry.connect(owner).addFunds(upkeepId, toWei('100'))
- await getTransmitTx(registry, keeper1, [upkeepId])
- })
-
- it('reverts if called by anyone but the payee', async () => {
- await 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.getState()).state.ownerLinkBalance
-
- // Withdrawing for first time, last collected = 0
- assert.equal(keeperBefore.lastCollected.toString(), '0')
-
- //// Do the thing
- await registry
- .connect(payee1)
- .withdrawPayment(await keeper1.getAddress(), to)
-
- const keeperAfter = await registry.getTransmitterInfo(
- await keeper1.getAddress(),
- )
- const registrationAfter = (await registry.getUpkeep(upkeepId)).balance
- const toLinkAfter = await linkToken.balanceOf(to)
- const registryLinkAfter = await linkToken.balanceOf(registry.address)
- const registryPremiumAfter = (await registry.getState()).state
- .totalPremium
- const ownerAfter = (await registry.getState()).state.ownerLinkBalance
-
- // registry total premium should not change
- assert.isTrue(registryPremiumBefore.eq(registryPremiumAfter))
-
- // Last collected should be updated to premium-change
- assert.isTrue(
- keeperAfter.lastCollected.eq(
- registryPremiumBefore.sub(
- registryPremiumBefore.mod(keeperAddresses.length),
- ),
- ),
- )
-
- // owner balance should remain unchanged
- assert.isTrue(ownerAfter.eq(ownerBefore))
-
- assert.isTrue(keeperAfter.balance.eq(BigNumber.from(0)))
- assert.isTrue(registrationBefore.eq(registrationAfter))
- assert.isTrue(toLinkBefore.add(keeperBefore.balance).eq(toLinkAfter))
- assert.isTrue(
- registryLinkBefore.sub(keeperBefore.balance).eq(registryLinkAfter),
- )
- })
-
- it('emits a log announcing the withdrawal', async () => {
- const balance = (
- await registry.getTransmitterInfo(await keeper1.getAddress())
- ).balance
- const tx = await registry
- .connect(payee1)
- .withdrawPayment(
- await keeper1.getAddress(),
- await nonkeeper.getAddress(),
- )
- await expect(tx)
- .to.emit(registry, 'PaymentWithdrawn')
- .withArgs(
- await keeper1.getAddress(),
- balance,
- await nonkeeper.getAddress(),
- await payee1.getAddress(),
- )
- })
- })
-
- describe('#checkCallback', () => {
- it('returns false with appropriate failure reason when target callback reverts', async () => {
- await streamsLookupUpkeep.setShouldRevertCallback(true)
-
- const values: any[] = ['0x1234', '0xabcd']
- const res = await registry
- .connect(zeroAddress)
- .callStatic.checkCallback(streamsLookupUpkeepId, values, '0x')
-
- assert.isFalse(res.upkeepNeeded)
- assert.equal(res.performData, '0x')
- assert.equal(
- res.upkeepFailureReason,
- UpkeepFailureReason.CHECK_CALLBACK_REVERTED,
- )
- assert.isTrue(res.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used
- })
-
- it('returns false with appropriate failure reason when target callback returns big performData', async () => {
- let longBytes = '0x'
- for (let i = 0; i <= maxPerformDataSize.toNumber(); i++) {
- longBytes += '11'
- }
- const values: any[] = [longBytes, longBytes]
- const res = await registry
- .connect(zeroAddress)
- .callStatic.checkCallback(streamsLookupUpkeepId, values, '0x')
-
- assert.isFalse(res.upkeepNeeded)
- assert.equal(res.performData, '0x')
- assert.equal(
- res.upkeepFailureReason,
- UpkeepFailureReason.PERFORM_DATA_EXCEEDS_LIMIT,
- )
- assert.isTrue(res.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used
- })
-
- it('returns false with appropriate failure reason when target callback returns false', async () => {
- await streamsLookupUpkeep.setCallbackReturnBool(false)
- const values: any[] = ['0x1234', '0xabcd']
- const res = await registry
- .connect(zeroAddress)
- .callStatic.checkCallback(streamsLookupUpkeepId, values, '0x')
-
- assert.isFalse(res.upkeepNeeded)
- assert.equal(res.performData, '0x')
- assert.equal(
- res.upkeepFailureReason,
- UpkeepFailureReason.UPKEEP_NOT_NEEDED,
- )
- assert.isTrue(res.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used
- })
-
- it('succeeds with upkeep needed', async () => {
- const values: any[] = ['0x1234', '0xabcd']
-
- const res = await registry
- .connect(zeroAddress)
- .callStatic.checkCallback(streamsLookupUpkeepId, values, '0x')
- const expectedPerformData = ethers.utils.defaultAbiCoder.encode(
- ['bytes[]', 'bytes'],
- [values, '0x'],
- )
-
- assert.isTrue(res.upkeepNeeded)
- assert.equal(res.performData, expectedPerformData)
- assert.equal(res.upkeepFailureReason, UpkeepFailureReason.NONE)
- assert.isTrue(res.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used
- })
- })
-
- describe('#setUpkeepPrivilegeConfig() / #getUpkeepPrivilegeConfig()', () => {
- it('reverts when non manager tries to set privilege config', async () => {
- await 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'))
- await registry.connect(owner).addFunds(upkeepId, toWei('100'))
- })
-
- it('splits premium evenly across transmitters', async () => {
- // Do a transmit from keeper1
- await getTransmitTx(registry, keeper1, [upkeepId])
-
- const registryPremium = (await registry.getState()).state.totalPremium
- assert.isTrue(registryPremium.gt(BigNumber.from(0)))
-
- const premiumPerTransmitter = registryPremium.div(
- BigNumber.from(keeperAddresses.length),
- )
- const k1Balance = (
- await registry.getTransmitterInfo(await keeper1.getAddress())
- ).balance
- // transmitter should be reimbursed for gas and get the premium
- assert.isTrue(k1Balance.gt(premiumPerTransmitter))
- const k1GasReimbursement = k1Balance.sub(premiumPerTransmitter)
-
- const k2Balance = (
- await registry.getTransmitterInfo(await keeper2.getAddress())
- ).balance
- // non transmitter should get its share of premium
- assert.isTrue(k2Balance.eq(premiumPerTransmitter))
-
- // Now do a transmit from keeper 2
- await getTransmitTx(registry, keeper2, [upkeepId])
- const registryPremiumNew = (await registry.getState()).state.totalPremium
- assert.isTrue(registryPremiumNew.gt(registryPremium))
- const premiumPerTransmitterNew = registryPremiumNew.div(
- BigNumber.from(keeperAddresses.length),
- )
- const additionalPremium = premiumPerTransmitterNew.sub(
- premiumPerTransmitter,
- )
-
- const k1BalanceNew = (
- await registry.getTransmitterInfo(await keeper1.getAddress())
- ).balance
- // k1 should get the new premium
- assert.isTrue(
- k1BalanceNew.eq(k1GasReimbursement.add(premiumPerTransmitterNew)),
- )
-
- const k2BalanceNew = (
- await registry.getTransmitterInfo(await keeper2.getAddress())
- ).balance
- // k2 should get gas reimbursement in addition to new premium
- assert.isTrue(k2BalanceNew.gt(k2Balance.add(additionalPremium)))
- })
-
- it('updates last collected upon payment withdrawn', async () => {
- // Do a transmit from keeper1
- await getTransmitTx(registry, keeper1, [upkeepId])
-
- const registryPremium = (await registry.getState()).state.totalPremium
- const k1 = await registry.getTransmitterInfo(await keeper1.getAddress())
- const k2 = await registry.getTransmitterInfo(await keeper2.getAddress())
-
- // Withdrawing for first time, last collected = 0
- assert.isTrue(k1.lastCollected.eq(BigNumber.from(0)))
- assert.isTrue(k2.lastCollected.eq(BigNumber.from(0)))
-
- //// Do the thing
- await registry
- .connect(payee1)
- .withdrawPayment(
- await keeper1.getAddress(),
- await nonkeeper.getAddress(),
- )
-
- const k1New = await registry.getTransmitterInfo(
- await keeper1.getAddress(),
- )
- const k2New = await registry.getTransmitterInfo(
- await keeper2.getAddress(),
- )
-
- // transmitter info lastCollected should be updated for k1, not for k2
- assert.isTrue(
- k1New.lastCollected.eq(
- registryPremium.sub(registryPremium.mod(keeperAddresses.length)),
- ),
- )
- assert.isTrue(k2New.lastCollected.eq(BigNumber.from(0)))
- })
-
- itMaybe(
- 'maintains consistent balance information across all parties',
- async () => {
- // throughout transmits, withdrawals, setConfigs total claim on balances should remain less than expected balance
- // some spare change can get lost but it should be less than maxAllowedSpareChange
-
- let maxAllowedSpareChange = BigNumber.from('0')
- await verifyConsistentAccounting(maxAllowedSpareChange)
-
- await getTransmitTx(registry, keeper1, [upkeepId])
- maxAllowedSpareChange = maxAllowedSpareChange.add(BigNumber.from('31'))
- await verifyConsistentAccounting(maxAllowedSpareChange)
-
- await registry
- .connect(payee1)
- .withdrawPayment(
- await keeper1.getAddress(),
- await nonkeeper.getAddress(),
- )
- await verifyConsistentAccounting(maxAllowedSpareChange)
-
- await registry
- .connect(payee2)
- .withdrawPayment(
- await keeper2.getAddress(),
- await nonkeeper.getAddress(),
- )
- await verifyConsistentAccounting(maxAllowedSpareChange)
-
- await getTransmitTx(registry, keeper1, [upkeepId])
- maxAllowedSpareChange = maxAllowedSpareChange.add(BigNumber.from('31'))
- await verifyConsistentAccounting(maxAllowedSpareChange)
-
- await registry.connect(owner).setConfigTypeSafe(
- signerAddresses.slice(2, 15), // only use 2-14th index keepers
- keeperAddresses.slice(2, 15),
- f,
- config,
- offchainVersion,
- offchainBytes,
- )
- await verifyConsistentAccounting(maxAllowedSpareChange)
-
- await getTransmitTx(registry, keeper3, [upkeepId], {
- startingSignerIndex: 2,
- })
- maxAllowedSpareChange = maxAllowedSpareChange.add(BigNumber.from('13'))
- await verifyConsistentAccounting(maxAllowedSpareChange)
-
- await registry
- .connect(payee1)
- .withdrawPayment(
- await keeper1.getAddress(),
- await nonkeeper.getAddress(),
- )
- await verifyConsistentAccounting(maxAllowedSpareChange)
-
- await registry
- .connect(payee3)
- .withdrawPayment(
- await keeper3.getAddress(),
- await nonkeeper.getAddress(),
- )
- await verifyConsistentAccounting(maxAllowedSpareChange)
-
- await registry.connect(owner).setConfigTypeSafe(
- signerAddresses.slice(0, 4), // only use 0-3rd index keepers
- keeperAddresses.slice(0, 4),
- f,
- config,
- offchainVersion,
- offchainBytes,
- )
- await verifyConsistentAccounting(maxAllowedSpareChange)
- await getTransmitTx(registry, keeper1, [upkeepId])
- maxAllowedSpareChange = maxAllowedSpareChange.add(BigNumber.from('4'))
- await getTransmitTx(registry, keeper3, [upkeepId])
- maxAllowedSpareChange = maxAllowedSpareChange.add(BigNumber.from('4'))
-
- await verifyConsistentAccounting(maxAllowedSpareChange)
- await registry
- .connect(payee5)
- .withdrawPayment(
- await keeper5.getAddress(),
- await nonkeeper.getAddress(),
- )
- await verifyConsistentAccounting(maxAllowedSpareChange)
-
- await registry
- .connect(payee1)
- .withdrawPayment(
- await keeper1.getAddress(),
- await nonkeeper.getAddress(),
- )
- await verifyConsistentAccounting(maxAllowedSpareChange)
- },
- )
- })
-})
diff --git a/contracts/test/v0.8/automation/CronUpkeep.test.ts b/contracts/test/v0.8/automation/CronUpkeep.test.ts
deleted file mode 100644
index 7b769797f12..00000000000
--- a/contracts/test/v0.8/automation/CronUpkeep.test.ts
+++ /dev/null
@@ -1,576 +0,0 @@
-import moment from 'moment'
-import { ethers } from 'hardhat'
-import { Contract } from 'ethers'
-import { assert, expect } from 'chai'
-import { CronUpkeepTestHelper } from '../../../typechain/CronUpkeepTestHelper'
-import { CronUpkeepDelegate } from '../../../typechain/CronUpkeepDelegate'
-import { CronUpkeepFactory } from '../../../typechain/CronUpkeepFactory'
-import { CronUpkeepTestHelper__factory as CronUpkeepTestHelperFactory } from '../../../typechain/factories/CronUpkeepTestHelper__factory'
-import { CronInternalTestHelper } from '../../../typechain/CronInternalTestHelper'
-import { CronReceiver } from '../../../typechain/CronReceiver'
-import { BigNumber, BigNumberish } from '@ethersproject/bignumber'
-import type { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'
-import { validCrons } from '../../test-helpers/fixtures'
-import * as h from '../../test-helpers/helpers'
-
-const { utils } = ethers
-const { AddressZero } = ethers.constants
-
-const OWNABLE_ERR = 'Only callable by owner'
-const CRON_NOT_FOUND_ERR = 'CronJobIDNotFound'
-
-let cron: CronUpkeepTestHelper
-let cronFactory: CronUpkeepTestHelperFactory // the typechain factory that deploys cron upkeep contracts
-let cronFactoryContract: CronUpkeepFactory // the cron factory contract
-let cronDelegate: CronUpkeepDelegate
-let cronTestHelper: CronInternalTestHelper
-let cronReceiver1: CronReceiver
-let cronReceiver2: CronReceiver
-
-let admin: SignerWithAddress
-let owner: SignerWithAddress
-let stranger: SignerWithAddress
-
-const timeStamp = 32503680000 // Jan 1, 3000 12:00AM
-const basicCronString = '0 * * * *'
-
-let handler1Sig: string
-let handler2Sig: string
-let revertHandlerSig: string
-let basicSpec: string
-
-async function assertJobIDsEqual(expected: number[]) {
- const ids = (await cron.getActiveCronJobIDs()).map((n) => n.toNumber())
- assert.deepEqual(ids.sort(), expected.sort())
-}
-
-function decodePayload(payload: string) {
- return utils.defaultAbiCoder.decode(
- ['uint256', 'uint256', 'address', 'bytes'],
- payload,
- ) as [BigNumber, BigNumber, string, string]
-}
-
-function encodePayload(payload: [BigNumberish, BigNumberish, string, string]) {
- return utils.defaultAbiCoder.encode(
- ['uint256', 'uint256', 'address', 'bytes'],
- payload,
- )
-}
-
-async function createBasicCron() {
- return await cron.createCronJobFromEncodedSpec(
- cronReceiver1.address,
- handler1Sig,
- basicSpec,
- )
-}
-
-describe('CronUpkeep', () => {
- beforeEach(async () => {
- const accounts = await ethers.getSigners()
- admin = accounts[0]
- owner = accounts[1]
- stranger = accounts[2]
- const crFactory = await ethers.getContractFactory('CronReceiver', owner)
- cronReceiver1 = await crFactory.deploy()
- cronReceiver2 = await crFactory.deploy()
- const cronDelegateFactory = await ethers.getContractFactory(
- 'CronUpkeepDelegate',
- admin,
- )
- cronDelegate = await cronDelegateFactory.deploy()
- const cronExternalFactory = await ethers.getContractFactory(
- 'src/v0.8/automation/libraries/external/Cron.sol:Cron',
- admin,
- )
- const cronExternalLib = await cronExternalFactory.deploy()
- cronFactory = await ethers.getContractFactory('CronUpkeepTestHelper', {
- signer: admin,
- libraries: { Cron: cronExternalLib.address },
- })
- cron = (
- await cronFactory.deploy(owner.address, cronDelegate.address, 5, [])
- ).connect(owner)
- const cronFactoryContractFactory = await ethers.getContractFactory(
- 'CronUpkeepFactory',
- { signer: admin, libraries: { Cron: cronExternalLib.address } },
- ) // the typechain factory that creates the cron factory contract
- cronFactoryContract = await cronFactoryContractFactory.deploy()
- const fs = cronReceiver1.interface.functions
- handler1Sig = utils.id(fs['handler1()'].format('sighash')).slice(0, 10)
- handler2Sig = utils.id(fs['handler2()'].format('sighash')).slice(0, 10)
- revertHandlerSig = utils
- .id(fs['revertHandler()'].format('sighash'))
- .slice(0, 10)
- const cronTHFactory = await ethers.getContractFactory(
- 'CronInternalTestHelper',
- )
- cronTestHelper = await cronTHFactory.deploy()
- basicSpec = await cronFactoryContract.encodeCronString(basicCronString)
- })
-
- afterEach(async () => {
- await h.reset()
- })
-
- it('has a limited public ABI [ @skip-coverage ]', () => {
- // Casting cron is necessary due to a tricky versioning mismatch issue, likely between ethers
- // and typechain. Remove once the version issue is resolved.
- // https://smartcontract-it.atlassian.net/browse/ARCHIVE-22094
- h.publicAbi(cron as unknown as Contract, [
- 's_maxJobs',
- 'performUpkeep',
- 'createCronJobFromEncodedSpec',
- 'updateCronJob',
- 'deleteCronJob',
- 'checkUpkeep',
- 'getActiveCronJobIDs',
- 'getCronJob',
- // Ownable methods:
- 'acceptOwnership',
- 'owner',
- 'transferOwnership',
- // Pausable methods
- 'paused',
- 'pause',
- 'unpause',
- // Cron helper methods
- 'createCronJobFromString',
- 'txCheckUpkeep',
- ])
- })
-
- describe('constructor()', () => {
- it('sets the initial values', async () => {
- expect(await cron.owner()).to.equal(owner.address)
- expect(await cron.s_maxJobs()).to.equal(5)
- })
-
- it('optionally creates a first job', async () => {
- const payload = await cronFactoryContract.encodeCronJob(
- cronReceiver1.address,
- handler1Sig,
- basicCronString,
- )
- cron = (
- await cronFactory.deploy(
- owner.address,
- cronDelegate.address,
- 5,
- payload,
- )
- ).connect(owner)
- const job = await cron.getCronJob(1)
- assert.equal(job.target, cronReceiver1.address)
- assert.equal(job.handler, handler1Sig)
- assert.equal(job.cronString, basicCronString)
- })
- })
-
- describe('checkUpkeep() / performUpkeep()', () => {
- beforeEach(async () => {
- await h.setTimestamp(timeStamp)
- // id 1
- await cron.createCronJobFromString(
- cronReceiver1.address,
- handler1Sig,
- '0 0 31 * *', // 31st day of every month
- )
- // id 2
- await cron.createCronJobFromString(
- cronReceiver1.address,
- handler2Sig,
- '10 * * * *', // on the 10 min mark
- )
- // id 3
- await cron.createCronJobFromString(
- cronReceiver2.address,
- handler1Sig,
- '0 0 * 7 *', // every day in July
- )
- // id 4
- await cron.createCronJobFromString(
- cronReceiver2.address,
- revertHandlerSig,
- '20 * * * *', // on the 20 min mark
- )
- })
-
- describe('checkUpkeep()', () => {
- it('returns false if no one is elligible', async () => {
- const [needsUpkeep] = await cron
- .connect(AddressZero)
- .callStatic.checkUpkeep('0x')
- assert.isFalse(needsUpkeep)
- })
-
- it('returns the id of eligible cron jobs', async () => {
- await h.fastForward(moment.duration(11, 'minutes').asSeconds())
- const [needsUpkeep, payload] = await cron
- .connect(AddressZero)
- .callStatic.checkUpkeep('0x')
- assert.isTrue(needsUpkeep)
- const [id, ..._] = decodePayload(payload)
- assert.equal(id.toNumber(), 2)
- })
-
- describe('when mutiple crons are elligible', () => {
- it('cycles through the cron IDs based on block number', async () => {
- await h.fastForward(moment.duration(1, 'year').asSeconds())
- let [_, payload] = await cron
- .connect(AddressZero)
- .callStatic.checkUpkeep('0x')
- const [id1] = decodePayload(payload)
- await h.mineBlock(ethers.provider)
- ;[_, payload] = await cron
- .connect(AddressZero)
- .callStatic.checkUpkeep('0x')
- const [id2] = decodePayload(payload)
- await h.mineBlock(ethers.provider)
- ;[_, payload] = await cron
- .connect(AddressZero)
- .callStatic.checkUpkeep('0x')
- const [id3] = decodePayload(payload)
- await h.mineBlock(ethers.provider)
- ;[_, payload] = await cron
- .connect(AddressZero)
- .callStatic.checkUpkeep('0x')
- const [id4] = decodePayload(payload)
- assert.deepEqual(
- [id1, id2, id3, id4].map((n) => n.toNumber()).sort(),
- [1, 2, 3, 4],
- )
- })
- })
- })
-
- describe('performUpkeep()', () => {
- it('forwards the call to the appropriate target/handler', async () => {
- await h.fastForward(moment.duration(11, 'minutes').asSeconds())
- const [needsUpkeep, payload] = await cron
- .connect(AddressZero)
- .callStatic.checkUpkeep('0x')
- assert.isTrue(needsUpkeep)
- await expect(cron.performUpkeep(payload)).to.emit(
- cronReceiver1,
- 'Received2',
- )
- })
-
- it('emits an event', async () => {
- await h.fastForward(moment.duration(11, 'minutes').asSeconds())
- const [needsUpkeep, payload] = await cron
- .connect(AddressZero)
- .callStatic.checkUpkeep('0x')
- assert.isTrue(needsUpkeep)
- await expect(cron.performUpkeep(payload))
- .to.emit(cron, 'CronJobExecuted')
- .withArgs(2, true)
- })
-
- it('succeeds even if the call to the target fails', async () => {
- await cron.deleteCronJob(2)
- await h.fastForward(moment.duration(21, 'minutes').asSeconds())
- const payload = encodePayload([
- 4,
- moment.unix(timeStamp).add(20, 'minutes').unix(),
- cronReceiver2.address,
- revertHandlerSig,
- ])
- await expect(cron.performUpkeep(payload))
- .to.emit(cron, 'CronJobExecuted')
- .withArgs(4, false)
- })
-
- it('is only callable by anyone', async () => {
- await h.fastForward(moment.duration(11, 'minutes').asSeconds())
- const [needsUpkeep, payload] = await cron
- .connect(AddressZero)
- .callStatic.checkUpkeep('0x')
- assert.isTrue(needsUpkeep)
- await cron.connect(stranger).performUpkeep(payload)
- })
-
- it('is only callable once for a given tick', async () => {
- await h.fastForward(moment.duration(10, 'minutes').asSeconds())
- const [needsUpkeep, payload] = await cron
- .connect(AddressZero)
- .callStatic.checkUpkeep('0x')
- assert.isTrue(needsUpkeep)
- const maliciousPayload = encodePayload([
- 2,
- moment.unix(timeStamp).add(10, 'minutes').add(59, 'seconds').unix(),
- cronReceiver1.address,
- handler2Sig,
- ])
- await cron.performUpkeep(payload)
- await expect(cron.performUpkeep(payload)).to.be.reverted
- await expect(cron.performUpkeep(maliciousPayload)).to.be.reverted
- await h.fastForward(moment.duration(1, 'minute').asSeconds())
- await expect(cron.performUpkeep(payload)).to.be.reverted
- await expect(cron.performUpkeep(maliciousPayload)).to.be.reverted
- await h.fastForward(moment.duration(10, 'minute').asSeconds())
- await expect(cron.performUpkeep(payload)).to.be.reverted
- await expect(cron.performUpkeep(maliciousPayload)).to.be.reverted
- })
- })
- })
-
- describe('createCronJobFromEncodedSpec()', () => {
- it('creates jobs with sequential IDs', async () => {
- const cronString1 = '0 * * * *'
- const cronString2 = '0 1,2,3 */4 5-6 1-2'
- const encodedSpec1 =
- await cronFactoryContract.encodeCronString(cronString1)
- const encodedSpec2 =
- await cronFactoryContract.encodeCronString(cronString2)
- const nextTick1 = (
- await cronTestHelper.calculateNextTick(cronString1)
- ).toNumber()
- const nextTick2 = (
- await cronTestHelper.calculateNextTick(cronString2)
- ).toNumber()
- await cron.createCronJobFromEncodedSpec(
- cronReceiver1.address,
- handler1Sig,
- encodedSpec1,
- )
- await assertJobIDsEqual([1])
- await cron.createCronJobFromEncodedSpec(
- cronReceiver1.address,
- handler2Sig,
- encodedSpec1,
- )
- await assertJobIDsEqual([1, 2])
- await cron.createCronJobFromEncodedSpec(
- cronReceiver2.address,
- handler1Sig,
- encodedSpec2,
- )
- await assertJobIDsEqual([1, 2, 3])
- await cron.createCronJobFromEncodedSpec(
- cronReceiver2.address,
- handler2Sig,
- encodedSpec2,
- )
- await assertJobIDsEqual([1, 2, 3, 4])
- const cron1 = await cron.getCronJob(1)
- const cron2 = await cron.getCronJob(2)
- const cron3 = await cron.getCronJob(3)
- const cron4 = await cron.getCronJob(4)
- assert.equal(cron1.target, cronReceiver1.address)
- assert.equal(cron1.handler, handler1Sig)
- assert.equal(cron1.cronString, cronString1)
- assert.equal(cron1.nextTick.toNumber(), nextTick1)
- assert.equal(cron2.target, cronReceiver1.address)
- assert.equal(cron2.handler, handler2Sig)
- assert.equal(cron2.cronString, cronString1)
- assert.equal(cron2.nextTick.toNumber(), nextTick1)
- assert.equal(cron3.target, cronReceiver2.address)
- assert.equal(cron3.handler, handler1Sig)
- assert.equal(cron3.cronString, cronString2)
- assert.equal(cron3.nextTick.toNumber(), nextTick2)
- assert.equal(cron4.target, cronReceiver2.address)
- assert.equal(cron4.handler, handler2Sig)
- assert.equal(cron4.cronString, cronString2)
- assert.equal(cron4.nextTick.toNumber(), nextTick2)
- })
-
- it('emits an event', async () => {
- await expect(createBasicCron()).to.emit(cron, 'CronJobCreated')
- })
-
- it('is only callable by the owner', async () => {
- await expect(
- cron
- .connect(stranger)
- .createCronJobFromEncodedSpec(
- cronReceiver1.address,
- handler1Sig,
- basicSpec,
- ),
- ).to.be.revertedWith(OWNABLE_ERR)
- })
-
- it('errors if trying to create more jobs than allowed', async () => {
- for (let idx = 0; idx < 5; idx++) {
- await createBasicCron()
- }
- await expect(createBasicCron()).to.be.revertedWithCustomError(
- cron,
- 'ExceedsMaxJobs',
- )
- })
- })
-
- describe('updateCronJob()', () => {
- const newCronString = '0 0 1 1 1'
- let newEncodedSpec: string
- beforeEach(async () => {
- await createBasicCron()
- newEncodedSpec = await cronFactoryContract.encodeCronString(newCronString)
- })
-
- it('updates a cron job', async () => {
- let cron1 = await cron.getCronJob(1)
- assert.equal(cron1.target, cronReceiver1.address)
- assert.equal(cron1.handler, handler1Sig)
- assert.equal(cron1.cronString, basicCronString)
- await cron.updateCronJob(
- 1,
- cronReceiver2.address,
- handler2Sig,
- newEncodedSpec,
- )
- cron1 = await cron.getCronJob(1)
- assert.equal(cron1.target, cronReceiver2.address)
- assert.equal(cron1.handler, handler2Sig)
- assert.equal(cron1.cronString, newCronString)
- })
-
- it('emits an event', async () => {
- await expect(
- await cron.updateCronJob(
- 1,
- cronReceiver2.address,
- handler2Sig,
- newEncodedSpec,
- ),
- ).to.emit(cron, 'CronJobUpdated')
- })
-
- it('is only callable by the owner', async () => {
- await expect(
- cron
- .connect(stranger)
- .updateCronJob(1, cronReceiver2.address, handler2Sig, newEncodedSpec),
- ).to.be.revertedWith(OWNABLE_ERR)
- })
-
- it('reverts if trying to update a non-existent ID', async () => {
- await expect(
- cron.updateCronJob(
- 2,
- cronReceiver2.address,
- handler2Sig,
- newEncodedSpec,
- ),
- ).to.be.revertedWithCustomError(cron, CRON_NOT_FOUND_ERR)
- })
- })
-
- describe('deleteCronJob()', () => {
- it("deletes a jobs by it's ID", async () => {
- await createBasicCron()
- await createBasicCron()
- await createBasicCron()
- await createBasicCron()
- await assertJobIDsEqual([1, 2, 3, 4])
- await cron.deleteCronJob(2)
- await expect(cron.getCronJob(2)).to.be.revertedWithCustomError(
- cron,
- CRON_NOT_FOUND_ERR,
- )
- await expect(cron.deleteCronJob(2)).to.be.revertedWithCustomError(
- cron,
- CRON_NOT_FOUND_ERR,
- )
- await assertJobIDsEqual([1, 3, 4])
- await cron.deleteCronJob(1)
- await assertJobIDsEqual([3, 4])
- await cron.deleteCronJob(4)
- await assertJobIDsEqual([3])
- await cron.deleteCronJob(3)
- await assertJobIDsEqual([])
- })
-
- it('emits an event', async () => {
- await createBasicCron()
- await expect(cron.deleteCronJob(1)).to.emit(cron, 'CronJobDeleted')
- })
-
- it('reverts if trying to delete a non-existent ID', async () => {
- await createBasicCron()
- await createBasicCron()
- await expect(cron.deleteCronJob(0)).to.be.revertedWithCustomError(
- cron,
- CRON_NOT_FOUND_ERR,
- )
- await expect(cron.deleteCronJob(3)).to.be.revertedWithCustomError(
- cron,
- CRON_NOT_FOUND_ERR,
- )
- })
- })
-
- describe('pause() / unpause()', () => {
- it('is only callable by the owner', async () => {
- await expect(cron.connect(stranger).pause()).to.be.reverted
- await expect(cron.connect(stranger).unpause()).to.be.reverted
- })
-
- it('pauses / unpauses the contract', async () => {
- expect(await cron.paused()).to.be.false
- await cron.pause()
- expect(await cron.paused()).to.be.true
- await cron.unpause()
- expect(await cron.paused()).to.be.false
- })
- })
-})
-
-// only run during pnpm test:gas
-describe.skip('Cron Gas Usage', () => {
- before(async () => {
- const accounts = await ethers.getSigners()
- admin = accounts[0]
- owner = accounts[1]
- const crFactory = await ethers.getContractFactory('CronReceiver', owner)
- cronReceiver1 = await crFactory.deploy()
- const cronDelegateFactory = await ethers.getContractFactory(
- 'CronUpkeepDelegate',
- owner,
- )
- const cronDelegate = await cronDelegateFactory.deploy()
- const cronExternalFactory = await ethers.getContractFactory(
- 'src/v0.8/automation/libraries/external/Cron.sol:Cron',
- admin,
- )
- const cronExternalLib = await cronExternalFactory.deploy()
- const cronFactory = await ethers.getContractFactory(
- 'CronUpkeepTestHelper',
- {
- signer: owner,
- libraries: { Cron: cronExternalLib.address },
- },
- )
- cron = await cronFactory.deploy(owner.address, cronDelegate.address, 5, [])
- const fs = cronReceiver1.interface.functions
- handler1Sig = utils
- .id(fs['handler1()'].format('sighash')) // TODO this seems like an ethers bug
- .slice(0, 10)
- })
-
- describe('checkUpkeep() / performUpkeep()', () => {
- it('uses gas', async () => {
- for (let idx = 0; idx < validCrons.length; idx++) {
- const cronString = validCrons[idx]
- const cronID = idx + 1
- await cron.createCronJobFromString(
- cronReceiver1.address,
- handler1Sig,
- cronString,
- )
- await h.fastForward(moment.duration(100, 'years').asSeconds()) // long enough that at least 1 tick occurs
- const [needsUpkeep, data] = await cron
- .connect(AddressZero)
- .callStatic.checkUpkeep('0x')
- assert.isTrue(needsUpkeep, `failed for cron string ${cronString}`)
- await cron.txCheckUpkeep('0x')
- await cron.performUpkeep(data)
- await cron.deleteCronJob(cronID)
- }
- })
- })
-})
diff --git a/contracts/test/v0.8/automation/CronUpkeepFactory.test.ts b/contracts/test/v0.8/automation/CronUpkeepFactory.test.ts
deleted file mode 100644
index e9a7de837b7..00000000000
--- a/contracts/test/v0.8/automation/CronUpkeepFactory.test.ts
+++ /dev/null
@@ -1,107 +0,0 @@
-import { ethers } from 'hardhat'
-import { Contract } from 'ethers'
-import { assert, expect } from 'chai'
-import { CronUpkeepFactory } from '../../../typechain/CronUpkeepFactory'
-import type { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'
-import * as h from '../../test-helpers/helpers'
-import { reset } from '../../test-helpers/helpers'
-
-const OWNABLE_ERR = 'Only callable by owner'
-
-let cronExternalLib: Contract
-let factory: CronUpkeepFactory
-
-let admin: SignerWithAddress
-let owner: SignerWithAddress
-let stranger: SignerWithAddress
-
-describe('CronUpkeepFactory', () => {
- beforeEach(async () => {
- const accounts = await ethers.getSigners()
- admin = accounts[0]
- owner = accounts[1]
- stranger = accounts[2]
- const cronExternalFactory = await ethers.getContractFactory(
- 'src/v0.8/automation/libraries/external/Cron.sol:Cron',
- admin,
- )
- cronExternalLib = await cronExternalFactory.deploy()
- const cronUpkeepFactoryFactory = await ethers.getContractFactory(
- 'CronUpkeepFactory',
- {
- signer: admin,
- libraries: {
- Cron: cronExternalLib.address,
- },
- },
- )
- factory = await cronUpkeepFactoryFactory.deploy()
- })
-
- afterEach(async () => {
- await reset()
- })
-
- it('has a limited public ABI [ @skip-coverage ]', () => {
- h.publicAbi(factory as unknown as Contract, [
- 's_maxJobs',
- 'newCronUpkeep',
- 'newCronUpkeepWithJob',
- 'setMaxJobs',
- 'cronDelegateAddress',
- 'encodeCronString',
- 'encodeCronJob',
- // Ownable methods:
- 'acceptOwnership',
- 'owner',
- 'transferOwnership',
- ])
- })
-
- describe('constructor()', () => {
- it('deploys a delegate contract', async () => {
- assert.notEqual(
- await factory.cronDelegateAddress(),
- ethers.constants.AddressZero,
- )
- })
- })
-
- describe('newCronUpkeep()', () => {
- it('emits an event', async () => {
- await expect(factory.connect(owner).newCronUpkeep()).to.emit(
- factory,
- 'NewCronUpkeepCreated',
- )
- })
- it('sets the deployer as the owner', async () => {
- const response = await factory.connect(owner).newCronUpkeep()
- const { events } = await response.wait()
- if (!events) {
- assert.fail('no events emitted')
- }
- const upkeepAddress = events[0].args?.upkeep
- const cronUpkeepFactory = await ethers.getContractFactory('CronUpkeep', {
- libraries: { Cron: cronExternalLib.address },
- })
- assert(
- await cronUpkeepFactory.attach(upkeepAddress).owner(),
- owner.address,
- )
- })
- })
-
- describe('setMaxJobs()', () => {
- it('sets the max jobs value', async () => {
- expect(await factory.s_maxJobs()).to.equal(5)
- await factory.setMaxJobs(6)
- expect(await factory.s_maxJobs()).to.equal(6)
- })
-
- it('is only callable by the owner', async () => {
- await expect(factory.connect(stranger).setMaxJobs(6)).to.be.revertedWith(
- OWNABLE_ERR,
- )
- })
- })
-})
diff --git a/contracts/test/v0.8/automation/ERC20BalanceMonitor.test.ts b/contracts/test/v0.8/automation/ERC20BalanceMonitor.test.ts
deleted file mode 100644
index 2d5d113abca..00000000000
--- a/contracts/test/v0.8/automation/ERC20BalanceMonitor.test.ts
+++ /dev/null
@@ -1,695 +0,0 @@
-import { ethers } from 'hardhat'
-import { assert, expect } from 'chai'
-import type { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'
-import { ReceiveEmitter } from '../../../typechain/ReceiveEmitter'
-import { ReceiveFallbackEmitter } from '../../../typechain/ReceiveFallbackEmitter'
-import * as h from '../../test-helpers/helpers'
-import { ERC20BalanceMonitorExposed, LinkToken } from '../../../typechain'
-import { BigNumber } from 'ethers'
-
-const OWNABLE_ERR = 'Only callable by owner'
-const INVALID_WATCHLIST_ERR = `InvalidWatchList`
-const PAUSED_ERR = 'Pausable: paused'
-const ONLY_KEEPER_ERR = `OnlyKeeperRegistry`
-
-const zeroLINK = ethers.utils.parseEther('0')
-const oneLINK = ethers.utils.parseEther('1')
-const twoLINK = ethers.utils.parseEther('2')
-const threeLINK = ethers.utils.parseEther('3')
-const fiveLINK = ethers.utils.parseEther('5')
-const sixLINK = ethers.utils.parseEther('6')
-const tenLINK = ethers.utils.parseEther('10')
-
-const oneHundredLINK = ethers.utils.parseEther('100')
-
-const watchAddress1 = ethers.Wallet.createRandom().address
-const watchAddress2 = ethers.Wallet.createRandom().address
-const watchAddress3 = ethers.Wallet.createRandom().address
-const watchAddress4 = ethers.Wallet.createRandom().address
-let watchAddress5: string
-let watchAddress6: string
-
-let bm: ERC20BalanceMonitorExposed
-let lt: LinkToken
-let receiveEmitter: ReceiveEmitter
-let receiveFallbackEmitter: ReceiveFallbackEmitter
-let owner: SignerWithAddress
-let stranger: SignerWithAddress
-let keeperRegistry: SignerWithAddress
-
-async function assertWatchlistBalances(
- balance1: BigNumber,
- balance2: BigNumber,
- balance3: BigNumber,
- balance4: BigNumber,
- balance5: BigNumber,
- balance6: BigNumber,
-) {
- await h.assertLinkTokenBalance(lt, watchAddress1, balance1, 'address 1')
- await h.assertLinkTokenBalance(lt, watchAddress2, balance2, 'address 2')
- await h.assertLinkTokenBalance(lt, watchAddress3, balance3, 'address 3')
- await h.assertLinkTokenBalance(lt, watchAddress4, balance4, 'address 4')
- await h.assertLinkTokenBalance(lt, watchAddress5, balance5, 'address 5')
- await h.assertLinkTokenBalance(lt, watchAddress6, balance6, 'address 6')
-}
-
-describe('ERC20BalanceMonitor', () => {
- beforeEach(async () => {
- const accounts = await ethers.getSigners()
- owner = accounts[0]
- stranger = accounts[1]
- keeperRegistry = accounts[2]
- watchAddress5 = accounts[3].address
- watchAddress6 = accounts[4].address
-
- const bmFactory = await ethers.getContractFactory(
- 'ERC20BalanceMonitorExposed',
- owner,
- )
- const ltFactory = await ethers.getContractFactory(
- 'src/v0.8/shared/test/helpers/LinkTokenTestHelper.sol:LinkTokenTestHelper',
- owner,
- )
- const reFactory = await ethers.getContractFactory('ReceiveEmitter', owner)
- const rfeFactory = await ethers.getContractFactory(
- 'ReceiveFallbackEmitter',
- owner,
- )
-
- lt = await ltFactory.deploy()
- bm = await bmFactory.deploy(lt.address, keeperRegistry.address, 0)
-
- for (let i = 1; i <= 4; i++) {
- const recipient = await accounts[i].getAddress()
- await lt.connect(owner).transfer(recipient, oneHundredLINK)
- }
-
- receiveEmitter = await reFactory.deploy()
- receiveFallbackEmitter = await rfeFactory.deploy()
- await Promise.all([
- bm.deployed(),
- receiveEmitter.deployed(),
- receiveFallbackEmitter.deployed(),
- ])
- })
-
- afterEach(async () => {
- await h.reset()
- })
-
- describe('add funds', () => {
- it('Should allow anyone to add funds', async () => {
- await lt.transfer(bm.address, oneLINK)
- await lt.connect(stranger).transfer(bm.address, oneLINK)
- })
- })
-
- describe('withdraw()', () => {
- beforeEach(async () => {
- const tx = await lt.connect(owner).transfer(bm.address, oneLINK)
- await tx.wait()
- })
-
- it('Should allow the owner to withdraw', async () => {
- const beforeBalance = await lt.balanceOf(owner.address)
- const tx = await bm.connect(owner).withdraw(oneLINK, owner.address)
- await tx.wait()
- const afterBalance = await lt.balanceOf(owner.address)
- assert.isTrue(
- afterBalance.gt(beforeBalance),
- 'balance did not increase after withdraw',
- )
- })
-
- it('Should emit an event', async () => {
- const tx = await bm.connect(owner).withdraw(oneLINK, owner.address)
- await expect(tx)
- .to.emit(bm, 'FundsWithdrawn')
- .withArgs(oneLINK, owner.address)
- })
-
- it('Should allow the owner to withdraw to anyone', async () => {
- const beforeBalance = await lt.balanceOf(stranger.address)
- const tx = await bm.connect(owner).withdraw(oneLINK, stranger.address)
- await tx.wait()
- const afterBalance = await lt.balanceOf(stranger.address)
- assert.isTrue(
- beforeBalance.add(oneLINK).eq(afterBalance),
- 'balance did not increase after withdraw',
- )
- })
-
- it('Should not allow strangers to withdraw', async () => {
- const tx = bm.connect(stranger).withdraw(oneLINK, owner.address)
- await expect(tx).to.be.revertedWith(OWNABLE_ERR)
- })
- })
-
- describe('pause() / unpause()', () => {
- it('Should allow owner to pause / unpause', async () => {
- const pauseTx = await bm.connect(owner).pause()
- await pauseTx.wait()
- const unpauseTx = await bm.connect(owner).unpause()
- await unpauseTx.wait()
- })
-
- it('Should not allow strangers to pause / unpause', async () => {
- const pauseTxStranger = bm.connect(stranger).pause()
- await expect(pauseTxStranger).to.be.revertedWith(OWNABLE_ERR)
- const pauseTxOwner = await bm.connect(owner).pause()
- await pauseTxOwner.wait()
- const unpauseTxStranger = bm.connect(stranger).unpause()
- await expect(unpauseTxStranger).to.be.revertedWith(OWNABLE_ERR)
- })
- })
-
- describe('setWatchList() / getWatchList() / getAccountInfo()', () => {
- it('Should allow owner to set the watchlist', async () => {
- // should start unactive
- assert.isFalse((await bm.getAccountInfo(watchAddress1)).isActive)
- // add first watchlist
- let setTx = await bm
- .connect(owner)
- .setWatchList([watchAddress1], [oneLINK], [twoLINK])
- await setTx.wait()
- let watchList = await bm.getWatchList()
- assert.deepEqual(watchList, [watchAddress1])
- const accountInfo = await bm.getAccountInfo(watchAddress1)
- assert.isTrue(accountInfo.isActive)
- expect(accountInfo.minBalance).to.equal(oneLINK)
- expect(accountInfo.topUpLevel).to.equal(twoLINK)
- // add more to watchlist
- setTx = await bm
- .connect(owner)
- .setWatchList(
- [watchAddress1, watchAddress2, watchAddress3],
- [oneLINK, twoLINK, threeLINK],
- [twoLINK, threeLINK, fiveLINK],
- )
- await setTx.wait()
- watchList = await bm.getWatchList()
- assert.deepEqual(watchList, [watchAddress1, watchAddress2, watchAddress3])
- let accountInfo1 = await bm.getAccountInfo(watchAddress1)
- let accountInfo2 = await bm.getAccountInfo(watchAddress2)
- let accountInfo3 = await bm.getAccountInfo(watchAddress3)
- expect(accountInfo1.isActive).to.be.true
- expect(accountInfo1.minBalance).to.equal(oneLINK)
- expect(accountInfo1.topUpLevel).to.equal(twoLINK)
- expect(accountInfo2.isActive).to.be.true
- expect(accountInfo2.minBalance).to.equal(twoLINK)
- expect(accountInfo2.topUpLevel).to.equal(threeLINK)
- expect(accountInfo3.isActive).to.be.true
- expect(accountInfo3.minBalance).to.equal(threeLINK)
- expect(accountInfo3.topUpLevel).to.equal(fiveLINK)
- // remove some from watchlist
- setTx = await bm
- .connect(owner)
- .setWatchList(
- [watchAddress3, watchAddress1],
- [threeLINK, oneLINK],
- [fiveLINK, twoLINK],
- )
- await setTx.wait()
- watchList = await bm.getWatchList()
- assert.deepEqual(watchList, [watchAddress3, watchAddress1])
- accountInfo1 = await bm.getAccountInfo(watchAddress1)
- accountInfo2 = await bm.getAccountInfo(watchAddress2)
- accountInfo3 = await bm.getAccountInfo(watchAddress3)
- expect(accountInfo1.isActive).to.be.true
- expect(accountInfo2.isActive).to.be.false
- expect(accountInfo3.isActive).to.be.true
- })
-
- it('Should not allow duplicates in the watchlist', async () => {
- const errMsg = `DuplicateAddress`
- const setTx = bm
- .connect(owner)
- .setWatchList(
- [watchAddress1, watchAddress2, watchAddress1],
- [oneLINK, twoLINK, threeLINK],
- [twoLINK, threeLINK, fiveLINK],
- )
- await expect(setTx)
- .to.be.revertedWithCustomError(bm, errMsg)
- .withArgs(watchAddress1)
- })
-
- it('Should not allow a topUpLevel les than or equal to minBalance in the watchlist', async () => {
- const setTx = bm
- .connect(owner)
- .setWatchList(
- [watchAddress1, watchAddress2, watchAddress1],
- [oneLINK, twoLINK, threeLINK],
- [zeroLINK, twoLINK, threeLINK],
- )
- await expect(setTx).to.be.revertedWithCustomError(
- bm,
- INVALID_WATCHLIST_ERR,
- )
- })
-
- it('Should not allow larger than maximum watchlist size', async () => {
- const watchlist: any[][] = [[], [], []]
- Array.from(Array(301).keys()).forEach(() => {
- watchlist[0].push(owner.address)
- watchlist[1].push(oneLINK)
- watchlist[2].push(twoLINK)
- })
- const tx = bm
- .connect(owner)
- .setWatchList(watchlist[0], watchlist[1], watchlist[2])
- await expect(tx).to.be.revertedWithCustomError(bm, INVALID_WATCHLIST_ERR)
- })
-
- it('Should not allow strangers to set the watchlist', async () => {
- const setTxStranger = bm
- .connect(stranger)
- .setWatchList([watchAddress1], [oneLINK], [twoLINK])
- await expect(setTxStranger).to.be.revertedWith(OWNABLE_ERR)
- })
-
- it('Should revert if the list lengths differ', async () => {
- let tx = bm.connect(owner).setWatchList([watchAddress1], [], [twoLINK])
- await expect(tx).to.be.revertedWithCustomError(bm, INVALID_WATCHLIST_ERR)
- tx = bm.connect(owner).setWatchList([watchAddress1], [oneLINK], [])
- await expect(tx).to.be.revertedWithCustomError(bm, INVALID_WATCHLIST_ERR)
- tx = bm.connect(owner).setWatchList([], [oneLINK], [twoLINK])
- await expect(tx).to.be.revertedWithCustomError(bm, INVALID_WATCHLIST_ERR)
- })
-
- it('Should revert if any of the addresses are empty', async () => {
- let tx = bm
- .connect(owner)
- .setWatchList(
- [watchAddress1, ethers.constants.AddressZero],
- [oneLINK, oneLINK],
- [twoLINK, twoLINK],
- )
- await expect(tx).to.be.revertedWithCustomError(bm, INVALID_WATCHLIST_ERR)
- })
-
- it('Should revert if any of the top up amounts are 0', async () => {
- const tx = bm
- .connect(owner)
- .setWatchList(
- [watchAddress1, watchAddress2],
- [oneLINK, oneLINK],
- [twoLINK, zeroLINK],
- )
- await expect(tx).to.be.revertedWithCustomError(bm, INVALID_WATCHLIST_ERR)
- })
- })
-
- describe('getKeeperRegistryAddress() / setKeeperRegistryAddress()', () => {
- const newAddress = ethers.Wallet.createRandom().address
-
- it('Should initialize with the registry address provided to the constructor', async () => {
- const address = await bm.getKeeperRegistryAddress()
- assert.equal(address, keeperRegistry.address)
- })
-
- it('Should allow the owner to set the registry address', async () => {
- const setTx = await bm.connect(owner).setKeeperRegistryAddress(newAddress)
- await setTx.wait()
- const address = await bm.getKeeperRegistryAddress()
- assert.equal(address, newAddress)
- })
-
- it('Should not allow strangers to set the registry address', async () => {
- const setTx = bm.connect(stranger).setKeeperRegistryAddress(newAddress)
- await expect(setTx).to.be.revertedWith(OWNABLE_ERR)
- })
-
- it('Should emit an event', async () => {
- const setTx = await bm.connect(owner).setKeeperRegistryAddress(newAddress)
- await expect(setTx)
- .to.emit(bm, 'KeeperRegistryAddressUpdated')
- .withArgs(keeperRegistry.address, newAddress)
- })
- })
-
- describe('getMinWaitPeriodSeconds / setMinWaitPeriodSeconds()', () => {
- const newWaitPeriod = BigNumber.from(1)
-
- it('Should initialize with the wait period provided to the constructor', async () => {
- const minWaitPeriod = await bm.getMinWaitPeriodSeconds()
- expect(minWaitPeriod).to.equal(0)
- })
-
- it('Should allow owner to set the wait period', async () => {
- const setTx = await bm
- .connect(owner)
- .setMinWaitPeriodSeconds(newWaitPeriod)
- await setTx.wait()
- const minWaitPeriod = await bm.getMinWaitPeriodSeconds()
- expect(minWaitPeriod).to.equal(newWaitPeriod)
- })
-
- it('Should not allow strangers to set the wait period', async () => {
- const setTx = bm.connect(stranger).setMinWaitPeriodSeconds(newWaitPeriod)
- await expect(setTx).to.be.revertedWith(OWNABLE_ERR)
- })
-
- it('Should emit an event', async () => {
- const setTx = await bm
- .connect(owner)
- .setMinWaitPeriodSeconds(newWaitPeriod)
- await expect(setTx)
- .to.emit(bm, 'MinWaitPeriodUpdated')
- .withArgs(0, newWaitPeriod)
- })
- })
-
- describe('checkUpkeep() / getUnderfundedAddresses()', () => {
- beforeEach(async () => {
- const setTx = await bm.connect(owner).setWatchList(
- [
- watchAddress1, // needs funds
- watchAddress5, // funded
- watchAddress2, // needs funds
- watchAddress6, // funded
- watchAddress3, // needs funds
- ],
- new Array(5).fill(oneLINK),
- new Array(5).fill(twoLINK),
- )
- await setTx.wait()
- })
-
- it('Should return list of address that are underfunded', async () => {
- const fundTx = await lt.connect(owner).transfer(
- bm.address,
- sixLINK, // needs 6 total
- )
- await fundTx.wait()
- const [should, payload] = await bm.checkUpkeep('0x')
- assert.isTrue(should)
- let [addresses] = ethers.utils.defaultAbiCoder.decode(
- ['address[]'],
- payload,
- )
- assert.deepEqual(addresses, [watchAddress1, watchAddress2, watchAddress3])
- // checkUpkeep payload should match getUnderfundedAddresses()
- addresses = await bm.getUnderfundedAddresses()
- assert.deepEqual(addresses, [watchAddress1, watchAddress2, watchAddress3])
- })
-
- it('Should return some results even if contract cannot fund all eligible targets', async () => {
- const fundTx = await lt.connect(owner).transfer(
- bm.address,
- fiveLINK, // needs 6 total
- )
- await fundTx.wait()
- const [should, payload] = await bm.checkUpkeep('0x')
- assert.isTrue(should)
- const [addresses] = ethers.utils.defaultAbiCoder.decode(
- ['address[]'],
- payload,
- )
- assert.deepEqual(addresses, [watchAddress1, watchAddress2])
- })
-
- it('Should omit addresses that have been funded recently', async () => {
- const setWaitPdTx = await bm.setMinWaitPeriodSeconds(3600) // 1 hour
- const fundTx = await lt.connect(owner).transfer(bm.address, sixLINK)
- await Promise.all([setWaitPdTx.wait(), fundTx.wait()])
- const block = await ethers.provider.getBlock('latest')
- const setTopUpTx = await bm.setLastTopUpXXXTestOnly(
- watchAddress2,
- block.timestamp - 100,
- )
- await setTopUpTx.wait()
- const [should, payload] = await bm.checkUpkeep('0x')
- assert.isTrue(should)
- const [addresses] = ethers.utils.defaultAbiCoder.decode(
- ['address[]'],
- payload,
- )
- assert.deepEqual(addresses, [watchAddress1, watchAddress3])
- })
-
- it('Should revert when paused', async () => {
- const tx = await bm.connect(owner).pause()
- await tx.wait()
- const ethCall = bm.checkUpkeep('0x')
- await expect(ethCall).to.be.revertedWith(PAUSED_ERR)
- })
- })
-
- describe('performUpkeep()', () => {
- let validPayload: string
- let invalidPayload: string
-
- beforeEach(async () => {
- validPayload = ethers.utils.defaultAbiCoder.encode(
- ['address[]'],
- [[watchAddress1, watchAddress2, watchAddress3]],
- )
- invalidPayload = ethers.utils.defaultAbiCoder.encode(
- ['address[]'],
- [[watchAddress1, watchAddress2, watchAddress4, watchAddress5]],
- )
- const setTx = await bm.connect(owner).setWatchList(
- [
- watchAddress1, // needs funds
- watchAddress5, // funded
- watchAddress2, // needs funds
- watchAddress6, // funded
- watchAddress3, // needs funds
- // watchAddress4 - omitted
- ],
- new Array(5).fill(oneLINK),
- new Array(5).fill(twoLINK),
- )
- await setTx.wait()
- })
-
- it('Should revert when paused', async () => {
- const pauseTx = await bm.connect(owner).pause()
- await pauseTx.wait()
- const performTx = bm.connect(keeperRegistry).performUpkeep(validPayload)
- await expect(performTx).to.be.revertedWith(PAUSED_ERR)
- })
-
- context('when partially funded', () => {
- it('Should fund as many addresses as possible', async () => {
- const fundTx = await lt.connect(owner).transfer(
- bm.address,
- fiveLINK, // only enough LINK to fund 2 addresses
- )
- await fundTx.wait()
- await assertWatchlistBalances(
- zeroLINK,
- zeroLINK,
- zeroLINK,
- zeroLINK,
- oneHundredLINK,
- oneHundredLINK,
- )
- const performTx = await bm
- .connect(keeperRegistry)
- .performUpkeep(validPayload)
- await assertWatchlistBalances(
- twoLINK,
- twoLINK,
- zeroLINK,
- zeroLINK,
- oneHundredLINK,
- oneHundredLINK,
- )
- await expect(performTx)
- .to.emit(bm, 'TopUpSucceeded')
- .withArgs(watchAddress1)
- await expect(performTx)
- .to.emit(bm, 'TopUpSucceeded')
- .withArgs(watchAddress2)
- })
- })
-
- context('when fully funded', () => {
- beforeEach(async () => {
- const fundTx = await lt.connect(owner).transfer(bm.address, tenLINK)
- await fundTx.wait()
- })
-
- it('Should fund the appropriate addresses', async () => {
- await assertWatchlistBalances(
- zeroLINK,
- zeroLINK,
- zeroLINK,
- zeroLINK,
- oneHundredLINK,
- oneHundredLINK,
- )
- const performTx = await bm
- .connect(keeperRegistry)
- .performUpkeep(validPayload, { gasLimit: 2_500_000 })
- await performTx.wait()
- await assertWatchlistBalances(
- twoLINK,
- twoLINK,
- twoLINK,
- zeroLINK,
- oneHundredLINK,
- oneHundredLINK,
- )
- })
-
- it('Should only fund active, underfunded addresses', async () => {
- await assertWatchlistBalances(
- zeroLINK,
- zeroLINK,
- zeroLINK,
- zeroLINK,
- oneHundredLINK,
- oneHundredLINK,
- )
- const performTx = await bm
- .connect(keeperRegistry)
- .performUpkeep(invalidPayload, { gasLimit: 2_500_000 })
- await performTx.wait()
- await assertWatchlistBalances(
- twoLINK,
- twoLINK,
- zeroLINK,
- zeroLINK,
- oneHundredLINK,
- oneHundredLINK,
- )
- })
-
- it('Should not fund addresses that have been funded recently', async () => {
- const setWaitPdTx = await bm.setMinWaitPeriodSeconds(3600) // 1 hour
- await setWaitPdTx.wait()
- const block = await ethers.provider.getBlock('latest')
- const setTopUpTx = await bm.setLastTopUpXXXTestOnly(
- watchAddress2,
- block.timestamp - 100,
- )
- await setTopUpTx.wait()
- await assertWatchlistBalances(
- zeroLINK,
- zeroLINK,
- zeroLINK,
- zeroLINK,
- oneHundredLINK,
- oneHundredLINK,
- )
- const performTx = await bm
- .connect(keeperRegistry)
- .performUpkeep(validPayload, { gasLimit: 2_500_000 })
- await performTx.wait()
- await assertWatchlistBalances(
- twoLINK,
- zeroLINK,
- twoLINK,
- zeroLINK,
- oneHundredLINK,
- oneHundredLINK,
- )
- })
-
- it('Should only be callable by the keeper registry contract', async () => {
- let performTx = bm.connect(owner).performUpkeep(validPayload)
- await expect(performTx).to.be.revertedWithCustomError(
- bm,
- ONLY_KEEPER_ERR,
- )
- performTx = bm.connect(stranger).performUpkeep(validPayload)
- await expect(performTx).to.be.revertedWithCustomError(
- bm,
- ONLY_KEEPER_ERR,
- )
- })
-
- it('Should protect against running out of gas', async () => {
- await assertWatchlistBalances(
- zeroLINK,
- zeroLINK,
- zeroLINK,
- zeroLINK,
- oneHundredLINK,
- oneHundredLINK,
- )
- const performTx = await bm
- .connect(keeperRegistry)
- .performUpkeep(validPayload, { gasLimit: 130_000 }) // too little for all 3 transfers
- await performTx.wait()
- const balance1 = await lt.balanceOf(watchAddress1)
- const balance2 = await lt.balanceOf(watchAddress2)
- const balance3 = await lt.balanceOf(watchAddress3)
- const balances = [balance1, balance2, balance3].map((n) => n.toString())
- expect(balances)
- .to.include(twoLINK.toString()) // expect at least 1 transfer
- .to.include(zeroLINK.toString()) // expect at least 1 out of funds
- })
-
- it('Should provide enough gas to support receive and fallback functions', async () => {
- const addresses = [
- receiveEmitter.address,
- receiveFallbackEmitter.address,
- ]
- const payload = ethers.utils.defaultAbiCoder.encode(
- ['address[]'],
- [addresses],
- )
- const setTx = await bm
- .connect(owner)
- .setWatchList(
- addresses,
- new Array(2).fill(oneLINK),
- new Array(2).fill(twoLINK),
- )
- await setTx.wait()
-
- const reBalanceBefore = await lt.balanceOf(receiveEmitter.address)
- const rfeBalanceBefore = await lt.balanceOf(
- receiveFallbackEmitter.address,
- )
- const performTx = await bm
- .connect(keeperRegistry)
- .performUpkeep(payload, { gasLimit: 2_500_000 })
- await h.assertLinkTokenBalance(
- lt,
- receiveEmitter.address,
- reBalanceBefore.add(twoLINK),
- )
- await h.assertLinkTokenBalance(
- lt,
- receiveFallbackEmitter.address,
- rfeBalanceBefore.add(twoLINK),
- )
-
- await expect(performTx)
- .to.emit(bm, 'TopUpSucceeded')
- .withArgs(receiveEmitter.address)
- await expect(performTx)
- .to.emit(bm, 'TopUpSucceeded')
- .withArgs(receiveFallbackEmitter.address)
- })
- })
- })
-
- describe('topUp()', () => {
- context('when not paused', () => {
- it('Should be callable by anyone', async () => {
- const users = [owner, keeperRegistry, stranger]
- for (let idx = 0; idx < users.length; idx++) {
- const user = users[idx]
- await bm.connect(user).topUp([])
- }
- })
- })
- context('when paused', () => {
- it('Should be callable by no one', async () => {
- await bm.connect(owner).pause()
- const users = [owner, keeperRegistry, stranger]
- for (let idx = 0; idx < users.length; idx++) {
- const user = users[idx]
- const tx = bm.connect(user).topUp([])
- await expect(tx).to.be.revertedWith(PAUSED_ERR)
- }
- })
- })
- })
-})
diff --git a/contracts/test/v0.8/automation/EthBalanceMonitor.test.ts b/contracts/test/v0.8/automation/EthBalanceMonitor.test.ts
deleted file mode 100644
index edcf1b564c9..00000000000
--- a/contracts/test/v0.8/automation/EthBalanceMonitor.test.ts
+++ /dev/null
@@ -1,663 +0,0 @@
-import { ethers } from 'hardhat'
-import { assert, expect } from 'chai'
-import type { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'
-import { EthBalanceMonitorExposed } from '../../../typechain/EthBalanceMonitorExposed'
-import { ReceiveReverter } from '../../../typechain/ReceiveReverter'
-import { ReceiveEmitter } from '../../../typechain/ReceiveEmitter'
-import { ReceiveFallbackEmitter } from '../../../typechain/ReceiveFallbackEmitter'
-import { BigNumber } from 'ethers'
-import * as h from '../../test-helpers/helpers'
-
-const OWNABLE_ERR = 'Only callable by owner'
-const INVALID_WATCHLIST_ERR = `InvalidWatchList`
-const PAUSED_ERR = 'Pausable: paused'
-const ONLY_KEEPER_ERR = `OnlyKeeperRegistry`
-
-const zeroEth = ethers.utils.parseEther('0')
-const oneEth = ethers.utils.parseEther('1')
-const twoEth = ethers.utils.parseEther('2')
-const threeEth = ethers.utils.parseEther('3')
-const fiveEth = ethers.utils.parseEther('5')
-const sixEth = ethers.utils.parseEther('6')
-const tenEth = ethers.utils.parseEther('10')
-
-const watchAddress1 = ethers.Wallet.createRandom().address
-const watchAddress2 = ethers.Wallet.createRandom().address
-const watchAddress3 = ethers.Wallet.createRandom().address
-const watchAddress4 = ethers.Wallet.createRandom().address
-let watchAddress5: string
-let watchAddress6: string
-
-async function assertWatchlistBalances(
- balance1: number,
- balance2: number,
- balance3: number,
- balance4: number,
- balance5: number,
- balance6: number,
-) {
- const toEth = (n: number) => ethers.utils.parseUnits(n.toString(), 'ether')
- await h.assertBalance(watchAddress1, toEth(balance1), 'address 1')
- await h.assertBalance(watchAddress2, toEth(balance2), 'address 2')
- await h.assertBalance(watchAddress3, toEth(balance3), 'address 3')
- await h.assertBalance(watchAddress4, toEth(balance4), 'address 4')
- await h.assertBalance(watchAddress5, toEth(balance5), 'address 5')
- await h.assertBalance(watchAddress6, toEth(balance6), 'address 6')
-}
-
-let bm: EthBalanceMonitorExposed
-let receiveReverter: ReceiveReverter
-let receiveEmitter: ReceiveEmitter
-let receiveFallbackEmitter: ReceiveFallbackEmitter
-let owner: SignerWithAddress
-let stranger: SignerWithAddress
-let keeperRegistry: SignerWithAddress
-
-describe('EthBalanceMonitor', () => {
- beforeEach(async () => {
- const accounts = await ethers.getSigners()
- owner = accounts[0]
- stranger = accounts[1]
- keeperRegistry = accounts[2]
- watchAddress5 = accounts[3].address
- watchAddress6 = accounts[4].address
-
- const bmFactory = await ethers.getContractFactory(
- 'EthBalanceMonitorExposed',
- owner,
- )
- const rrFactory = await ethers.getContractFactory('ReceiveReverter', owner)
- const reFactory = await ethers.getContractFactory('ReceiveEmitter', owner)
- const rfeFactory = await ethers.getContractFactory(
- 'ReceiveFallbackEmitter',
- owner,
- )
-
- bm = await bmFactory.deploy(keeperRegistry.address, 0)
- receiveReverter = await rrFactory.deploy()
- receiveEmitter = await reFactory.deploy()
- receiveFallbackEmitter = await rfeFactory.deploy()
- await Promise.all([
- bm.deployed(),
- receiveReverter.deployed(),
- receiveEmitter.deployed(),
- receiveFallbackEmitter.deployed(),
- ])
- })
-
- afterEach(async () => {
- await h.reset()
- })
-
- describe('receive()', () => {
- it('Should allow anyone to add funds', async () => {
- await owner.sendTransaction({
- to: bm.address,
- value: oneEth,
- })
- await stranger.sendTransaction({
- to: bm.address,
- value: oneEth,
- })
- })
-
- it('Should emit an event', async () => {
- await owner.sendTransaction({
- to: bm.address,
- value: oneEth,
- })
- const tx = stranger.sendTransaction({
- to: bm.address,
- value: oneEth,
- })
- await expect(tx)
- .to.emit(bm, 'FundsAdded')
- .withArgs(oneEth, twoEth, stranger.address)
- })
- })
-
- describe('withdraw()', () => {
- beforeEach(async () => {
- const tx = await owner.sendTransaction({
- to: bm.address,
- value: oneEth,
- })
- await tx.wait()
- })
-
- it('Should allow the owner to withdraw', async () => {
- const beforeBalance = await owner.getBalance()
- const tx = await bm.connect(owner).withdraw(oneEth, owner.address)
- await tx.wait()
- const afterBalance = await owner.getBalance()
- assert.isTrue(
- afterBalance.gt(beforeBalance),
- 'balance did not increase after withdraw',
- )
- })
-
- it('Should emit an event', async () => {
- const tx = await bm.connect(owner).withdraw(oneEth, owner.address)
- await expect(tx)
- .to.emit(bm, 'FundsWithdrawn')
- .withArgs(oneEth, owner.address)
- })
-
- it('Should allow the owner to withdraw to anyone', async () => {
- const beforeBalance = await stranger.getBalance()
- const tx = await bm.connect(owner).withdraw(oneEth, stranger.address)
- await tx.wait()
- const afterBalance = await stranger.getBalance()
- assert.isTrue(
- beforeBalance.add(oneEth).eq(afterBalance),
- 'balance did not increase after withdraw',
- )
- })
-
- it('Should not allow strangers to withdraw', async () => {
- const tx = bm.connect(stranger).withdraw(oneEth, owner.address)
- await expect(tx).to.be.revertedWith(OWNABLE_ERR)
- })
- })
-
- describe('pause() / unpause()', () => {
- it('Should allow owner to pause / unpause', async () => {
- const pauseTx = await bm.connect(owner).pause()
- await pauseTx.wait()
- const unpauseTx = await bm.connect(owner).unpause()
- await unpauseTx.wait()
- })
-
- it('Should not allow strangers to pause / unpause', async () => {
- const pauseTxStranger = bm.connect(stranger).pause()
- await expect(pauseTxStranger).to.be.revertedWith(OWNABLE_ERR)
- const pauseTxOwner = await bm.connect(owner).pause()
- await pauseTxOwner.wait()
- const unpauseTxStranger = bm.connect(stranger).unpause()
- await expect(unpauseTxStranger).to.be.revertedWith(OWNABLE_ERR)
- })
- })
-
- describe('setWatchList() / getWatchList() / getAccountInfo()', () => {
- it('Should allow owner to set the watchlist', async () => {
- // should start unactive
- assert.isFalse((await bm.getAccountInfo(watchAddress1)).isActive)
- // add first watchlist
- let setTx = await bm
- .connect(owner)
- .setWatchList([watchAddress1], [oneEth], [twoEth])
- await setTx.wait()
- let watchList = await bm.getWatchList()
- assert.deepEqual(watchList, [watchAddress1])
- const accountInfo = await bm.getAccountInfo(watchAddress1)
- assert.isTrue(accountInfo.isActive)
- expect(accountInfo.minBalanceWei).to.equal(oneEth)
- expect(accountInfo.topUpAmountWei).to.equal(twoEth)
- // add more to watchlist
- setTx = await bm
- .connect(owner)
- .setWatchList(
- [watchAddress1, watchAddress2, watchAddress3],
- [oneEth, twoEth, threeEth],
- [oneEth, twoEth, threeEth],
- )
- await setTx.wait()
- watchList = await bm.getWatchList()
- assert.deepEqual(watchList, [watchAddress1, watchAddress2, watchAddress3])
- let accountInfo1 = await bm.getAccountInfo(watchAddress1)
- let accountInfo2 = await bm.getAccountInfo(watchAddress2)
- let accountInfo3 = await bm.getAccountInfo(watchAddress3)
- expect(accountInfo1.isActive).to.be.true
- expect(accountInfo1.minBalanceWei).to.equal(oneEth)
- expect(accountInfo1.topUpAmountWei).to.equal(oneEth)
- expect(accountInfo2.isActive).to.be.true
- expect(accountInfo2.minBalanceWei).to.equal(twoEth)
- expect(accountInfo2.topUpAmountWei).to.equal(twoEth)
- expect(accountInfo3.isActive).to.be.true
- expect(accountInfo3.minBalanceWei).to.equal(threeEth)
- expect(accountInfo3.topUpAmountWei).to.equal(threeEth)
- // remove some from watchlist
- setTx = await bm
- .connect(owner)
- .setWatchList(
- [watchAddress3, watchAddress1],
- [threeEth, oneEth],
- [threeEth, oneEth],
- )
- await setTx.wait()
- watchList = await bm.getWatchList()
- assert.deepEqual(watchList, [watchAddress3, watchAddress1])
- accountInfo1 = await bm.getAccountInfo(watchAddress1)
- accountInfo2 = await bm.getAccountInfo(watchAddress2)
- accountInfo3 = await bm.getAccountInfo(watchAddress3)
- expect(accountInfo1.isActive).to.be.true
- expect(accountInfo2.isActive).to.be.false
- expect(accountInfo3.isActive).to.be.true
- })
-
- it('Should not allow duplicates in the watchlist', async () => {
- const errMsg = `DuplicateAddress`
- const setTx = bm
- .connect(owner)
- .setWatchList(
- [watchAddress1, watchAddress2, watchAddress1],
- [oneEth, twoEth, threeEth],
- [oneEth, twoEth, threeEth],
- )
- await expect(setTx)
- .to.be.revertedWithCustomError(bm, errMsg)
- .withArgs(watchAddress1)
- })
-
- it('Should not allow strangers to set the watchlist', async () => {
- const setTxStranger = bm
- .connect(stranger)
- .setWatchList([watchAddress1], [oneEth], [twoEth])
- await expect(setTxStranger).to.be.revertedWith(OWNABLE_ERR)
- })
-
- it('Should revert if the list lengths differ', async () => {
- let tx = bm.connect(owner).setWatchList([watchAddress1], [], [twoEth])
- await expect(tx).to.be.revertedWithCustomError(bm, INVALID_WATCHLIST_ERR)
- tx = bm.connect(owner).setWatchList([watchAddress1], [oneEth], [])
- await expect(tx).to.be.revertedWithCustomError(bm, INVALID_WATCHLIST_ERR)
- tx = bm.connect(owner).setWatchList([], [oneEth], [twoEth])
- await expect(tx).to.be.revertedWithCustomError(bm, INVALID_WATCHLIST_ERR)
- })
-
- it('Should revert if any of the addresses are empty', async () => {
- let tx = bm
- .connect(owner)
- .setWatchList(
- [watchAddress1, ethers.constants.AddressZero],
- [oneEth, oneEth],
- [twoEth, twoEth],
- )
- await expect(tx).to.be.revertedWithCustomError(bm, INVALID_WATCHLIST_ERR)
- })
-
- it('Should revert if any of the top up amounts are 0', async () => {
- const tx = bm
- .connect(owner)
- .setWatchList(
- [watchAddress1, watchAddress2],
- [oneEth, oneEth],
- [twoEth, zeroEth],
- )
- await expect(tx).to.be.revertedWithCustomError(bm, INVALID_WATCHLIST_ERR)
- })
- })
-
- describe('getKeeperRegistryAddress() / setKeeperRegistryAddress()', () => {
- const newAddress = ethers.Wallet.createRandom().address
-
- it('Should initialize with the registry address provided to the constructor', async () => {
- const address = await bm.getKeeperRegistryAddress()
- assert.equal(address, keeperRegistry.address)
- })
-
- it('Should allow the owner to set the registry address', async () => {
- const setTx = await bm.connect(owner).setKeeperRegistryAddress(newAddress)
- await setTx.wait()
- const address = await bm.getKeeperRegistryAddress()
- assert.equal(address, newAddress)
- })
-
- it('Should not allow strangers to set the registry address', async () => {
- const setTx = bm.connect(stranger).setKeeperRegistryAddress(newAddress)
- await expect(setTx).to.be.revertedWith(OWNABLE_ERR)
- })
-
- it('Should emit an event', async () => {
- const setTx = await bm.connect(owner).setKeeperRegistryAddress(newAddress)
- await expect(setTx)
- .to.emit(bm, 'KeeperRegistryAddressUpdated')
- .withArgs(keeperRegistry.address, newAddress)
- })
- })
-
- describe('getMinWaitPeriodSeconds / setMinWaitPeriodSeconds()', () => {
- const newWaitPeriod = BigNumber.from(1)
-
- it('Should initialize with the wait period provided to the constructor', async () => {
- const minWaitPeriod = await bm.getMinWaitPeriodSeconds()
- expect(minWaitPeriod).to.equal(0)
- })
-
- it('Should allow owner to set the wait period', async () => {
- const setTx = await bm
- .connect(owner)
- .setMinWaitPeriodSeconds(newWaitPeriod)
- await setTx.wait()
- const minWaitPeriod = await bm.getMinWaitPeriodSeconds()
- expect(minWaitPeriod).to.equal(newWaitPeriod)
- })
-
- it('Should not allow strangers to set the wait period', async () => {
- const setTx = bm.connect(stranger).setMinWaitPeriodSeconds(newWaitPeriod)
- await expect(setTx).to.be.revertedWith(OWNABLE_ERR)
- })
-
- it('Should emit an event', async () => {
- const setTx = await bm
- .connect(owner)
- .setMinWaitPeriodSeconds(newWaitPeriod)
- await expect(setTx)
- .to.emit(bm, 'MinWaitPeriodUpdated')
- .withArgs(0, newWaitPeriod)
- })
- })
-
- describe('checkUpkeep() / getUnderfundedAddresses()', () => {
- beforeEach(async () => {
- const setTx = await bm.connect(owner).setWatchList(
- [
- watchAddress1, // needs funds
- watchAddress5, // funded
- watchAddress2, // needs funds
- watchAddress6, // funded
- watchAddress3, // needs funds
- ],
- new Array(5).fill(oneEth),
- new Array(5).fill(twoEth),
- )
- await setTx.wait()
- })
-
- it('Should return list of address that are underfunded', async () => {
- const fundTx = await owner.sendTransaction({
- to: bm.address,
- value: sixEth, // needs 6 total
- })
- await fundTx.wait()
- const [should, payload] = await bm.checkUpkeep('0x')
- assert.isTrue(should)
- let [addresses] = ethers.utils.defaultAbiCoder.decode(
- ['address[]'],
- payload,
- )
- assert.deepEqual(addresses, [watchAddress1, watchAddress2, watchAddress3])
- // checkUpkeep payload should match getUnderfundedAddresses()
- addresses = await bm.getUnderfundedAddresses()
- assert.deepEqual(addresses, [watchAddress1, watchAddress2, watchAddress3])
- })
-
- it('Should return some results even if contract cannot fund all eligible targets', async () => {
- const fundTx = await owner.sendTransaction({
- to: bm.address,
- value: fiveEth, // needs 6 total
- })
- await fundTx.wait()
- const [should, payload] = await bm.checkUpkeep('0x')
- assert.isTrue(should)
- const [addresses] = ethers.utils.defaultAbiCoder.decode(
- ['address[]'],
- payload,
- )
- assert.deepEqual(addresses, [watchAddress1, watchAddress2])
- })
-
- it('Should omit addresses that have been funded recently', async () => {
- const setWaitPdTx = await bm.setMinWaitPeriodSeconds(3600) // 1 hour
- const fundTx = await owner.sendTransaction({
- to: bm.address,
- value: sixEth,
- })
- await Promise.all([setWaitPdTx.wait(), fundTx.wait()])
- const block = await ethers.provider.getBlock('latest')
- const setTopUpTx = await bm.setLastTopUpXXXTestOnly(
- watchAddress2,
- block.timestamp - 100,
- )
- await setTopUpTx.wait()
- const [should, payload] = await bm.checkUpkeep('0x')
- assert.isTrue(should)
- const [addresses] = ethers.utils.defaultAbiCoder.decode(
- ['address[]'],
- payload,
- )
- assert.deepEqual(addresses, [watchAddress1, watchAddress3])
- })
-
- it('Should revert when paused', async () => {
- const tx = await bm.connect(owner).pause()
- await tx.wait()
- const ethCall = bm.checkUpkeep('0x')
- await expect(ethCall).to.be.revertedWith(PAUSED_ERR)
- })
- })
-
- describe('performUpkeep()', () => {
- let validPayload: string
- let invalidPayload: string
-
- beforeEach(async () => {
- validPayload = ethers.utils.defaultAbiCoder.encode(
- ['address[]'],
- [[watchAddress1, watchAddress2, watchAddress3]],
- )
- invalidPayload = ethers.utils.defaultAbiCoder.encode(
- ['address[]'],
- [[watchAddress1, watchAddress2, watchAddress4, watchAddress5]],
- )
- const setTx = await bm.connect(owner).setWatchList(
- [
- watchAddress1, // needs funds
- watchAddress5, // funded
- watchAddress2, // needs funds
- watchAddress6, // funded
- watchAddress3, // needs funds
- // watchAddress4 - omitted
- ],
- new Array(5).fill(oneEth),
- new Array(5).fill(twoEth),
- )
- await setTx.wait()
- })
-
- it('Should revert when paused', async () => {
- const pauseTx = await bm.connect(owner).pause()
- await pauseTx.wait()
- const performTx = bm.connect(keeperRegistry).performUpkeep(validPayload)
- await expect(performTx).to.be.revertedWith(PAUSED_ERR)
- })
-
- context('when partially funded', () => {
- it('Should fund as many addresses as possible', async () => {
- const fundTx = await owner.sendTransaction({
- to: bm.address,
- value: fiveEth, // only enough eth to fund 2 addresses
- })
- await fundTx.wait()
- await assertWatchlistBalances(0, 0, 0, 0, 10_000, 10_000)
- const performTx = await bm
- .connect(keeperRegistry)
- .performUpkeep(validPayload)
- await assertWatchlistBalances(2, 2, 0, 0, 10_000, 10_000)
- await expect(performTx)
- .to.emit(bm, 'TopUpSucceeded')
- .withArgs(watchAddress1)
- await expect(performTx)
- .to.emit(bm, 'TopUpSucceeded')
- .withArgs(watchAddress2)
- })
- })
-
- context('when fully funded', () => {
- beforeEach(async () => {
- const fundTx = await owner.sendTransaction({
- to: bm.address,
- value: tenEth,
- })
- await fundTx.wait()
- })
-
- it('Should fund the appropriate addresses', async () => {
- await assertWatchlistBalances(0, 0, 0, 0, 10_000, 10_000)
- const performTx = await bm
- .connect(keeperRegistry)
- .performUpkeep(validPayload, { gasLimit: 2_500_000 })
- await performTx.wait()
- await assertWatchlistBalances(2, 2, 2, 0, 10_000, 10_000)
- })
-
- it('Should only fund active, underfunded addresses', async () => {
- await assertWatchlistBalances(0, 0, 0, 0, 10_000, 10_000)
- const performTx = await bm
- .connect(keeperRegistry)
- .performUpkeep(invalidPayload, { gasLimit: 2_500_000 })
- await performTx.wait()
- await assertWatchlistBalances(2, 2, 0, 0, 10_000, 10_000)
- })
-
- it('Should continue funding addresses even if one reverts', async () => {
- await assertWatchlistBalances(0, 0, 0, 0, 10_000, 10_000)
- const addresses = [
- watchAddress1,
- receiveReverter.address,
- watchAddress2,
- ]
- const setTx = await bm
- .connect(owner)
- .setWatchList(
- addresses,
- new Array(3).fill(oneEth),
- new Array(3).fill(twoEth),
- )
- await setTx.wait()
- const payload = ethers.utils.defaultAbiCoder.encode(
- ['address[]'],
- [addresses],
- )
- const performTx = await bm
- .connect(keeperRegistry)
- .performUpkeep(payload, { gasLimit: 2_500_000 })
- await performTx.wait()
- await assertWatchlistBalances(2, 2, 0, 0, 10_000, 10_000)
- await h.assertBalance(receiveReverter.address, 0)
- await expect(performTx)
- .to.emit(bm, 'TopUpSucceeded')
- .withArgs(watchAddress1)
- await expect(performTx)
- .to.emit(bm, 'TopUpSucceeded')
- .withArgs(watchAddress2)
- await expect(performTx)
- .to.emit(bm, 'TopUpFailed')
- .withArgs(receiveReverter.address)
- })
-
- it('Should not fund addresses that have been funded recently', async () => {
- const setWaitPdTx = await bm.setMinWaitPeriodSeconds(3600) // 1 hour
- await setWaitPdTx.wait()
- const block = await ethers.provider.getBlock('latest')
- const setTopUpTx = await bm.setLastTopUpXXXTestOnly(
- watchAddress2,
- block.timestamp - 100,
- )
- await setTopUpTx.wait()
- await assertWatchlistBalances(0, 0, 0, 0, 10_000, 10_000)
- const performTx = await bm
- .connect(keeperRegistry)
- .performUpkeep(validPayload, { gasLimit: 2_500_000 })
- await performTx.wait()
- await assertWatchlistBalances(2, 0, 2, 0, 10_000, 10_000)
- })
-
- it('Should only be callable by the keeper registry contract', async () => {
- let performTx = bm.connect(owner).performUpkeep(validPayload)
- await expect(performTx).to.be.revertedWithCustomError(
- bm,
- ONLY_KEEPER_ERR,
- )
- performTx = bm.connect(stranger).performUpkeep(validPayload)
- await expect(performTx).to.be.revertedWithCustomError(
- bm,
- ONLY_KEEPER_ERR,
- )
- })
-
- it('Should protect against running out of gas', async () => {
- await assertWatchlistBalances(0, 0, 0, 0, 10_000, 10_000)
- const performTx = await bm
- .connect(keeperRegistry)
- .performUpkeep(validPayload, { gasLimit: 130_000 }) // too little for all 3 transfers
- await performTx.wait()
- const balance1 = await ethers.provider.getBalance(watchAddress1)
- const balance2 = await ethers.provider.getBalance(watchAddress2)
- const balance3 = await ethers.provider.getBalance(watchAddress3)
- const balances = [balance1, balance2, balance3].map((n) => n.toString())
- expect(balances)
- .to.include(twoEth.toString()) // expect at least 1 transfer
- .to.include(zeroEth.toString()) // expect at least 1 out of funds
- })
-
- it('Should provide enough gas to support receive and fallback functions', async () => {
- const addresses = [
- receiveEmitter.address,
- receiveFallbackEmitter.address,
- ]
- const payload = ethers.utils.defaultAbiCoder.encode(
- ['address[]'],
- [addresses],
- )
- const setTx = await bm
- .connect(owner)
- .setWatchList(
- addresses,
- new Array(2).fill(oneEth),
- new Array(2).fill(twoEth),
- )
- await setTx.wait()
-
- const reBalanceBefore = await ethers.provider.getBalance(
- receiveEmitter.address,
- )
- const rfeBalanceBefore = await ethers.provider.getBalance(
- receiveFallbackEmitter.address,
- )
-
- const performTx = await bm
- .connect(keeperRegistry)
- .performUpkeep(payload, { gasLimit: 2_500_000 })
- await h.assertBalance(
- receiveEmitter.address,
- reBalanceBefore.add(twoEth),
- )
- await h.assertBalance(
- receiveFallbackEmitter.address,
- rfeBalanceBefore.add(twoEth),
- )
-
- await expect(performTx)
- .to.emit(bm, 'TopUpSucceeded')
- .withArgs(receiveEmitter.address)
- await expect(performTx)
- .to.emit(bm, 'TopUpSucceeded')
- .withArgs(receiveFallbackEmitter.address)
- })
- })
- })
-
- describe('topUp()', () => {
- context('when not paused', () => {
- it('Should be callable by anyone', async () => {
- const users = [owner, keeperRegistry, stranger]
- for (let idx = 0; idx < users.length; idx++) {
- const user = users[idx]
- await bm.connect(user).topUp([])
- }
- })
- })
- context('when paused', () => {
- it('Should be callable by no one', async () => {
- await bm.connect(owner).pause()
- const users = [owner, keeperRegistry, stranger]
- for (let idx = 0; idx < users.length; idx++) {
- const user = users[idx]
- const tx = bm.connect(user).topUp([])
- await expect(tx).to.be.revertedWith(PAUSED_ERR)
- }
- })
- })
- })
-})
diff --git a/contracts/test/v0.8/automation/IAutomationRegistryMaster2_2.test.ts b/contracts/test/v0.8/automation/IAutomationRegistryMaster2_2.test.ts
deleted file mode 100644
index 11da7273ab9..00000000000
--- a/contracts/test/v0.8/automation/IAutomationRegistryMaster2_2.test.ts
+++ /dev/null
@@ -1,117 +0,0 @@
-import fs from 'fs'
-import { ethers } from 'hardhat'
-import { assert } from 'chai'
-import { AutomationRegistry2_2__factory as AutomationRegistryFactory } from '../../../typechain/factories/AutomationRegistry2_2__factory'
-import { AutomationRegistryLogicA2_2__factory as AutomationRegistryLogicAFactory } from '../../../typechain/factories/AutomationRegistryLogicA2_2__factory'
-import { AutomationRegistryLogicB2_2__factory as AutomationRegistryLogicBFactory } from '../../../typechain/factories/AutomationRegistryLogicB2_2__factory'
-import { AutomationRegistryBase2_2__factory as AutomationRegistryBaseFactory } from '../../../typechain/factories/AutomationRegistryBase2_2__factory'
-import { Chainable__factory as ChainableFactory } from '../../../typechain/factories/Chainable__factory'
-import { IAutomationRegistryMaster__factory as IAutomationRegistryMasterFactory } from '../../../typechain/factories/IAutomationRegistryMaster__factory'
-import { IAutomationRegistryConsumer__factory as IAutomationRegistryConsumerFactory } from '../../../typechain/factories/IAutomationRegistryConsumer__factory'
-import { MigratableKeeperRegistryInterface__factory as MigratableKeeperRegistryInterfaceFactory } from '../../../typechain/factories/MigratableKeeperRegistryInterface__factory'
-import { MigratableKeeperRegistryInterfaceV2__factory as MigratableKeeperRegistryInterfaceV2Factory } from '../../../typechain/factories/MigratableKeeperRegistryInterfaceV2__factory'
-import { OCR2Abstract__factory as OCR2AbstractFactory } from '../../../typechain/factories/OCR2Abstract__factory'
-import { IAutomationV21PlusCommon__factory as IAutomationV21PlusCommonFactory } from '../../../typechain/factories/IAutomationV21PlusCommon__factory'
-import {
- assertSatisfiesEvents,
- assertSatisfiesInterface,
- entryID,
-} from './helpers'
-
-const compositeABIs = [
- AutomationRegistryFactory.abi,
- AutomationRegistryLogicAFactory.abi,
- AutomationRegistryLogicBFactory.abi,
-]
-
-/**
- * @dev because the keeper master interface is a composite of several different contracts,
- * it is possible that an interface could be satisfied by functions across different
- * contracts, and therefore not enforceable by the compiler directly. Instead, we use this
- * test to assert that the master interface satisfies the constraints of an individual interface
- */
-describe('IAutomationRegistryMaster2_2', () => {
- it('is up to date', async () => {
- const checksum = ethers.utils.id(compositeABIs.join(''))
- const knownChecksum = fs
- .readFileSync(
- 'src/v0.8/automation/interfaces/v2_2/IAutomationRegistryMaster.sol',
- )
- .toString()
- .slice(17, 83) // checksum located at top of file
- assert.equal(
- checksum,
- knownChecksum,
- 'master interface is out of date - regenerate using "pnpm ts-node ./scripts/generate-automation-master-interface.ts"',
- )
- })
-
- it('is generated from composite contracts without competing definitions', async () => {
- const sharedEntries = [
- ...ChainableFactory.abi,
- ...AutomationRegistryBaseFactory.abi,
- ]
- const abiSet = new Set()
- const sharedSet = new Set()
- for (const entry of sharedEntries) {
- sharedSet.add(entryID(entry))
- }
- for (const abi of compositeABIs) {
- for (const entry of abi) {
- const id = entryID(entry)
- if (!abiSet.has(id)) {
- abiSet.add(id)
- } else if (!sharedSet.has(id)) {
- assert.fail(
- `composite contracts contain duplicate entry: ${JSON.stringify(
- entry,
- )}`,
- )
- }
- }
- }
- })
-
- it('satisfies the IAutomationRegistryConsumer interface', async () => {
- assertSatisfiesInterface(
- IAutomationRegistryMasterFactory.abi,
- IAutomationRegistryConsumerFactory.abi,
- )
- })
-
- it('satisfies the MigratableKeeperRegistryInterface interface', async () => {
- assertSatisfiesInterface(
- IAutomationRegistryMasterFactory.abi,
- MigratableKeeperRegistryInterfaceFactory.abi,
- )
- })
-
- it('satisfies the MigratableKeeperRegistryInterfaceV2 interface', async () => {
- assertSatisfiesInterface(
- IAutomationRegistryMasterFactory.abi,
- MigratableKeeperRegistryInterfaceV2Factory.abi,
- )
- })
-
- // temporarily disable this test due to this update: https://github.com/smartcontractkit/chainlink/pull/14369/files#diff-6e79d46ea0ef204dea679ffd2a9f4dfccd090d8f405ba2d9bffad527d7b862c6L44
- it.skip('satisfies the OCR2Abstract interface', async () => {
- assertSatisfiesInterface(
- IAutomationRegistryMasterFactory.abi,
- OCR2AbstractFactory.abi,
- )
- })
-
- it('satisfies the IAutomationV2Common interface', async () => {
- assertSatisfiesInterface(
- IAutomationRegistryMasterFactory.abi,
- IAutomationV21PlusCommonFactory.abi,
- )
- })
-
- it('satisfies the IAutomationV2Common events', async () => {
- assertSatisfiesEvents(
- IAutomationRegistryMasterFactory.abi,
- IAutomationV21PlusCommonFactory.abi,
- )
- })
-})
diff --git a/contracts/test/v0.8/automation/LinkAvailableBalanceMonitor.test.ts b/contracts/test/v0.8/automation/LinkAvailableBalanceMonitor.test.ts
deleted file mode 100644
index f63de3498b1..00000000000
--- a/contracts/test/v0.8/automation/LinkAvailableBalanceMonitor.test.ts
+++ /dev/null
@@ -1,1077 +0,0 @@
-import { ethers } from 'hardhat'
-import chai, { assert, expect } from 'chai'
-import type { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'
-import { loadFixture } from '@nomicfoundation/hardhat-network-helpers'
-import * as h from '../../test-helpers/helpers'
-import { mineBlock } from '../../test-helpers/helpers'
-import { IAggregatorProxy__factory as IAggregatorProxyFactory } from '../../../typechain/factories/IAggregatorProxy__factory'
-import { ILinkAvailable__factory as ILinkAvailableFactory } from '../../../typechain/factories/ILinkAvailable__factory'
-import { LinkAvailableBalanceMonitor, LinkToken } from '../../../typechain'
-import { BigNumber } from 'ethers'
-import deepEqualInAnyOrder from 'deep-equal-in-any-order'
-import {
- deployMockContract,
- MockContract,
-} from '@ethereum-waffle/mock-contract'
-
-chai.use(deepEqualInAnyOrder)
-
-//////////////////////////////// GAS USAGE LIMITS - CHANGE WITH CAUTION //////////////////////////
-// //
-// we try to keep gas usage under this amount (max is 5M) //
-const TARGET_PERFORM_GAS_LIMIT = 2_000_000
-// we try to keep gas usage under this amount (max is 5M) the test is not a perfectly accurate //
-// measurement of gas usage because it relies on mocks which may do fewer storage reads //
-// therefore, we keep a healthy margin to avoid running over the limit! //
-const TARGET_CHECK_GAS_LIMIT = 3_500_000
-// //
-//////////////////////////////////////////////////////////////////////////////////////////////////
-const INVALID_WATCHLIST_ERR = `InvalidWatchList`
-const PAUSED_ERR = 'Pausable: paused'
-
-const zeroLINK = ethers.utils.parseEther('0')
-const oneLINK = ethers.utils.parseEther('1')
-const twoLINK = ethers.utils.parseEther('2')
-const fourLINK = ethers.utils.parseEther('4')
-const tenLINK = ethers.utils.parseEther('10')
-const oneHundredLINK = ethers.utils.parseEther('100')
-
-const randAddr = () => ethers.Wallet.createRandom().address
-
-let labm: LinkAvailableBalanceMonitor
-let lt: LinkToken
-let owner: SignerWithAddress
-let stranger: SignerWithAddress
-let keeperRegistry: SignerWithAddress
-let proxy1: MockContract
-let proxy2: MockContract
-let proxy3: MockContract
-let proxy4: MockContract // leave this proxy / aggregator unconfigured for topUp() testing
-let aggregator1: MockContract
-let aggregator2: MockContract
-let aggregator3: MockContract
-let aggregator4: MockContract // leave this proxy / aggregator unconfigured for topUp() testing
-
-let directTarget1: MockContract // Contracts which are direct target of balance monitoring without proxy
-let directTarget2: MockContract
-
-let watchListAddresses: string[]
-let watchListMinBalances: BigNumber[]
-let watchListTopUpAmounts: BigNumber[]
-let watchListDstChainSelectors: number[]
-
-async function assertContractLinkBalances(
- balance1: BigNumber,
- balance2: BigNumber,
- balance3: BigNumber,
- balance4: BigNumber,
- balance5: BigNumber,
-) {
- await h.assertLinkTokenBalance(lt, aggregator1.address, balance1, 'address 1')
- await h.assertLinkTokenBalance(lt, aggregator2.address, balance2, 'address 2')
- await h.assertLinkTokenBalance(lt, aggregator3.address, balance3, 'address 3')
- await h.assertLinkTokenBalance(
- lt,
- directTarget1.address,
- balance4,
- 'address 4',
- )
- await h.assertLinkTokenBalance(
- lt,
- directTarget2.address,
- balance5,
- 'address 5',
- )
-}
-
-const setup = async () => {
- const accounts = await ethers.getSigners()
- owner = accounts[0]
- stranger = accounts[1]
- keeperRegistry = accounts[2]
-
- proxy1 = await deployMockContract(owner, IAggregatorProxyFactory.abi)
- proxy2 = await deployMockContract(owner, IAggregatorProxyFactory.abi)
- proxy3 = await deployMockContract(owner, IAggregatorProxyFactory.abi)
- proxy4 = await deployMockContract(owner, IAggregatorProxyFactory.abi)
- aggregator1 = await deployMockContract(owner, ILinkAvailableFactory.abi)
- aggregator2 = await deployMockContract(owner, ILinkAvailableFactory.abi)
- aggregator3 = await deployMockContract(owner, ILinkAvailableFactory.abi)
- aggregator4 = await deployMockContract(owner, ILinkAvailableFactory.abi)
- directTarget1 = await deployMockContract(owner, ILinkAvailableFactory.abi)
- directTarget2 = await deployMockContract(owner, ILinkAvailableFactory.abi)
-
- await proxy1.deployed()
- await proxy2.deployed()
- await proxy3.deployed()
- await proxy4.deployed()
- await aggregator1.deployed()
- await aggregator2.deployed()
- await aggregator3.deployed()
- await aggregator4.deployed()
- await directTarget1.deployed()
- await directTarget2.deployed()
-
- watchListAddresses = [
- proxy1.address,
- proxy2.address,
- proxy3.address,
- directTarget1.address,
- directTarget2.address,
- ]
- watchListMinBalances = [oneLINK, oneLINK, oneLINK, twoLINK, twoLINK]
- watchListTopUpAmounts = [twoLINK, twoLINK, twoLINK, twoLINK, twoLINK]
- watchListDstChainSelectors = [1, 2, 3, 4, 5]
-
- await proxy1.mock.aggregator.returns(aggregator1.address)
- await proxy2.mock.aggregator.returns(aggregator2.address)
- await proxy3.mock.aggregator.returns(aggregator3.address)
-
- await aggregator1.mock.linkAvailableForPayment.returns(0)
- await aggregator2.mock.linkAvailableForPayment.returns(0)
- await aggregator3.mock.linkAvailableForPayment.returns(0)
-
- await directTarget1.mock.linkAvailableForPayment.returns(0)
- await directTarget2.mock.linkAvailableForPayment.returns(0)
-
- const labmFactory = await ethers.getContractFactory(
- 'LinkAvailableBalanceMonitor',
- owner,
- )
- const ltFactory = await ethers.getContractFactory(
- 'src/v0.8/shared/test/helpers/LinkTokenTestHelper.sol:LinkTokenTestHelper',
- owner,
- )
-
- // New parameters needed by the constructor
- const maxPerform = 5
- const maxCheck = 20
- const minWaitPeriodSeconds = 0
- const upkeepInterval = 10
-
- lt = (await ltFactory.deploy()) as LinkToken
- labm = await labmFactory.deploy(
- owner.address,
- lt.address,
- minWaitPeriodSeconds,
- maxPerform,
- maxCheck,
- upkeepInterval,
- )
- await labm.deployed()
-
- for (let i = 1; i <= 4; i++) {
- const recipient = await accounts[i].getAddress()
- await lt.connect(owner).transfer(recipient, oneHundredLINK)
- }
-
- const setTx = await labm
- .connect(owner)
- .setWatchList(
- watchListAddresses,
- watchListMinBalances,
- watchListTopUpAmounts,
- watchListDstChainSelectors,
- )
- await setTx.wait()
-}
-
-describe('LinkAvailableBalanceMonitor', () => {
- beforeEach(async () => {
- await loadFixture(setup)
- })
-
- describe('add funds', () => {
- it('should allow anyone to add funds', async () => {
- await lt.transfer(labm.address, oneLINK)
- await lt.connect(stranger).transfer(labm.address, oneLINK)
- })
- })
-
- describe('setTopUpAmount()', () => {
- it('configures the top-up amount', async () => {
- await labm
- .connect(owner)
- .setTopUpAmount(directTarget1.address, BigNumber.from(100))
- const report = await labm.getAccountInfo(directTarget1.address)
- assert.equal(report.topUpAmount.toString(), '100')
- })
-
- it('is only callable by the owner', async () => {
- await expect(
- labm.connect(stranger).setTopUpAmount(directTarget1.address, 100),
- ).to.be.reverted
- })
- })
-
- describe('setMinBalance()', () => {
- it('configures the min balance', async () => {
- await labm
- .connect(owner)
- .setMinBalance(proxy1.address, BigNumber.from(100))
- const report = await labm.getAccountInfo(proxy1.address)
- assert.equal(report.minBalance.toString(), '100')
- })
-
- it('reverts if address is not in the watchlist', async () => {
- await expect(labm.connect(owner).setMinBalance(proxy4.address, 100)).to.be
- .reverted
- })
-
- it('is only callable by the owner', async () => {
- await expect(labm.connect(stranger).setMinBalance(proxy1.address, 100)).to
- .be.reverted
- })
- })
-
- describe('setMaxPerform()', () => {
- it('configures the MaxPerform', async () => {
- await labm.connect(owner).setMaxPerform(BigNumber.from(100))
- const report = await labm.getMaxPerform()
- assert.equal(report.toString(), '100')
- })
-
- it('is only callable by the owner', async () => {
- await expect(labm.connect(stranger).setMaxPerform(100)).to.be.reverted
- })
- })
-
- describe('setMaxCheck()', () => {
- it('configures the MaxCheck', async () => {
- await labm.connect(owner).setMaxCheck(BigNumber.from(100))
- const report = await labm.getMaxCheck()
- assert.equal(report.toString(), '100')
- })
-
- it('is only callable by the owner', async () => {
- await expect(labm.connect(stranger).setMaxCheck(100)).to.be.reverted
- })
- })
-
- describe('setUpkeepInterval()', () => {
- it('configures the UpkeepInterval', async () => {
- await labm.connect(owner).setUpkeepInterval(BigNumber.from(100))
- const report = await labm.getUpkeepInterval()
- assert.equal(report.toString(), '100')
- })
-
- it('is only callable by the owner', async () => {
- await expect(labm.connect(stranger).setUpkeepInterval(100)).to.be.reverted
- })
- })
-
- describe('withdraw()', () => {
- beforeEach(async () => {
- const tx = await lt.connect(owner).transfer(labm.address, oneLINK)
- await tx.wait()
- })
-
- it('should allow the owner to withdraw', async () => {
- const beforeBalance = await lt.balanceOf(owner.address)
- const tx = await labm.connect(owner).withdraw(oneLINK, owner.address)
- await tx.wait()
- const afterBalance = await lt.balanceOf(owner.address)
- assert.isTrue(
- afterBalance.gt(beforeBalance),
- 'balance did not increase after withdraw',
- )
- })
-
- it('should emit an event', async () => {
- const tx = await labm.connect(owner).withdraw(oneLINK, owner.address)
- await expect(tx)
- .to.emit(labm, 'FundsWithdrawn')
- .withArgs(oneLINK, owner.address)
- })
-
- it('should allow the owner to withdraw to anyone', async () => {
- const beforeBalance = await lt.balanceOf(stranger.address)
- const tx = await labm.connect(owner).withdraw(oneLINK, stranger.address)
- await tx.wait()
- const afterBalance = await lt.balanceOf(stranger.address)
- assert.isTrue(
- beforeBalance.add(oneLINK).eq(afterBalance),
- 'balance did not increase after withdraw',
- )
- })
-
- it('should not allow strangers to withdraw', async () => {
- const tx = labm.connect(stranger).withdraw(oneLINK, owner.address)
- await expect(tx).to.be.reverted
- })
- })
-
- describe('pause() / unpause()', () => {
- it('should allow owner to pause / unpause', async () => {
- const pauseTx = await labm.connect(owner).pause()
- await pauseTx.wait()
- const unpauseTx = await labm.connect(owner).unpause()
- await unpauseTx.wait()
- })
-
- it('should not allow strangers to pause / unpause', async () => {
- const pauseTxStranger = labm.connect(stranger).pause()
- await expect(pauseTxStranger).to.be.reverted
- const pauseTxOwner = await labm.connect(owner).pause()
- await pauseTxOwner.wait()
- const unpauseTxStranger = labm.connect(stranger).unpause()
- await expect(unpauseTxStranger).to.be.reverted
- })
- })
-
- describe('setWatchList() / addToWatchListOrDecommissionOrDecommission() / removeFromWatchlist() / getWatchList()', () => {
- const watchAddress1 = randAddr()
- const watchAddress2 = randAddr()
- const watchAddress3 = randAddr()
-
- beforeEach(async () => {
- // reset watchlist to empty before running these tests
- await labm.connect(owner).setWatchList([], [], [], [])
- const watchList = await labm.getWatchList()
- assert.deepEqual(watchList, [])
- })
-
- it('should allow owner to adjust the watchlist', async () => {
- // add first watchlist
- await labm
- .connect(owner)
- .setWatchList([watchAddress1], [oneLINK], [oneLINK], [0])
- let watchList = await labm.getWatchList()
- assert.deepEqual(watchList[0], watchAddress1)
- // add more to watchlist
- const tx = await labm
- .connect(owner)
- .setWatchList(
- [watchAddress1, watchAddress2, watchAddress3],
- [oneLINK, oneLINK, oneLINK],
- [oneLINK, oneLINK, oneLINK],
- [1, 2, 3],
- )
- await tx.wait()
- watchList = await labm.getWatchList()
- assert.deepEqual(watchList, [watchAddress1, watchAddress2, watchAddress3])
- })
-
- it('should not allow different length arrays in the watchlist', async () => {
- const tx = labm
- .connect(owner)
- .setWatchList(
- [watchAddress1, watchAddress2, watchAddress1],
- [oneLINK, oneLINK],
- [oneLINK, oneLINK],
- [1, 2],
- )
- await expect(tx).to.be.revertedWithCustomError(
- labm,
- INVALID_WATCHLIST_ERR,
- )
- })
-
- it('should not allow duplicates in the watchlist', async () => {
- const tx = labm
- .connect(owner)
- .setWatchList(
- [watchAddress1, watchAddress2, watchAddress1],
- [oneLINK, oneLINK, oneLINK],
- [oneLINK, oneLINK, oneLINK],
- [1, 2, 3],
- )
- await expect(tx)
- .to.be.revertedWithCustomError(labm, 'DuplicateAddress')
- .withArgs(watchAddress1)
- })
-
- it('should not allow strangers to set the watchlist', async () => {
- const setTxStranger = labm
- .connect(stranger)
- .setWatchList([watchAddress1], [oneLINK], [oneLINK], [0])
- await expect(setTxStranger).to.be.reverted
- })
-
- it('should revert if any of the addresses are empty', async () => {
- const tx = labm
- .connect(owner)
- .setWatchList(
- [watchAddress1, ethers.constants.AddressZero],
- [oneLINK, oneLINK],
- [oneLINK, oneLINK],
- [1, 2],
- )
- await expect(tx).to.be.revertedWithCustomError(
- labm,
- INVALID_WATCHLIST_ERR,
- )
- })
-
- it('should allow owner to add multiple addresses with dstChainSelector 0 to the watchlist', async () => {
- let tx = await labm
- .connect(owner)
- .addToWatchListOrDecommission(watchAddress1, 0)
- await tx.wait
- let watchList = await labm.getWatchList()
- assert.deepEqual(watchList[0], watchAddress1)
-
- tx = await labm
- .connect(owner)
- .addToWatchListOrDecommission(watchAddress2, 0)
- await tx.wait
- watchList = await labm.getWatchList()
- assert.deepEqual(watchList[0], watchAddress1)
- assert.deepEqual(watchList[1], watchAddress2)
-
- tx = await labm
- .connect(owner)
- .addToWatchListOrDecommission(watchAddress3, 0)
- await tx.wait
- watchList = await labm.getWatchList()
- assert.deepEqual(watchList[0], watchAddress1)
- assert.deepEqual(watchList[1], watchAddress2)
- assert.deepEqual(watchList[2], watchAddress3)
- })
-
- it('should allow owner to add only one address with an unique non-zero dstChainSelector 0 to the watchlist', async () => {
- let tx = await labm
- .connect(owner)
- .addToWatchListOrDecommission(watchAddress1, 1)
- await tx.wait
- let watchList = await labm.getWatchList()
- assert.deepEqual(watchList[0], watchAddress1)
-
- // 1 is active
- let report = await labm.getAccountInfo(watchAddress1)
- assert.isTrue(report.isActive)
-
- tx = await labm
- .connect(owner)
- .addToWatchListOrDecommission(watchAddress2, 1)
- await tx.wait
- watchList = await labm.getWatchList()
- assert.deepEqual(watchList[0], watchAddress2)
-
- // 2 is active, 1 should be false
- report = await labm.getAccountInfo(watchAddress2)
- assert.isTrue(report.isActive)
- report = await labm.getAccountInfo(watchAddress1)
- assert.isFalse(report.isActive)
-
- tx = await labm
- .connect(owner)
- .addToWatchListOrDecommission(watchAddress3, 1)
- await tx.wait
- watchList = await labm.getWatchList()
- assert.deepEqual(watchList[0], watchAddress3)
-
- // 3 is active, 1 and 2 should be false
- report = await labm.getAccountInfo(watchAddress3)
- assert.isTrue(report.isActive)
- report = await labm.getAccountInfo(watchAddress2)
- assert.isFalse(report.isActive)
- report = await labm.getAccountInfo(watchAddress1)
- assert.isFalse(report.isActive)
- })
-
- it('should not add address 0 to the watchlist', async () => {
- await labm
- .connect(owner)
- .addToWatchListOrDecommission(ethers.constants.AddressZero, 1)
- expect(await labm.getWatchList()).to.not.contain(
- ethers.constants.AddressZero,
- )
- })
-
- it('should not allow stangers to add addresses to the watchlist', async () => {
- await expect(
- labm.connect(stranger).addToWatchListOrDecommission(watchAddress1, 1),
- ).to.be.reverted
- })
-
- it('should allow owner to remove addresses from the watchlist', async () => {
- const tx = await labm
- .connect(owner)
- .addToWatchListOrDecommission(watchAddress1, 1)
- await tx.wait
- let watchList = await labm.getWatchList()
- assert.deepEqual(watchList[0], watchAddress1)
- let report = await labm.getAccountInfo(watchAddress1)
- assert.isTrue(report.isActive)
-
- // remove address
- await labm.connect(owner).removeFromWatchList(watchAddress1)
-
- // address should be false
- report = await labm.getAccountInfo(watchAddress1)
- assert.isFalse(report.isActive)
-
- watchList = await labm.getWatchList()
- assert.deepEqual(watchList, [])
- })
-
- it('should allow only one address per dstChainSelector', async () => {
- // add address1
- await labm.connect(owner).addToWatchListOrDecommission(watchAddress1, 1)
- expect(await labm.getWatchList()).to.contain(watchAddress1)
-
- // add address2
- await labm.connect(owner).addToWatchListOrDecommission(watchAddress2, 1)
-
- // only address2 has to be in the watchlist
- const watchlist = await labm.getWatchList()
- expect(watchlist).to.not.contain(watchAddress1)
- expect(watchlist).to.contain(watchAddress2)
- })
-
- it('should delete the onRamp address on a zero-address with same dstChainSelector', async () => {
- // add address1
- await labm.connect(owner).addToWatchListOrDecommission(watchAddress1, 1)
- expect(await labm.getWatchList()).to.contain(watchAddress1)
-
- // simulates an onRampSet(zeroAddress, same dstChainSelector)
- await labm
- .connect(owner)
- .addToWatchListOrDecommission(ethers.constants.AddressZero, 1)
-
- // address1 should be cleaned
- const watchlist = await labm.getWatchList()
- expect(watchlist).to.not.contain(watchAddress1)
- assert.deepEqual(watchlist, [])
- })
- })
-
- describe('checkUpkeep() / sampleUnderfundedAddresses() [ @skip-coverage ]', () => {
- it('should return list of address that are underfunded', async () => {
- const fundTx = await lt
- .connect(owner)
- .transfer(labm.address, oneHundredLINK)
- await fundTx.wait()
-
- await labm.setWatchList(
- watchListAddresses,
- watchListMinBalances,
- watchListTopUpAmounts,
- watchListDstChainSelectors,
- )
-
- const [should, payload] = await labm.checkUpkeep('0x')
- assert.isTrue(should)
- let [addresses] = ethers.utils.defaultAbiCoder.decode(
- ['address[]'],
- payload,
- )
-
- expect(addresses).to.deep.equalInAnyOrder(watchListAddresses)
- addresses = await labm.sampleUnderfundedAddresses()
- expect(addresses).to.deep.equalInAnyOrder(watchListAddresses)
- })
-
- it('should return false because the monitor is underfunded', async () => {
- // it needs 10 LINKs to fund all 5 upkeeps, but it only has 8 LINKs
- const fundTx = await lt
- .connect(owner)
- .transfer(labm.address, fourLINK.add(fourLINK))
- await fundTx.wait()
-
- await labm.setWatchList(
- watchListAddresses,
- watchListMinBalances,
- watchListTopUpAmounts,
- watchListDstChainSelectors,
- )
-
- const [should, _] = await labm.checkUpkeep('0x')
- assert.isFalse(should)
- })
-
- it('should omit aggregators that have sufficient funding', async () => {
- const fundTx = await lt.connect(owner).transfer(
- labm.address,
- oneHundredLINK, // enough for anything that needs funding
- )
- await fundTx.wait()
-
- await labm.setWatchList(
- [aggregator2.address, directTarget1.address, directTarget2.address],
- [oneLINK, twoLINK, twoLINK],
- [oneLINK, oneLINK, oneLINK],
- [1, 2, 3],
- )
-
- // all of them are underfunded, return 3
- await aggregator2.mock.linkAvailableForPayment.returns(zeroLINK)
- await directTarget1.mock.linkAvailableForPayment.returns(zeroLINK)
- await directTarget2.mock.linkAvailableForPayment.returns(zeroLINK)
-
- let addresses = await labm.sampleUnderfundedAddresses()
- expect(addresses).to.deep.equalInAnyOrder([
- aggregator2.address,
- directTarget1.address,
- directTarget2.address,
- ])
-
- await aggregator2.mock.linkAvailableForPayment.returns(oneLINK) // aggregator2 is enough funded
- await directTarget1.mock.linkAvailableForPayment.returns(oneLINK) // directTarget1 is NOT enough funded
- await directTarget2.mock.linkAvailableForPayment.returns(oneLINK) // directTarget2 is NOT funded
- addresses = await labm.sampleUnderfundedAddresses()
- expect(addresses).to.deep.equalInAnyOrder([
- directTarget1.address,
- directTarget2.address,
- ])
-
- await directTarget1.mock.linkAvailableForPayment.returns(tenLINK)
- addresses = await labm.sampleUnderfundedAddresses()
- expect(addresses).to.deep.equalInAnyOrder([directTarget2.address])
-
- await directTarget2.mock.linkAvailableForPayment.returns(tenLINK)
- addresses = await labm.sampleUnderfundedAddresses()
- expect(addresses).to.deep.equalInAnyOrder([])
- })
-
- it('should revert when paused', async () => {
- const tx = await labm.connect(owner).pause()
- await tx.wait()
- const ethCall = labm.checkUpkeep('0x')
- await expect(ethCall).to.be.revertedWith(PAUSED_ERR)
- })
-
- context('with a large set of proxies', async () => {
- // in this test, we cheat a little bit and point each proxy to the same aggregator,
- // which helps cut down on test time
- let MAX_PERFORM: number
- let MAX_CHECK: number
- let proxyAddresses: string[]
- let minBalances: BigNumber[]
- let topUpAmount: BigNumber[]
- let aggregators: MockContract[]
- let dstChainSelectors: number[]
-
- beforeEach(async () => {
- MAX_PERFORM = await labm.getMaxPerform()
- MAX_CHECK = await labm.getMaxCheck()
- proxyAddresses = []
- minBalances = []
- topUpAmount = []
- aggregators = []
- dstChainSelectors = []
- const numAggregators = MAX_CHECK + 50
- for (let idx = 0; idx < numAggregators; idx++) {
- const proxy = await deployMockContract(
- owner,
- IAggregatorProxyFactory.abi,
- )
- const aggregator = await deployMockContract(
- owner,
- ILinkAvailableFactory.abi,
- )
- await proxy.mock.aggregator.returns(aggregator.address)
- await aggregator.mock.linkAvailableForPayment.returns(0)
- proxyAddresses.push(proxy.address)
- minBalances.push(oneLINK)
- topUpAmount.push(oneLINK)
- aggregators.push(aggregator)
- dstChainSelectors.push(0)
- }
- await labm.setWatchList(
- proxyAddresses,
- minBalances,
- topUpAmount,
- dstChainSelectors,
- )
- const watchlist = await labm.getWatchList()
- expect(watchlist).to.deep.equalInAnyOrder(proxyAddresses)
- assert.equal(watchlist.length, minBalances.length)
- })
-
- it('should not include more than MAX_PERFORM addresses', async () => {
- const addresses = await labm.sampleUnderfundedAddresses()
- expect(addresses.length).to.be.lessThanOrEqual(MAX_PERFORM)
- })
-
- it('should sample from the list of addresses pseudorandomly', async () => {
- const firstAddress: string[] = []
- for (let idx = 0; idx < 10; idx++) {
- const addresses = await labm.sampleUnderfundedAddresses()
- assert.equal(addresses.length, MAX_PERFORM)
- assert.equal(
- new Set(addresses).size,
- MAX_PERFORM,
- 'duplicate address found',
- )
- firstAddress.push(addresses[0])
- await mineBlock(ethers.provider)
- }
- assert(
- new Set(firstAddress).size > 1,
- 'sample did not shuffle starting index',
- )
- })
-
- it('can check MAX_CHECK upkeeps within the allotted gas limit', async () => {
- for (const aggregator of aggregators) {
- // here we make no aggregators eligible for funding, requiring the function to
- // traverse the whole list
- await aggregator.mock.linkAvailableForPayment.returns(tenLINK)
- }
- await labm.checkUpkeep('0x', { gasLimit: TARGET_CHECK_GAS_LIMIT })
- })
- })
- })
-
- describe('performUpkeep()', () => {
- let validPayload: string
-
- beforeEach(async () => {
- validPayload = ethers.utils.defaultAbiCoder.encode(
- ['address[]'],
- [watchListAddresses],
- )
- await labm
- .connect(owner)
- .setWatchList(
- watchListAddresses,
- watchListMinBalances,
- watchListTopUpAmounts,
- watchListDstChainSelectors,
- )
- })
-
- it('should revert when paused', async () => {
- await labm.connect(owner).pause()
- const performTx = labm.connect(keeperRegistry).performUpkeep(validPayload)
- await expect(performTx).to.be.revertedWith(PAUSED_ERR)
- })
-
- it('should fund the appropriate addresses', async () => {
- await aggregator1.mock.linkAvailableForPayment.returns(zeroLINK)
- await aggregator2.mock.linkAvailableForPayment.returns(zeroLINK)
- await aggregator3.mock.linkAvailableForPayment.returns(zeroLINK)
- await directTarget1.mock.linkAvailableForPayment.returns(zeroLINK)
- await directTarget2.mock.linkAvailableForPayment.returns(zeroLINK)
-
- const fundTx = await lt.connect(owner).transfer(labm.address, tenLINK)
- await fundTx.wait()
-
- await h.assertLinkTokenBalance(lt, aggregator1.address, zeroLINK)
- await h.assertLinkTokenBalance(lt, aggregator2.address, zeroLINK)
- await h.assertLinkTokenBalance(lt, aggregator3.address, zeroLINK)
- await h.assertLinkTokenBalance(lt, directTarget1.address, zeroLINK)
- await h.assertLinkTokenBalance(lt, directTarget2.address, zeroLINK)
-
- const performTx = await labm
- .connect(keeperRegistry)
- .performUpkeep(validPayload, { gasLimit: 1_500_000 })
- await performTx.wait()
-
- await h.assertLinkTokenBalance(lt, aggregator1.address, twoLINK)
- await h.assertLinkTokenBalance(lt, aggregator2.address, twoLINK)
- await h.assertLinkTokenBalance(lt, aggregator3.address, twoLINK)
- await h.assertLinkTokenBalance(lt, directTarget1.address, twoLINK)
- await h.assertLinkTokenBalance(lt, directTarget2.address, twoLINK)
- })
-
- it('can handle MAX_PERFORM proxies within gas limit', async () => {
- const MAX_PERFORM = await labm.getMaxPerform()
- const proxyAddresses = []
- const minBalances = []
- const topUpAmount = []
- const dstChainSelectors = []
- for (let idx = 0; idx < MAX_PERFORM; idx++) {
- const proxy = await deployMockContract(
- owner,
- IAggregatorProxyFactory.abi,
- )
- const aggregator = await deployMockContract(
- owner,
- ILinkAvailableFactory.abi,
- )
- await proxy.mock.aggregator.returns(aggregator.address)
- await aggregator.mock.linkAvailableForPayment.returns(0)
- proxyAddresses.push(proxy.address)
- minBalances.push(oneLINK)
- topUpAmount.push(oneLINK)
- dstChainSelectors.push(0)
- }
- await labm.setWatchList(
- proxyAddresses,
- minBalances,
- topUpAmount,
- dstChainSelectors,
- )
- const watchlist = await labm.getWatchList()
- expect(watchlist).to.deep.equalInAnyOrder(proxyAddresses)
- assert.equal(watchlist.length, minBalances.length)
-
- // add funds
- const wl = await labm.getWatchList()
- const fundsNeeded = BigNumber.from(0)
- for (let idx = 0; idx < wl.length; idx++) {
- const targetInfo = await labm.getAccountInfo(wl[idx])
- const targetTopUpAmount = targetInfo.topUpAmount
- fundsNeeded.add(targetTopUpAmount)
- }
- await lt.connect(owner).transfer(labm.address, fundsNeeded)
-
- // encode payload
- const payload = ethers.utils.defaultAbiCoder.encode(
- ['address[]'],
- [proxyAddresses],
- )
-
- // do the thing
- await labm
- .connect(keeperRegistry)
- .performUpkeep(payload, { gasLimit: TARGET_PERFORM_GAS_LIMIT })
- })
- })
-
- describe('topUp()', () => {
- it('should revert topUp address(0)', async () => {
- const tx = await labm.connect(owner).topUp([ethers.constants.AddressZero])
- await expect(tx).to.emit(labm, 'TopUpBlocked')
- })
-
- context('when not paused', () => {
- it('should be callable by anyone', async () => {
- const users = [owner, keeperRegistry, stranger]
- for (let idx = 0; idx < users.length; idx++) {
- const user = users[idx]
- await labm.connect(user).topUp([])
- }
- })
- })
-
- context('when paused', () => {
- it('should be callable by no one', async () => {
- await labm.connect(owner).pause()
- const users = [owner, keeperRegistry, stranger]
- for (let idx = 0; idx < users.length; idx++) {
- const user = users[idx]
- const tx = labm.connect(user).topUp([])
- await expect(tx).to.be.revertedWith(PAUSED_ERR)
- }
- })
- })
-
- context('when fully funded', () => {
- beforeEach(async () => {
- await lt.connect(owner).transfer(labm.address, tenLINK)
- await assertContractLinkBalances(
- zeroLINK,
- zeroLINK,
- zeroLINK,
- zeroLINK,
- zeroLINK,
- )
- })
-
- it('should fund the appropriate addresses', async () => {
- const ai1 = await labm.getAccountInfo(proxy1.address)
- assert.equal(0, ai1.lastTopUpTimestamp.toNumber())
- const ai4 = await labm.getAccountInfo(directTarget1.address)
- assert.equal(0, ai4.lastTopUpTimestamp.toNumber())
-
- const tx = await labm.connect(keeperRegistry).topUp(watchListAddresses)
-
- await aggregator1.mock.linkAvailableForPayment.returns(twoLINK)
- await aggregator2.mock.linkAvailableForPayment.returns(twoLINK)
- await aggregator3.mock.linkAvailableForPayment.returns(twoLINK)
- await directTarget1.mock.linkAvailableForPayment.returns(twoLINK)
- await directTarget2.mock.linkAvailableForPayment.returns(twoLINK)
-
- await expect(tx)
- .to.emit(labm, 'TopUpSucceeded')
- .withArgs(aggregator1.address, twoLINK)
- assert.equal(
- (await lt.balanceOf(aggregator1.address)).toBigInt(),
- twoLINK.toBigInt(),
- )
- const targetInfo1 = await labm.getAccountInfo(proxy1.address)
- assert.notEqual(0, targetInfo1.lastTopUpTimestamp.toNumber())
- await expect(tx)
- .to.emit(labm, 'TopUpSucceeded')
- .withArgs(aggregator2.address, twoLINK)
- await expect(tx)
- .to.emit(labm, 'TopUpSucceeded')
- .withArgs(aggregator3.address, twoLINK)
- await expect(tx)
- .to.emit(labm, 'TopUpSucceeded')
- .withArgs(directTarget1.address, twoLINK)
- assert.equal(
- (await lt.balanceOf(directTarget1.address)).toBigInt(),
- twoLINK.toBigInt(),
- )
- const targetInfo4 = await labm.getAccountInfo(directTarget1.address)
- assert.notEqual(0, targetInfo4.lastTopUpTimestamp.toNumber())
- await expect(tx)
- .to.emit(labm, 'TopUpSucceeded')
- .withArgs(directTarget2.address, twoLINK)
- })
-
- it('should only fund the addresses provided', async () => {
- await labm
- .connect(keeperRegistry)
- .topUp([proxy1.address, directTarget1.address])
-
- await aggregator1.mock.linkAvailableForPayment.returns(twoLINK)
- await aggregator2.mock.linkAvailableForPayment.returns(zeroLINK)
- await aggregator3.mock.linkAvailableForPayment.returns(zeroLINK)
- await directTarget1.mock.linkAvailableForPayment.returns(twoLINK)
- await directTarget2.mock.linkAvailableForPayment.returns(zeroLINK)
- })
-
- it('should skip un-approved addresses', async () => {
- await labm
- .connect(owner)
- .setWatchList(
- [proxy1.address, directTarget1.address],
- [oneLINK, oneLINK],
- [oneLINK, oneLINK],
- [1, 2],
- )
- const tx = await labm
- .connect(keeperRegistry)
- .topUp([
- proxy1.address,
- proxy2.address,
- proxy3.address,
- directTarget1.address,
- directTarget2.address,
- ])
-
- await h.assertLinkTokenBalance(lt, aggregator1.address, oneLINK)
- await h.assertLinkTokenBalance(lt, aggregator2.address, zeroLINK)
- await h.assertLinkTokenBalance(lt, aggregator3.address, zeroLINK)
- await h.assertLinkTokenBalance(lt, directTarget1.address, oneLINK)
- await h.assertLinkTokenBalance(lt, directTarget2.address, zeroLINK)
-
- await expect(tx)
- .to.emit(labm, 'TopUpSucceeded')
- .withArgs(aggregator1.address, oneLINK)
- const targetInfo1 = await labm.getAccountInfo(proxy1.address)
- assert.notEqual(0, targetInfo1.lastTopUpTimestamp.toNumber())
-
- await expect(tx)
- .to.emit(labm, 'TopUpSucceeded')
- .withArgs(directTarget1.address, oneLINK)
- await expect(tx).to.emit(labm, 'TopUpBlocked').withArgs(proxy2.address)
- await expect(tx).to.emit(labm, 'TopUpBlocked').withArgs(proxy3.address)
- await expect(tx)
- .to.emit(labm, 'TopUpBlocked')
- .withArgs(directTarget2.address)
- const targetInfo5 = await labm.getAccountInfo(directTarget2.address)
- assert.equal(0, targetInfo5.lastTopUpTimestamp.toNumber())
- })
-
- it('should skip an address if the proxy is invalid and it is not a direct target', async () => {
- await labm
- .connect(owner)
- .setWatchList(
- [proxy1.address, proxy4.address],
- [oneLINK, oneLINK],
- [oneLINK, oneLINK],
- [1, 2],
- )
- const tx = await labm
- .connect(keeperRegistry)
- .topUp([proxy1.address, proxy4.address])
- await expect(tx)
- .to.emit(labm, 'TopUpSucceeded')
- .withArgs(aggregator1.address, oneLINK)
- await expect(tx).to.emit(labm, 'TopUpBlocked').withArgs(proxy4.address)
- })
-
- it('should skip an address if the aggregator is invalid', async () => {
- await proxy4.mock.aggregator.returns(aggregator4.address)
- await labm
- .connect(owner)
- .setWatchList(
- [proxy1.address, proxy4.address],
- [oneLINK, oneLINK],
- [oneLINK, oneLINK],
- [1, 2],
- )
- const tx = await labm
- .connect(keeperRegistry)
- .topUp([proxy1.address, proxy4.address])
- await expect(tx)
- .to.emit(labm, 'TopUpSucceeded')
- .withArgs(aggregator1.address, oneLINK)
- await expect(tx).to.emit(labm, 'TopUpBlocked').withArgs(proxy4.address)
- })
-
- it('should skip an address if the aggregator has sufficient funding', async () => {
- await proxy4.mock.aggregator.returns(aggregator4.address)
- await aggregator4.mock.linkAvailableForPayment.returns(tenLINK)
- await labm
- .connect(owner)
- .setWatchList(
- [proxy1.address, proxy4.address],
- [oneLINK, oneLINK],
- [oneLINK, oneLINK],
- [1, 2],
- )
- const tx = await labm
- .connect(keeperRegistry)
- .topUp([proxy1.address, proxy4.address])
- await expect(tx)
- .to.emit(labm, 'TopUpSucceeded')
- .withArgs(aggregator1.address, oneLINK)
- await expect(tx).to.emit(labm, 'TopUpBlocked').withArgs(proxy4.address)
- })
-
- it('should skip an address if the direct target has sufficient funding', async () => {
- await directTarget1.mock.linkAvailableForPayment.returns(tenLINK)
- await labm
- .connect(owner)
- .setWatchList(
- [proxy1.address, directTarget1.address],
- [oneLINK, oneLINK],
- [oneLINK, oneLINK],
- [1, 2],
- )
- const tx = await labm
- .connect(keeperRegistry)
- .topUp([proxy1.address, directTarget1.address])
- await expect(tx)
- .to.emit(labm, 'TopUpSucceeded')
- .withArgs(aggregator1.address, oneLINK)
- assert.equal(
- (await lt.balanceOf(aggregator1.address)).toBigInt(),
- oneLINK.toBigInt(),
- )
- await expect(tx)
- .to.emit(labm, 'TopUpBlocked')
- .withArgs(directTarget1.address)
- })
- })
-
- context('when partially funded', () => {
- it('should fund as many addresses as possible', async () => {
- await lt.connect(owner).transfer(
- labm.address,
- fourLINK, // only enough LINK to fund 2 addresses
- )
-
- await aggregator1.mock.linkAvailableForPayment.returns(twoLINK)
- await aggregator2.mock.linkAvailableForPayment.returns(twoLINK)
- await aggregator3.mock.linkAvailableForPayment.returns(zeroLINK)
- await directTarget1.mock.linkAvailableForPayment.returns(zeroLINK)
- await directTarget2.mock.linkAvailableForPayment.returns(zeroLINK)
-
- const tx = await labm.connect(keeperRegistry).topUp(watchListAddresses)
- await expect(tx)
- .to.emit(labm, 'TopUpSucceeded')
- .withArgs(aggregator3.address, twoLINK)
- await expect(tx)
- .to.emit(labm, 'TopUpSucceeded')
- .withArgs(directTarget1.address, twoLINK)
- assert.equal(
- (await lt.balanceOf(aggregator3.address)).toBigInt(),
- twoLINK.toBigInt(),
- )
- assert.equal(
- (await lt.balanceOf(directTarget1.address)).toBigInt(),
- twoLINK.toBigInt(),
- )
- })
- })
- })
-})
diff --git a/contracts/test/v0.8/automation/UpkeepBalanceMonitor.test.ts b/contracts/test/v0.8/automation/UpkeepBalanceMonitor.test.ts
deleted file mode 100644
index 0ee244130ab..00000000000
--- a/contracts/test/v0.8/automation/UpkeepBalanceMonitor.test.ts
+++ /dev/null
@@ -1,402 +0,0 @@
-import { ethers } from 'hardhat'
-import { expect } from 'chai'
-import type { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'
-import { randomAddress } from '../../test-helpers/helpers'
-import { loadFixture } from '@nomicfoundation/hardhat-network-helpers'
-import { IKeeperRegistryMaster__factory as RegistryFactory } from '../../../typechain/factories/IKeeperRegistryMaster__factory'
-import { IAutomationForwarder__factory as ForwarderFactory } from '../../../typechain/factories/IAutomationForwarder__factory'
-import { UpkeepBalanceMonitor } from '../../../typechain/UpkeepBalanceMonitor'
-import { LinkToken } from '../../../typechain/LinkToken'
-import { BigNumber } from 'ethers'
-import {
- deployMockContract,
- MockContract,
-} from '@ethereum-waffle/mock-contract'
-
-let owner: SignerWithAddress
-let stranger: SignerWithAddress
-let registry: MockContract
-let registry2: MockContract
-let forwarder: MockContract
-let linkToken: LinkToken
-let upkeepBalanceMonitor: UpkeepBalanceMonitor
-
-const setup = async () => {
- const accounts = await ethers.getSigners()
- owner = accounts[0]
- stranger = accounts[1]
-
- const ltFactory = await ethers.getContractFactory(
- 'src/v0.8/shared/test/helpers/LinkTokenTestHelper.sol:LinkTokenTestHelper',
- owner,
- )
- linkToken = (await ltFactory.deploy()) as LinkToken
- const bmFactory = await ethers.getContractFactory(
- 'UpkeepBalanceMonitor',
- owner,
- )
- upkeepBalanceMonitor = await bmFactory.deploy(linkToken.address, {
- maxBatchSize: 10,
- minPercentage: 120,
- targetPercentage: 300,
- maxTopUpAmount: ethers.utils.parseEther('100'),
- })
- registry = await deployMockContract(owner, RegistryFactory.abi)
- registry2 = await deployMockContract(owner, RegistryFactory.abi)
- forwarder = await deployMockContract(owner, ForwarderFactory.abi)
- await forwarder.mock.getRegistry.returns(registry.address)
- await upkeepBalanceMonitor.setForwarder(forwarder.address)
- await linkToken
- .connect(owner)
- .transfer(upkeepBalanceMonitor.address, ethers.utils.parseEther('10000'))
- await upkeepBalanceMonitor
- .connect(owner)
- .setWatchList(registry.address, [0, 1, 2, 3, 4, 5, 6, 7, 8])
- await upkeepBalanceMonitor
- .connect(owner)
- .setWatchList(registry2.address, [9, 10, 11])
- for (let i = 0; i < 9; i++) {
- await registry.mock.getMinBalance.withArgs(i).returns(100)
- await registry.mock.getBalance.withArgs(i).returns(121) // all upkeeps are sufficiently funded
- }
- for (let i = 9; i < 12; i++) {
- await registry2.mock.getMinBalance.withArgs(i).returns(100)
- await registry2.mock.getBalance.withArgs(i).returns(121) // all upkeeps are sufficiently funded
- }
-}
-
-describe('UpkeepBalanceMonitor', () => {
- beforeEach(async () => {
- await loadFixture(setup)
- })
-
- describe('constructor()', () => {
- it('should set the initial values correctly', async () => {
- const config = await upkeepBalanceMonitor.getConfig()
- expect(config.maxBatchSize).to.equal(10)
- expect(config.minPercentage).to.equal(120)
- expect(config.targetPercentage).to.equal(300)
- expect(config.maxTopUpAmount).to.equal(ethers.utils.parseEther('100'))
- })
- })
-
- describe('setConfig()', () => {
- const newConfig = {
- maxBatchSize: 100,
- minPercentage: 150,
- targetPercentage: 500,
- maxTopUpAmount: 1,
- }
-
- it('should set config correctly', async () => {
- await upkeepBalanceMonitor.connect(owner).setConfig(newConfig)
- const config = await upkeepBalanceMonitor.getConfig()
- expect(config.maxBatchSize).to.equal(newConfig.maxBatchSize)
- expect(config.minPercentage).to.equal(newConfig.minPercentage)
- expect(config.targetPercentage).to.equal(newConfig.targetPercentage)
- expect(config.maxTopUpAmount).to.equal(newConfig.maxTopUpAmount)
- })
-
- it('cannot be called by a non-owner', async () => {
- await expect(
- upkeepBalanceMonitor.connect(stranger).setConfig(newConfig),
- ).to.be.revertedWith('Only callable by owner')
- })
-
- it('should emit an event', async () => {
- await expect(
- upkeepBalanceMonitor.connect(owner).setConfig(newConfig),
- ).to.emit(upkeepBalanceMonitor, 'ConfigSet')
- })
- })
-
- describe('setForwarder()', () => {
- const newForwarder = randomAddress()
-
- it('should set the forwarder correctly', async () => {
- await upkeepBalanceMonitor.connect(owner).setForwarder(newForwarder)
- const forwarderAddress = await upkeepBalanceMonitor.getForwarder()
- expect(forwarderAddress).to.equal(newForwarder)
- })
-
- it('cannot be called by a non-owner', async () => {
- await expect(
- upkeepBalanceMonitor.connect(stranger).setForwarder(randomAddress()),
- ).to.be.revertedWith('Only callable by owner')
- })
-
- it('should emit an event', async () => {
- await expect(
- upkeepBalanceMonitor.connect(owner).setForwarder(newForwarder),
- )
- .to.emit(upkeepBalanceMonitor, 'ForwarderSet')
- .withArgs(newForwarder)
- })
- })
-
- describe('setWatchList()', () => {
- const newWatchList = [
- BigNumber.from(1),
- BigNumber.from(2),
- BigNumber.from(10),
- ]
-
- it('should add addresses to the watchlist', async () => {
- await upkeepBalanceMonitor
- .connect(owner)
- .setWatchList(registry.address, newWatchList)
- const [_, upkeepIDs] = await upkeepBalanceMonitor.getWatchList()
- expect(upkeepIDs[0]).to.deep.equal(newWatchList)
- })
-
- it('cannot be called by a non-owner', async () => {
- await expect(
- upkeepBalanceMonitor
- .connect(stranger)
- .setWatchList(registry.address, [1, 2, 3]),
- ).to.be.revertedWith('Only callable by owner')
- })
-
- it('should emit an event', async () => {
- await expect(
- upkeepBalanceMonitor
- .connect(owner)
- .setWatchList(registry.address, newWatchList),
- )
- .to.emit(upkeepBalanceMonitor, 'WatchListSet')
- .withArgs(registry.address)
- })
- })
-
- describe('withdraw()', () => {
- const payee = randomAddress()
- const withdrawAmount = 100
-
- it('should withdraw funds to a payee', async () => {
- const initialBalance = await linkToken.balanceOf(
- upkeepBalanceMonitor.address,
- )
- await upkeepBalanceMonitor.connect(owner).withdraw(withdrawAmount, payee)
- const finalBalance = await linkToken.balanceOf(
- upkeepBalanceMonitor.address,
- )
- const payeeBalance = await linkToken.balanceOf(payee)
- expect(finalBalance).to.equal(initialBalance.sub(withdrawAmount))
- expect(payeeBalance).to.equal(withdrawAmount)
- })
-
- it('cannot be called by a non-owner', async () => {
- await expect(
- upkeepBalanceMonitor.connect(stranger).withdraw(withdrawAmount, payee),
- ).to.be.revertedWith('Only callable by owner')
- })
-
- it('should emit an event', async () => {
- await expect(
- upkeepBalanceMonitor.connect(owner).withdraw(withdrawAmount, payee),
- )
- .to.emit(upkeepBalanceMonitor, 'FundsWithdrawn')
- .withArgs(100, payee)
- })
- })
-
- describe('pause() and unpause()', () => {
- it('should pause and unpause the contract', async () => {
- await upkeepBalanceMonitor.connect(owner).pause()
- expect(await upkeepBalanceMonitor.paused()).to.be.true
- await upkeepBalanceMonitor.connect(owner).unpause()
- expect(await upkeepBalanceMonitor.paused()).to.be.false
- })
-
- it('cannot be called by a non-owner', async () => {
- await expect(
- upkeepBalanceMonitor.connect(stranger).pause(),
- ).to.be.revertedWith('Only callable by owner')
- await upkeepBalanceMonitor.connect(owner).pause()
- await expect(
- upkeepBalanceMonitor.connect(stranger).unpause(),
- ).to.be.revertedWith('Only callable by owner')
- })
- })
-
- describe('checkUpkeep() / getUnderfundedUpkeeps()', () => {
- it('should find the underfunded upkeeps', async () => {
- let [upkeepIDs, registries, topUpAmounts] =
- await upkeepBalanceMonitor.getUnderfundedUpkeeps()
- expect(upkeepIDs.length).to.equal(0)
- expect(registries.length).to.equal(0)
- expect(topUpAmounts.length).to.equal(0)
- let [upkeepNeeded, performData] =
- await upkeepBalanceMonitor.checkUpkeep('0x')
- expect(upkeepNeeded).to.be.false
- expect(performData).to.equal('0x')
- // update the balance for some upkeeps
- await registry.mock.getBalance.withArgs(2).returns(120)
- await registry.mock.getBalance.withArgs(4).returns(15)
- await registry.mock.getBalance.withArgs(5).returns(0)
- ;[upkeepIDs, registries, topUpAmounts] =
- await upkeepBalanceMonitor.getUnderfundedUpkeeps()
- expect(upkeepIDs.map((v) => v.toNumber())).to.deep.equal([2, 4, 5])
- expect(registries).to.deep.equal([
- registry.address,
- registry.address,
- registry.address,
- ])
- expect(topUpAmounts.map((v) => v.toNumber())).to.deep.equal([
- 180, 285, 300,
- ])
- ;[upkeepNeeded, performData] =
- await upkeepBalanceMonitor.checkUpkeep('0x')
- expect(upkeepNeeded).to.be.true
- expect(performData).to.equal(
- ethers.utils.defaultAbiCoder.encode(
- ['uint256[]', 'address[]', 'uint256[]'],
- [
- [2, 4, 5],
- [registry.address, registry.address, registry.address],
- [180, 285, 300],
- ],
- ),
- )
- // update all to need funding
- for (let i = 0; i < 9; i++) {
- await registry.mock.getBalance.withArgs(i).returns(0)
- }
- for (let i = 9; i < 12; i++) {
- await registry2.mock.getBalance.withArgs(i).returns(0)
- }
- // only the max batch size are included in the list
- ;[upkeepIDs, registries, topUpAmounts] =
- await upkeepBalanceMonitor.getUnderfundedUpkeeps()
- expect(upkeepIDs.length).to.equal(10)
- expect(topUpAmounts.length).to.equal(10)
- expect(upkeepIDs.map((v) => v.toNumber())).to.deep.equal([
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
- ])
- expect(registries).to.deep.equal([
- ...Array(9).fill(registry.address),
- registry2.address,
- ])
- expect(topUpAmounts.map((v) => v.toNumber())).to.deep.equal([
- ...Array(10).fill(300),
- ])
- // update the balance for some upkeeps
- await registry.mock.getBalance.withArgs(0).returns(300)
- await registry.mock.getBalance.withArgs(5).returns(300)
- ;[upkeepIDs, registries, topUpAmounts] =
- await upkeepBalanceMonitor.getUnderfundedUpkeeps()
- expect(upkeepIDs.length).to.equal(10)
- expect(topUpAmounts.length).to.equal(10)
- expect(upkeepIDs.map((v) => v.toNumber())).to.deep.equal([
- 1, 2, 3, 4, 6, 7, 8, 9, 10, 11,
- ])
- expect(registries).to.deep.equal([
- ...Array(7).fill(registry.address),
- ...Array(3).fill(registry2.address),
- ])
- expect(topUpAmounts.map((v) => v.toNumber())).to.deep.equal([
- ...Array(10).fill(300),
- ])
- })
- })
-
- describe('topUp()', () => {
- beforeEach(async () => {
- await registry.mock.onTokenTransfer
- .withArgs(
- upkeepBalanceMonitor.address,
- 100,
- ethers.utils.defaultAbiCoder.encode(['uint256'], [1]),
- )
- .returns()
- await registry.mock.onTokenTransfer
- .withArgs(
- upkeepBalanceMonitor.address,
- 50,
- ethers.utils.defaultAbiCoder.encode(['uint256'], [7]),
- )
- .returns()
- })
-
- it('cannot be called by a non-owner', async () => {
- await expect(
- upkeepBalanceMonitor.connect(stranger).topUp([], [], []),
- ).to.be.revertedWithCustomError(
- upkeepBalanceMonitor,
- 'OnlyForwarderOrOwner',
- )
- })
-
- it('should revert if the contract is paused', async () => {
- await upkeepBalanceMonitor.connect(owner).pause()
- await expect(
- upkeepBalanceMonitor.connect(owner).topUp([], [], []),
- ).to.be.revertedWith('Pausable: paused')
- })
-
- it('tops up the upkeeps by the amounts provided', async () => {
- const initialBalance = await linkToken.balanceOf(registry.address)
- const tx = await upkeepBalanceMonitor
- .connect(owner)
- .topUp([1, 7], [registry.address, registry.address], [100, 50])
- const finalBalance = await linkToken.balanceOf(registry.address)
- expect(finalBalance).to.equal(initialBalance.add(150))
- await expect(tx)
- .to.emit(upkeepBalanceMonitor, 'TopUpSucceeded')
- .withArgs(1, 100)
- await expect(tx)
- .to.emit(upkeepBalanceMonitor, 'TopUpSucceeded')
- .withArgs(7, 50)
- })
-
- it('does not abort if one top-up fails', async () => {
- const initialBalance = await linkToken.balanceOf(registry.address)
- const tx = await upkeepBalanceMonitor
- .connect(owner)
- .topUp(
- [1, 7, 100],
- [registry.address, registry.address, registry.address],
- [100, 50, 100],
- )
- const finalBalance = await linkToken.balanceOf(registry.address)
- expect(finalBalance).to.equal(initialBalance.add(150))
- await expect(tx)
- .to.emit(upkeepBalanceMonitor, 'TopUpSucceeded')
- .withArgs(1, 100)
- await expect(tx)
- .to.emit(upkeepBalanceMonitor, 'TopUpSucceeded')
- .withArgs(7, 50)
- await expect(tx)
- .to.emit(upkeepBalanceMonitor, 'TopUpFailed')
- .withArgs(100)
- })
- })
-
- describe('checkUpkeep() / performUpkeep()', () => {
- it('works round-trip', async () => {
- await registry.mock.getBalance.withArgs(1).returns(100) // needs 200
- await registry.mock.getBalance.withArgs(7).returns(0) // needs 300
- await registry.mock.onTokenTransfer
- .withArgs(
- upkeepBalanceMonitor.address,
- 200,
- ethers.utils.defaultAbiCoder.encode(['uint256'], [1]),
- )
- .returns()
- await registry.mock.onTokenTransfer
- .withArgs(
- upkeepBalanceMonitor.address,
- 300,
- ethers.utils.defaultAbiCoder.encode(['uint256'], [7]),
- )
- .returns()
- const [upkeepNeeded, performData] =
- await upkeepBalanceMonitor.checkUpkeep('0x')
- expect(upkeepNeeded).to.be.true
- const initialBalance = await linkToken.balanceOf(registry.address)
- await upkeepBalanceMonitor.connect(owner).performUpkeep(performData)
- const finalBalance = await linkToken.balanceOf(registry.address)
- expect(finalBalance).to.equal(initialBalance.add(500))
- })
- })
-})
diff --git a/contracts/test/v0.8/automation/UpkeepTranscoder3_0.test.ts b/contracts/test/v0.8/automation/UpkeepTranscoder3_0.test.ts
deleted file mode 100644
index 7fd811d8226..00000000000
--- a/contracts/test/v0.8/automation/UpkeepTranscoder3_0.test.ts
+++ /dev/null
@@ -1,576 +0,0 @@
-import { ethers } from 'hardhat'
-import { assert, expect } from 'chai'
-import { UpkeepTranscoder30__factory as UpkeepTranscoderFactory } from '../../../typechain/factories/UpkeepTranscoder30__factory'
-import { UpkeepTranscoder30 as UpkeepTranscoder } from '../../../typechain/UpkeepTranscoder30'
-import { KeeperRegistry2_0__factory as KeeperRegistry2_0Factory } from '../../../typechain/factories/KeeperRegistry2_0__factory'
-import { LinkToken__factory as LinkTokenFactory } from '../../../typechain/factories/LinkToken__factory'
-import { MockV3Aggregator__factory as MockV3AggregatorFactory } from '../../../typechain/factories/MockV3Aggregator__factory'
-import { UpkeepMock__factory as UpkeepMockFactory } from '../../../typechain/factories/UpkeepMock__factory'
-import { evmRevert } from '../../test-helpers/matchers'
-import { BigNumber, Signer } from 'ethers'
-import { getUsers, Personas } from '../../test-helpers/setup'
-import { KeeperRegistryLogic2_0__factory as KeeperRegistryLogic20Factory } from '../../../typechain/factories/KeeperRegistryLogic2_0__factory'
-import { KeeperRegistry1_3__factory as KeeperRegistry1_3Factory } from '../../../typechain/factories/KeeperRegistry1_3__factory'
-import { KeeperRegistryLogic1_3__factory as KeeperRegistryLogicFactory } from '../../../typechain/factories/KeeperRegistryLogic1_3__factory'
-import { toWei } from '../../test-helpers/helpers'
-import { LinkToken } from '../../../typechain'
-
-let upkeepMockFactory: UpkeepMockFactory
-let upkeepTranscoderFactory: UpkeepTranscoderFactory
-let transcoder: UpkeepTranscoder
-let linkTokenFactory: LinkTokenFactory
-let mockV3AggregatorFactory: MockV3AggregatorFactory
-let keeperRegistryFactory20: KeeperRegistry2_0Factory
-let keeperRegistryFactory13: KeeperRegistry1_3Factory
-let keeperRegistryLogicFactory20: KeeperRegistryLogic20Factory
-let keeperRegistryLogicFactory13: KeeperRegistryLogicFactory
-let personas: Personas
-let owner: Signer
-let upkeepsV1: any[]
-let upkeepsV2: any[]
-let upkeepsV3: any[]
-let admins: string[]
-let admin0: Signer
-let admin1: Signer
-const executeGas = BigNumber.from('100000')
-const paymentPremiumPPB = BigNumber.from('250000000')
-const flatFeeMicroLink = BigNumber.from(0)
-const blockCountPerTurn = BigNumber.from(3)
-const randomBytes = '0x1234abcd'
-const stalenessSeconds = BigNumber.from(43820)
-const gasCeilingMultiplier = BigNumber.from(1)
-const checkGasLimit = BigNumber.from(20000000)
-const fallbackGasPrice = BigNumber.from(200)
-const fallbackLinkPrice = BigNumber.from(200000000)
-const maxPerformGas = BigNumber.from(5000000)
-const minUpkeepSpend = BigNumber.from(0)
-const maxCheckDataSize = BigNumber.from(1000)
-const maxPerformDataSize = BigNumber.from(1000)
-const mode = BigNumber.from(0)
-const linkEth = BigNumber.from(300000000)
-const gasWei = BigNumber.from(100)
-const registryGasOverhead = BigNumber.from('80000')
-const balance = 50000000000000
-const amountSpent = 200000000000000
-const target0 = '0xffffffffffffffffffffffffffffffffffffffff'
-const target1 = '0xfffffffffffffffffffffffffffffffffffffffe'
-const lastKeeper0 = '0x233a95ccebf3c9f934482c637c08b4015cdd6ddd'
-const lastKeeper1 = '0x233a95ccebf3c9f934482c637c08b4015cdd6ddc'
-enum UpkeepFormat {
- V1,
- V2,
- V3,
- V4,
-}
-const idx = [123, 124]
-
-async function getUpkeepID(tx: any) {
- const receipt = await tx.wait()
- return receipt.events[0].args.id
-}
-
-const encodeConfig = (config: any) => {
- return ethers.utils.defaultAbiCoder.encode(
- [
- 'tuple(uint32 paymentPremiumPPB,uint32 flatFeeMicroLink,uint32 checkGasLimit,uint24 stalenessSeconds\
- ,uint16 gasCeilingMultiplier,uint96 minUpkeepSpend,uint32 maxPerformGas,uint32 maxCheckDataSize,\
- uint32 maxPerformDataSize,uint256 fallbackGasPrice,uint256 fallbackLinkPrice,address transcoder,\
- address registrar)',
- ],
- [config],
- )
-}
-
-const encodeUpkeepV1 = (ids: number[], upkeeps: any[], checkDatas: any[]) => {
- return ethers.utils.defaultAbiCoder.encode(
- [
- 'uint256[]',
- 'tuple(uint96,address,uint32,uint64,address,uint96,address)[]',
- 'bytes[]',
- ],
- [ids, upkeeps, checkDatas],
- )
-}
-
-const encodeUpkeepV2 = (ids: number[], upkeeps: any[], checkDatas: any[]) => {
- return ethers.utils.defaultAbiCoder.encode(
- [
- 'uint256[]',
- 'tuple(uint96,address,uint96,address,uint32,uint32,address,bool)[]',
- 'bytes[]',
- ],
- [ids, upkeeps, checkDatas],
- )
-}
-
-const encodeUpkeepV3 = (
- ids: number[],
- upkeeps: any[],
- checkDatas: any[],
- admins: string[],
-) => {
- return ethers.utils.defaultAbiCoder.encode(
- [
- 'uint256[]',
- 'tuple(uint32,uint32,bool,address,uint96,uint96,uint32)[]',
- 'bytes[]',
- 'address[]',
- ],
- [ids, upkeeps, checkDatas, admins],
- )
-}
-
-before(async () => {
- // @ts-ignore bug in autogen file
- upkeepTranscoderFactory = await ethers.getContractFactory(
- 'UpkeepTranscoder3_0',
- )
- personas = (await getUsers()).personas
-
- 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/shared/mocks/MockV3Aggregator.sol:MockV3Aggregator',
- )) as unknown as MockV3AggregatorFactory
-
- upkeepMockFactory = await ethers.getContractFactory('UpkeepMock')
-
- owner = personas.Norbert
- admin0 = personas.Neil
- admin1 = personas.Nick
- admins = [
- (await admin0.getAddress()).toLowerCase(),
- (await admin1.getAddress()).toLowerCase(),
- ]
-})
-
-async function deployLinkToken() {
- return await linkTokenFactory.connect(owner).deploy()
-}
-
-async function deployFeeds() {
- return [
- await mockV3AggregatorFactory.connect(owner).deploy(0, gasWei),
- await mockV3AggregatorFactory.connect(owner).deploy(9, linkEth),
- ]
-}
-
-async function deployLegacyRegistry1_2(
- linkToken: LinkToken,
- gasPriceFeed: any,
- linkEthFeed: any,
-) {
- const mock = await upkeepMockFactory.deploy()
- // @ts-ignore bug in autogen file
- const keeperRegistryFactory =
- await ethers.getContractFactory('KeeperRegistry1_2')
- transcoder = await upkeepTranscoderFactory.connect(owner).deploy()
- const legacyRegistry = await keeperRegistryFactory
- .connect(owner)
- .deploy(linkToken.address, linkEthFeed.address, gasPriceFeed.address, {
- paymentPremiumPPB,
- flatFeeMicroLink,
- blockCountPerTurn,
- checkGasLimit,
- stalenessSeconds,
- gasCeilingMultiplier,
- minUpkeepSpend,
- maxPerformGas,
- fallbackGasPrice,
- fallbackLinkPrice,
- transcoder: transcoder.address,
- registrar: ethers.constants.AddressZero,
- })
- const tx = await legacyRegistry
- .connect(owner)
- .registerUpkeep(
- mock.address,
- executeGas,
- await admin0.getAddress(),
- randomBytes,
- )
- const id = await getUpkeepID(tx)
- return [id, legacyRegistry]
-}
-
-async function deployLegacyRegistry1_3(
- linkToken: LinkToken,
- gasPriceFeed: any,
- linkEthFeed: any,
-) {
- const mock = await upkeepMockFactory.deploy()
- // @ts-ignore bug in autogen file
- keeperRegistryFactory13 = await ethers.getContractFactory('KeeperRegistry1_3')
- // @ts-ignore bug in autogen file
- keeperRegistryLogicFactory13 = await ethers.getContractFactory(
- 'KeeperRegistryLogic1_3',
- )
-
- const registryLogic13 = await keeperRegistryLogicFactory13
- .connect(owner)
- .deploy(
- 0,
- registryGasOverhead,
- linkToken.address,
- linkEthFeed.address,
- gasPriceFeed.address,
- )
-
- const config = {
- paymentPremiumPPB,
- flatFeeMicroLink,
- blockCountPerTurn,
- checkGasLimit,
- stalenessSeconds,
- gasCeilingMultiplier,
- minUpkeepSpend,
- maxPerformGas,
- fallbackGasPrice,
- fallbackLinkPrice,
- transcoder: transcoder.address,
- registrar: ethers.constants.AddressZero,
- }
- const Registry1_3 = await keeperRegistryFactory13
- .connect(owner)
- .deploy(registryLogic13.address, config)
-
- const tx = await Registry1_3.connect(owner).registerUpkeep(
- mock.address,
- executeGas,
- await admin0.getAddress(),
- randomBytes,
- )
- const id = await getUpkeepID(tx)
-
- return [id, Registry1_3]
-}
-
-async function deployRegistry2_0(
- linkToken: LinkToken,
- gasPriceFeed: any,
- linkEthFeed: any,
-) {
- // @ts-ignore bug in autogen file
- keeperRegistryFactory20 = await ethers.getContractFactory('KeeperRegistry2_0')
- // @ts-ignore bug in autogen file
- keeperRegistryLogicFactory20 = await ethers.getContractFactory(
- 'KeeperRegistryLogic2_0',
- )
-
- const config = {
- paymentPremiumPPB,
- flatFeeMicroLink,
- checkGasLimit,
- stalenessSeconds,
- gasCeilingMultiplier,
- minUpkeepSpend,
- maxCheckDataSize,
- maxPerformDataSize,
- maxPerformGas,
- fallbackGasPrice,
- fallbackLinkPrice,
- transcoder: transcoder.address,
- registrar: ethers.constants.AddressZero,
- }
-
- const registryLogic = await keeperRegistryLogicFactory20
- .connect(owner)
- .deploy(mode, linkToken.address, linkEthFeed.address, gasPriceFeed.address)
-
- const Registry2_0 = await keeperRegistryFactory20
- .connect(owner)
- .deploy(registryLogic.address)
-
- // deploys a registry, setups of initial configuration, registers an upkeep
- const keeper1 = personas.Carol
- const keeper2 = personas.Eddy
- const keeper3 = personas.Nancy
- const keeper4 = personas.Norbert
- const keeper5 = personas.Nick
- const payee1 = personas.Nelly
- const payee2 = personas.Norbert
- const payee3 = personas.Nick
- const payee4 = personas.Eddy
- const payee5 = personas.Carol
- // signers
- const signer1 = new ethers.Wallet(
- '0x7777777000000000000000000000000000000000000000000000000000000001',
- )
- const signer2 = new ethers.Wallet(
- '0x7777777000000000000000000000000000000000000000000000000000000002',
- )
- const signer3 = new ethers.Wallet(
- '0x7777777000000000000000000000000000000000000000000000000000000003',
- )
- const signer4 = new ethers.Wallet(
- '0x7777777000000000000000000000000000000000000000000000000000000004',
- )
- const signer5 = new ethers.Wallet(
- '0x7777777000000000000000000000000000000000000000000000000000000005',
- )
-
- const keeperAddresses = [
- await keeper1.getAddress(),
- await keeper2.getAddress(),
- await keeper3.getAddress(),
- await keeper4.getAddress(),
- await keeper5.getAddress(),
- ]
- const payees = [
- await payee1.getAddress(),
- await payee2.getAddress(),
- await payee3.getAddress(),
- await payee4.getAddress(),
- await payee5.getAddress(),
- ]
- const signers = [signer1, signer2, signer3, signer4, signer5]
-
- const signerAddresses = []
- for (const signer of signers) {
- signerAddresses.push(await signer.getAddress())
- }
-
- const f = 1
- const offchainVersion = 1
- const offchainBytes = '0x'
-
- await Registry2_0.connect(owner).setConfig(
- signerAddresses,
- keeperAddresses,
- f,
- encodeConfig(config),
- offchainVersion,
- offchainBytes,
- )
- await Registry2_0.connect(owner).setPayees(payees)
- return Registry2_0
-}
-
-describe('UpkeepTranscoder3_0', () => {
- beforeEach(async () => {
- transcoder = await upkeepTranscoderFactory.connect(owner).deploy()
- })
-
- describe('#typeAndVersion', () => {
- it('uses the correct type and version', async () => {
- const typeAndVersion = await transcoder.typeAndVersion()
- assert.equal(typeAndVersion, 'UpkeepTranscoder 3.0.0')
- })
- })
-
- describe('#transcodeUpkeeps', () => {
- const encodedData = '0xabcd'
-
- it('reverts if the from type is not V1 or V2', async () => {
- await evmRevert(
- transcoder.transcodeUpkeeps(
- UpkeepFormat.V3,
- UpkeepFormat.V1,
- encodedData,
- ),
- )
- await evmRevert(
- transcoder.transcodeUpkeeps(
- UpkeepFormat.V4,
- UpkeepFormat.V1,
- encodedData,
- ),
- )
- })
-
- context('when from and to versions are correct', () => {
- upkeepsV3 = [
- [executeGas, 2 ** 32 - 1, false, target0, amountSpent, balance, 0],
- [executeGas, 2 ** 32 - 1, false, target1, amountSpent, balance, 0],
- ]
-
- it('transcodes V1 upkeeps to V3 properly, regardless of toVersion value', async () => {
- upkeepsV1 = [
- [
- balance,
- lastKeeper0,
- executeGas,
- 2 ** 32,
- target0,
- amountSpent,
- await admin0.getAddress(),
- ],
- [
- balance,
- lastKeeper1,
- executeGas,
- 2 ** 32,
- target1,
- amountSpent,
- await admin1.getAddress(),
- ],
- ]
-
- const data = await transcoder.transcodeUpkeeps(
- UpkeepFormat.V1,
- UpkeepFormat.V1,
- encodeUpkeepV1(idx, upkeepsV1, ['0xabcd', '0xffff']),
- )
- assert.equal(
- encodeUpkeepV3(idx, upkeepsV3, ['0xabcd', '0xffff'], admins),
- data,
- )
- })
-
- it('transcodes V2 upkeeps to V3 properly, regardless of toVersion value', async () => {
- upkeepsV2 = [
- [
- balance,
- lastKeeper0,
- amountSpent,
- await admin0.getAddress(),
- executeGas,
- 2 ** 32 - 1,
- target0,
- false,
- ],
- [
- balance,
- lastKeeper1,
- amountSpent,
- await admin1.getAddress(),
- executeGas,
- 2 ** 32 - 1,
- target1,
- false,
- ],
- ]
-
- const data = await transcoder.transcodeUpkeeps(
- UpkeepFormat.V2,
- UpkeepFormat.V2,
- encodeUpkeepV2(idx, upkeepsV2, ['0xabcd', '0xffff']),
- )
- assert.equal(
- encodeUpkeepV3(idx, upkeepsV3, ['0xabcd', '0xffff'], admins),
- data,
- )
- })
-
- it('migrates upkeeps from 1.2 registry to 2.0', async () => {
- const linkToken = await deployLinkToken()
- const [gasPriceFeed, linkEthFeed] = await deployFeeds()
- const [id, legacyRegistry] = await deployLegacyRegistry1_2(
- linkToken,
- gasPriceFeed,
- linkEthFeed,
- )
- const Registry2_0 = await deployRegistry2_0(
- linkToken,
- gasPriceFeed,
- linkEthFeed,
- )
-
- await linkToken
- .connect(owner)
- .approve(legacyRegistry.address, toWei('1000'))
- await legacyRegistry.connect(owner).addFunds(id, toWei('1000'))
-
- // set outgoing permission to registry 2_0 and incoming permission for registry 1_2
- await legacyRegistry.setPeerRegistryMigrationPermission(
- Registry2_0.address,
- 1,
- )
- await Registry2_0.setPeerRegistryMigrationPermission(
- legacyRegistry.address,
- 2,
- )
-
- expect((await legacyRegistry.getUpkeep(id)).balance).to.equal(
- toWei('1000'),
- )
- expect((await legacyRegistry.getUpkeep(id)).checkData).to.equal(
- randomBytes,
- )
- expect((await legacyRegistry.getState()).state.numUpkeeps).to.equal(1)
-
- await legacyRegistry
- .connect(admin0)
- .migrateUpkeeps([id], Registry2_0.address)
-
- expect((await legacyRegistry.getState()).state.numUpkeeps).to.equal(0)
- expect((await Registry2_0.getState()).state.numUpkeeps).to.equal(1)
- expect((await legacyRegistry.getUpkeep(id)).balance).to.equal(0)
- expect((await legacyRegistry.getUpkeep(id)).checkData).to.equal('0x')
- expect((await Registry2_0.getUpkeep(id)).balance).to.equal(
- toWei('1000'),
- )
- expect(
- (await Registry2_0.getState()).state.expectedLinkBalance,
- ).to.equal(toWei('1000'))
- expect(await linkToken.balanceOf(Registry2_0.address)).to.equal(
- toWei('1000'),
- )
- expect((await Registry2_0.getUpkeep(id)).checkData).to.equal(
- randomBytes,
- )
- })
-
- it('migrates upkeeps from 1.3 registry to 2.0', async () => {
- const linkToken = await deployLinkToken()
- const [gasPriceFeed, linkEthFeed] = await deployFeeds()
- const [id, legacyRegistry] = await deployLegacyRegistry1_3(
- linkToken,
- gasPriceFeed,
- linkEthFeed,
- )
- const Registry2_0 = await deployRegistry2_0(
- linkToken,
- gasPriceFeed,
- linkEthFeed,
- )
-
- await linkToken
- .connect(owner)
- .approve(legacyRegistry.address, toWei('1000'))
- await legacyRegistry.connect(owner).addFunds(id, toWei('1000'))
-
- // set outgoing permission to registry 2_0 and incoming permission for registry 1_3
- await legacyRegistry.setPeerRegistryMigrationPermission(
- Registry2_0.address,
- 1,
- )
- await Registry2_0.setPeerRegistryMigrationPermission(
- legacyRegistry.address,
- 2,
- )
-
- expect((await legacyRegistry.getUpkeep(id)).balance).to.equal(
- toWei('1000'),
- )
- expect((await legacyRegistry.getUpkeep(id)).checkData).to.equal(
- randomBytes,
- )
- expect((await legacyRegistry.getState()).state.numUpkeeps).to.equal(1)
-
- await legacyRegistry
- .connect(admin0)
- .migrateUpkeeps([id], Registry2_0.address)
-
- expect((await legacyRegistry.getState()).state.numUpkeeps).to.equal(0)
- expect((await Registry2_0.getState()).state.numUpkeeps).to.equal(1)
- expect((await legacyRegistry.getUpkeep(id)).balance).to.equal(0)
- expect((await legacyRegistry.getUpkeep(id)).checkData).to.equal('0x')
- expect((await Registry2_0.getUpkeep(id)).balance).to.equal(
- toWei('1000'),
- )
- expect(
- (await Registry2_0.getState()).state.expectedLinkBalance,
- ).to.equal(toWei('1000'))
- expect(await linkToken.balanceOf(Registry2_0.address)).to.equal(
- toWei('1000'),
- )
- expect((await Registry2_0.getUpkeep(id)).checkData).to.equal(
- randomBytes,
- )
- })
- })
- })
-})
diff --git a/contracts/test/v0.8/automation/UpkeepTranscoder4_0.test.ts b/contracts/test/v0.8/automation/UpkeepTranscoder4_0.test.ts
deleted file mode 100644
index b49dfb1d5b4..00000000000
--- a/contracts/test/v0.8/automation/UpkeepTranscoder4_0.test.ts
+++ /dev/null
@@ -1,654 +0,0 @@
-import { ethers } from 'hardhat'
-import { assert, expect } from 'chai'
-import { UpkeepTranscoder4_0 as UpkeepTranscoder } from '../../../typechain/UpkeepTranscoder4_0'
-import { KeeperRegistry2_0__factory as KeeperRegistry2_0Factory } from '../../../typechain/factories/KeeperRegistry2_0__factory'
-import { LinkToken__factory as LinkTokenFactory } from '../../../typechain/factories/LinkToken__factory'
-import { MockV3Aggregator__factory as MockV3AggregatorFactory } from '../../../typechain/factories/MockV3Aggregator__factory'
-import { evmRevert } from '../../test-helpers/matchers'
-import { BigNumber, Signer } from 'ethers'
-import { getUsers, Personas } from '../../test-helpers/setup'
-import { KeeperRegistryLogic2_0__factory as KeeperRegistryLogic20Factory } from '../../../typechain/factories/KeeperRegistryLogic2_0__factory'
-import { KeeperRegistry1_3__factory as KeeperRegistry1_3Factory } from '../../../typechain/factories/KeeperRegistry1_3__factory'
-import { KeeperRegistryLogic1_3__factory as KeeperRegistryLogicFactory } from '../../../typechain/factories/KeeperRegistryLogic1_3__factory'
-import { UpkeepTranscoder4_0__factory as UpkeepTranscoderFactory } from '../../../typechain/factories/UpkeepTranscoder4_0__factory'
-import { toWei } from '../../test-helpers/helpers'
-import { loadFixture } from '@nomicfoundation/hardhat-network-helpers'
-import {
- IKeeperRegistryMaster,
- KeeperRegistry1_2,
- KeeperRegistry1_3,
- KeeperRegistry2_0,
- LinkToken,
- MockV3Aggregator,
- UpkeepMock,
-} from '../../../typechain'
-import { deployRegistry21 } from './helpers'
-
-//////////////////////////////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////////////////////////////
-
-/*********************************** TRANSCODER v4.0 IS FROZEN ************************************/
-
-// We are leaving the original tests enabled, however as automation v2.1 is still actively being deployed
-
-describe('UpkeepTranscoder v4.0 - Frozen [ @skip-coverage ]', () => {
- it('has not changed', () => {
- assert.equal(
- ethers.utils.id(UpkeepTranscoderFactory.bytecode),
- '0xf22c4701b0088e6e69c389a34a22041a69f00890a89246e3c2a6d38172222dae',
- 'UpkeepTranscoder bytecode has changed',
- )
- })
-})
-
-//////////////////////////////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////////////////////////////
-
-let transcoder: UpkeepTranscoder
-let linkTokenFactory: LinkTokenFactory
-let keeperRegistryFactory20: KeeperRegistry2_0Factory
-let keeperRegistryFactory13: KeeperRegistry1_3Factory
-let keeperRegistryLogicFactory20: KeeperRegistryLogic20Factory
-let keeperRegistryLogicFactory13: KeeperRegistryLogicFactory
-let linkToken: LinkToken
-let registry12: KeeperRegistry1_2
-let registry13: KeeperRegistry1_3
-let registry20: KeeperRegistry2_0
-let registry21: IKeeperRegistryMaster
-let gasPriceFeed: MockV3Aggregator
-let linkEthFeed: MockV3Aggregator
-let mock: UpkeepMock
-let personas: Personas
-let owner: Signer
-let upkeepsV12: any[]
-let upkeepsV13: any[]
-let upkeepsV21: any[]
-let admins: string[]
-let admin0: Signer
-let admin1: Signer
-let id12: BigNumber
-let id13: BigNumber
-let id20: BigNumber
-const executeGas = BigNumber.from('100000')
-const paymentPremiumPPB = BigNumber.from('250000000')
-const flatFeeMicroLink = BigNumber.from(0)
-const blockCountPerTurn = BigNumber.from(3)
-const randomBytes = '0x1234abcd'
-const stalenessSeconds = BigNumber.from(43820)
-const gasCeilingMultiplier = BigNumber.from(1)
-const checkGasLimit = BigNumber.from(20000000)
-const fallbackGasPrice = BigNumber.from(200)
-const fallbackLinkPrice = BigNumber.from(200000000)
-const maxPerformGas = BigNumber.from(5000000)
-const minUpkeepSpend = BigNumber.from(0)
-const maxCheckDataSize = BigNumber.from(1000)
-const maxPerformDataSize = BigNumber.from(1000)
-const mode = BigNumber.from(0)
-const linkEth = BigNumber.from(300000000)
-const gasWei = BigNumber.from(100)
-const registryGasOverhead = BigNumber.from('80000')
-const balance = 50000000000000
-const amountSpent = 200000000000000
-const { AddressZero } = ethers.constants
-const target0 = '0xffffffffffffffffffffffffffffffffffffffff'
-const target1 = '0xfffffffffffffffffffffffffffffffffffffffe'
-const lastKeeper0 = '0x233a95ccebf3c9f934482c637c08b4015cdd6ddd'
-const lastKeeper1 = '0x233a95ccebf3c9f934482c637c08b4015cdd6ddc'
-
-const f = 1
-const offchainVersion = 1
-const offchainBytes = '0x'
-let keeperAddresses: string[]
-let signerAddresses: string[]
-let payees: string[]
-
-enum UpkeepFormat {
- V12,
- V13,
- V20,
- V21,
- V30, // Does not exist
-}
-const idx = [123, 124]
-
-async function getUpkeepID(tx: any): Promise {
- const receipt = await tx.wait()
- return receipt.events[0].args.id
-}
-
-const encodeConfig20 = (config: any) => {
- return ethers.utils.defaultAbiCoder.encode(
- [
- 'tuple(uint32 paymentPremiumPPB,uint32 flatFeeMicroLink,uint32 checkGasLimit,uint24 stalenessSeconds\
- ,uint16 gasCeilingMultiplier,uint96 minUpkeepSpend,uint32 maxPerformGas,uint32 maxCheckDataSize,\
- uint32 maxPerformDataSize,uint256 fallbackGasPrice,uint256 fallbackLinkPrice,address transcoder,\
- address registrar)',
- ],
- [config],
- )
-}
-
-const encodeUpkeepV12 = (ids: number[], upkeeps: any[], checkDatas: any[]) => {
- return ethers.utils.defaultAbiCoder.encode(
- [
- 'uint256[]',
- 'tuple(uint96,address,uint32,uint64,address,uint96,address)[]',
- 'bytes[]',
- ],
- [ids, upkeeps, checkDatas],
- )
-}
-
-async function deployRegistry1_2(): Promise<[BigNumber, KeeperRegistry1_2]> {
- const keeperRegistryFactory =
- await ethers.getContractFactory('KeeperRegistry1_2')
- const registry12 = await keeperRegistryFactory
- .connect(owner)
- .deploy(linkToken.address, linkEthFeed.address, gasPriceFeed.address, {
- paymentPremiumPPB,
- flatFeeMicroLink,
- blockCountPerTurn,
- checkGasLimit,
- stalenessSeconds,
- gasCeilingMultiplier,
- minUpkeepSpend,
- maxPerformGas,
- fallbackGasPrice,
- fallbackLinkPrice,
- transcoder: transcoder.address,
- registrar: ethers.constants.AddressZero,
- })
- const tx = await registry12
- .connect(owner)
- .registerUpkeep(
- mock.address,
- executeGas,
- await admin0.getAddress(),
- randomBytes,
- )
- const id = await getUpkeepID(tx)
- return [id, registry12]
-}
-
-async function deployRegistry1_3(): Promise<[BigNumber, KeeperRegistry1_3]> {
- keeperRegistryFactory13 = await ethers.getContractFactory('KeeperRegistry1_3')
- keeperRegistryLogicFactory13 = await ethers.getContractFactory(
- 'KeeperRegistryLogic1_3',
- )
-
- const registryLogic13 = await keeperRegistryLogicFactory13
- .connect(owner)
- .deploy(
- 0,
- registryGasOverhead,
- linkToken.address,
- linkEthFeed.address,
- gasPriceFeed.address,
- )
-
- const config = {
- paymentPremiumPPB,
- flatFeeMicroLink,
- blockCountPerTurn,
- checkGasLimit,
- stalenessSeconds,
- gasCeilingMultiplier,
- minUpkeepSpend,
- maxPerformGas,
- fallbackGasPrice,
- fallbackLinkPrice,
- transcoder: transcoder.address,
- registrar: ethers.constants.AddressZero,
- }
- const registry13 = await keeperRegistryFactory13
- .connect(owner)
- .deploy(registryLogic13.address, config)
-
- const tx = await registry13
- .connect(owner)
- .registerUpkeep(
- mock.address,
- executeGas,
- await admin0.getAddress(),
- randomBytes,
- )
- const id = await getUpkeepID(tx)
-
- return [id, registry13]
-}
-
-async function deployRegistry2_0(): Promise<[BigNumber, KeeperRegistry2_0]> {
- keeperRegistryFactory20 = await ethers.getContractFactory('KeeperRegistry2_0')
- keeperRegistryLogicFactory20 = await ethers.getContractFactory(
- 'KeeperRegistryLogic2_0',
- )
-
- const config = {
- paymentPremiumPPB,
- flatFeeMicroLink,
- checkGasLimit,
- stalenessSeconds,
- gasCeilingMultiplier,
- minUpkeepSpend,
- maxCheckDataSize,
- maxPerformDataSize,
- maxPerformGas,
- fallbackGasPrice,
- fallbackLinkPrice,
- transcoder: transcoder.address,
- registrar: ethers.constants.AddressZero,
- }
-
- const registryLogic = await keeperRegistryLogicFactory20
- .connect(owner)
- .deploy(mode, linkToken.address, linkEthFeed.address, gasPriceFeed.address)
-
- const registry20 = await keeperRegistryFactory20
- .connect(owner)
- .deploy(registryLogic.address)
-
- await registry20
- .connect(owner)
- .setConfig(
- signerAddresses,
- keeperAddresses,
- f,
- encodeConfig20(config),
- offchainVersion,
- offchainBytes,
- )
- await registry20.connect(owner).setPayees(payees)
-
- const tx = await registry20
- .connect(owner)
- .registerUpkeep(
- mock.address,
- executeGas,
- await admin0.getAddress(),
- randomBytes,
- randomBytes,
- )
- const id = await getUpkeepID(tx)
-
- return [id, registry20]
-}
-
-async function deployRegistry2_1() {
- const registry = await deployRegistry21(
- owner,
- mode,
- linkToken.address,
- linkEthFeed.address,
- gasPriceFeed.address,
- )
-
- const onchainConfig = {
- paymentPremiumPPB,
- flatFeeMicroLink,
- checkGasLimit,
- stalenessSeconds,
- gasCeilingMultiplier,
- minUpkeepSpend,
- maxCheckDataSize,
- maxPerformDataSize,
- maxRevertDataSize: 1000,
- maxPerformGas,
- fallbackGasPrice,
- fallbackLinkPrice,
- transcoder: ethers.constants.AddressZero,
- registrars: [],
- upkeepPrivilegeManager: await owner.getAddress(),
- }
-
- await registry
- .connect(owner)
- .setConfigTypeSafe(
- signerAddresses,
- keeperAddresses,
- f,
- onchainConfig,
- offchainVersion,
- offchainBytes,
- )
-
- return registry
-}
-
-const setup = async () => {
- personas = (await getUsers()).personas
- owner = personas.Norbert
- admin0 = personas.Neil
- admin1 = personas.Nick
- admins = [
- (await admin0.getAddress()).toLowerCase(),
- (await admin1.getAddress()).toLowerCase(),
- ]
-
- const upkeepTranscoderFactory = await ethers.getContractFactory(
- 'UpkeepTranscoder4_0',
- )
- transcoder = await upkeepTranscoderFactory.connect(owner).deploy()
-
- linkTokenFactory = await ethers.getContractFactory(
- 'src/v0.8/shared/test/helpers/LinkTokenTestHelper.sol:LinkTokenTestHelper',
- )
- linkToken = await linkTokenFactory.connect(owner).deploy()
- // need full path because there are two contracts with name MockV3Aggregator
- const mockV3AggregatorFactory = (await ethers.getContractFactory(
- 'src/v0.8/shared/mocks/MockV3Aggregator.sol:MockV3Aggregator',
- )) as unknown as MockV3AggregatorFactory
-
- gasPriceFeed = await mockV3AggregatorFactory.connect(owner).deploy(0, gasWei)
- linkEthFeed = await mockV3AggregatorFactory.connect(owner).deploy(9, linkEth)
-
- const upkeepMockFactory = await ethers.getContractFactory('UpkeepMock')
- mock = await upkeepMockFactory.deploy()
-
- const keeper1 = personas.Carol
- const keeper2 = personas.Eddy
- const keeper3 = personas.Nancy
- const keeper4 = personas.Norbert
- const keeper5 = personas.Nick
- const payee1 = personas.Nelly
- const payee2 = personas.Norbert
- const payee3 = personas.Nick
- const payee4 = personas.Eddy
- const payee5 = personas.Carol
- // signers
- const signer1 = new ethers.Wallet(
- '0x7777777000000000000000000000000000000000000000000000000000000001',
- )
- const signer2 = new ethers.Wallet(
- '0x7777777000000000000000000000000000000000000000000000000000000002',
- )
- const signer3 = new ethers.Wallet(
- '0x7777777000000000000000000000000000000000000000000000000000000003',
- )
- const signer4 = new ethers.Wallet(
- '0x7777777000000000000000000000000000000000000000000000000000000004',
- )
- const 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(),
- ]
- const signers = [signer1, signer2, signer3, signer4, signer5]
-
- signerAddresses = signers.map((signer) => signer.address)
- ;[id12, registry12] = await deployRegistry1_2()
- ;[id13, registry13] = await deployRegistry1_3()
- ;[id20, registry20] = await deployRegistry2_0()
- registry21 = await deployRegistry2_1()
-
- upkeepsV12 = [
- [
- balance,
- lastKeeper0,
- executeGas,
- 2 ** 32,
- target0,
- amountSpent,
- await admin0.getAddress(),
- ],
- [
- balance,
- lastKeeper1,
- executeGas,
- 2 ** 32,
- target1,
- amountSpent,
- await admin1.getAddress(),
- ],
- ]
-
- upkeepsV13 = [
- [
- balance,
- lastKeeper0,
- amountSpent,
- await admin0.getAddress(),
- executeGas,
- 2 ** 32 - 1,
- target0,
- false,
- ],
- [
- balance,
- lastKeeper1,
- amountSpent,
- await admin1.getAddress(),
- executeGas,
- 2 ** 32 - 1,
- target1,
- false,
- ],
- ]
-
- upkeepsV21 = [
- [
- false,
- executeGas,
- 2 ** 32 - 1,
- AddressZero, // forwarder will always be zero
- amountSpent,
- balance,
- 0,
- target0,
- ],
- [
- false,
- executeGas,
- 2 ** 32 - 1,
- AddressZero, // forwarder will always be zero
- amountSpent,
- balance,
- 0,
- target1,
- ],
- ]
-}
-
-describe('UpkeepTranscoder4_0', () => {
- beforeEach(async () => {
- await loadFixture(setup)
- })
-
- describe('#typeAndVersion', () => {
- it('uses the correct type and version', async () => {
- const typeAndVersion = await transcoder.typeAndVersion()
- assert.equal(typeAndVersion, 'UpkeepTranscoder 4.0.0')
- })
- })
-
- describe('#transcodeUpkeeps', () => {
- const encodedData = '0xabcd'
-
- it('reverts if the from type is not v1.2, v1.3, v2.0, or v2.1', async () => {
- await evmRevert(
- transcoder.transcodeUpkeeps(
- UpkeepFormat.V30,
- UpkeepFormat.V12,
- encodedData,
- ),
- )
- })
-
- context('when from version is correct', () => {
- // note this is a bugfix - the "to" version should be accounted for in
- // future versions of the transcoder
- it('transcodes to v2.1, regardless of toVersion value', async () => {
- const data1 = await transcoder.transcodeUpkeeps(
- UpkeepFormat.V12,
- UpkeepFormat.V12,
- encodeUpkeepV12(idx, upkeepsV12, ['0xabcd', '0xffff']),
- )
- const data2 = await transcoder.transcodeUpkeeps(
- UpkeepFormat.V12,
- UpkeepFormat.V13,
- encodeUpkeepV12(idx, upkeepsV12, ['0xabcd', '0xffff']),
- )
- const data3 = await transcoder.transcodeUpkeeps(
- UpkeepFormat.V12,
- 100,
- encodeUpkeepV12(idx, upkeepsV12, ['0xabcd', '0xffff']),
- )
- assert.equal(data1, data2)
- assert.equal(data1, data3)
- })
-
- it('migrates upkeeps from 1.2 registry to 2.1', async () => {
- await linkToken
- .connect(owner)
- .approve(registry12.address, toWei('1000'))
- await registry12.connect(owner).addFunds(id12, toWei('1000'))
-
- await registry12.setPeerRegistryMigrationPermission(
- registry21.address,
- 1,
- )
- await registry21.setPeerRegistryMigrationPermission(
- registry12.address,
- 2,
- )
-
- expect((await registry12.getUpkeep(id12)).balance).to.equal(
- toWei('1000'),
- )
- expect((await registry12.getUpkeep(id12)).checkData).to.equal(
- randomBytes,
- )
- expect((await registry12.getState()).state.numUpkeeps).to.equal(1)
-
- await registry12
- .connect(admin0)
- .migrateUpkeeps([id12], registry21.address)
-
- expect((await registry12.getState()).state.numUpkeeps).to.equal(0)
- expect((await registry21.getState()).state.numUpkeeps).to.equal(1)
- expect((await registry12.getUpkeep(id12)).balance).to.equal(0)
- expect((await registry12.getUpkeep(id12)).checkData).to.equal('0x')
- expect((await registry21.getUpkeep(id12)).balance).to.equal(
- toWei('1000'),
- )
- expect(
- (await registry21.getState()).state.expectedLinkBalance,
- ).to.equal(toWei('1000'))
- expect(await linkToken.balanceOf(registry21.address)).to.equal(
- toWei('1000'),
- )
- expect((await registry21.getUpkeep(id12)).checkData).to.equal(
- randomBytes,
- )
- expect((await registry21.getUpkeep(id12)).offchainConfig).to.equal('0x')
- expect(await registry21.getUpkeepTriggerConfig(id12)).to.equal('0x')
- })
-
- it('migrates upkeeps from 1.3 registry to 2.1', async () => {
- await linkToken
- .connect(owner)
- .approve(registry13.address, toWei('1000'))
- await registry13.connect(owner).addFunds(id13, toWei('1000'))
-
- await registry13.setPeerRegistryMigrationPermission(
- registry21.address,
- 1,
- )
- await registry21.setPeerRegistryMigrationPermission(
- registry13.address,
- 2,
- )
-
- expect((await registry13.getUpkeep(id13)).balance).to.equal(
- toWei('1000'),
- )
- expect((await registry13.getUpkeep(id13)).checkData).to.equal(
- randomBytes,
- )
- expect((await registry13.getState()).state.numUpkeeps).to.equal(1)
-
- await registry13
- .connect(admin0)
- .migrateUpkeeps([id13], registry21.address)
-
- expect((await registry13.getState()).state.numUpkeeps).to.equal(0)
- expect((await registry21.getState()).state.numUpkeeps).to.equal(1)
- expect((await registry13.getUpkeep(id13)).balance).to.equal(0)
- expect((await registry13.getUpkeep(id13)).checkData).to.equal('0x')
- expect((await registry21.getUpkeep(id13)).balance).to.equal(
- toWei('1000'),
- )
- expect(
- (await registry21.getState()).state.expectedLinkBalance,
- ).to.equal(toWei('1000'))
- expect(await linkToken.balanceOf(registry21.address)).to.equal(
- toWei('1000'),
- )
- expect((await registry21.getUpkeep(id13)).checkData).to.equal(
- randomBytes,
- )
- expect((await registry21.getUpkeep(id13)).offchainConfig).to.equal('0x')
- expect(await registry21.getUpkeepTriggerConfig(id13)).to.equal('0x')
- })
-
- it('migrates upkeeps from 2.0 registry to 2.1', async () => {
- await linkToken
- .connect(owner)
- .approve(registry20.address, toWei('1000'))
- await registry20.connect(owner).addFunds(id20, toWei('1000'))
-
- await registry20.setPeerRegistryMigrationPermission(
- registry21.address,
- 1,
- )
- await registry21.setPeerRegistryMigrationPermission(
- registry20.address,
- 2,
- )
-
- expect((await registry20.getUpkeep(id20)).balance).to.equal(
- toWei('1000'),
- )
- expect((await registry20.getUpkeep(id20)).checkData).to.equal(
- randomBytes,
- )
- expect((await registry20.getState()).state.numUpkeeps).to.equal(1)
-
- await registry20
- .connect(admin0)
- .migrateUpkeeps([id20], registry21.address)
-
- expect((await registry20.getState()).state.numUpkeeps).to.equal(0)
- expect((await registry21.getState()).state.numUpkeeps).to.equal(1)
- expect((await registry20.getUpkeep(id20)).balance).to.equal(0)
- expect((await registry20.getUpkeep(id20)).checkData).to.equal('0x')
- expect((await registry21.getUpkeep(id20)).balance).to.equal(
- toWei('1000'),
- )
- expect(
- (await registry21.getState()).state.expectedLinkBalance,
- ).to.equal(toWei('1000'))
- expect(await linkToken.balanceOf(registry21.address)).to.equal(
- toWei('1000'),
- )
- expect((await registry21.getUpkeep(id20)).checkData).to.equal(
- randomBytes,
- )
- expect(await registry21.getUpkeepTriggerConfig(id20)).to.equal('0x')
- })
- })
- })
-})
diff --git a/contracts/test/v0.8/automation/helpers.ts b/contracts/test/v0.8/automation/helpers.ts
index 99f2cef9b87..130bdcbfecf 100644
--- a/contracts/test/v0.8/automation/helpers.ts
+++ b/contracts/test/v0.8/automation/helpers.ts
@@ -1,11 +1,5 @@
import { Signer } from 'ethers'
import { ethers } from 'hardhat'
-import { KeeperRegistryLogicB2_1__factory as KeeperRegistryLogicBFactory } from '../../../typechain/factories/KeeperRegistryLogicB2_1__factory'
-import { IKeeperRegistryMaster as IKeeperRegistry } from '../../../typechain/IKeeperRegistryMaster'
-import { IKeeperRegistryMaster__factory as IKeeperRegistryMasterFactory } from '../../../typechain/factories/IKeeperRegistryMaster__factory'
-import { AutomationRegistryLogicB2_2__factory as AutomationRegistryLogicBFactory } from '../../../typechain/factories/AutomationRegistryLogicB2_2__factory'
-import { IAutomationRegistryMaster as IAutomationRegistry } from '../../../typechain/IAutomationRegistryMaster'
-import { IAutomationRegistryMaster__factory as IAutomationRegistryMasterFactory } from '../../../typechain/factories/IAutomationRegistryMaster__factory'
import { assert } from 'chai'
import { FunctionFragment } from '@ethersproject/abi'
import { AutomationRegistryLogicC2_3__factory as AutomationRegistryLogicC2_3Factory } from '../../../typechain/factories/AutomationRegistryLogicC2_3__factory'
@@ -13,32 +7,6 @@ import { ZKSyncAutomationRegistryLogicC2_3__factory as ZKSyncAutomationRegistryL
import { IAutomationRegistryMaster2_3 as IAutomationRegistry2_3 } from '../../../typechain/IAutomationRegistryMaster2_3'
import { IAutomationRegistryMaster2_3__factory as IAutomationRegistryMaster2_3Factory } from '../../../typechain/factories/IAutomationRegistryMaster2_3__factory'
-export const deployRegistry21 = async (
- from: Signer,
- mode: Parameters[0],
- link: Parameters[1],
- linkNative: Parameters[2],
- fastgas: Parameters[3],
-): Promise => {
- const logicBFactory = await ethers.getContractFactory(
- 'KeeperRegistryLogicB2_1',
- )
- const logicAFactory = await ethers.getContractFactory(
- 'KeeperRegistryLogicA2_1',
- )
- const registryFactory = await ethers.getContractFactory('KeeperRegistry2_1')
- const forwarderLogicFactory = await ethers.getContractFactory(
- 'AutomationForwarderLogic',
- )
- const forwarderLogic = await forwarderLogicFactory.connect(from).deploy()
- const logicB = await logicBFactory
- .connect(from)
- .deploy(mode, link, linkNative, fastgas, forwarderLogic.address)
- const logicA = await logicAFactory.connect(from).deploy(logicB.address)
- const master = await registryFactory.connect(from).deploy(logicA.address)
- return IKeeperRegistryMasterFactory.connect(master.address, from)
-}
-
type InterfaceABI = ConstructorParameters[0]
type Entry = {
inputs?: any[]
@@ -130,42 +98,6 @@ export const assertSatisfiesInterface = (
}
}
-export const deployRegistry22 = async (
- from: Signer,
- link: Parameters[0],
- linkNative: Parameters[1],
- fastgas: Parameters[2],
- allowedReadOnlyAddress: Parameters<
- AutomationRegistryLogicBFactory['deploy']
- >[3],
-): Promise => {
- const logicBFactory = await ethers.getContractFactory(
- 'AutomationRegistryLogicB2_2',
- )
- const logicAFactory = await ethers.getContractFactory(
- 'AutomationRegistryLogicA2_2',
- )
- const registryFactory = await ethers.getContractFactory(
- 'AutomationRegistry2_2',
- )
- const forwarderLogicFactory = await ethers.getContractFactory(
- 'AutomationForwarderLogic',
- )
- const forwarderLogic = await forwarderLogicFactory.connect(from).deploy()
- const logicB = await logicBFactory
- .connect(from)
- .deploy(
- link,
- linkNative,
- fastgas,
- forwarderLogic.address,
- allowedReadOnlyAddress,
- )
- const logicA = await logicAFactory.connect(from).deploy(logicB.address)
- const master = await registryFactory.connect(from).deploy(logicA.address)
- return IAutomationRegistryMasterFactory.connect(master.address, from)
-}
-
export const deployRegistry23 = async (
from: Signer,
link: Parameters[0],
From da03b850e76296ef652dfe3532c7aebefd58bea2 Mon Sep 17 00:00:00 2001
From: Mateusz Sekara
Date: Wed, 8 Jan 2025 17:23:37 +0100
Subject: [PATCH 13/91] CCIP Config backported from CCIP repo (#15856)
* Moving configs directly from CCIP repo
* Moving configs directly from CCIP repo
---
ccip/config/evm/Astar_Mainnet.toml | 8 +-
ccip/config/evm/Astar_Shibuya.toml | 7 +-
ccip/config/evm/Avalanche_ANZ_testnet.toml | 4 +-
ccip/config/evm/Avalanche_Fuji.toml | 3 +-
ccip/config/evm/Avalanche_Mainnet.toml | 4 +-
ccip/config/evm/BOB_Mainnet.toml | 28 +++++++
ccip/config/evm/BOB_Testnet.toml | 28 +++++++
ccip/config/evm/BSC_Mainnet.toml | 7 ++
ccip/config/evm/BSC_Testnet.toml | 3 +-
ccip/config/evm/Base_Mainnet.toml | 3 +
ccip/config/evm/Base_Sepolia.toml | 3 +
ccip/config/evm/Berachain_Testnet.toml | 24 ++++++
ccip/config/evm/Bitlayer_Mainnet.toml | 16 ++++
ccip/config/evm/Bitlayer_Testnet.toml | 16 ++++
ccip/config/evm/Blast_Mainnet.toml | 5 +-
ccip/config/evm/Blast_Sepolia.toml | 5 +-
ccip/config/evm/Bsquared_Mainnet.toml | 23 ++++++
ccip/config/evm/Bsquared_Testnet.toml | 23 ++++++
ccip/config/evm/Celo_Mainnet.toml | 8 +-
ccip/config/evm/Celo_Testnet.toml | 3 +
ccip/config/evm/Ethereum_Mainnet.toml | 5 ++
ccip/config/evm/Ethereum_Sepolia.toml | 2 +
ccip/config/evm/Fantom_Mainnet.toml | 7 +-
ccip/config/evm/Fantom_Testnet.toml | 7 +-
ccip/config/evm/Gnosis_Chiado.toml | 5 ++
ccip/config/evm/Gnosis_Mainnet.toml | 5 ++
ccip/config/evm/Harmony_Mainnet.toml | 13 +++
ccip/config/evm/Harmony_Testnet.toml | 13 +++
ccip/config/evm/Hashkey_Mainnet.toml | 16 ++++
ccip/config/evm/Hashkey_Testnet.toml | 16 ++++
ccip/config/evm/Heco_Mainnet.toml | 26 ++++++
ccip/config/evm/Hedera_Mainnet.toml | 35 ++++++++
ccip/config/evm/Hedera_Testnet.toml | 35 ++++++++
ccip/config/evm/Klaytn_Mainnet.toml | 15 ++++
ccip/config/evm/Klaytn_Testnet.toml | 15 ++++
ccip/config/evm/Kroma_Mainnet.toml | 8 +-
ccip/config/evm/Kroma_Sepolia.toml | 8 +-
ccip/config/evm/L3X_Mainnet.toml | 6 +-
ccip/config/evm/L3X_Sepolia.toml | 6 +-
ccip/config/evm/Linea_Goerli.toml | 17 ++++
ccip/config/evm/Linea_Mainnet.toml | 7 +-
ccip/config/evm/Linea_Sepolia.toml | 5 +-
ccip/config/evm/Mantle_Mainnet.toml | 33 ++++++++
ccip/config/evm/Mantle_Sepolia.toml | 31 +++++--
ccip/config/evm/Metis_Mainnet.toml | 15 +++-
ccip/config/evm/Metis_Sepolia.toml | 5 +-
ccip/config/evm/Mode_Mainnet.toml | 3 +
ccip/config/evm/Mode_Sepolia.toml | 3 +
ccip/config/evm/Optimism_Mainnet.toml | 3 +
ccip/config/evm/Optimism_Sepolia.toml | 3 +
ccip/config/evm/Polygon_Amoy.toml | 7 +-
ccip/config/evm/Polygon_Mainnet.toml | 3 +
ccip/config/evm/Polygon_Mumbai.toml | 31 +++++++
ccip/config/evm/Polygon_Zkevm_Cardona.toml | 13 ++-
ccip/config/evm/Polygon_Zkevm_Mainnet.toml | 12 +--
ccip/config/evm/RSK_Mainnet.toml | 13 +++
ccip/config/evm/RSK_Testnet.toml | 10 +++
ccip/config/evm/Ronin_Mainnet.toml | 16 ++++
ccip/config/evm/Ronin_Saigon.toml | 16 ++++
ccip/config/evm/Scroll_Mainnet.toml | 3 +
ccip/config/evm/Scroll_Sepolia.toml | 3 +
ccip/config/evm/Simulated.toml | 6 +-
ccip/config/evm/Soneium_Sepolia.toml | 35 ++++++++
ccip/config/evm/Sonic_Mainnet.toml | 28 +++++++
ccip/config/evm/Sonic_Testnet.toml | 28 +++++++
ccip/config/evm/Unichain_Testnet.toml | 26 ++++++
ccip/config/evm/WeMix_Mainnet.toml | 4 +-
ccip/config/evm/WeMix_Testnet.toml | 3 +-
ccip/config/evm/Worldchain_Mainnet.toml | 23 ++++++
ccip/config/evm/Worldchain_Testnet.toml | 23 ++++++
ccip/config/evm/XLayer_Mainnet.toml | 2 +-
ccip/config/evm/XLayer_Sepolia.toml | 3 +
ccip/config/evm/fallback.toml | 95 ++++++++++++++++++++++
ccip/config/evm/zkSync_Mainnet.toml | 2 +-
ccip/config/evm/zkSync_Sepolia.toml | 15 ++--
75 files changed, 955 insertions(+), 54 deletions(-)
create mode 100644 ccip/config/evm/BOB_Mainnet.toml
create mode 100644 ccip/config/evm/BOB_Testnet.toml
create mode 100644 ccip/config/evm/Berachain_Testnet.toml
create mode 100644 ccip/config/evm/Bitlayer_Mainnet.toml
create mode 100644 ccip/config/evm/Bitlayer_Testnet.toml
create mode 100644 ccip/config/evm/Bsquared_Mainnet.toml
create mode 100644 ccip/config/evm/Bsquared_Testnet.toml
create mode 100644 ccip/config/evm/Harmony_Mainnet.toml
create mode 100644 ccip/config/evm/Harmony_Testnet.toml
create mode 100644 ccip/config/evm/Hashkey_Mainnet.toml
create mode 100644 ccip/config/evm/Hashkey_Testnet.toml
create mode 100644 ccip/config/evm/Heco_Mainnet.toml
create mode 100644 ccip/config/evm/Hedera_Mainnet.toml
create mode 100644 ccip/config/evm/Hedera_Testnet.toml
create mode 100644 ccip/config/evm/Klaytn_Mainnet.toml
create mode 100644 ccip/config/evm/Klaytn_Testnet.toml
create mode 100644 ccip/config/evm/Linea_Goerli.toml
create mode 100644 ccip/config/evm/Mantle_Mainnet.toml
create mode 100644 ccip/config/evm/Polygon_Mumbai.toml
create mode 100644 ccip/config/evm/RSK_Mainnet.toml
create mode 100644 ccip/config/evm/RSK_Testnet.toml
create mode 100644 ccip/config/evm/Ronin_Mainnet.toml
create mode 100644 ccip/config/evm/Ronin_Saigon.toml
create mode 100755 ccip/config/evm/Soneium_Sepolia.toml
create mode 100644 ccip/config/evm/Sonic_Mainnet.toml
create mode 100644 ccip/config/evm/Sonic_Testnet.toml
create mode 100644 ccip/config/evm/Unichain_Testnet.toml
create mode 100644 ccip/config/evm/Worldchain_Mainnet.toml
create mode 100644 ccip/config/evm/Worldchain_Testnet.toml
create mode 100644 ccip/config/evm/fallback.toml
diff --git a/ccip/config/evm/Astar_Mainnet.toml b/ccip/config/evm/Astar_Mainnet.toml
index 87808001eb7..5405a67d563 100644
--- a/ccip/config/evm/Astar_Mainnet.toml
+++ b/ccip/config/evm/Astar_Mainnet.toml
@@ -1,4 +1,5 @@
ChainID = '592'
+ChainType = 'astar'
FinalityTagEnabled = true
FinalityDepth = 100
LogPollInterval = '6s'
@@ -6,4 +7,9 @@ LogPollInterval = '6s'
[GasEstimator]
EIP1559DynamicFees = false
PriceMax = '100000 gwei'
-LimitDefault = 8000000
\ No newline at end of file
+LimitDefault = 8000000
+
+[HeadTracker]
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
diff --git a/ccip/config/evm/Astar_Shibuya.toml b/ccip/config/evm/Astar_Shibuya.toml
index 5a5df06f6f0..cfcd7c31c75 100644
--- a/ccip/config/evm/Astar_Shibuya.toml
+++ b/ccip/config/evm/Astar_Shibuya.toml
@@ -6,4 +6,9 @@ LogPollInterval = '6s'
[GasEstimator]
EIP1559DynamicFees = false
PriceMax = '100000 gwei'
-LimitDefault = 8000000
\ No newline at end of file
+LimitDefault = 8000000
+
+[HeadTracker]
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
\ No newline at end of file
diff --git a/ccip/config/evm/Avalanche_ANZ_testnet.toml b/ccip/config/evm/Avalanche_ANZ_testnet.toml
index 1242e1ec06e..936a82d5092 100644
--- a/ccip/config/evm/Avalanche_ANZ_testnet.toml
+++ b/ccip/config/evm/Avalanche_ANZ_testnet.toml
@@ -19,4 +19,6 @@ PriceMin = '25 gwei'
BlockHistorySize = 24
[HeadTracker]
-PersistenceEnabled = false
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
diff --git a/ccip/config/evm/Avalanche_Fuji.toml b/ccip/config/evm/Avalanche_Fuji.toml
index 7df1d26a336..4340b6b861d 100644
--- a/ccip/config/evm/Avalanche_Fuji.toml
+++ b/ccip/config/evm/Avalanche_Fuji.toml
@@ -17,5 +17,6 @@ PriceDefault = '1 gwei'
BlockHistorySize = 24
[HeadTracker]
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
FinalityTagBypass = false
-PersistenceEnabled = false
diff --git a/ccip/config/evm/Avalanche_Mainnet.toml b/ccip/config/evm/Avalanche_Mainnet.toml
index 341ae5478b3..ac73a7b98fa 100644
--- a/ccip/config/evm/Avalanche_Mainnet.toml
+++ b/ccip/config/evm/Avalanche_Mainnet.toml
@@ -18,4 +18,6 @@ PriceDefault = '1 gwei'
BlockHistorySize = 24
[HeadTracker]
-PersistenceEnabled = false
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
diff --git a/ccip/config/evm/BOB_Mainnet.toml b/ccip/config/evm/BOB_Mainnet.toml
new file mode 100644
index 00000000000..70cc2fb8ba4
--- /dev/null
+++ b/ccip/config/evm/BOB_Mainnet.toml
@@ -0,0 +1,28 @@
+ChainID = '60808'
+# OP stack https://docs.gobob.xyz/learn/introduction/stack-overview#rollup-layer
+ChainType = 'optimismBedrock'
+# FinalityDepth in mainnet showed more than 3k
+FinalityDepth = 3150
+# block_time was: 2s, adding 1 second buffer
+LogPollInterval = '3s'
+
+# finality_depth * block_time / 60 secs = ~105 min (finality time)
+NoNewFinalizedHeadsThreshold = '110m'
+
+FinalityTagEnabled = true
+
+[GasEstimator]
+EIP1559DynamicFees = true
+Mode = 'FeeHistory'
+
+[GasEstimator.FeeHistory]
+# block_time was: 2s, per recommendation skip 1-2 blocks
+CacheTimeout = '4s'
+
+[GasEstimator.BlockHistory]
+BlockHistorySize = 100
+
+[HeadTracker]
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
diff --git a/ccip/config/evm/BOB_Testnet.toml b/ccip/config/evm/BOB_Testnet.toml
new file mode 100644
index 00000000000..bd8505c4e44
--- /dev/null
+++ b/ccip/config/evm/BOB_Testnet.toml
@@ -0,0 +1,28 @@
+ChainID = '808813'
+# OP stack https://docs.gobob.xyz/learn/introduction/stack-overview#rollup-layer
+ChainType = 'optimismBedrock'
+# FinalityDepth in mainnet showed more than 3k
+FinalityDepth = 3150
+# block_time was: 2s, adding 1 second buffer
+LogPollInterval = '3s'
+
+# finality_depth * block_time / 60 secs = ~105 min (finality time)
+NoNewFinalizedHeadsThreshold = '110m'
+
+FinalityTagEnabled = true
+
+[GasEstimator]
+EIP1559DynamicFees = true
+Mode = 'FeeHistory'
+
+[GasEstimator.FeeHistory]
+# block_time was: 2s, per recommendation skip 1-2 blocks
+CacheTimeout = '4s'
+
+[GasEstimator.BlockHistory]
+BlockHistorySize = 100
+
+[HeadTracker]
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
diff --git a/ccip/config/evm/BSC_Mainnet.toml b/ccip/config/evm/BSC_Mainnet.toml
index 10f4c570bef..df140e63973 100644
--- a/ccip/config/evm/BSC_Mainnet.toml
+++ b/ccip/config/evm/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
@@ -26,3 +28,8 @@ ObservationGracePeriod = '500ms'
[NodePool]
SyncThreshold = 10
+
+[HeadTracker]
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
diff --git a/ccip/config/evm/BSC_Testnet.toml b/ccip/config/evm/BSC_Testnet.toml
index bb13501f1a2..9c528f816ea 100644
--- a/ccip/config/evm/BSC_Testnet.toml
+++ b/ccip/config/evm/BSC_Testnet.toml
@@ -22,8 +22,9 @@ BlockHistorySize = 24
[HeadTracker]
HistoryDepth = 100
SamplingInterval = '1s'
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
FinalityTagBypass = false
-PersistenceEnabled = false
[OCR]
DatabaseTimeout = '2s'
diff --git a/ccip/config/evm/Base_Mainnet.toml b/ccip/config/evm/Base_Mainnet.toml
index da38182b194..0f895e1bc6b 100644
--- a/ccip/config/evm/Base_Mainnet.toml
+++ b/ccip/config/evm/Base_Mainnet.toml
@@ -20,6 +20,9 @@ ResendAfterThreshold = '30s'
[HeadTracker]
HistoryDepth = 300
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
[NodePool]
SyncThreshold = 10
diff --git a/ccip/config/evm/Base_Sepolia.toml b/ccip/config/evm/Base_Sepolia.toml
index 92f7717b27d..202c544fb4b 100644
--- a/ccip/config/evm/Base_Sepolia.toml
+++ b/ccip/config/evm/Base_Sepolia.toml
@@ -21,6 +21,9 @@ ResendAfterThreshold = '30s'
[HeadTracker]
HistoryDepth = 300
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
[NodePool]
SyncThreshold = 10
diff --git a/ccip/config/evm/Berachain_Testnet.toml b/ccip/config/evm/Berachain_Testnet.toml
new file mode 100644
index 00000000000..9fc810e8908
--- /dev/null
+++ b/ccip/config/evm/Berachain_Testnet.toml
@@ -0,0 +1,24 @@
+ChainID = '80084'
+# finality_depth: instant
+FinalityDepth = 10
+# block_time: 5s, adding 1 second buffer
+LogPollInterval = '6s'
+
+# finality_depth * block_time / 60 secs = ~0.8 min (finality time)
+NoNewFinalizedHeadsThreshold = '5m'
+
+[GasEstimator]
+EIP1559DynamicFees = true
+Mode = 'FeeHistory'
+
+[GasEstimator.FeeHistory]
+# block_time was: 5s, per recommendation skip 1-2 blocks
+CacheTimeout = '10s'
+
+[GasEstimator.BlockHistory]
+BlockHistorySize = 100
+
+[HeadTracker]
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
diff --git a/ccip/config/evm/Bitlayer_Mainnet.toml b/ccip/config/evm/Bitlayer_Mainnet.toml
new file mode 100644
index 00000000000..f6d669d4f78
--- /dev/null
+++ b/ccip/config/evm/Bitlayer_Mainnet.toml
@@ -0,0 +1,16 @@
+ChainID = '200901'
+FinalityTagEnabled = false
+FinalityDepth = 21 # confirmed with Bitlayer team and recommended by docs: https://docs.bitlayer.org/docs/Learn/BitlayerNetwork/AboutFinality/#about-finality-at-stage-bitlayer-pos-bitlayer-mainnet-v1
+
+[GasEstimator]
+Mode = 'FeeHistory'
+EIP1559DynamicFees = false
+PriceMax = '1 gwei' # DS&A recommended value
+PriceMin = '40 mwei' # During testing, we saw minimum gas prices ~50 mwei
+PriceDefault = '1 gwei' # As we set PriceMax to '1 gwei' and PriceDefault must be less than or equal to PriceMax
+FeeCapDefault = '1 gwei' # As we set PriceMax to '1 gwei' and FeeCapDefault must be less than or equal to PriceMax
+
+[HeadTracker]
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
diff --git a/ccip/config/evm/Bitlayer_Testnet.toml b/ccip/config/evm/Bitlayer_Testnet.toml
new file mode 100644
index 00000000000..7107527ce2f
--- /dev/null
+++ b/ccip/config/evm/Bitlayer_Testnet.toml
@@ -0,0 +1,16 @@
+ChainID = '200810'
+FinalityTagEnabled = false
+FinalityDepth = 21 # confirmed with Bitlayer team and recommended by docs: https://docs.bitlayer.org/docs/Learn/BitlayerNetwork/AboutFinality/#about-finality-at-stage-bitlayer-pos-bitlayer-mainnet-v1
+
+[GasEstimator]
+Mode='FeeHistory'
+EIP1559DynamicFees = false
+PriceMax = '1 gwei' # DS&A recommended value
+PriceMin = '40 mwei' # During testing, we saw minimum gas prices ~50 mwei
+PriceDefault = '1 gwei' # As we set PriceMax to '1 gwei' and PriceDefault must be less than or equal to PriceMax
+FeeCapDefault = '1 gwei' # As we set PriceMax to '1 gwei' and FeeCapDefault must be less than or equal to PriceMax
+
+[HeadTracker]
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
diff --git a/ccip/config/evm/Blast_Mainnet.toml b/ccip/config/evm/Blast_Mainnet.toml
index f8b501723ff..26ecddeec54 100644
--- a/ccip/config/evm/Blast_Mainnet.toml
+++ b/ccip/config/evm/Blast_Mainnet.toml
@@ -26,9 +26,12 @@ EIP1559FeeCapBufferBlocks = 0
[HeadTracker]
HistoryDepth = 300
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
[NodePool]
# 4 block sync time between nodes to ensure they aren't labelled unreachable too soon
PollFailureThreshold = 4
# polls every 4sec to check if there is a block produced, since blockRate is ~3sec
-PollInterval = '4s'
\ No newline at end of file
+PollInterval = '4s'
diff --git a/ccip/config/evm/Blast_Sepolia.toml b/ccip/config/evm/Blast_Sepolia.toml
index 96dc5c67871..55f2356ad3a 100644
--- a/ccip/config/evm/Blast_Sepolia.toml
+++ b/ccip/config/evm/Blast_Sepolia.toml
@@ -26,9 +26,12 @@ EIP1559FeeCapBufferBlocks = 0
[HeadTracker]
HistoryDepth = 300
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
[NodePool]
# 4 block sync time between nodes to ensure they aren't labelled unreachable too soon
PollFailureThreshold = 4
# polls every 4sec to check if there is a block produced, since blockRate is ~3sec
-PollInterval = '4s'
\ No newline at end of file
+PollInterval = '4s'
diff --git a/ccip/config/evm/Bsquared_Mainnet.toml b/ccip/config/evm/Bsquared_Mainnet.toml
new file mode 100644
index 00000000000..61b0e5337c7
--- /dev/null
+++ b/ccip/config/evm/Bsquared_Mainnet.toml
@@ -0,0 +1,23 @@
+ChainID = '223'
+# OP stack from questionnaire https://docs.google.com/spreadsheets/d/1l8dx1GzxEnjgwH5x3vB60FUr5iFALzPcs6W_wOAiuDs/edit?gid=625078687#gid=625078687
+ChainType = 'optimismBedrock'
+# finality_depth was: ~1900
+FinalityDepth = 2000
+# block_time: ~2s, adding 1 second buffer
+LogPollInterval = '3s'
+
+# finality_depth * block_time / 60 secs = ~66 min (finality time)
+NoNewFinalizedHeadsThreshold = '70m'
+
+FinalityTagEnabled = true
+
+[GasEstimator]
+EIP1559DynamicFees = true
+Mode = 'FeeHistory'
+
+[GasEstimator.FeeHistory]
+# block_time was: 2s, per recommendation skip 1-2 blocks
+CacheTimeout = '4s'
+
+[GasEstimator.BlockHistory]
+BlockHistorySize = 100
diff --git a/ccip/config/evm/Bsquared_Testnet.toml b/ccip/config/evm/Bsquared_Testnet.toml
new file mode 100644
index 00000000000..b7cfd35fc41
--- /dev/null
+++ b/ccip/config/evm/Bsquared_Testnet.toml
@@ -0,0 +1,23 @@
+ChainID = '1123'
+# OP stack from questionnaire https://docs.google.com/spreadsheets/d/1l8dx1GzxEnjgwH5x3vB60FUr5iFALzPcs6W_wOAiuDs/edit?gid=625078687#gid=625078687
+ChainType = 'optimismBedrock'
+# finality_depth was: ~1900
+FinalityDepth = 2000
+# block_time: ~2s, adding 1 second buffer
+LogPollInterval = '3s'
+
+# finality_depth * block_time / 60 secs = ~66 min (finality time)
+NoNewFinalizedHeadsThreshold = '70m'
+
+FinalityTagEnabled = true
+
+[GasEstimator]
+EIP1559DynamicFees = true
+Mode = 'FeeHistory'
+
+[GasEstimator.FeeHistory]
+# block_time was: 2s, per recommendation skip 1-2 blocks
+CacheTimeout = '4s'
+
+[GasEstimator.BlockHistory]
+BlockHistorySize = 100
diff --git a/ccip/config/evm/Celo_Mainnet.toml b/ccip/config/evm/Celo_Mainnet.toml
index 0ed08986d32..9da7d632d0d 100644
--- a/ccip/config/evm/Celo_Mainnet.toml
+++ b/ccip/config/evm/Celo_Mainnet.toml
@@ -1,6 +1,10 @@
ChainID = '42220'
ChainType = 'celo'
+# FT and FD are both present here because the dev effort rely only on FinalityTagEnabled are still in progress.
+# We expect to be able to rely only on FinalityTagEnabled=true in the short future.
+# https://chainlink-core.slack.com/archives/C05CS33N08N/p1715102940763339?thread_ts=1715102478.537529&cid=C05CS33N08N
FinalityDepth = 10
+FinalityTagEnabled = true
LogPollInterval = '5s'
MinIncomingConfirmations = 1
NoNewHeadsThreshold = '1m'
@@ -18,4 +22,6 @@ BlockHistorySize = 12
[HeadTracker]
HistoryDepth = 50
-PersistenceEnabled = false
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
diff --git a/ccip/config/evm/Celo_Testnet.toml b/ccip/config/evm/Celo_Testnet.toml
index 0e4594150dd..c03d855acf6 100644
--- a/ccip/config/evm/Celo_Testnet.toml
+++ b/ccip/config/evm/Celo_Testnet.toml
@@ -1,5 +1,8 @@
ChainID = '44787'
ChainType = 'celo'
+# FT and FD are both present here because the dev effort rely only on FinalityTagEnabled are still in progress.
+# We expect to be able to rely only on FinalityTagEnabled=true in the short future.
+# https://chainlink-core.slack.com/archives/C05CS33N08N/p1715102940763339?thread_ts=1715102478.537529&cid=C05CS33N08N
FinalityTagEnabled = true
FinalityDepth = 2750 # mean finality time of ~37 minutes + 500 block buffer
LogPollInterval = '1s' # 1 sec block rate
diff --git a/ccip/config/evm/Ethereum_Mainnet.toml b/ccip/config/evm/Ethereum_Mainnet.toml
index 0bcaf35c648..ec3a78156ed 100644
--- a/ccip/config/evm/Ethereum_Mainnet.toml
+++ b/ccip/config/evm/Ethereum_Mainnet.toml
@@ -15,3 +15,8 @@ TransactionPercentile = 50
[OCR2.Automation]
GasLimit = 10500000
+
+[HeadTracker]
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
diff --git a/ccip/config/evm/Ethereum_Sepolia.toml b/ccip/config/evm/Ethereum_Sepolia.toml
index 24a0e68f77a..966f091f891 100644
--- a/ccip/config/evm/Ethereum_Sepolia.toml
+++ b/ccip/config/evm/Ethereum_Sepolia.toml
@@ -14,4 +14,6 @@ TransactionPercentile = 50
GasLimit = 10500000
[HeadTracker]
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
FinalityTagBypass = false
diff --git a/ccip/config/evm/Fantom_Mainnet.toml b/ccip/config/evm/Fantom_Mainnet.toml
index 7e76d94278d..2af504796e0 100644
--- a/ccip/config/evm/Fantom_Mainnet.toml
+++ b/ccip/config/evm/Fantom_Mainnet.toml
@@ -9,4 +9,9 @@ RPCBlockQueryDelay = 2
Mode = 'SuggestedPrice'
[OCR2.Automation]
-GasLimit = 3800000
\ No newline at end of file
+GasLimit = 3800000
+
+[HeadTracker]
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
diff --git a/ccip/config/evm/Fantom_Testnet.toml b/ccip/config/evm/Fantom_Testnet.toml
index 5f24a76c2e7..b361a8d14dd 100644
--- a/ccip/config/evm/Fantom_Testnet.toml
+++ b/ccip/config/evm/Fantom_Testnet.toml
@@ -9,4 +9,9 @@ RPCBlockQueryDelay = 2
Mode = 'SuggestedPrice'
[OCR2.Automation]
-GasLimit = 3800000
\ No newline at end of file
+GasLimit = 3800000
+
+[HeadTracker]
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
diff --git a/ccip/config/evm/Gnosis_Chiado.toml b/ccip/config/evm/Gnosis_Chiado.toml
index 379377a2266..320aa087209 100644
--- a/ccip/config/evm/Gnosis_Chiado.toml
+++ b/ccip/config/evm/Gnosis_Chiado.toml
@@ -8,3 +8,8 @@ NoNewFinalizedHeadsThreshold = '2m'
[GasEstimator]
EIP1559DynamicFees = true
PriceMax = '500 gwei'
+
+[HeadTracker]
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
diff --git a/ccip/config/evm/Gnosis_Mainnet.toml b/ccip/config/evm/Gnosis_Mainnet.toml
index 628646364f5..ec8ac227f78 100644
--- a/ccip/config/evm/Gnosis_Mainnet.toml
+++ b/ccip/config/evm/Gnosis_Mainnet.toml
@@ -16,3 +16,8 @@ PriceDefault = '1 gwei'
PriceMax = '500 gwei'
# 1 Gwei is the minimum accepted by the validators (unless whitelisted)
PriceMin = '1 gwei'
+
+[HeadTracker]
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
diff --git a/ccip/config/evm/Harmony_Mainnet.toml b/ccip/config/evm/Harmony_Mainnet.toml
new file mode 100644
index 00000000000..1cee98e77c7
--- /dev/null
+++ b/ccip/config/evm/Harmony_Mainnet.toml
@@ -0,0 +1,13 @@
+ChainID = '1666600000'
+LinkContractAddress = '0x218532a12a389a4a92fC0C5Fb22901D1c19198aA'
+LogPollInterval = '2s'
+MinIncomingConfirmations = 1
+NoNewHeadsThreshold = '30s'
+
+[GasEstimator]
+PriceDefault = '5 gwei'
+
+[HeadTracker]
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
diff --git a/ccip/config/evm/Harmony_Testnet.toml b/ccip/config/evm/Harmony_Testnet.toml
new file mode 100644
index 00000000000..8b7c85b9c28
--- /dev/null
+++ b/ccip/config/evm/Harmony_Testnet.toml
@@ -0,0 +1,13 @@
+ChainID = '1666700000'
+LinkContractAddress = '0x8b12Ac23BFe11cAb03a634C1F117D64a7f2cFD3e'
+LogPollInterval = '2s'
+MinIncomingConfirmations = 1
+NoNewHeadsThreshold = '30s'
+
+[GasEstimator]
+PriceDefault = '5 gwei'
+
+[HeadTracker]
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
diff --git a/ccip/config/evm/Hashkey_Mainnet.toml b/ccip/config/evm/Hashkey_Mainnet.toml
new file mode 100644
index 00000000000..69450c96f80
--- /dev/null
+++ b/ccip/config/evm/Hashkey_Mainnet.toml
@@ -0,0 +1,16 @@
+ChainID = '177'
+ChainType = 'optimismBedrock'
+FinalityTagEnabled = true
+
+[GasEstimator]
+PriceMax = '1000 gwei'
+LimitDefault = 8000000
+FeeCapDefault = '1000 gwei'
+
+[NodePool]
+PollFailureThreshold = 2
+PollInterval = '8s'
+
+[GasEstimator.DAOracle]
+OracleType = 'opstack'
+OracleAddress = '0x420000000000000000000000000000000000000F'
\ No newline at end of file
diff --git a/ccip/config/evm/Hashkey_Testnet.toml b/ccip/config/evm/Hashkey_Testnet.toml
new file mode 100644
index 00000000000..c342e503a33
--- /dev/null
+++ b/ccip/config/evm/Hashkey_Testnet.toml
@@ -0,0 +1,16 @@
+ChainID = '133'
+ChainType = 'optimismBedrock'
+FinalityTagEnabled = true
+
+[GasEstimator]
+PriceMax = '1000 gwei'
+LimitDefault = 8000000
+FeeCapDefault = '1000 gwei'
+
+[NodePool]
+PollFailureThreshold = 2
+PollInterval = '8s'
+
+[GasEstimator.DAOracle]
+OracleType = 'opstack'
+OracleAddress = '0x420000000000000000000000000000000000000F'
\ No newline at end of file
diff --git a/ccip/config/evm/Heco_Mainnet.toml b/ccip/config/evm/Heco_Mainnet.toml
new file mode 100644
index 00000000000..a39e405be31
--- /dev/null
+++ b/ccip/config/evm/Heco_Mainnet.toml
@@ -0,0 +1,26 @@
+# Heco uses BSC's settings.
+ChainID = '128'
+LinkContractAddress = '0x404460C6A5EdE2D891e8297795264fDe62ADBB75'
+LogPollInterval = '3s'
+NoNewHeadsThreshold = '30s'
+RPCBlockQueryDelay = 2
+
+[GasEstimator]
+PriceDefault = '5 gwei'
+BumpThreshold = 5
+
+[GasEstimator.BlockHistory]
+BlockHistorySize = 24
+
+[OCR]
+DatabaseTimeout = '2s'
+ContractTransmitterTransmitTimeout = '2s'
+ObservationGracePeriod = '500ms'
+
+[NodePool]
+SyncThreshold = 10
+
+[HeadTracker]
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
diff --git a/ccip/config/evm/Hedera_Mainnet.toml b/ccip/config/evm/Hedera_Mainnet.toml
new file mode 100644
index 00000000000..fdd6528e0a4
--- /dev/null
+++ b/ccip/config/evm/Hedera_Mainnet.toml
@@ -0,0 +1,35 @@
+ChainID = '295'
+ChainType = 'hedera'
+# Considering the 3-5 (6 including a buffer) seconds of finality and 2 seconds block production
+# We set the depth to 6/2 = 3 blocks, setting to 10 for safety
+FinalityDepth = 10
+# Hedera has high TPS, so polling less often
+LogPollInterval = '10s'
+MinIncomingConfirmations = 1
+
+[BalanceMonitor]
+Enabled = true
+
+[GasEstimator]
+Mode = 'SuggestedPrice'
+# Since Hedera dont have mempool and there's no way for a node to front run or a user to bribe a node to submit the transaction earlier than it's consensus timestamp,
+# But they have automated congesting pricing throttling which would mean at high sustained level the gasPrice itself could be increased to prevent malicious behaviour.
+# Disabling the Bumpthreshold as TXM now implicity handles the bumping after checking on-chain nonce & re-broadcast for Hedera chain type
+BumpThreshold = 0
+BumpMin = '10 gwei'
+BumpPercent = 20
+
+[Transactions]
+# To hit throttling you'd need to maintain 15 m gas /sec over a prolonged period of time.
+# Because Hedera's block times are every 2 secs it's less less likely to happen as compared to other chains
+# Setting this to little higher even though Hedera has High TPS, We have seen 10-12s to get the trasaction mined & 20-25s incase of failures
+# Accounting for Node syncs & avoid re-sending txns before fetching the receipt, setting to 2m
+ResendAfterThreshold = '2m'
+
+[NodePool]
+SyncThreshold = 10
+
+[HeadTracker]
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
diff --git a/ccip/config/evm/Hedera_Testnet.toml b/ccip/config/evm/Hedera_Testnet.toml
new file mode 100644
index 00000000000..7e9ec3fe2c6
--- /dev/null
+++ b/ccip/config/evm/Hedera_Testnet.toml
@@ -0,0 +1,35 @@
+ChainID = '296'
+ChainType = 'hedera'
+# Considering the 3-5 (6 including a buffer) seconds of finality and 2 seconds block production
+# We set the depth to 6/2 = 3 blocks, setting to 10 for safety
+FinalityDepth = 10
+# Hedera has high TPS, so polling less often
+LogPollInterval = '10s'
+MinIncomingConfirmations = 1
+
+[BalanceMonitor]
+Enabled = true
+
+[GasEstimator]
+Mode = 'SuggestedPrice'
+# Since Hedera dont have mempool and there's no way for a node to front run or a user to bribe a node to submit the transaction earlier than it's consensus timestamp,
+# But they have automated congesting pricing throttling which would mean at high sustained level the gasPrice itself could be increased to prevent malicious behaviour.
+# Disabling the Bumpthreshold as TXM now implicity handles the bumping after checking on-chain nonce & re-broadcast for Hedera chain type
+BumpThreshold = 0
+BumpMin = '10 gwei'
+BumpPercent = 20
+
+[Transactions]
+# To hit throttling you'd need to maintain 15 m gas /sec over a prolonged period of time.
+# Because Hedera's block times are every 2 secs it's less less likely to happen as compared to other chains
+# Setting this to little higher even though Hedera has High TPS, We have seen 10-12s to get the trasaction mined & 20-25s incase of failures
+# Accounting for Node syncs & avoid re-sending txns before fetching the receipt, setting to 2m
+ResendAfterThreshold = '2m'
+
+[NodePool]
+SyncThreshold = 10
+
+[HeadTracker]
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
diff --git a/ccip/config/evm/Klaytn_Mainnet.toml b/ccip/config/evm/Klaytn_Mainnet.toml
new file mode 100644
index 00000000000..ff8b97de970
--- /dev/null
+++ b/ccip/config/evm/Klaytn_Mainnet.toml
@@ -0,0 +1,15 @@
+ChainID = '8217'
+FinalityDepth = 10
+MinIncomingConfirmations = 1
+NoNewHeadsThreshold = '30s'
+OCR.ContractConfirmations = 1
+
+[GasEstimator]
+Mode = 'SuggestedPrice'
+PriceDefault = '750 gwei' # gwei = ston
+BumpThreshold = 5
+
+[HeadTracker]
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
diff --git a/ccip/config/evm/Klaytn_Testnet.toml b/ccip/config/evm/Klaytn_Testnet.toml
new file mode 100644
index 00000000000..599b604f086
--- /dev/null
+++ b/ccip/config/evm/Klaytn_Testnet.toml
@@ -0,0 +1,15 @@
+ChainID = '1001'
+FinalityDepth = 10
+MinIncomingConfirmations = 1
+NoNewHeadsThreshold = '30s'
+OCR.ContractConfirmations = 1
+
+[GasEstimator]
+Mode = 'SuggestedPrice'
+PriceDefault = '750 gwei' # gwei = ston
+BumpThreshold = 5
+
+[HeadTracker]
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
diff --git a/ccip/config/evm/Kroma_Mainnet.toml b/ccip/config/evm/Kroma_Mainnet.toml
index 3a48aa8ae1b..21bbe7c357c 100644
--- a/ccip/config/evm/Kroma_Mainnet.toml
+++ b/ccip/config/evm/Kroma_Mainnet.toml
@@ -1,6 +1,9 @@
ChainID = '255'
ChainType = 'kroma' # Kroma is based on the Optimism Bedrock architechture
-FinalityDepth = 400
+# FT and FD are both present here because the dev effort rely only on FinalityTagEnabled are still in progress.
+# We expect to be able to rely only on FinalityTagEnabled=true in the short future.
+# https://chainlink-core.slack.com/archives/C05CS33N08N/p1715102940763339?thread_ts=1715102478.537529&cid=C05CS33N08N
+FinalityDepth = 700
FinalityTagEnabled = true
LogPollInterval = '2s'
NoNewHeadsThreshold = '40s'
@@ -19,6 +22,9 @@ ResendAfterThreshold = '30s'
[HeadTracker]
HistoryDepth = 400
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
[NodePool]
SyncThreshold = 10
diff --git a/ccip/config/evm/Kroma_Sepolia.toml b/ccip/config/evm/Kroma_Sepolia.toml
index 9609a09e076..120737df47b 100644
--- a/ccip/config/evm/Kroma_Sepolia.toml
+++ b/ccip/config/evm/Kroma_Sepolia.toml
@@ -1,6 +1,9 @@
ChainID = '2358'
ChainType = 'kroma' # Kroma is based on the Optimism Bedrock architechture
-FinalityDepth = 400
+# FT and FD are both present here because the dev effort rely only on FinalityTagEnabled are still in progress.
+# We expect to be able to rely only on FinalityTagEnabled=true in the short future.
+# https://chainlink-core.slack.com/archives/C05CS33N08N/p1715102940763339?thread_ts=1715102478.537529&cid=C05CS33N08N
+FinalityDepth = 700
FinalityTagEnabled = true
LogPollInterval = '2s'
NoNewHeadsThreshold = '40s'
@@ -19,6 +22,9 @@ ResendAfterThreshold = '30s'
[HeadTracker]
HistoryDepth = 400
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
[NodePool]
SyncThreshold = 10
diff --git a/ccip/config/evm/L3X_Mainnet.toml b/ccip/config/evm/L3X_Mainnet.toml
index 9dd33c9e15d..5f14e5e8e8c 100644
--- a/ccip/config/evm/L3X_Mainnet.toml
+++ b/ccip/config/evm/L3X_Mainnet.toml
@@ -17,5 +17,7 @@ PriceDefault = '0.1 gwei'
FeeCapDefault = '1000 gwei'
BumpThreshold = 5
-[GasEstimator.DAOracle]
-OracleType = 'arbitrum'
+[HeadTracker]
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
diff --git a/ccip/config/evm/L3X_Sepolia.toml b/ccip/config/evm/L3X_Sepolia.toml
index c0f6a60e943..ca21bc13d6e 100644
--- a/ccip/config/evm/L3X_Sepolia.toml
+++ b/ccip/config/evm/L3X_Sepolia.toml
@@ -17,5 +17,7 @@ PriceDefault = '0.1 gwei'
FeeCapDefault = '1000 gwei'
BumpThreshold = 5
-[GasEstimator.DAOracle]
-OracleType = 'arbitrum'
+[HeadTracker]
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
diff --git a/ccip/config/evm/Linea_Goerli.toml b/ccip/config/evm/Linea_Goerli.toml
new file mode 100644
index 00000000000..2c85f9cbc02
--- /dev/null
+++ b/ccip/config/evm/Linea_Goerli.toml
@@ -0,0 +1,17 @@
+ChainID = '59140'
+# Block time 12s, finality < 3m
+FinalityDepth = 15
+# Blocks are only emitted when a transaction happens / no empty blocks
+NoNewHeadsThreshold = '0'
+
+[GasEstimator]
+BumpPercent = 40
+
+[Transactions]
+# increase resend time to align with finality
+ResendAfterThreshold = '3m'
+
+[HeadTracker]
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
diff --git a/ccip/config/evm/Linea_Mainnet.toml b/ccip/config/evm/Linea_Mainnet.toml
index 5a89873acae..6614fef9d4e 100644
--- a/ccip/config/evm/Linea_Mainnet.toml
+++ b/ccip/config/evm/Linea_Mainnet.toml
@@ -1,6 +1,6 @@
ChainID = '59144'
-# Block time 12s, finality < 60m
-FinalityDepth = 300
+#3s block time ~ 20m finality based on committee decision
+FinalityDepth = 600
# Blocks are only emitted when a transaction happens / no empty blocks
NoNewHeadsThreshold = '0'
@@ -15,6 +15,9 @@ ResendAfterThreshold = '3m'
# set greater than finality depth
[HeadTracker]
HistoryDepth = 350
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
[Transactions.AutoPurge]
Enabled = true
diff --git a/ccip/config/evm/Linea_Sepolia.toml b/ccip/config/evm/Linea_Sepolia.toml
index 8f168ee93a6..2837c7ca601 100644
--- a/ccip/config/evm/Linea_Sepolia.toml
+++ b/ccip/config/evm/Linea_Sepolia.toml
@@ -1,5 +1,5 @@
ChainID = '59141'
-FinalityDepth = 900
+FinalityDepth = 200
NoNewHeadsThreshold = '0'
[GasEstimator]
@@ -11,6 +11,9 @@ ResendAfterThreshold = '3m'
[HeadTracker]
HistoryDepth = 1000
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
[Transactions.AutoPurge]
Enabled = true
diff --git a/ccip/config/evm/Mantle_Mainnet.toml b/ccip/config/evm/Mantle_Mainnet.toml
new file mode 100644
index 00000000000..23d5168a7e9
--- /dev/null
+++ b/ccip/config/evm/Mantle_Mainnet.toml
@@ -0,0 +1,33 @@
+ChainID = '5000'
+FinalityTagEnabled = true
+FinalityDepth = 1200
+ChainType = 'optimismBedrock'
+LogPollInterval = '2s'
+MinIncomingConfirmations = 1
+NoNewFinalizedHeadsThreshold = '40m0s'
+
+[HeadTracker]
+HistoryDepth = 1250
+
+[GasEstimator]
+PriceMax = '120 gwei'
+# Limit values are high as Mantle's GasPrice is in native token (MNT) instead of ETH. Their proprietary TokenRatio parameter is used to adjust fees
+LimitDefault = 80_000_000_000
+LimitMax = 100_000_000_000
+BumpMin = '100 wei'
+BumpThreshold = 60
+EIP1559DynamicFees = true
+FeeCapDefault = '120 gwei'
+# Mantle recommends setting Priority Fee to 0 in their docs linked here: https://docs-v2.mantle.xyz/devs/concepts/tx-fee/eip-1559#application-of-eip-1559-in-mantle-v2-tectonic
+TipCapDefault = '0 wei'
+TipCapMin = '0 wei'
+
+[GasEstimator.BlockHistory]
+# Default is 24, which leads to bumpy gas prices. In CCIP
+# we want to smooth out the gas prices, so we increase the sample size.
+BlockHistorySize = 200
+# The formula for FeeCap is (current block base fee * (1.125 ^ EIP1559FeeCapBufferBlocks) + tipcap)
+# where tipcap is managed by the block history estimators. In the context of CCIP,
+# the gas price is relayed to other changes for quotes so we want accurate/avg not pessimistic values.
+# So we set this to zero so FeeCap = baseFee + tipcap.
+EIP1559FeeCapBufferBlocks = 0
\ No newline at end of file
diff --git a/ccip/config/evm/Mantle_Sepolia.toml b/ccip/config/evm/Mantle_Sepolia.toml
index ee994a71826..705f91142f2 100644
--- a/ccip/config/evm/Mantle_Sepolia.toml
+++ b/ccip/config/evm/Mantle_Sepolia.toml
@@ -1,19 +1,34 @@
ChainID = '5003'
+FinalityTagEnabled = true
+FinalityDepth = 1200
ChainType = 'optimismBedrock'
-FinalityDepth = 500
LogPollInterval = '2s'
-NoNewHeadsThreshold = '0'
MinIncomingConfirmations = 1
+NoNewFinalizedHeadsThreshold = '60m0s'
[HeadTracker]
-HistoryDepth = 600
+HistoryDepth = 1250
-[GasEstimator]
-Mode = 'L2Suggested'
-PriceMax = '200 gwei'
-LimitDefault = 100000000
-FeeCapDefault = '200 gwei'
+[GasEstimator]
+PriceMax = '120 gwei'
+# Limit values are high as Mantle's GasPrice is in native token (MNT) instead of ETH. Their proprietary TokenRatio parameter is used to adjust fees
+LimitDefault = 80000000000
+LimitMax = 100000000000
+BumpMin = '100 wei'
+BumpPercent = 20
+BumpThreshold = 60
+EIP1559DynamicFees = true
+FeeCapDefault = '120 gwei'
+# Mantle reccomends setting Priority Fee to 0 in their docs linked here: https://docs-v2.mantle.xyz/devs/concepts/tx-fee/eip-1559#application-of-eip-1559-in-mantle-v2-tectonic
+TipCapDefault = '0 wei'
+TipCapMin = '0 wei'
[GasEstimator.BlockHistory]
+# Default is 24, which leads to bumpy gas prices. In CCIP
+# we want to smooth out the gas prices, so we increase the sample size.
BlockHistorySize = 200
+# The formula for FeeCap is (current block base fee * (1.125 ^ EIP1559FeeCapBufferBlocks) + tipcap)
+# where tipcap is managed by the block history estimators. In the context of CCIP,
+# the gas price is relayed to other changes for quotes so we want accurate/avg not pessimistic values.
+# So we set this to zero so FeeCap = baseFee + tipcap.
EIP1559FeeCapBufferBlocks = 0
\ No newline at end of file
diff --git a/ccip/config/evm/Metis_Mainnet.toml b/ccip/config/evm/Metis_Mainnet.toml
index f057400d014..a95945e9f1b 100644
--- a/ccip/config/evm/Metis_Mainnet.toml
+++ b/ccip/config/evm/Metis_Mainnet.toml
@@ -1,8 +1,14 @@
# Metis is an L2 chain based on Optimism.
ChainID = '1088'
-ChainType = 'metis'
+ChainType = 'optimismBedrock'
# Sequencer offers absolute finality
-FinalityDepth = 10
+# High variation on finality depth triggered a commitee to investigate
+# and set 500 as a secure finality depth.
+# https://chainlink-core.slack.com/archives/C0725LNLJLA/p1717118469587219
+FinalityDepth = 500
+# FT and FD are both present here because the dev effort rely only on FinalityTagEnabled are still in progress.
+# We expect to be able to rely only on FinalityTagEnabled=true in the short future.
+# https://chainlink-core.slack.com/archives/C05CS33N08N/p1715102940763339?thread_ts=1715102478.537529&cid=C05CS33N08N
FinalityTagEnabled = true
MinIncomingConfirmations = 1
NoNewHeadsThreshold = '0'
@@ -19,3 +25,8 @@ BlockHistorySize = 0
[NodePool]
SyncThreshold = 10
+
+[HeadTracker]
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
diff --git a/ccip/config/evm/Metis_Sepolia.toml b/ccip/config/evm/Metis_Sepolia.toml
index 4ff4056c75d..65247991d31 100644
--- a/ccip/config/evm/Metis_Sepolia.toml
+++ b/ccip/config/evm/Metis_Sepolia.toml
@@ -1,6 +1,9 @@
ChainID = '59902'
ChainType = 'optimismBedrock'
-FinalityDepth = 10
+# FT and FD are both present here because the dev effort rely only on FinalityTagEnabled are still in progress.
+# We expect to be able to rely only on FinalityTagEnabled=true in the short future.
+# https://chainlink-core.slack.com/archives/C05CS33N08N/p1715102940763339?thread_ts=1715102478.537529&cid=C05CS33N08N
+FinalityDepth = 3000
FinalityTagEnabled = true
MinIncomingConfirmations = 1
NoNewHeadsThreshold = '0'
diff --git a/ccip/config/evm/Mode_Mainnet.toml b/ccip/config/evm/Mode_Mainnet.toml
index 69a8e93fecd..b586cdacc78 100644
--- a/ccip/config/evm/Mode_Mainnet.toml
+++ b/ccip/config/evm/Mode_Mainnet.toml
@@ -24,6 +24,9 @@ EIP1559FeeCapBufferBlocks = 0
[HeadTracker]
HistoryDepth = 300
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
[NodePool]
PollFailureThreshold = 2
diff --git a/ccip/config/evm/Mode_Sepolia.toml b/ccip/config/evm/Mode_Sepolia.toml
index f7398869beb..d621010b4ef 100644
--- a/ccip/config/evm/Mode_Sepolia.toml
+++ b/ccip/config/evm/Mode_Sepolia.toml
@@ -24,6 +24,9 @@ EIP1559FeeCapBufferBlocks = 0
[HeadTracker]
HistoryDepth = 300
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
[NodePool]
PollFailureThreshold = 2
diff --git a/ccip/config/evm/Optimism_Mainnet.toml b/ccip/config/evm/Optimism_Mainnet.toml
index b0f56a49d90..e1398775495 100644
--- a/ccip/config/evm/Optimism_Mainnet.toml
+++ b/ccip/config/evm/Optimism_Mainnet.toml
@@ -21,6 +21,9 @@ ResendAfterThreshold = '30s'
[HeadTracker]
HistoryDepth = 300
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
[NodePool]
SyncThreshold = 10
diff --git a/ccip/config/evm/Optimism_Sepolia.toml b/ccip/config/evm/Optimism_Sepolia.toml
index 1c71aa5dd83..2590feec51a 100644
--- a/ccip/config/evm/Optimism_Sepolia.toml
+++ b/ccip/config/evm/Optimism_Sepolia.toml
@@ -20,6 +20,9 @@ ResendAfterThreshold = '30s'
[HeadTracker]
HistoryDepth = 300
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
[NodePool]
SyncThreshold = 10
diff --git a/ccip/config/evm/Polygon_Amoy.toml b/ccip/config/evm/Polygon_Amoy.toml
index b05b3053a8e..eb75eab271b 100644
--- a/ccip/config/evm/Polygon_Amoy.toml
+++ b/ccip/config/evm/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
@@ -23,6 +23,9 @@ BlockHistorySize = 24
[HeadTracker]
HistoryDepth = 2000
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
[NodePool]
SyncThreshold = 10
diff --git a/ccip/config/evm/Polygon_Mainnet.toml b/ccip/config/evm/Polygon_Mainnet.toml
index bf605cab3c6..555dbfff815 100644
--- a/ccip/config/evm/Polygon_Mainnet.toml
+++ b/ccip/config/evm/Polygon_Mainnet.toml
@@ -33,6 +33,9 @@ BlockHistorySize = 24
[HeadTracker]
# Polygon suffers from a tremendous number of re-orgs, we need to set this to something very large to be conservative enough
HistoryDepth = 2000
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
[NodePool]
SyncThreshold = 10
diff --git a/ccip/config/evm/Polygon_Mumbai.toml b/ccip/config/evm/Polygon_Mumbai.toml
new file mode 100644
index 00000000000..83f275a0643
--- /dev/null
+++ b/ccip/config/evm/Polygon_Mumbai.toml
@@ -0,0 +1,31 @@
+ChainID = '80001'
+FinalityDepth = 500
+FinalityTagEnabled = true
+LinkContractAddress = '0x326C977E6efc84E512bB9C30f76E30c160eD06FB'
+LogPollInterval = '1s'
+MinIncomingConfirmations = 5
+NoNewHeadsThreshold = '30s'
+RPCBlockQueryDelay = 10
+RPCDefaultBatchSize = 100
+
+[Transactions]
+MaxQueued = 5000
+
+[GasEstimator]
+PriceDefault = '25 gwei'
+PriceMax = '115792089237316195423570985008687907853269984665.640564039457584007913129639935 tether'
+PriceMin = '25 gwei'
+BumpMin = '20 gwei'
+BumpThreshold = 5
+
+[GasEstimator.BlockHistory]
+BlockHistorySize = 24
+
+[HeadTracker]
+HistoryDepth = 2000
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
+
+[NodePool]
+SyncThreshold = 10
diff --git a/ccip/config/evm/Polygon_Zkevm_Cardona.toml b/ccip/config/evm/Polygon_Zkevm_Cardona.toml
index 5e4861f9d44..146c23a8024 100644
--- a/ccip/config/evm/Polygon_Zkevm_Cardona.toml
+++ b/ccip/config/evm/Polygon_Zkevm_Cardona.toml
@@ -13,15 +13,20 @@ ContractConfirmations = 1
ResendAfterThreshold = '3m'
[GasEstimator]
-PriceMin = '1 mwei'
+Mode = 'FeeHistory'
+# The FeeHistory estimator does not enforce PriceMin, setting it to 0 to not place any limits on the price
+PriceMin = '0'
BumpPercent = 40
-BumpMin = '20 mwei'
-[GasEstimator.BlockHistory]
-BlockHistorySize = 12
+[GasEstimator.FeeHistory]
+# Refresh the suggested price every 4 seconds, to stay slightly below their polling rate of 5s
+CacheTimeout = '4s'
[HeadTracker]
HistoryDepth = 2000
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
[Transactions.AutoPurge]
Enabled = true
diff --git a/ccip/config/evm/Polygon_Zkevm_Mainnet.toml b/ccip/config/evm/Polygon_Zkevm_Mainnet.toml
index b38a483ff35..d42ef9b057e 100644
--- a/ccip/config/evm/Polygon_Zkevm_Mainnet.toml
+++ b/ccip/config/evm/Polygon_Zkevm_Mainnet.toml
@@ -1,6 +1,6 @@
ChainID = '1101'
ChainType = 'zkevm'
-FinalityDepth = 500
+FinalityDepth = 1000
NoNewHeadsThreshold = '6m'
MinIncomingConfirmations = 1
LogPollInterval = '30s'
@@ -14,12 +14,14 @@ ContractConfirmations = 1
ResendAfterThreshold = '3m'
[GasEstimator]
-PriceMin = '100 mwei'
+Mode = 'FeeHistory'
+# The FeeHistory estimator does not enforce PriceMin, setting it to 0 to not place any limits on the price
+PriceMin = '0'
BumpPercent = 40
-BumpMin = '100 mwei'
-[GasEstimator.BlockHistory]
-BlockHistorySize = 12
+[GasEstimator.FeeHistory]
+# Refresh the suggested price every 4 seconds, to stay slightly below their polling rate of 5s
+CacheTimeout = '4s'
[HeadTracker]
# Polygon suffers from a tremendous number of re-orgs, we need to set this to something very large to be conservative enough
diff --git a/ccip/config/evm/RSK_Mainnet.toml b/ccip/config/evm/RSK_Mainnet.toml
new file mode 100644
index 00000000000..8290481a331
--- /dev/null
+++ b/ccip/config/evm/RSK_Mainnet.toml
@@ -0,0 +1,13 @@
+# RSK prices its txes in sats not wei
+ChainID = '30'
+LinkContractAddress = '0x14AdaE34beF7ca957Ce2dDe5ADD97ea050123827'
+LogPollInterval = '30s'
+MinContractPayment = '0.001 link'
+
+[GasEstimator]
+# It's about 100 times more expensive than Wei, very roughly speaking
+PriceDefault = '50 mwei'
+PriceMax = '50 gwei'
+PriceMin = '0'
+# rsk does not yet support EIP-1559 but this allows validation to pass
+FeeCapDefault = '100 mwei'
diff --git a/ccip/config/evm/RSK_Testnet.toml b/ccip/config/evm/RSK_Testnet.toml
new file mode 100644
index 00000000000..2fde16aa7cc
--- /dev/null
+++ b/ccip/config/evm/RSK_Testnet.toml
@@ -0,0 +1,10 @@
+ChainID = '31'
+LinkContractAddress = '0x8bBbd80981FE76d44854D8DF305e8985c19f0e78'
+MinContractPayment = '0.001 link'
+LogPollInterval = '30s'
+
+[GasEstimator]
+PriceDefault = '50 mwei'
+PriceMax = '50 gwei'
+PriceMin = '0'
+FeeCapDefault = '100 mwei'
diff --git a/ccip/config/evm/Ronin_Mainnet.toml b/ccip/config/evm/Ronin_Mainnet.toml
new file mode 100644
index 00000000000..14bb9d1e258
--- /dev/null
+++ b/ccip/config/evm/Ronin_Mainnet.toml
@@ -0,0 +1,16 @@
+ChainID = "2020"
+FinalityTagEnabled = true
+LinkContractAddress = "0x3902228D6A3d2Dc44731fD9d45FeE6a61c722D0b"
+# Ronin produces blocks every 3 seconds
+LogPollInterval = "3s"
+NoNewHeadsThreshold = "3m"
+
+[GasEstimator]
+# Ronin uses default gas price of 20 gwei https://docs.skymavis.com/mavis/mpc/guides/estimate-gas#overview
+Mode = 'FeeHistory'
+PriceMax = "1000 gwei"
+
+[HeadTracker]
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
diff --git a/ccip/config/evm/Ronin_Saigon.toml b/ccip/config/evm/Ronin_Saigon.toml
new file mode 100644
index 00000000000..b775f8f0626
--- /dev/null
+++ b/ccip/config/evm/Ronin_Saigon.toml
@@ -0,0 +1,16 @@
+ChainID = "2021"
+FinalityTagEnabled = true
+LinkContractAddress = "0x5bB50A6888ee6a67E22afFDFD9513be7740F1c15"
+# Ronin produces blocks every 3 seconds
+LogPollInterval = "3s"
+NoNewHeadsThreshold = "3m"
+
+[GasEstimator]
+# Ronin uses default gas price of 20 gwei https://docs.skymavis.com/mavis/mpc/guides/estimate-gas#overview
+Mode = 'FeeHistory'
+PriceMax = "1000 gwei"
+
+[HeadTracker]
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
diff --git a/ccip/config/evm/Scroll_Mainnet.toml b/ccip/config/evm/Scroll_Mainnet.toml
index b8e7bd09e80..f0449ef12be 100644
--- a/ccip/config/evm/Scroll_Mainnet.toml
+++ b/ccip/config/evm/Scroll_Mainnet.toml
@@ -17,6 +17,9 @@ BlockHistorySize = 24
[HeadTracker]
HistoryDepth = 50
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
[OCR]
ContractConfirmations = 1
diff --git a/ccip/config/evm/Scroll_Sepolia.toml b/ccip/config/evm/Scroll_Sepolia.toml
index baee2080d96..aca06ae18d3 100644
--- a/ccip/config/evm/Scroll_Sepolia.toml
+++ b/ccip/config/evm/Scroll_Sepolia.toml
@@ -17,6 +17,9 @@ BlockHistorySize = 24
[HeadTracker]
HistoryDepth = 50
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
[OCR]
ContractConfirmations = 1
diff --git a/ccip/config/evm/Simulated.toml b/ccip/config/evm/Simulated.toml
index e21dc0990f0..4ec8d962b21 100644
--- a/ccip/config/evm/Simulated.toml
+++ b/ccip/config/evm/Simulated.toml
@@ -1,5 +1,5 @@
ChainID = '1337'
-FinalityDepth = 1
+FinalityDepth = 10
MinIncomingConfirmations = 1
MinContractPayment = '100'
NoNewHeadsThreshold = '0s'
@@ -19,7 +19,9 @@ PriceMax = '100 micro'
HistoryDepth = 10
MaxBufferSize = 100
SamplingInterval = '0s'
-PersistenceEnabled = false
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
[OCR]
ContractConfirmations = 1
diff --git a/ccip/config/evm/Soneium_Sepolia.toml b/ccip/config/evm/Soneium_Sepolia.toml
new file mode 100755
index 00000000000..e0ea59ca22f
--- /dev/null
+++ b/ccip/config/evm/Soneium_Sepolia.toml
@@ -0,0 +1,35 @@
+ChainID = '1946'
+ChainType = 'optimismBedrock'
+LinkContractAddress = '0x7ea13478Ea3961A0e8b538cb05a9DF0477c79Cd2'
+FinalityDepth = 200
+LogPollInterval = '2s'
+NoNewHeadsThreshold = '40s'
+MinIncomingConfirmations = 1
+NoNewFinalizedHeadsThreshold = '120m' # Soneium can take upto 2Hours to finalize
+FinalityTagEnabled = true
+
+[GasEstimator]
+EIP1559DynamicFees = true
+PriceMin = '1 wei'
+BumpMin = '1 mwei'
+
+[GasEstimator.BlockHistory]
+BlockHistorySize = 60
+
+[Transactions]
+ResendAfterThreshold = '30s'
+
+[HeadTracker]
+HistoryDepth = 300
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
+
+[NodePool]
+SyncThreshold = 10
+
+[OCR]
+ContractConfirmations = 1
+
+[OCR2.Automation]
+GasLimit = 6500000
diff --git a/ccip/config/evm/Sonic_Mainnet.toml b/ccip/config/evm/Sonic_Mainnet.toml
new file mode 100644
index 00000000000..523a931c8d6
--- /dev/null
+++ b/ccip/config/evm/Sonic_Mainnet.toml
@@ -0,0 +1,28 @@
+ChainId = '146'
+FinalityDepth = 10
+FinalityTagEnabled = false
+LogPollInterval = "1s" #1s block rate
+MinIncomingConfirmations = 5
+RPCBlockQueryDelay = 10
+RPCDefaultBatchSize = 100
+
+[GasEstimator]
+Mode = 'FeeHistory'
+EIP1559DynamicFees = true
+BumpPercent = 10
+LimitDefault = 8000000 # default ccip value
+
+[GasEstimator.FeeHistory]
+CacheTimeout = '2s'
+
+[GasEstimator.BlockHistory]
+BlockHistorySize = 100
+
+[HeadTracker]
+HistoryDepth = 50
+
+[NodePool]
+SyncThreshold = 10
+
+[Transactions]
+MaxQueued = 500
\ No newline at end of file
diff --git a/ccip/config/evm/Sonic_Testnet.toml b/ccip/config/evm/Sonic_Testnet.toml
new file mode 100644
index 00000000000..ca3ccf8f718
--- /dev/null
+++ b/ccip/config/evm/Sonic_Testnet.toml
@@ -0,0 +1,28 @@
+ChainId = '57054'
+FinalityDepth = 10
+FinalityTagEnabled = false
+LogPollInterval = "1s" #1s block rate
+MinIncomingConfirmations = 5
+RPCBlockQueryDelay = 10
+RPCDefaultBatchSize = 100
+
+[GasEstimator]
+Mode = 'FeeHistory'
+EIP1559DynamicFees = true
+BumpPercent = 10
+LimitDefault = 8000000 # default ccip value
+
+[GasEstimator.FeeHistory]
+CacheTimeout = '2s'
+
+[GasEstimator.BlockHistory]
+BlockHistorySize = 100
+
+[HeadTracker]
+HistoryDepth = 50
+
+[NodePool]
+SyncThreshold = 10
+
+[Transactions]
+MaxQueued = 500
\ No newline at end of file
diff --git a/ccip/config/evm/Unichain_Testnet.toml b/ccip/config/evm/Unichain_Testnet.toml
new file mode 100644
index 00000000000..5e18f0d4716
--- /dev/null
+++ b/ccip/config/evm/Unichain_Testnet.toml
@@ -0,0 +1,26 @@
+ChainID = '1301'
+# OP stack: https://docs.unichain.org/docs/getting-started/set-up-a-node#overview
+ChainType = 'optimismBedrock'
+# finality_depth was: ~1900
+FinalityDepth = 2000
+# block_time was: ~1s, adding 1 second buffer
+LogPollInterval = '2s'
+
+# batching_size_finalization_percentage = 30% according to the explorer batching view
+# ( batching_size_finalization_percentage * finality_depth) * block_time / 60 secs = ~10 min (finality time)
+# After running soak tests using 10m threw issues as there are batchs that take 35m, so we are bumping it to 45m to be sure
+NoNewFinalizedHeadsThreshold = '45m'
+
+FinalityTagEnabled = true
+
+[GasEstimator]
+EIP1559DynamicFees = true
+Mode = 'FeeHistory'
+
+[GasEstimator.FeeHistory]
+# block_time was: 1s, per recommendation skip 1-2 blocks
+CacheTimeout = '2s'
+
+[GasEstimator.BlockHistory]
+# As we see blocks containing between ~[8-12]tx, to get about ~1000 tx to check we would need to rougly go 100 tx back
+BlockHistorySize = 100
diff --git a/ccip/config/evm/WeMix_Mainnet.toml b/ccip/config/evm/WeMix_Mainnet.toml
index be7c278f692..a4e742d7300 100644
--- a/ccip/config/evm/WeMix_Mainnet.toml
+++ b/ccip/config/evm/WeMix_Mainnet.toml
@@ -16,4 +16,6 @@ EIP1559DynamicFees = true
TipCapDefault = '100 gwei'
[HeadTracker]
-PersistenceEnabled = false
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
diff --git a/ccip/config/evm/WeMix_Testnet.toml b/ccip/config/evm/WeMix_Testnet.toml
index 4591fc4c572..bfb75f158e3 100644
--- a/ccip/config/evm/WeMix_Testnet.toml
+++ b/ccip/config/evm/WeMix_Testnet.toml
@@ -16,5 +16,6 @@ EIP1559DynamicFees = true
TipCapDefault = '100 gwei'
[HeadTracker]
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
FinalityTagBypass = false
-PersistenceEnabled = false
diff --git a/ccip/config/evm/Worldchain_Mainnet.toml b/ccip/config/evm/Worldchain_Mainnet.toml
new file mode 100644
index 00000000000..9b25d89d98c
--- /dev/null
+++ b/ccip/config/evm/Worldchain_Mainnet.toml
@@ -0,0 +1,23 @@
+ChainID = '480'
+# OP stack: https://worldcoin.notion.site/World-Chain-Developer-Preview-Guide-23c94a67683f4e71986e5303ab88c9f3
+ChainType = 'optimismBedrock'
+# finality_depth was: ~2400
+FinalityDepth = 2500
+# block_time was: 2s, adding 1 second buffer
+LogPollInterval = '3s'
+
+# finality_depth * block_time / 60 secs = ~83 min (finality time)
+NoNewFinalizedHeadsThreshold = '90m'
+
+FinalityTagEnabled = true
+
+[GasEstimator]
+EIP1559DynamicFees = true
+Mode = 'FeeHistory'
+
+[GasEstimator.FeeHistory]
+# block_time was: 2s, per recommendation skip 1-2 blocks
+CacheTimeout = '4s'
+
+[GasEstimator.BlockHistory]
+BlockHistorySize = 100
diff --git a/ccip/config/evm/Worldchain_Testnet.toml b/ccip/config/evm/Worldchain_Testnet.toml
new file mode 100644
index 00000000000..01618322285
--- /dev/null
+++ b/ccip/config/evm/Worldchain_Testnet.toml
@@ -0,0 +1,23 @@
+ChainID = '4801'
+# OP stack: https://worldcoin.notion.site/World-Chain-Developer-Preview-Guide-23c94a67683f4e71986e5303ab88c9f3
+ChainType = 'optimismBedrock'
+# finality_depth was: ~2400
+FinalityDepth = 2500
+# block_time was: 2s, adding 1 second buffer
+LogPollInterval = '3s'
+
+# finality_depth * block_time / 60 secs = ~83 min (finality time)
+NoNewFinalizedHeadsThreshold = '90m'
+
+FinalityTagEnabled = true
+
+[GasEstimator]
+EIP1559DynamicFees = true
+Mode = 'FeeHistory'
+
+[GasEstimator.FeeHistory]
+# block_time was: 2s, per recommendation skip 1-2 blocks
+CacheTimeout = '4s'
+
+[GasEstimator.BlockHistory]
+BlockHistorySize = 100
diff --git a/ccip/config/evm/XLayer_Mainnet.toml b/ccip/config/evm/XLayer_Mainnet.toml
index a39a9231ae2..28f7819276c 100644
--- a/ccip/config/evm/XLayer_Mainnet.toml
+++ b/ccip/config/evm/XLayer_Mainnet.toml
@@ -1,6 +1,6 @@
ChainID = '196'
ChainType = 'xlayer'
-FinalityDepth = 500
+FinalityDepth = 1000
NoNewHeadsThreshold = '6m'
MinIncomingConfirmations = 1
LogPollInterval = '30s'
diff --git a/ccip/config/evm/XLayer_Sepolia.toml b/ccip/config/evm/XLayer_Sepolia.toml
index 2aa6e58469b..163d909542e 100644
--- a/ccip/config/evm/XLayer_Sepolia.toml
+++ b/ccip/config/evm/XLayer_Sepolia.toml
@@ -23,6 +23,9 @@ BlockHistorySize = 12
[HeadTracker]
HistoryDepth = 2000
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
[Transactions.AutoPurge]
Enabled = true
diff --git a/ccip/config/evm/fallback.toml b/ccip/config/evm/fallback.toml
new file mode 100644
index 00000000000..c1f963a33ff
--- /dev/null
+++ b/ccip/config/evm/fallback.toml
@@ -0,0 +1,95 @@
+AutoCreateKey = true
+BlockBackfillDepth = 10
+BlockBackfillSkip = false
+FinalityDepth = 50
+FinalityTagEnabled = false
+LogBackfillBatchSize = 1000
+LogPollInterval = '15s'
+LogKeepBlocksDepth = 100000
+# CCIP uses paging when removing logs to avoid pushing too much pressure on the database
+LogPrunePageSize = 10000
+BackupLogPollerBlockDelay = 100
+MinContractPayment = '.00001 link'
+MinIncomingConfirmations = 3
+NonceAutoSync = true
+NoNewHeadsThreshold = '3m'
+RPCDefaultBatchSize = 250
+RPCBlockQueryDelay = 1
+FinalizedBlockOffset = 0
+NoNewFinalizedHeadsThreshold = '0'
+LogBroadcasterEnabled = true
+
+[Transactions]
+ForwardersEnabled = false
+MaxInFlight = 16
+MaxQueued = 250
+ReaperInterval = '1h'
+ReaperThreshold = '168h'
+ResendAfterThreshold = '1m'
+
+[Transactions.AutoPurge]
+Enabled = false
+
+[BalanceMonitor]
+Enabled = true
+
+[GasEstimator]
+Mode = 'BlockHistory'
+PriceDefault = '20 gwei'
+PriceMax = '115792089237316195423570985008687907853269984665.640564039457584007913129639935 tether'
+PriceMin = '1 gwei'
+LimitDefault = 8_000_000
+LimitMax = 8_000_000
+LimitMultiplier = '1'
+LimitTransfer = 21_000
+BumpMin = '5 gwei'
+BumpPercent = 20
+BumpThreshold = 3
+EIP1559DynamicFees = false
+FeeCapDefault = '100 gwei'
+TipCapDefault = '1'
+TipCapMin = '1'
+EstimateLimit = false
+
+[GasEstimator.BlockHistory]
+BatchSize = 25
+BlockHistorySize = 8
+CheckInclusionBlocks = 12
+CheckInclusionPercentile = 90
+TransactionPercentile = 60
+
+[GasEstimator.FeeHistory]
+CacheTimeout = '10s'
+
+[HeadTracker]
+HistoryDepth = 100
+MaxBufferSize = 3
+SamplingInterval = '1s'
+FinalityTagBypass = true
+MaxAllowedFinalityDepth = 10000
+
+[NodePool]
+PollFailureThreshold = 5
+PollInterval = '10s'
+SelectionMode = 'HighestHead'
+SyncThreshold = 5
+LeaseDuration = '0s'
+NodeIsSyncingEnabled = false
+FinalizedBlockPollInterval = '5s'
+EnforceRepeatableRead = false
+DeathDeclarationDelay = '10s'
+NewHeadsPollInterval = '0s'
+
+[OCR]
+ContractConfirmations = 4
+ContractTransmitterTransmitTimeout = '10s'
+DatabaseTimeout = '10s'
+DeltaCOverride = '168h'
+DeltaCJitterOverride = '1h'
+ObservationGracePeriod = '1s'
+
+[OCR2.Automation]
+GasLimit = 5400000
+
+[Workflow]
+GasLimitDefault = 400_000
diff --git a/ccip/config/evm/zkSync_Mainnet.toml b/ccip/config/evm/zkSync_Mainnet.toml
index a8910a37e4a..a29098690b4 100644
--- a/ccip/config/evm/zkSync_Mainnet.toml
+++ b/ccip/config/evm/zkSync_Mainnet.toml
@@ -28,4 +28,4 @@ OracleType = 'zksync'
[HeadTracker]
# tracks top N blocks to keep in heads database table. Should store atleast the same # of blocks as finalityDepth
-HistoryDepth = 1500
\ No newline at end of file
+HistoryDepth = 1500
diff --git a/ccip/config/evm/zkSync_Sepolia.toml b/ccip/config/evm/zkSync_Sepolia.toml
index 6eb4ba4137e..36b0c9282da 100644
--- a/ccip/config/evm/zkSync_Sepolia.toml
+++ b/ccip/config/evm/zkSync_Sepolia.toml
@@ -1,23 +1,23 @@
ChainID = '300'
ChainType = 'zksync'
# 200block ~ 20min concurrent with the l1_committed tag
-FinalityDepth = 200
+FinalityDepth = 200
# block rate is ~2-5sec, so this ensures blocks are polled correctly
LogPollInterval = '5s'
# sufficient time for RPC to be labelled out of sync, since blockRate is pretty fast
NoNewHeadsThreshold = '1m'
[GasEstimator]
-# no EIP1559 to ensure our estimator doesnot estimate gas with MaxPriorityFee which will break minFunding requirement
-EIP1559DynamicFees = false
-# high LimitDefault for worst case pubdata bytes with BatchGasLimit reduced to 4M in OCR2Config
+# no EIP1559 to ensure our estimator doesnot estimate gas with MaxPriorityFee which will break minFunding requirement
+EIP1559DynamicFees = false
+# high LimitDefault for worst case pubdata bytes with BatchGasLimit reduced to 4M in OCR2Config
LimitDefault = 2_500_000_000
FeeCapDefault = '500 mwei'
PriceDefault = '25 mwei'
# p999 value for gasPrice based on historical data
PriceMax = '500 mwei'
# avg gasPrices are at 0.025 gwei
-PriceMin = '25 mwei'
+PriceMin = '25 mwei'
[GasEstimator.BlockHistory]
# increasing this to smooth out gas estimation
@@ -28,4 +28,7 @@ OracleType = 'zksync'
[HeadTracker]
# tracks top N blocks to keep in heads database table. Should store atleast the same # of blocks as finalityDepth
-HistoryDepth = 250
\ No newline at end of file
+HistoryDepth = 250
+# FinalityDepth < 1k => FinalityTagBypass = false
+# https://smartcontract-it.atlassian.net/browse/SHIP-4078
+FinalityTagBypass = false
From d2cb00789be0690e62e7dc8db0220218dae99ce7 Mon Sep 17 00:00:00 2001
From: Sam
Date: Wed, 8 Jan 2025 12:46:01 -0500
Subject: [PATCH 14/91] Remove panic recovery for wsrpc (#15865)
- Due to some other bug in the library, the redialled connection never
becomes ready.
---
.../relay/evm/mercury/wsrpc/client.go | 14 ----
.../relay/evm/mercury/wsrpc/client_test.go | 66 -------------------
2 files changed, 80 deletions(-)
diff --git a/core/services/relay/evm/mercury/wsrpc/client.go b/core/services/relay/evm/mercury/wsrpc/client.go
index ebd282e6093..1cd8c9af50d 100644
--- a/core/services/relay/evm/mercury/wsrpc/client.go
+++ b/core/services/relay/evm/mercury/wsrpc/client.go
@@ -286,13 +286,6 @@ func (w *client) waitForReady(ctx context.Context) (err error) {
func (w *client) Transmit(ctx context.Context, req *pb.TransmitRequest) (resp *pb.TransmitResponse, err error) {
ok := w.IfStarted(func() {
- defer func() {
- if r := recover(); r != nil {
- w.handlePanic(r)
- resp = nil
- err = fmt.Errorf("Transmit: caught panic: %v", r)
- }
- }()
w.logger.Trace("Transmit")
start := time.Now()
if err = w.waitForReady(ctx); err != nil {
@@ -360,13 +353,6 @@ func (w *client) handleTimeout(err error) {
func (w *client) LatestReport(ctx context.Context, req *pb.LatestReportRequest) (resp *pb.LatestReportResponse, err error) {
ok := w.IfStarted(func() {
- defer func() {
- if r := recover(); r != nil {
- w.handlePanic(r)
- resp = nil
- err = fmt.Errorf("LatestReport: caught panic: %v", r)
- }
- }()
lggr := w.logger.With("req.FeedId", hexutil.Encode(req.FeedId))
lggr.Trace("LatestReport")
if err = w.waitForReady(ctx); err != nil {
diff --git a/core/services/relay/evm/mercury/wsrpc/client_test.go b/core/services/relay/evm/mercury/wsrpc/client_test.go
index b2bbf074e91..7c6706eddf8 100644
--- a/core/services/relay/evm/mercury/wsrpc/client_test.go
+++ b/core/services/relay/evm/mercury/wsrpc/client_test.go
@@ -2,16 +2,11 @@ package wsrpc
import (
"context"
- "math/big"
- "math/rand"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
- grpc_connectivity "google.golang.org/grpc/connectivity"
-
- "github.com/smartcontractkit/wsrpc"
"github.com/smartcontractkit/chainlink-common/pkg/services/servicetest"
"github.com/smartcontractkit/chainlink/v2/core/internal/testutils"
@@ -128,67 +123,6 @@ func Test_Client_Transmit(t *testing.T) {
}
})
})
-
- t.Run("recovers panics in underlying client and attempts redial", func(t *testing.T) {
- makeConn := func() *mocks.MockConn {
- return &mocks.MockConn{
- Ready: true,
- State: grpc_connectivity.Ready,
- InvokeF: func(ctx context.Context, method string, args interface{}, reply interface{}) error {
- panic("TESTING CONN INVOKE PANIC")
- },
- }
- }
-
- ch := make(chan *mocks.MockConn, 100)
- cnt := 0
-
- f := func(ctxCaller context.Context, target string, opts ...wsrpc.DialOption) (Conn, error) {
- cnt++
- switch cnt {
- case 1, 2:
- conn := makeConn()
- ch <- conn
- return conn, nil
- default:
- t.Fatalf("too many dials, got: %d", cnt)
- return nil, nil
- }
- }
-
- clientKey := csakey.MustNewV2XXXTestingOnly(big.NewInt(rand.Int63()))
- serverKey := csakey.MustNewV2XXXTestingOnly(big.NewInt(rand.Int63()))
- opts := ClientOpts{
- lggr,
- clientKey,
- serverKey.PublicKey,
- "",
- noopCacheSet,
- f,
- }
- c := newClient(opts)
-
- servicetest.Run(t, c)
-
- // drain the channel
- var conn *mocks.MockConn
- select {
- case conn = <-ch:
- assert.Equal(t, 1, cnt)
- default:
- t.Fatalf("expected dial to be called")
- }
-
- _, err := c.Transmit(ctx, req)
- require.EqualError(t, err, "Transmit: caught panic: TESTING CONN INVOKE PANIC")
-
- // expect conn to be closed and re-dialed
- conn2 := <-ch
- assert.Equal(t, 2, cnt)
-
- assert.True(t, conn.Closed)
- assert.False(t, conn2.Closed)
- })
}
func Test_Client_LatestReport(t *testing.T) {
From 0747d30778e62cbd5bd913f8754f3c70f1ffb6ea Mon Sep 17 00:00:00 2001
From: Dmytro Haidashenko <34754799+dhaidashenko@users.noreply.github.com>
Date: Wed, 8 Jan 2025 19:17:42 +0100
Subject: [PATCH 15/91] Added is_backfiled filed to solana's filter table
(#15796)
---
.../0263_solana_filter_is_backfilled.sql | 14 ++++++++++++++
1 file changed, 14 insertions(+)
create mode 100644 core/store/migrate/migrations/0263_solana_filter_is_backfilled.sql
diff --git a/core/store/migrate/migrations/0263_solana_filter_is_backfilled.sql b/core/store/migrate/migrations/0263_solana_filter_is_backfilled.sql
new file mode 100644
index 00000000000..7e81e7c4c5e
--- /dev/null
+++ b/core/store/migrate/migrations/0263_solana_filter_is_backfilled.sql
@@ -0,0 +1,14 @@
+-- +goose Up
+-- +goose StatementBegin
+
+ALTER TABLE solana.log_poller_filters ADD COLUMN is_backfilled BOOLEAN;
+UPDATE solana.log_poller_filters SET is_backfilled = true;
+ALTER TABLE solana.log_poller_filters ALTER COLUMN is_backfilled SET NOT NULL;
+-- +goose StatementEnd
+
+
+-- +goose Down
+-- +goose StatementBegin
+
+ALTER TABLE solana.log_poller_filters DROP COLUMN is_backfilled;
+-- +goose StatementEnd
From 2450fff71db772d7e771babb5cbe1a55f5a51f84 Mon Sep 17 00:00:00 2001
From: Dylan Tinianov
Date: Wed, 8 Jan 2025 13:43:31 -0500
Subject: [PATCH 16/91] Extract MultiNode to chainlink-framework (#15791)
* Extract MultiNode
* tidy
* tidy
* Fix generate
* Add mock Subscription
* lint
* Fix sendonly allocation
* lint
* Add QueryTimeout to client
* Use multinode
* Update rpc_client_test.go
---
.changeset/cold-pillows-sleep.md | 5 +
.mockery.yaml | 20 +-
common/client/ctx.go | 17 -
common/client/ctx_test.go | 16 -
common/client/mock_hashable_test.go | 18 -
common/client/mock_head_test.go | 173 --
common/client/mock_node_selector_test.go | 127 --
common/client/mock_node_test.go | 567 -----
.../mock_pool_chain_info_provider_test.go | 132 --
common/client/mock_rpc_client_test.go | 510 -----
common/client/mock_send_only_client_test.go | 173 --
common/client/mock_send_only_node_test.go | 357 ---
common/client/mocks/config.go | 31 -
common/client/models.go | 121 -
common/client/models_test.go | 50 -
common/client/multi_node.go | 364 ---
common/client/multi_node_test.go | 517 -----
common/client/node.go | 336 ---
common/client/node_fsm.go | 377 ----
common/client/node_fsm_test.go | 131 --
common/client/node_lifecycle.go | 700 ------
common/client/node_lifecycle_test.go | 1983 -----------------
common/client/node_selector.go | 43 -
common/client/node_selector_highest_head.go | 40 -
.../client/node_selector_highest_head_test.go | 176 --
common/client/node_selector_priority_level.go | 123 -
.../node_selector_priority_level_test.go | 91 -
common/client/node_selector_round_robin.go | 48 -
.../client/node_selector_round_robin_test.go | 61 -
common/client/node_selector_test.go | 18 -
.../client/node_selector_total_difficulty.go | 53 -
.../node_selector_total_difficulty_test.go | 178 --
common/client/node_test.go | 107 -
common/client/poller.go | 95 -
common/client/poller_test.go | 194 --
common/client/send_only_node.go | 183 --
common/client/send_only_node_lifecycle.go | 67 -
common/client/send_only_node_test.go | 139 --
common/client/timeout.go | 5 +
common/client/transaction_sender.go | 301 ---
common/client/transaction_sender_test.go | 419 ----
common/client/types.go | 83 -
common/client/types_test.go | 34 -
common/txmgr/broadcaster.go | 34 +-
common/txmgr/confirmer.go | 20 +-
common/txmgr/resender.go | 8 +-
common/txmgr/types/client.go | 7 +-
common/types/mocks/head.go | 607 -----
core/chains/evm/client/chain_client.go | 18 +-
core/chains/evm/client/chain_client_test.go | 82 +-
core/chains/evm/client/config_builder.go | 4 +-
core/chains/evm/client/errors.go | 42 +-
core/chains/evm/client/errors_test.go | 7 +-
core/chains/evm/client/evm_client.go | 15 +-
core/chains/evm/client/helpers_test.go | 29 +-
core/chains/evm/client/mocks/client.go | 18 +-
core/chains/evm/client/null_client.go | 6 +-
core/chains/evm/client/rpc_client.go | 51 +-
.../evm/client/rpc_client_internal_test.go | 5 +-
core/chains/evm/client/rpc_client_test.go | 54 +-
.../evm/client/simulated_backend_client.go | 10 +-
core/chains/evm/txmgr/broadcaster_test.go | 88 +-
core/chains/evm/txmgr/client.go | 10 +-
core/chains/evm/txmgr/confirmer_test.go | 78 +-
core/cmd/shell_local_test.go | 8 +-
core/internal/cltest/cltest.go | 4 +-
core/scripts/go.mod | 9 +-
core/scripts/go.sum | 18 +-
core/services/chainlink/config_test.go | 6 +-
deployment/go.mod | 9 +-
deployment/go.sum | 18 +-
go.md | 6 +
go.mod | 9 +-
go.sum | 18 +-
integration-tests/go.mod | 9 +-
integration-tests/go.sum | 18 +-
integration-tests/load/go.mod | 9 +-
integration-tests/load/go.sum | 18 +-
78 files changed, 399 insertions(+), 10136 deletions(-)
create mode 100644 .changeset/cold-pillows-sleep.md
delete mode 100644 common/client/ctx.go
delete mode 100644 common/client/ctx_test.go
delete mode 100644 common/client/mock_hashable_test.go
delete mode 100644 common/client/mock_head_test.go
delete mode 100644 common/client/mock_node_selector_test.go
delete mode 100644 common/client/mock_node_test.go
delete mode 100644 common/client/mock_pool_chain_info_provider_test.go
delete mode 100644 common/client/mock_rpc_client_test.go
delete mode 100644 common/client/mock_send_only_client_test.go
delete mode 100644 common/client/mock_send_only_node_test.go
delete mode 100644 common/client/mocks/config.go
delete mode 100644 common/client/models.go
delete mode 100644 common/client/models_test.go
delete mode 100644 common/client/multi_node.go
delete mode 100644 common/client/multi_node_test.go
delete mode 100644 common/client/node.go
delete mode 100644 common/client/node_fsm.go
delete mode 100644 common/client/node_fsm_test.go
delete mode 100644 common/client/node_lifecycle.go
delete mode 100644 common/client/node_lifecycle_test.go
delete mode 100644 common/client/node_selector.go
delete mode 100644 common/client/node_selector_highest_head.go
delete mode 100644 common/client/node_selector_highest_head_test.go
delete mode 100644 common/client/node_selector_priority_level.go
delete mode 100644 common/client/node_selector_priority_level_test.go
delete mode 100644 common/client/node_selector_round_robin.go
delete mode 100644 common/client/node_selector_round_robin_test.go
delete mode 100644 common/client/node_selector_test.go
delete mode 100644 common/client/node_selector_total_difficulty.go
delete mode 100644 common/client/node_selector_total_difficulty_test.go
delete mode 100644 common/client/node_test.go
delete mode 100644 common/client/poller.go
delete mode 100644 common/client/poller_test.go
delete mode 100644 common/client/send_only_node.go
delete mode 100644 common/client/send_only_node_lifecycle.go
delete mode 100644 common/client/send_only_node_test.go
create mode 100644 common/client/timeout.go
delete mode 100644 common/client/transaction_sender.go
delete mode 100644 common/client/transaction_sender_test.go
delete mode 100644 common/client/types.go
delete mode 100644 common/client/types_test.go
delete mode 100644 common/types/mocks/head.go
diff --git a/.changeset/cold-pillows-sleep.md b/.changeset/cold-pillows-sleep.md
new file mode 100644
index 00000000000..45e4e999111
--- /dev/null
+++ b/.changeset/cold-pillows-sleep.md
@@ -0,0 +1,5 @@
+---
+"chainlink": patch
+---
+
+Extract EVM MultiNode to chainlink-framework. #internal
diff --git a/.mockery.yaml b/.mockery.yaml
index 7efde733145..73f46deed57 100644
--- a/.mockery.yaml
+++ b/.mockery.yaml
@@ -3,20 +3,6 @@ mockname: "{{ .InterfaceName }}"
outpkg: mocks
filename: "{{ .InterfaceName | snakecase }}.go"
packages:
- github.com/smartcontractkit/chainlink/v2/common/client:
- config:
- dir: "{{ .InterfaceDir }}"
- filename: "mock_{{ .InterfaceName | snakecase }}_test.go"
- inpackage: true
- mockname: "mock{{ .InterfaceName | camelcase }}"
- interfaces:
- Node:
- NodeSelector:
- sendOnlyClient:
- SendOnlyNode:
- RPCClient:
- Head:
- PoolChainInfoProvider:
github.com/smartcontractkit/chainlink/v2/common/headtracker:
interfaces:
HeadTrackable:
@@ -35,9 +21,11 @@ packages:
TxStrategy:
TxAttemptBuilder:
TxStore:
- github.com/smartcontractkit/chainlink/v2/common/types:
+ github.com/smartcontractkit/chainlink-framework/multinode:
+ config:
+ dir: common/types/mocks
+ outpkg: mocks
interfaces:
- Head:
Subscription:
github.com/smartcontractkit/chainlink/v2/core/bridges:
interfaces:
diff --git a/common/client/ctx.go b/common/client/ctx.go
deleted file mode 100644
index 57b2fc8a866..00000000000
--- a/common/client/ctx.go
+++ /dev/null
@@ -1,17 +0,0 @@
-package client
-
-import "context"
-
-type multiNodeContextKey int
-
-const (
- contextKeyHeathCheckRequest multiNodeContextKey = iota + 1
-)
-
-func CtxAddHealthCheckFlag(ctx context.Context) context.Context {
- return context.WithValue(ctx, contextKeyHeathCheckRequest, struct{}{})
-}
-
-func CtxIsHeathCheckRequest(ctx context.Context) bool {
- return ctx.Value(contextKeyHeathCheckRequest) != nil
-}
diff --git a/common/client/ctx_test.go b/common/client/ctx_test.go
deleted file mode 100644
index 822b36c3f81..00000000000
--- a/common/client/ctx_test.go
+++ /dev/null
@@ -1,16 +0,0 @@
-package client
-
-import (
- "testing"
-
- "github.com/stretchr/testify/assert"
-
- "github.com/smartcontractkit/chainlink-common/pkg/utils/tests"
-)
-
-func TestContext(t *testing.T) {
- ctx := tests.Context(t)
- assert.False(t, CtxIsHeathCheckRequest(ctx), "expected false for test context")
- ctx = CtxAddHealthCheckFlag(ctx)
- assert.True(t, CtxIsHeathCheckRequest(ctx), "expected context to contain the healthcheck flag")
-}
diff --git a/common/client/mock_hashable_test.go b/common/client/mock_hashable_test.go
deleted file mode 100644
index d9f1670c073..00000000000
--- a/common/client/mock_hashable_test.go
+++ /dev/null
@@ -1,18 +0,0 @@
-package client
-
-import "cmp"
-
-// Hashable - simple implementation of types.Hashable interface to be used as concrete type in tests
-type Hashable string
-
-func (h Hashable) Cmp(c Hashable) int {
- return cmp.Compare(h, c)
-}
-
-func (h Hashable) String() string {
- return string(h)
-}
-
-func (h Hashable) Bytes() []byte {
- return []byte(h)
-}
diff --git a/common/client/mock_head_test.go b/common/client/mock_head_test.go
deleted file mode 100644
index 01884d3fcfc..00000000000
--- a/common/client/mock_head_test.go
+++ /dev/null
@@ -1,173 +0,0 @@
-// Code generated by mockery v2.50.0. DO NOT EDIT.
-
-package client
-
-import (
- big "math/big"
-
- mock "github.com/stretchr/testify/mock"
-)
-
-// mockHead is an autogenerated mock type for the Head type
-type mockHead struct {
- mock.Mock
-}
-
-type mockHead_Expecter struct {
- mock *mock.Mock
-}
-
-func (_m *mockHead) EXPECT() *mockHead_Expecter {
- return &mockHead_Expecter{mock: &_m.Mock}
-}
-
-// BlockDifficulty provides a mock function with no fields
-func (_m *mockHead) BlockDifficulty() *big.Int {
- ret := _m.Called()
-
- if len(ret) == 0 {
- panic("no return value specified for BlockDifficulty")
- }
-
- var r0 *big.Int
- if rf, ok := ret.Get(0).(func() *big.Int); ok {
- r0 = rf()
- } else {
- if ret.Get(0) != nil {
- r0 = ret.Get(0).(*big.Int)
- }
- }
-
- return r0
-}
-
-// mockHead_BlockDifficulty_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BlockDifficulty'
-type mockHead_BlockDifficulty_Call struct {
- *mock.Call
-}
-
-// BlockDifficulty is a helper method to define mock.On call
-func (_e *mockHead_Expecter) BlockDifficulty() *mockHead_BlockDifficulty_Call {
- return &mockHead_BlockDifficulty_Call{Call: _e.mock.On("BlockDifficulty")}
-}
-
-func (_c *mockHead_BlockDifficulty_Call) Run(run func()) *mockHead_BlockDifficulty_Call {
- _c.Call.Run(func(args mock.Arguments) {
- run()
- })
- return _c
-}
-
-func (_c *mockHead_BlockDifficulty_Call) Return(_a0 *big.Int) *mockHead_BlockDifficulty_Call {
- _c.Call.Return(_a0)
- return _c
-}
-
-func (_c *mockHead_BlockDifficulty_Call) RunAndReturn(run func() *big.Int) *mockHead_BlockDifficulty_Call {
- _c.Call.Return(run)
- return _c
-}
-
-// BlockNumber provides a mock function with no fields
-func (_m *mockHead) BlockNumber() int64 {
- ret := _m.Called()
-
- if len(ret) == 0 {
- panic("no return value specified for BlockNumber")
- }
-
- var r0 int64
- if rf, ok := ret.Get(0).(func() int64); ok {
- r0 = rf()
- } else {
- r0 = ret.Get(0).(int64)
- }
-
- return r0
-}
-
-// mockHead_BlockNumber_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BlockNumber'
-type mockHead_BlockNumber_Call struct {
- *mock.Call
-}
-
-// BlockNumber is a helper method to define mock.On call
-func (_e *mockHead_Expecter) BlockNumber() *mockHead_BlockNumber_Call {
- return &mockHead_BlockNumber_Call{Call: _e.mock.On("BlockNumber")}
-}
-
-func (_c *mockHead_BlockNumber_Call) Run(run func()) *mockHead_BlockNumber_Call {
- _c.Call.Run(func(args mock.Arguments) {
- run()
- })
- return _c
-}
-
-func (_c *mockHead_BlockNumber_Call) Return(_a0 int64) *mockHead_BlockNumber_Call {
- _c.Call.Return(_a0)
- return _c
-}
-
-func (_c *mockHead_BlockNumber_Call) RunAndReturn(run func() int64) *mockHead_BlockNumber_Call {
- _c.Call.Return(run)
- return _c
-}
-
-// IsValid provides a mock function with no fields
-func (_m *mockHead) IsValid() bool {
- ret := _m.Called()
-
- if len(ret) == 0 {
- panic("no return value specified for IsValid")
- }
-
- var r0 bool
- if rf, ok := ret.Get(0).(func() bool); ok {
- r0 = rf()
- } else {
- r0 = ret.Get(0).(bool)
- }
-
- return r0
-}
-
-// mockHead_IsValid_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsValid'
-type mockHead_IsValid_Call struct {
- *mock.Call
-}
-
-// IsValid is a helper method to define mock.On call
-func (_e *mockHead_Expecter) IsValid() *mockHead_IsValid_Call {
- return &mockHead_IsValid_Call{Call: _e.mock.On("IsValid")}
-}
-
-func (_c *mockHead_IsValid_Call) Run(run func()) *mockHead_IsValid_Call {
- _c.Call.Run(func(args mock.Arguments) {
- run()
- })
- return _c
-}
-
-func (_c *mockHead_IsValid_Call) Return(_a0 bool) *mockHead_IsValid_Call {
- _c.Call.Return(_a0)
- return _c
-}
-
-func (_c *mockHead_IsValid_Call) RunAndReturn(run func() bool) *mockHead_IsValid_Call {
- _c.Call.Return(run)
- return _c
-}
-
-// newMockHead creates a new instance of mockHead. 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 newMockHead(t interface {
- mock.TestingT
- Cleanup(func())
-}) *mockHead {
- mock := &mockHead{}
- mock.Mock.Test(t)
-
- t.Cleanup(func() { mock.AssertExpectations(t) })
-
- return mock
-}
diff --git a/common/client/mock_node_selector_test.go b/common/client/mock_node_selector_test.go
deleted file mode 100644
index c4201664b4a..00000000000
--- a/common/client/mock_node_selector_test.go
+++ /dev/null
@@ -1,127 +0,0 @@
-// Code generated by mockery v2.50.0. DO NOT EDIT.
-
-package client
-
-import (
- types "github.com/smartcontractkit/chainlink/v2/common/types"
- mock "github.com/stretchr/testify/mock"
-)
-
-// mockNodeSelector is an autogenerated mock type for the NodeSelector type
-type mockNodeSelector[CHAIN_ID types.ID, RPC interface{}] struct {
- mock.Mock
-}
-
-type mockNodeSelector_Expecter[CHAIN_ID types.ID, RPC interface{}] struct {
- mock *mock.Mock
-}
-
-func (_m *mockNodeSelector[CHAIN_ID, RPC]) EXPECT() *mockNodeSelector_Expecter[CHAIN_ID, RPC] {
- return &mockNodeSelector_Expecter[CHAIN_ID, RPC]{mock: &_m.Mock}
-}
-
-// Name provides a mock function with no fields
-func (_m *mockNodeSelector[CHAIN_ID, RPC]) Name() string {
- ret := _m.Called()
-
- if len(ret) == 0 {
- panic("no return value specified for Name")
- }
-
- var r0 string
- if rf, ok := ret.Get(0).(func() string); ok {
- r0 = rf()
- } else {
- r0 = ret.Get(0).(string)
- }
-
- return r0
-}
-
-// mockNodeSelector_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Name'
-type mockNodeSelector_Name_Call[CHAIN_ID types.ID, RPC interface{}] struct {
- *mock.Call
-}
-
-// Name is a helper method to define mock.On call
-func (_e *mockNodeSelector_Expecter[CHAIN_ID, RPC]) Name() *mockNodeSelector_Name_Call[CHAIN_ID, RPC] {
- return &mockNodeSelector_Name_Call[CHAIN_ID, RPC]{Call: _e.mock.On("Name")}
-}
-
-func (_c *mockNodeSelector_Name_Call[CHAIN_ID, RPC]) Run(run func()) *mockNodeSelector_Name_Call[CHAIN_ID, RPC] {
- _c.Call.Run(func(args mock.Arguments) {
- run()
- })
- return _c
-}
-
-func (_c *mockNodeSelector_Name_Call[CHAIN_ID, RPC]) Return(_a0 string) *mockNodeSelector_Name_Call[CHAIN_ID, RPC] {
- _c.Call.Return(_a0)
- return _c
-}
-
-func (_c *mockNodeSelector_Name_Call[CHAIN_ID, RPC]) RunAndReturn(run func() string) *mockNodeSelector_Name_Call[CHAIN_ID, RPC] {
- _c.Call.Return(run)
- return _c
-}
-
-// Select provides a mock function with no fields
-func (_m *mockNodeSelector[CHAIN_ID, RPC]) Select() Node[CHAIN_ID, RPC] {
- ret := _m.Called()
-
- if len(ret) == 0 {
- panic("no return value specified for Select")
- }
-
- var r0 Node[CHAIN_ID, RPC]
- if rf, ok := ret.Get(0).(func() Node[CHAIN_ID, RPC]); ok {
- r0 = rf()
- } else {
- if ret.Get(0) != nil {
- r0 = ret.Get(0).(Node[CHAIN_ID, RPC])
- }
- }
-
- return r0
-}
-
-// mockNodeSelector_Select_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Select'
-type mockNodeSelector_Select_Call[CHAIN_ID types.ID, RPC interface{}] struct {
- *mock.Call
-}
-
-// Select is a helper method to define mock.On call
-func (_e *mockNodeSelector_Expecter[CHAIN_ID, RPC]) Select() *mockNodeSelector_Select_Call[CHAIN_ID, RPC] {
- return &mockNodeSelector_Select_Call[CHAIN_ID, RPC]{Call: _e.mock.On("Select")}
-}
-
-func (_c *mockNodeSelector_Select_Call[CHAIN_ID, RPC]) Run(run func()) *mockNodeSelector_Select_Call[CHAIN_ID, RPC] {
- _c.Call.Run(func(args mock.Arguments) {
- run()
- })
- return _c
-}
-
-func (_c *mockNodeSelector_Select_Call[CHAIN_ID, RPC]) Return(_a0 Node[CHAIN_ID, RPC]) *mockNodeSelector_Select_Call[CHAIN_ID, RPC] {
- _c.Call.Return(_a0)
- return _c
-}
-
-func (_c *mockNodeSelector_Select_Call[CHAIN_ID, RPC]) RunAndReturn(run func() Node[CHAIN_ID, RPC]) *mockNodeSelector_Select_Call[CHAIN_ID, RPC] {
- _c.Call.Return(run)
- return _c
-}
-
-// newMockNodeSelector creates a new instance of mockNodeSelector. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-// The first argument is typically a *testing.T value.
-func newMockNodeSelector[CHAIN_ID types.ID, RPC interface{}](t interface {
- mock.TestingT
- Cleanup(func())
-}) *mockNodeSelector[CHAIN_ID, RPC] {
- mock := &mockNodeSelector[CHAIN_ID, RPC]{}
- mock.Mock.Test(t)
-
- t.Cleanup(func() { mock.AssertExpectations(t) })
-
- return mock
-}
diff --git a/common/client/mock_node_test.go b/common/client/mock_node_test.go
deleted file mode 100644
index f0fc6a4cb58..00000000000
--- a/common/client/mock_node_test.go
+++ /dev/null
@@ -1,567 +0,0 @@
-// Code generated by mockery v2.50.0. DO NOT EDIT.
-
-package client
-
-import (
- context "context"
-
- types "github.com/smartcontractkit/chainlink/v2/common/types"
- mock "github.com/stretchr/testify/mock"
-)
-
-// mockNode is an autogenerated mock type for the Node type
-type mockNode[CHAIN_ID types.ID, RPC interface{}] struct {
- mock.Mock
-}
-
-type mockNode_Expecter[CHAIN_ID types.ID, RPC interface{}] struct {
- mock *mock.Mock
-}
-
-func (_m *mockNode[CHAIN_ID, RPC]) EXPECT() *mockNode_Expecter[CHAIN_ID, RPC] {
- return &mockNode_Expecter[CHAIN_ID, RPC]{mock: &_m.Mock}
-}
-
-// Close provides a mock function with no fields
-func (_m *mockNode[CHAIN_ID, RPC]) Close() error {
- ret := _m.Called()
-
- if len(ret) == 0 {
- panic("no return value specified for Close")
- }
-
- var r0 error
- if rf, ok := ret.Get(0).(func() error); ok {
- r0 = rf()
- } else {
- r0 = ret.Error(0)
- }
-
- return r0
-}
-
-// mockNode_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close'
-type mockNode_Close_Call[CHAIN_ID types.ID, RPC interface{}] struct {
- *mock.Call
-}
-
-// Close is a helper method to define mock.On call
-func (_e *mockNode_Expecter[CHAIN_ID, RPC]) Close() *mockNode_Close_Call[CHAIN_ID, RPC] {
- return &mockNode_Close_Call[CHAIN_ID, RPC]{Call: _e.mock.On("Close")}
-}
-
-func (_c *mockNode_Close_Call[CHAIN_ID, RPC]) Run(run func()) *mockNode_Close_Call[CHAIN_ID, RPC] {
- _c.Call.Run(func(args mock.Arguments) {
- run()
- })
- return _c
-}
-
-func (_c *mockNode_Close_Call[CHAIN_ID, RPC]) Return(_a0 error) *mockNode_Close_Call[CHAIN_ID, RPC] {
- _c.Call.Return(_a0)
- return _c
-}
-
-func (_c *mockNode_Close_Call[CHAIN_ID, RPC]) RunAndReturn(run func() error) *mockNode_Close_Call[CHAIN_ID, RPC] {
- _c.Call.Return(run)
- return _c
-}
-
-// ConfiguredChainID provides a mock function with no fields
-func (_m *mockNode[CHAIN_ID, RPC]) ConfiguredChainID() CHAIN_ID {
- ret := _m.Called()
-
- if len(ret) == 0 {
- panic("no return value specified for ConfiguredChainID")
- }
-
- var r0 CHAIN_ID
- if rf, ok := ret.Get(0).(func() CHAIN_ID); ok {
- r0 = rf()
- } else {
- if ret.Get(0) != nil {
- r0 = ret.Get(0).(CHAIN_ID)
- }
- }
-
- return r0
-}
-
-// mockNode_ConfiguredChainID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ConfiguredChainID'
-type mockNode_ConfiguredChainID_Call[CHAIN_ID types.ID, RPC interface{}] struct {
- *mock.Call
-}
-
-// ConfiguredChainID is a helper method to define mock.On call
-func (_e *mockNode_Expecter[CHAIN_ID, RPC]) ConfiguredChainID() *mockNode_ConfiguredChainID_Call[CHAIN_ID, RPC] {
- return &mockNode_ConfiguredChainID_Call[CHAIN_ID, RPC]{Call: _e.mock.On("ConfiguredChainID")}
-}
-
-func (_c *mockNode_ConfiguredChainID_Call[CHAIN_ID, RPC]) Run(run func()) *mockNode_ConfiguredChainID_Call[CHAIN_ID, RPC] {
- _c.Call.Run(func(args mock.Arguments) {
- run()
- })
- return _c
-}
-
-func (_c *mockNode_ConfiguredChainID_Call[CHAIN_ID, RPC]) Return(_a0 CHAIN_ID) *mockNode_ConfiguredChainID_Call[CHAIN_ID, RPC] {
- _c.Call.Return(_a0)
- return _c
-}
-
-func (_c *mockNode_ConfiguredChainID_Call[CHAIN_ID, RPC]) RunAndReturn(run func() CHAIN_ID) *mockNode_ConfiguredChainID_Call[CHAIN_ID, RPC] {
- _c.Call.Return(run)
- return _c
-}
-
-// HighestUserObservations provides a mock function with no fields
-func (_m *mockNode[CHAIN_ID, RPC]) HighestUserObservations() ChainInfo {
- ret := _m.Called()
-
- if len(ret) == 0 {
- panic("no return value specified for HighestUserObservations")
- }
-
- var r0 ChainInfo
- if rf, ok := ret.Get(0).(func() ChainInfo); ok {
- r0 = rf()
- } else {
- r0 = ret.Get(0).(ChainInfo)
- }
-
- return r0
-}
-
-// mockNode_HighestUserObservations_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'HighestUserObservations'
-type mockNode_HighestUserObservations_Call[CHAIN_ID types.ID, RPC interface{}] struct {
- *mock.Call
-}
-
-// HighestUserObservations is a helper method to define mock.On call
-func (_e *mockNode_Expecter[CHAIN_ID, RPC]) HighestUserObservations() *mockNode_HighestUserObservations_Call[CHAIN_ID, RPC] {
- return &mockNode_HighestUserObservations_Call[CHAIN_ID, RPC]{Call: _e.mock.On("HighestUserObservations")}
-}
-
-func (_c *mockNode_HighestUserObservations_Call[CHAIN_ID, RPC]) Run(run func()) *mockNode_HighestUserObservations_Call[CHAIN_ID, RPC] {
- _c.Call.Run(func(args mock.Arguments) {
- run()
- })
- return _c
-}
-
-func (_c *mockNode_HighestUserObservations_Call[CHAIN_ID, RPC]) Return(_a0 ChainInfo) *mockNode_HighestUserObservations_Call[CHAIN_ID, RPC] {
- _c.Call.Return(_a0)
- return _c
-}
-
-func (_c *mockNode_HighestUserObservations_Call[CHAIN_ID, RPC]) RunAndReturn(run func() ChainInfo) *mockNode_HighestUserObservations_Call[CHAIN_ID, RPC] {
- _c.Call.Return(run)
- return _c
-}
-
-// Name provides a mock function with no fields
-func (_m *mockNode[CHAIN_ID, RPC]) Name() string {
- ret := _m.Called()
-
- if len(ret) == 0 {
- panic("no return value specified for Name")
- }
-
- var r0 string
- if rf, ok := ret.Get(0).(func() string); ok {
- r0 = rf()
- } else {
- r0 = ret.Get(0).(string)
- }
-
- return r0
-}
-
-// mockNode_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Name'
-type mockNode_Name_Call[CHAIN_ID types.ID, RPC interface{}] struct {
- *mock.Call
-}
-
-// Name is a helper method to define mock.On call
-func (_e *mockNode_Expecter[CHAIN_ID, RPC]) Name() *mockNode_Name_Call[CHAIN_ID, RPC] {
- return &mockNode_Name_Call[CHAIN_ID, RPC]{Call: _e.mock.On("Name")}
-}
-
-func (_c *mockNode_Name_Call[CHAIN_ID, RPC]) Run(run func()) *mockNode_Name_Call[CHAIN_ID, RPC] {
- _c.Call.Run(func(args mock.Arguments) {
- run()
- })
- return _c
-}
-
-func (_c *mockNode_Name_Call[CHAIN_ID, RPC]) Return(_a0 string) *mockNode_Name_Call[CHAIN_ID, RPC] {
- _c.Call.Return(_a0)
- return _c
-}
-
-func (_c *mockNode_Name_Call[CHAIN_ID, RPC]) RunAndReturn(run func() string) *mockNode_Name_Call[CHAIN_ID, RPC] {
- _c.Call.Return(run)
- return _c
-}
-
-// Order provides a mock function with no fields
-func (_m *mockNode[CHAIN_ID, RPC]) Order() int32 {
- ret := _m.Called()
-
- if len(ret) == 0 {
- panic("no return value specified for Order")
- }
-
- var r0 int32
- if rf, ok := ret.Get(0).(func() int32); ok {
- r0 = rf()
- } else {
- r0 = ret.Get(0).(int32)
- }
-
- return r0
-}
-
-// mockNode_Order_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Order'
-type mockNode_Order_Call[CHAIN_ID types.ID, RPC interface{}] struct {
- *mock.Call
-}
-
-// Order is a helper method to define mock.On call
-func (_e *mockNode_Expecter[CHAIN_ID, RPC]) Order() *mockNode_Order_Call[CHAIN_ID, RPC] {
- return &mockNode_Order_Call[CHAIN_ID, RPC]{Call: _e.mock.On("Order")}
-}
-
-func (_c *mockNode_Order_Call[CHAIN_ID, RPC]) Run(run func()) *mockNode_Order_Call[CHAIN_ID, RPC] {
- _c.Call.Run(func(args mock.Arguments) {
- run()
- })
- return _c
-}
-
-func (_c *mockNode_Order_Call[CHAIN_ID, RPC]) Return(_a0 int32) *mockNode_Order_Call[CHAIN_ID, RPC] {
- _c.Call.Return(_a0)
- return _c
-}
-
-func (_c *mockNode_Order_Call[CHAIN_ID, RPC]) RunAndReturn(run func() int32) *mockNode_Order_Call[CHAIN_ID, RPC] {
- _c.Call.Return(run)
- return _c
-}
-
-// RPC provides a mock function with no fields
-func (_m *mockNode[CHAIN_ID, RPC]) RPC() RPC {
- ret := _m.Called()
-
- if len(ret) == 0 {
- panic("no return value specified for RPC")
- }
-
- var r0 RPC
- if rf, ok := ret.Get(0).(func() RPC); ok {
- r0 = rf()
- } else {
- if ret.Get(0) != nil {
- r0 = ret.Get(0).(RPC)
- }
- }
-
- return r0
-}
-
-// mockNode_RPC_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RPC'
-type mockNode_RPC_Call[CHAIN_ID types.ID, RPC interface{}] struct {
- *mock.Call
-}
-
-// RPC is a helper method to define mock.On call
-func (_e *mockNode_Expecter[CHAIN_ID, RPC]) RPC() *mockNode_RPC_Call[CHAIN_ID, RPC] {
- return &mockNode_RPC_Call[CHAIN_ID, RPC]{Call: _e.mock.On("RPC")}
-}
-
-func (_c *mockNode_RPC_Call[CHAIN_ID, RPC]) Run(run func()) *mockNode_RPC_Call[CHAIN_ID, RPC] {
- _c.Call.Run(func(args mock.Arguments) {
- run()
- })
- return _c
-}
-
-func (_c *mockNode_RPC_Call[CHAIN_ID, RPC]) Return(_a0 RPC) *mockNode_RPC_Call[CHAIN_ID, RPC] {
- _c.Call.Return(_a0)
- return _c
-}
-
-func (_c *mockNode_RPC_Call[CHAIN_ID, RPC]) RunAndReturn(run func() RPC) *mockNode_RPC_Call[CHAIN_ID, RPC] {
- _c.Call.Return(run)
- return _c
-}
-
-// SetPoolChainInfoProvider provides a mock function with given fields: _a0
-func (_m *mockNode[CHAIN_ID, RPC]) SetPoolChainInfoProvider(_a0 PoolChainInfoProvider) {
- _m.Called(_a0)
-}
-
-// mockNode_SetPoolChainInfoProvider_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetPoolChainInfoProvider'
-type mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID types.ID, RPC interface{}] struct {
- *mock.Call
-}
-
-// SetPoolChainInfoProvider is a helper method to define mock.On call
-// - _a0 PoolChainInfoProvider
-func (_e *mockNode_Expecter[CHAIN_ID, RPC]) SetPoolChainInfoProvider(_a0 interface{}) *mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID, RPC] {
- return &mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID, RPC]{Call: _e.mock.On("SetPoolChainInfoProvider", _a0)}
-}
-
-func (_c *mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID, RPC]) Run(run func(_a0 PoolChainInfoProvider)) *mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID, RPC] {
- _c.Call.Run(func(args mock.Arguments) {
- run(args[0].(PoolChainInfoProvider))
- })
- return _c
-}
-
-func (_c *mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID, RPC]) Return() *mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID, RPC] {
- _c.Call.Return()
- return _c
-}
-
-func (_c *mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID, RPC]) RunAndReturn(run func(PoolChainInfoProvider)) *mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID, RPC] {
- _c.Run(run)
- return _c
-}
-
-// Start provides a mock function with given fields: _a0
-func (_m *mockNode[CHAIN_ID, RPC]) Start(_a0 context.Context) error {
- ret := _m.Called(_a0)
-
- if len(ret) == 0 {
- panic("no return value specified for Start")
- }
-
- var r0 error
- if rf, ok := ret.Get(0).(func(context.Context) error); ok {
- r0 = rf(_a0)
- } else {
- r0 = ret.Error(0)
- }
-
- return r0
-}
-
-// mockNode_Start_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Start'
-type mockNode_Start_Call[CHAIN_ID types.ID, RPC interface{}] struct {
- *mock.Call
-}
-
-// Start is a helper method to define mock.On call
-// - _a0 context.Context
-func (_e *mockNode_Expecter[CHAIN_ID, RPC]) Start(_a0 interface{}) *mockNode_Start_Call[CHAIN_ID, RPC] {
- return &mockNode_Start_Call[CHAIN_ID, RPC]{Call: _e.mock.On("Start", _a0)}
-}
-
-func (_c *mockNode_Start_Call[CHAIN_ID, RPC]) Run(run func(_a0 context.Context)) *mockNode_Start_Call[CHAIN_ID, RPC] {
- _c.Call.Run(func(args mock.Arguments) {
- run(args[0].(context.Context))
- })
- return _c
-}
-
-func (_c *mockNode_Start_Call[CHAIN_ID, RPC]) Return(_a0 error) *mockNode_Start_Call[CHAIN_ID, RPC] {
- _c.Call.Return(_a0)
- return _c
-}
-
-func (_c *mockNode_Start_Call[CHAIN_ID, RPC]) RunAndReturn(run func(context.Context) error) *mockNode_Start_Call[CHAIN_ID, RPC] {
- _c.Call.Return(run)
- return _c
-}
-
-// State provides a mock function with no fields
-func (_m *mockNode[CHAIN_ID, RPC]) State() nodeState {
- ret := _m.Called()
-
- if len(ret) == 0 {
- panic("no return value specified for State")
- }
-
- var r0 nodeState
- if rf, ok := ret.Get(0).(func() nodeState); ok {
- r0 = rf()
- } else {
- r0 = ret.Get(0).(nodeState)
- }
-
- return r0
-}
-
-// mockNode_State_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'State'
-type mockNode_State_Call[CHAIN_ID types.ID, RPC interface{}] struct {
- *mock.Call
-}
-
-// State is a helper method to define mock.On call
-func (_e *mockNode_Expecter[CHAIN_ID, RPC]) State() *mockNode_State_Call[CHAIN_ID, RPC] {
- return &mockNode_State_Call[CHAIN_ID, RPC]{Call: _e.mock.On("State")}
-}
-
-func (_c *mockNode_State_Call[CHAIN_ID, RPC]) Run(run func()) *mockNode_State_Call[CHAIN_ID, RPC] {
- _c.Call.Run(func(args mock.Arguments) {
- run()
- })
- return _c
-}
-
-func (_c *mockNode_State_Call[CHAIN_ID, RPC]) Return(_a0 nodeState) *mockNode_State_Call[CHAIN_ID, RPC] {
- _c.Call.Return(_a0)
- return _c
-}
-
-func (_c *mockNode_State_Call[CHAIN_ID, RPC]) RunAndReturn(run func() nodeState) *mockNode_State_Call[CHAIN_ID, RPC] {
- _c.Call.Return(run)
- return _c
-}
-
-// StateAndLatest provides a mock function with no fields
-func (_m *mockNode[CHAIN_ID, RPC]) StateAndLatest() (nodeState, ChainInfo) {
- ret := _m.Called()
-
- if len(ret) == 0 {
- panic("no return value specified for StateAndLatest")
- }
-
- var r0 nodeState
- var r1 ChainInfo
- if rf, ok := ret.Get(0).(func() (nodeState, ChainInfo)); ok {
- return rf()
- }
- if rf, ok := ret.Get(0).(func() nodeState); ok {
- r0 = rf()
- } else {
- r0 = ret.Get(0).(nodeState)
- }
-
- if rf, ok := ret.Get(1).(func() ChainInfo); ok {
- r1 = rf()
- } else {
- r1 = ret.Get(1).(ChainInfo)
- }
-
- return r0, r1
-}
-
-// mockNode_StateAndLatest_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'StateAndLatest'
-type mockNode_StateAndLatest_Call[CHAIN_ID types.ID, RPC interface{}] struct {
- *mock.Call
-}
-
-// StateAndLatest is a helper method to define mock.On call
-func (_e *mockNode_Expecter[CHAIN_ID, RPC]) StateAndLatest() *mockNode_StateAndLatest_Call[CHAIN_ID, RPC] {
- return &mockNode_StateAndLatest_Call[CHAIN_ID, RPC]{Call: _e.mock.On("StateAndLatest")}
-}
-
-func (_c *mockNode_StateAndLatest_Call[CHAIN_ID, RPC]) Run(run func()) *mockNode_StateAndLatest_Call[CHAIN_ID, RPC] {
- _c.Call.Run(func(args mock.Arguments) {
- run()
- })
- return _c
-}
-
-func (_c *mockNode_StateAndLatest_Call[CHAIN_ID, RPC]) Return(_a0 nodeState, _a1 ChainInfo) *mockNode_StateAndLatest_Call[CHAIN_ID, RPC] {
- _c.Call.Return(_a0, _a1)
- return _c
-}
-
-func (_c *mockNode_StateAndLatest_Call[CHAIN_ID, RPC]) RunAndReturn(run func() (nodeState, ChainInfo)) *mockNode_StateAndLatest_Call[CHAIN_ID, RPC] {
- _c.Call.Return(run)
- return _c
-}
-
-// String provides a mock function with no fields
-func (_m *mockNode[CHAIN_ID, RPC]) String() string {
- ret := _m.Called()
-
- if len(ret) == 0 {
- panic("no return value specified for String")
- }
-
- var r0 string
- if rf, ok := ret.Get(0).(func() string); ok {
- r0 = rf()
- } else {
- r0 = ret.Get(0).(string)
- }
-
- return r0
-}
-
-// mockNode_String_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'String'
-type mockNode_String_Call[CHAIN_ID types.ID, RPC interface{}] struct {
- *mock.Call
-}
-
-// String is a helper method to define mock.On call
-func (_e *mockNode_Expecter[CHAIN_ID, RPC]) String() *mockNode_String_Call[CHAIN_ID, RPC] {
- return &mockNode_String_Call[CHAIN_ID, RPC]{Call: _e.mock.On("String")}
-}
-
-func (_c *mockNode_String_Call[CHAIN_ID, RPC]) Run(run func()) *mockNode_String_Call[CHAIN_ID, RPC] {
- _c.Call.Run(func(args mock.Arguments) {
- run()
- })
- return _c
-}
-
-func (_c *mockNode_String_Call[CHAIN_ID, RPC]) Return(_a0 string) *mockNode_String_Call[CHAIN_ID, RPC] {
- _c.Call.Return(_a0)
- return _c
-}
-
-func (_c *mockNode_String_Call[CHAIN_ID, RPC]) RunAndReturn(run func() string) *mockNode_String_Call[CHAIN_ID, RPC] {
- _c.Call.Return(run)
- return _c
-}
-
-// UnsubscribeAllExceptAliveLoop provides a mock function with no fields
-func (_m *mockNode[CHAIN_ID, RPC]) UnsubscribeAllExceptAliveLoop() {
- _m.Called()
-}
-
-// mockNode_UnsubscribeAllExceptAliveLoop_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UnsubscribeAllExceptAliveLoop'
-type mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID types.ID, RPC interface{}] struct {
- *mock.Call
-}
-
-// UnsubscribeAllExceptAliveLoop is a helper method to define mock.On call
-func (_e *mockNode_Expecter[CHAIN_ID, RPC]) UnsubscribeAllExceptAliveLoop() *mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, RPC] {
- return &mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, RPC]{Call: _e.mock.On("UnsubscribeAllExceptAliveLoop")}
-}
-
-func (_c *mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, RPC]) Run(run func()) *mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, RPC] {
- _c.Call.Run(func(args mock.Arguments) {
- run()
- })
- return _c
-}
-
-func (_c *mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, RPC]) Return() *mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, RPC] {
- _c.Call.Return()
- return _c
-}
-
-func (_c *mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, RPC]) RunAndReturn(run func()) *mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, RPC] {
- _c.Run(run)
- return _c
-}
-
-// newMockNode creates a new instance of mockNode. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-// The first argument is typically a *testing.T value.
-func newMockNode[CHAIN_ID types.ID, RPC interface{}](t interface {
- mock.TestingT
- Cleanup(func())
-}) *mockNode[CHAIN_ID, RPC] {
- mock := &mockNode[CHAIN_ID, RPC]{}
- mock.Mock.Test(t)
-
- t.Cleanup(func() { mock.AssertExpectations(t) })
-
- return mock
-}
diff --git a/common/client/mock_pool_chain_info_provider_test.go b/common/client/mock_pool_chain_info_provider_test.go
deleted file mode 100644
index 7523a060329..00000000000
--- a/common/client/mock_pool_chain_info_provider_test.go
+++ /dev/null
@@ -1,132 +0,0 @@
-// Code generated by mockery v2.50.0. DO NOT EDIT.
-
-package client
-
-import mock "github.com/stretchr/testify/mock"
-
-// mockPoolChainInfoProvider is an autogenerated mock type for the PoolChainInfoProvider type
-type mockPoolChainInfoProvider struct {
- mock.Mock
-}
-
-type mockPoolChainInfoProvider_Expecter struct {
- mock *mock.Mock
-}
-
-func (_m *mockPoolChainInfoProvider) EXPECT() *mockPoolChainInfoProvider_Expecter {
- return &mockPoolChainInfoProvider_Expecter{mock: &_m.Mock}
-}
-
-// HighestUserObservations provides a mock function with no fields
-func (_m *mockPoolChainInfoProvider) HighestUserObservations() ChainInfo {
- ret := _m.Called()
-
- if len(ret) == 0 {
- panic("no return value specified for HighestUserObservations")
- }
-
- var r0 ChainInfo
- if rf, ok := ret.Get(0).(func() ChainInfo); ok {
- r0 = rf()
- } else {
- r0 = ret.Get(0).(ChainInfo)
- }
-
- return r0
-}
-
-// mockPoolChainInfoProvider_HighestUserObservations_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'HighestUserObservations'
-type mockPoolChainInfoProvider_HighestUserObservations_Call struct {
- *mock.Call
-}
-
-// HighestUserObservations is a helper method to define mock.On call
-func (_e *mockPoolChainInfoProvider_Expecter) HighestUserObservations() *mockPoolChainInfoProvider_HighestUserObservations_Call {
- return &mockPoolChainInfoProvider_HighestUserObservations_Call{Call: _e.mock.On("HighestUserObservations")}
-}
-
-func (_c *mockPoolChainInfoProvider_HighestUserObservations_Call) Run(run func()) *mockPoolChainInfoProvider_HighestUserObservations_Call {
- _c.Call.Run(func(args mock.Arguments) {
- run()
- })
- return _c
-}
-
-func (_c *mockPoolChainInfoProvider_HighestUserObservations_Call) Return(_a0 ChainInfo) *mockPoolChainInfoProvider_HighestUserObservations_Call {
- _c.Call.Return(_a0)
- return _c
-}
-
-func (_c *mockPoolChainInfoProvider_HighestUserObservations_Call) RunAndReturn(run func() ChainInfo) *mockPoolChainInfoProvider_HighestUserObservations_Call {
- _c.Call.Return(run)
- return _c
-}
-
-// LatestChainInfo provides a mock function with no fields
-func (_m *mockPoolChainInfoProvider) LatestChainInfo() (int, ChainInfo) {
- ret := _m.Called()
-
- if len(ret) == 0 {
- panic("no return value specified for LatestChainInfo")
- }
-
- var r0 int
- var r1 ChainInfo
- if rf, ok := ret.Get(0).(func() (int, ChainInfo)); ok {
- return rf()
- }
- if rf, ok := ret.Get(0).(func() int); ok {
- r0 = rf()
- } else {
- r0 = ret.Get(0).(int)
- }
-
- if rf, ok := ret.Get(1).(func() ChainInfo); ok {
- r1 = rf()
- } else {
- r1 = ret.Get(1).(ChainInfo)
- }
-
- return r0, r1
-}
-
-// mockPoolChainInfoProvider_LatestChainInfo_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LatestChainInfo'
-type mockPoolChainInfoProvider_LatestChainInfo_Call struct {
- *mock.Call
-}
-
-// LatestChainInfo is a helper method to define mock.On call
-func (_e *mockPoolChainInfoProvider_Expecter) LatestChainInfo() *mockPoolChainInfoProvider_LatestChainInfo_Call {
- return &mockPoolChainInfoProvider_LatestChainInfo_Call{Call: _e.mock.On("LatestChainInfo")}
-}
-
-func (_c *mockPoolChainInfoProvider_LatestChainInfo_Call) Run(run func()) *mockPoolChainInfoProvider_LatestChainInfo_Call {
- _c.Call.Run(func(args mock.Arguments) {
- run()
- })
- return _c
-}
-
-func (_c *mockPoolChainInfoProvider_LatestChainInfo_Call) Return(_a0 int, _a1 ChainInfo) *mockPoolChainInfoProvider_LatestChainInfo_Call {
- _c.Call.Return(_a0, _a1)
- return _c
-}
-
-func (_c *mockPoolChainInfoProvider_LatestChainInfo_Call) RunAndReturn(run func() (int, ChainInfo)) *mockPoolChainInfoProvider_LatestChainInfo_Call {
- _c.Call.Return(run)
- return _c
-}
-
-// newMockPoolChainInfoProvider creates a new instance of mockPoolChainInfoProvider. 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 newMockPoolChainInfoProvider(t interface {
- mock.TestingT
- Cleanup(func())
-}) *mockPoolChainInfoProvider {
- mock := &mockPoolChainInfoProvider{}
- mock.Mock.Test(t)
-
- t.Cleanup(func() { mock.AssertExpectations(t) })
-
- return mock
-}
diff --git a/common/client/mock_rpc_client_test.go b/common/client/mock_rpc_client_test.go
deleted file mode 100644
index 9ad71c646e4..00000000000
--- a/common/client/mock_rpc_client_test.go
+++ /dev/null
@@ -1,510 +0,0 @@
-// Code generated by mockery v2.50.0. DO NOT EDIT.
-
-package client
-
-import (
- context "context"
-
- types "github.com/smartcontractkit/chainlink/v2/common/types"
- mock "github.com/stretchr/testify/mock"
-)
-
-// mockRPCClient is an autogenerated mock type for the RPCClient type
-type mockRPCClient[CHAIN_ID types.ID, HEAD Head] struct {
- mock.Mock
-}
-
-type mockRPCClient_Expecter[CHAIN_ID types.ID, HEAD Head] struct {
- mock *mock.Mock
-}
-
-func (_m *mockRPCClient[CHAIN_ID, HEAD]) EXPECT() *mockRPCClient_Expecter[CHAIN_ID, HEAD] {
- return &mockRPCClient_Expecter[CHAIN_ID, HEAD]{mock: &_m.Mock}
-}
-
-// ChainID provides a mock function with given fields: ctx
-func (_m *mockRPCClient[CHAIN_ID, HEAD]) ChainID(ctx context.Context) (CHAIN_ID, error) {
- ret := _m.Called(ctx)
-
- if len(ret) == 0 {
- panic("no return value specified for ChainID")
- }
-
- var r0 CHAIN_ID
- var r1 error
- if rf, ok := ret.Get(0).(func(context.Context) (CHAIN_ID, error)); ok {
- return rf(ctx)
- }
- if rf, ok := ret.Get(0).(func(context.Context) CHAIN_ID); ok {
- r0 = rf(ctx)
- } else {
- if ret.Get(0) != nil {
- r0 = ret.Get(0).(CHAIN_ID)
- }
- }
-
- if rf, ok := ret.Get(1).(func(context.Context) error); ok {
- r1 = rf(ctx)
- } else {
- r1 = ret.Error(1)
- }
-
- return r0, r1
-}
-
-// mockRPCClient_ChainID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ChainID'
-type mockRPCClient_ChainID_Call[CHAIN_ID types.ID, HEAD Head] struct {
- *mock.Call
-}
-
-// ChainID is a helper method to define mock.On call
-// - ctx context.Context
-func (_e *mockRPCClient_Expecter[CHAIN_ID, HEAD]) ChainID(ctx interface{}) *mockRPCClient_ChainID_Call[CHAIN_ID, HEAD] {
- return &mockRPCClient_ChainID_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("ChainID", ctx)}
-}
-
-func (_c *mockRPCClient_ChainID_Call[CHAIN_ID, HEAD]) Run(run func(ctx context.Context)) *mockRPCClient_ChainID_Call[CHAIN_ID, HEAD] {
- _c.Call.Run(func(args mock.Arguments) {
- run(args[0].(context.Context))
- })
- return _c
-}
-
-func (_c *mockRPCClient_ChainID_Call[CHAIN_ID, HEAD]) Return(_a0 CHAIN_ID, _a1 error) *mockRPCClient_ChainID_Call[CHAIN_ID, HEAD] {
- _c.Call.Return(_a0, _a1)
- return _c
-}
-
-func (_c *mockRPCClient_ChainID_Call[CHAIN_ID, HEAD]) RunAndReturn(run func(context.Context) (CHAIN_ID, error)) *mockRPCClient_ChainID_Call[CHAIN_ID, HEAD] {
- _c.Call.Return(run)
- return _c
-}
-
-// Close provides a mock function with no fields
-func (_m *mockRPCClient[CHAIN_ID, HEAD]) Close() {
- _m.Called()
-}
-
-// mockRPCClient_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close'
-type mockRPCClient_Close_Call[CHAIN_ID types.ID, HEAD Head] struct {
- *mock.Call
-}
-
-// Close is a helper method to define mock.On call
-func (_e *mockRPCClient_Expecter[CHAIN_ID, HEAD]) Close() *mockRPCClient_Close_Call[CHAIN_ID, HEAD] {
- return &mockRPCClient_Close_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("Close")}
-}
-
-func (_c *mockRPCClient_Close_Call[CHAIN_ID, HEAD]) Run(run func()) *mockRPCClient_Close_Call[CHAIN_ID, HEAD] {
- _c.Call.Run(func(args mock.Arguments) {
- run()
- })
- return _c
-}
-
-func (_c *mockRPCClient_Close_Call[CHAIN_ID, HEAD]) Return() *mockRPCClient_Close_Call[CHAIN_ID, HEAD] {
- _c.Call.Return()
- return _c
-}
-
-func (_c *mockRPCClient_Close_Call[CHAIN_ID, HEAD]) RunAndReturn(run func()) *mockRPCClient_Close_Call[CHAIN_ID, HEAD] {
- _c.Run(run)
- return _c
-}
-
-// Dial provides a mock function with given fields: ctx
-func (_m *mockRPCClient[CHAIN_ID, HEAD]) Dial(ctx context.Context) error {
- ret := _m.Called(ctx)
-
- if len(ret) == 0 {
- panic("no return value specified for Dial")
- }
-
- var r0 error
- if rf, ok := ret.Get(0).(func(context.Context) error); ok {
- r0 = rf(ctx)
- } else {
- r0 = ret.Error(0)
- }
-
- return r0
-}
-
-// mockRPCClient_Dial_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Dial'
-type mockRPCClient_Dial_Call[CHAIN_ID types.ID, HEAD Head] struct {
- *mock.Call
-}
-
-// Dial is a helper method to define mock.On call
-// - ctx context.Context
-func (_e *mockRPCClient_Expecter[CHAIN_ID, HEAD]) Dial(ctx interface{}) *mockRPCClient_Dial_Call[CHAIN_ID, HEAD] {
- return &mockRPCClient_Dial_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("Dial", ctx)}
-}
-
-func (_c *mockRPCClient_Dial_Call[CHAIN_ID, HEAD]) Run(run func(ctx context.Context)) *mockRPCClient_Dial_Call[CHAIN_ID, HEAD] {
- _c.Call.Run(func(args mock.Arguments) {
- run(args[0].(context.Context))
- })
- return _c
-}
-
-func (_c *mockRPCClient_Dial_Call[CHAIN_ID, HEAD]) Return(_a0 error) *mockRPCClient_Dial_Call[CHAIN_ID, HEAD] {
- _c.Call.Return(_a0)
- return _c
-}
-
-func (_c *mockRPCClient_Dial_Call[CHAIN_ID, HEAD]) RunAndReturn(run func(context.Context) error) *mockRPCClient_Dial_Call[CHAIN_ID, HEAD] {
- _c.Call.Return(run)
- return _c
-}
-
-// GetInterceptedChainInfo provides a mock function with no fields
-func (_m *mockRPCClient[CHAIN_ID, HEAD]) GetInterceptedChainInfo() (ChainInfo, ChainInfo) {
- ret := _m.Called()
-
- if len(ret) == 0 {
- panic("no return value specified for GetInterceptedChainInfo")
- }
-
- var r0 ChainInfo
- var r1 ChainInfo
- if rf, ok := ret.Get(0).(func() (ChainInfo, ChainInfo)); ok {
- return rf()
- }
- if rf, ok := ret.Get(0).(func() ChainInfo); ok {
- r0 = rf()
- } else {
- r0 = ret.Get(0).(ChainInfo)
- }
-
- if rf, ok := ret.Get(1).(func() ChainInfo); ok {
- r1 = rf()
- } else {
- r1 = ret.Get(1).(ChainInfo)
- }
-
- return r0, r1
-}
-
-// mockRPCClient_GetInterceptedChainInfo_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetInterceptedChainInfo'
-type mockRPCClient_GetInterceptedChainInfo_Call[CHAIN_ID types.ID, HEAD Head] struct {
- *mock.Call
-}
-
-// GetInterceptedChainInfo is a helper method to define mock.On call
-func (_e *mockRPCClient_Expecter[CHAIN_ID, HEAD]) GetInterceptedChainInfo() *mockRPCClient_GetInterceptedChainInfo_Call[CHAIN_ID, HEAD] {
- return &mockRPCClient_GetInterceptedChainInfo_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("GetInterceptedChainInfo")}
-}
-
-func (_c *mockRPCClient_GetInterceptedChainInfo_Call[CHAIN_ID, HEAD]) Run(run func()) *mockRPCClient_GetInterceptedChainInfo_Call[CHAIN_ID, HEAD] {
- _c.Call.Run(func(args mock.Arguments) {
- run()
- })
- return _c
-}
-
-func (_c *mockRPCClient_GetInterceptedChainInfo_Call[CHAIN_ID, HEAD]) Return(latest ChainInfo, highestUserObservations ChainInfo) *mockRPCClient_GetInterceptedChainInfo_Call[CHAIN_ID, HEAD] {
- _c.Call.Return(latest, highestUserObservations)
- return _c
-}
-
-func (_c *mockRPCClient_GetInterceptedChainInfo_Call[CHAIN_ID, HEAD]) RunAndReturn(run func() (ChainInfo, ChainInfo)) *mockRPCClient_GetInterceptedChainInfo_Call[CHAIN_ID, HEAD] {
- _c.Call.Return(run)
- return _c
-}
-
-// IsSyncing provides a mock function with given fields: ctx
-func (_m *mockRPCClient[CHAIN_ID, HEAD]) IsSyncing(ctx context.Context) (bool, error) {
- ret := _m.Called(ctx)
-
- if len(ret) == 0 {
- panic("no return value specified for IsSyncing")
- }
-
- var r0 bool
- var r1 error
- if rf, ok := ret.Get(0).(func(context.Context) (bool, error)); ok {
- return rf(ctx)
- }
- if rf, ok := ret.Get(0).(func(context.Context) bool); ok {
- r0 = rf(ctx)
- } else {
- r0 = ret.Get(0).(bool)
- }
-
- if rf, ok := ret.Get(1).(func(context.Context) error); ok {
- r1 = rf(ctx)
- } else {
- r1 = ret.Error(1)
- }
-
- return r0, r1
-}
-
-// mockRPCClient_IsSyncing_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsSyncing'
-type mockRPCClient_IsSyncing_Call[CHAIN_ID types.ID, HEAD Head] struct {
- *mock.Call
-}
-
-// IsSyncing is a helper method to define mock.On call
-// - ctx context.Context
-func (_e *mockRPCClient_Expecter[CHAIN_ID, HEAD]) IsSyncing(ctx interface{}) *mockRPCClient_IsSyncing_Call[CHAIN_ID, HEAD] {
- return &mockRPCClient_IsSyncing_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("IsSyncing", ctx)}
-}
-
-func (_c *mockRPCClient_IsSyncing_Call[CHAIN_ID, HEAD]) Run(run func(ctx context.Context)) *mockRPCClient_IsSyncing_Call[CHAIN_ID, HEAD] {
- _c.Call.Run(func(args mock.Arguments) {
- run(args[0].(context.Context))
- })
- return _c
-}
-
-func (_c *mockRPCClient_IsSyncing_Call[CHAIN_ID, HEAD]) Return(_a0 bool, _a1 error) *mockRPCClient_IsSyncing_Call[CHAIN_ID, HEAD] {
- _c.Call.Return(_a0, _a1)
- return _c
-}
-
-func (_c *mockRPCClient_IsSyncing_Call[CHAIN_ID, HEAD]) RunAndReturn(run func(context.Context) (bool, error)) *mockRPCClient_IsSyncing_Call[CHAIN_ID, HEAD] {
- _c.Call.Return(run)
- return _c
-}
-
-// Ping provides a mock function with given fields: _a0
-func (_m *mockRPCClient[CHAIN_ID, HEAD]) Ping(_a0 context.Context) error {
- ret := _m.Called(_a0)
-
- if len(ret) == 0 {
- panic("no return value specified for Ping")
- }
-
- var r0 error
- if rf, ok := ret.Get(0).(func(context.Context) error); ok {
- r0 = rf(_a0)
- } else {
- r0 = ret.Error(0)
- }
-
- return r0
-}
-
-// mockRPCClient_Ping_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Ping'
-type mockRPCClient_Ping_Call[CHAIN_ID types.ID, HEAD Head] struct {
- *mock.Call
-}
-
-// Ping is a helper method to define mock.On call
-// - _a0 context.Context
-func (_e *mockRPCClient_Expecter[CHAIN_ID, HEAD]) Ping(_a0 interface{}) *mockRPCClient_Ping_Call[CHAIN_ID, HEAD] {
- return &mockRPCClient_Ping_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("Ping", _a0)}
-}
-
-func (_c *mockRPCClient_Ping_Call[CHAIN_ID, HEAD]) Run(run func(_a0 context.Context)) *mockRPCClient_Ping_Call[CHAIN_ID, HEAD] {
- _c.Call.Run(func(args mock.Arguments) {
- run(args[0].(context.Context))
- })
- return _c
-}
-
-func (_c *mockRPCClient_Ping_Call[CHAIN_ID, HEAD]) Return(_a0 error) *mockRPCClient_Ping_Call[CHAIN_ID, HEAD] {
- _c.Call.Return(_a0)
- return _c
-}
-
-func (_c *mockRPCClient_Ping_Call[CHAIN_ID, HEAD]) RunAndReturn(run func(context.Context) error) *mockRPCClient_Ping_Call[CHAIN_ID, HEAD] {
- _c.Call.Return(run)
- return _c
-}
-
-// SubscribeToFinalizedHeads provides a mock function with given fields: ctx
-func (_m *mockRPCClient[CHAIN_ID, HEAD]) SubscribeToFinalizedHeads(ctx context.Context) (<-chan HEAD, types.Subscription, error) {
- ret := _m.Called(ctx)
-
- if len(ret) == 0 {
- panic("no return value specified for SubscribeToFinalizedHeads")
- }
-
- var r0 <-chan HEAD
- var r1 types.Subscription
- var r2 error
- if rf, ok := ret.Get(0).(func(context.Context) (<-chan HEAD, types.Subscription, error)); ok {
- return rf(ctx)
- }
- if rf, ok := ret.Get(0).(func(context.Context) <-chan HEAD); ok {
- r0 = rf(ctx)
- } else {
- if ret.Get(0) != nil {
- r0 = ret.Get(0).(<-chan HEAD)
- }
- }
-
- if rf, ok := ret.Get(1).(func(context.Context) types.Subscription); ok {
- r1 = rf(ctx)
- } else {
- if ret.Get(1) != nil {
- r1 = ret.Get(1).(types.Subscription)
- }
- }
-
- if rf, ok := ret.Get(2).(func(context.Context) error); ok {
- r2 = rf(ctx)
- } else {
- r2 = ret.Error(2)
- }
-
- return r0, r1, r2
-}
-
-// mockRPCClient_SubscribeToFinalizedHeads_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubscribeToFinalizedHeads'
-type mockRPCClient_SubscribeToFinalizedHeads_Call[CHAIN_ID types.ID, HEAD Head] struct {
- *mock.Call
-}
-
-// SubscribeToFinalizedHeads is a helper method to define mock.On call
-// - ctx context.Context
-func (_e *mockRPCClient_Expecter[CHAIN_ID, HEAD]) SubscribeToFinalizedHeads(ctx interface{}) *mockRPCClient_SubscribeToFinalizedHeads_Call[CHAIN_ID, HEAD] {
- return &mockRPCClient_SubscribeToFinalizedHeads_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("SubscribeToFinalizedHeads", ctx)}
-}
-
-func (_c *mockRPCClient_SubscribeToFinalizedHeads_Call[CHAIN_ID, HEAD]) Run(run func(ctx context.Context)) *mockRPCClient_SubscribeToFinalizedHeads_Call[CHAIN_ID, HEAD] {
- _c.Call.Run(func(args mock.Arguments) {
- run(args[0].(context.Context))
- })
- return _c
-}
-
-func (_c *mockRPCClient_SubscribeToFinalizedHeads_Call[CHAIN_ID, HEAD]) Return(_a0 <-chan HEAD, _a1 types.Subscription, _a2 error) *mockRPCClient_SubscribeToFinalizedHeads_Call[CHAIN_ID, HEAD] {
- _c.Call.Return(_a0, _a1, _a2)
- return _c
-}
-
-func (_c *mockRPCClient_SubscribeToFinalizedHeads_Call[CHAIN_ID, HEAD]) RunAndReturn(run func(context.Context) (<-chan HEAD, types.Subscription, error)) *mockRPCClient_SubscribeToFinalizedHeads_Call[CHAIN_ID, HEAD] {
- _c.Call.Return(run)
- return _c
-}
-
-// SubscribeToHeads provides a mock function with given fields: ctx
-func (_m *mockRPCClient[CHAIN_ID, HEAD]) SubscribeToHeads(ctx context.Context) (<-chan HEAD, types.Subscription, error) {
- ret := _m.Called(ctx)
-
- if len(ret) == 0 {
- panic("no return value specified for SubscribeToHeads")
- }
-
- var r0 <-chan HEAD
- var r1 types.Subscription
- var r2 error
- if rf, ok := ret.Get(0).(func(context.Context) (<-chan HEAD, types.Subscription, error)); ok {
- return rf(ctx)
- }
- if rf, ok := ret.Get(0).(func(context.Context) <-chan HEAD); ok {
- r0 = rf(ctx)
- } else {
- if ret.Get(0) != nil {
- r0 = ret.Get(0).(<-chan HEAD)
- }
- }
-
- if rf, ok := ret.Get(1).(func(context.Context) types.Subscription); ok {
- r1 = rf(ctx)
- } else {
- if ret.Get(1) != nil {
- r1 = ret.Get(1).(types.Subscription)
- }
- }
-
- if rf, ok := ret.Get(2).(func(context.Context) error); ok {
- r2 = rf(ctx)
- } else {
- r2 = ret.Error(2)
- }
-
- return r0, r1, r2
-}
-
-// mockRPCClient_SubscribeToHeads_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubscribeToHeads'
-type mockRPCClient_SubscribeToHeads_Call[CHAIN_ID types.ID, HEAD Head] struct {
- *mock.Call
-}
-
-// SubscribeToHeads is a helper method to define mock.On call
-// - ctx context.Context
-func (_e *mockRPCClient_Expecter[CHAIN_ID, HEAD]) SubscribeToHeads(ctx interface{}) *mockRPCClient_SubscribeToHeads_Call[CHAIN_ID, HEAD] {
- return &mockRPCClient_SubscribeToHeads_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("SubscribeToHeads", ctx)}
-}
-
-func (_c *mockRPCClient_SubscribeToHeads_Call[CHAIN_ID, HEAD]) Run(run func(ctx context.Context)) *mockRPCClient_SubscribeToHeads_Call[CHAIN_ID, HEAD] {
- _c.Call.Run(func(args mock.Arguments) {
- run(args[0].(context.Context))
- })
- return _c
-}
-
-func (_c *mockRPCClient_SubscribeToHeads_Call[CHAIN_ID, HEAD]) Return(_a0 <-chan HEAD, _a1 types.Subscription, _a2 error) *mockRPCClient_SubscribeToHeads_Call[CHAIN_ID, HEAD] {
- _c.Call.Return(_a0, _a1, _a2)
- return _c
-}
-
-func (_c *mockRPCClient_SubscribeToHeads_Call[CHAIN_ID, HEAD]) RunAndReturn(run func(context.Context) (<-chan HEAD, types.Subscription, error)) *mockRPCClient_SubscribeToHeads_Call[CHAIN_ID, HEAD] {
- _c.Call.Return(run)
- return _c
-}
-
-// UnsubscribeAllExcept provides a mock function with given fields: subs
-func (_m *mockRPCClient[CHAIN_ID, HEAD]) UnsubscribeAllExcept(subs ...types.Subscription) {
- _va := make([]interface{}, len(subs))
- for _i := range subs {
- _va[_i] = subs[_i]
- }
- var _ca []interface{}
- _ca = append(_ca, _va...)
- _m.Called(_ca...)
-}
-
-// mockRPCClient_UnsubscribeAllExcept_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UnsubscribeAllExcept'
-type mockRPCClient_UnsubscribeAllExcept_Call[CHAIN_ID types.ID, HEAD Head] struct {
- *mock.Call
-}
-
-// UnsubscribeAllExcept is a helper method to define mock.On call
-// - subs ...types.Subscription
-func (_e *mockRPCClient_Expecter[CHAIN_ID, HEAD]) UnsubscribeAllExcept(subs ...interface{}) *mockRPCClient_UnsubscribeAllExcept_Call[CHAIN_ID, HEAD] {
- return &mockRPCClient_UnsubscribeAllExcept_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("UnsubscribeAllExcept",
- append([]interface{}{}, subs...)...)}
-}
-
-func (_c *mockRPCClient_UnsubscribeAllExcept_Call[CHAIN_ID, HEAD]) Run(run func(subs ...types.Subscription)) *mockRPCClient_UnsubscribeAllExcept_Call[CHAIN_ID, HEAD] {
- _c.Call.Run(func(args mock.Arguments) {
- variadicArgs := make([]types.Subscription, len(args)-0)
- for i, a := range args[0:] {
- if a != nil {
- variadicArgs[i] = a.(types.Subscription)
- }
- }
- run(variadicArgs...)
- })
- return _c
-}
-
-func (_c *mockRPCClient_UnsubscribeAllExcept_Call[CHAIN_ID, HEAD]) Return() *mockRPCClient_UnsubscribeAllExcept_Call[CHAIN_ID, HEAD] {
- _c.Call.Return()
- return _c
-}
-
-func (_c *mockRPCClient_UnsubscribeAllExcept_Call[CHAIN_ID, HEAD]) RunAndReturn(run func(...types.Subscription)) *mockRPCClient_UnsubscribeAllExcept_Call[CHAIN_ID, HEAD] {
- _c.Run(run)
- return _c
-}
-
-// newMockRPCClient creates a new instance of mockRPCClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-// The first argument is typically a *testing.T value.
-func newMockRPCClient[CHAIN_ID types.ID, HEAD Head](t interface {
- mock.TestingT
- Cleanup(func())
-}) *mockRPCClient[CHAIN_ID, HEAD] {
- mock := &mockRPCClient[CHAIN_ID, HEAD]{}
- mock.Mock.Test(t)
-
- t.Cleanup(func() { mock.AssertExpectations(t) })
-
- return mock
-}
diff --git a/common/client/mock_send_only_client_test.go b/common/client/mock_send_only_client_test.go
deleted file mode 100644
index 0def3c58a2e..00000000000
--- a/common/client/mock_send_only_client_test.go
+++ /dev/null
@@ -1,173 +0,0 @@
-// Code generated by mockery v2.50.0. DO NOT EDIT.
-
-package client
-
-import (
- context "context"
-
- types "github.com/smartcontractkit/chainlink/v2/common/types"
- mock "github.com/stretchr/testify/mock"
-)
-
-// mockSendOnlyClient is an autogenerated mock type for the sendOnlyClient type
-type mockSendOnlyClient[CHAIN_ID types.ID] struct {
- mock.Mock
-}
-
-type mockSendOnlyClient_Expecter[CHAIN_ID types.ID] struct {
- mock *mock.Mock
-}
-
-func (_m *mockSendOnlyClient[CHAIN_ID]) EXPECT() *mockSendOnlyClient_Expecter[CHAIN_ID] {
- return &mockSendOnlyClient_Expecter[CHAIN_ID]{mock: &_m.Mock}
-}
-
-// ChainID provides a mock function with given fields: _a0
-func (_m *mockSendOnlyClient[CHAIN_ID]) ChainID(_a0 context.Context) (CHAIN_ID, error) {
- ret := _m.Called(_a0)
-
- if len(ret) == 0 {
- panic("no return value specified for ChainID")
- }
-
- var r0 CHAIN_ID
- var r1 error
- if rf, ok := ret.Get(0).(func(context.Context) (CHAIN_ID, error)); ok {
- return rf(_a0)
- }
- if rf, ok := ret.Get(0).(func(context.Context) CHAIN_ID); ok {
- r0 = rf(_a0)
- } else {
- if ret.Get(0) != nil {
- r0 = ret.Get(0).(CHAIN_ID)
- }
- }
-
- if rf, ok := ret.Get(1).(func(context.Context) error); ok {
- r1 = rf(_a0)
- } else {
- r1 = ret.Error(1)
- }
-
- return r0, r1
-}
-
-// mockSendOnlyClient_ChainID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ChainID'
-type mockSendOnlyClient_ChainID_Call[CHAIN_ID types.ID] struct {
- *mock.Call
-}
-
-// ChainID is a helper method to define mock.On call
-// - _a0 context.Context
-func (_e *mockSendOnlyClient_Expecter[CHAIN_ID]) ChainID(_a0 interface{}) *mockSendOnlyClient_ChainID_Call[CHAIN_ID] {
- return &mockSendOnlyClient_ChainID_Call[CHAIN_ID]{Call: _e.mock.On("ChainID", _a0)}
-}
-
-func (_c *mockSendOnlyClient_ChainID_Call[CHAIN_ID]) Run(run func(_a0 context.Context)) *mockSendOnlyClient_ChainID_Call[CHAIN_ID] {
- _c.Call.Run(func(args mock.Arguments) {
- run(args[0].(context.Context))
- })
- return _c
-}
-
-func (_c *mockSendOnlyClient_ChainID_Call[CHAIN_ID]) Return(_a0 CHAIN_ID, _a1 error) *mockSendOnlyClient_ChainID_Call[CHAIN_ID] {
- _c.Call.Return(_a0, _a1)
- return _c
-}
-
-func (_c *mockSendOnlyClient_ChainID_Call[CHAIN_ID]) RunAndReturn(run func(context.Context) (CHAIN_ID, error)) *mockSendOnlyClient_ChainID_Call[CHAIN_ID] {
- _c.Call.Return(run)
- return _c
-}
-
-// Close provides a mock function with no fields
-func (_m *mockSendOnlyClient[CHAIN_ID]) Close() {
- _m.Called()
-}
-
-// mockSendOnlyClient_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close'
-type mockSendOnlyClient_Close_Call[CHAIN_ID types.ID] struct {
- *mock.Call
-}
-
-// Close is a helper method to define mock.On call
-func (_e *mockSendOnlyClient_Expecter[CHAIN_ID]) Close() *mockSendOnlyClient_Close_Call[CHAIN_ID] {
- return &mockSendOnlyClient_Close_Call[CHAIN_ID]{Call: _e.mock.On("Close")}
-}
-
-func (_c *mockSendOnlyClient_Close_Call[CHAIN_ID]) Run(run func()) *mockSendOnlyClient_Close_Call[CHAIN_ID] {
- _c.Call.Run(func(args mock.Arguments) {
- run()
- })
- return _c
-}
-
-func (_c *mockSendOnlyClient_Close_Call[CHAIN_ID]) Return() *mockSendOnlyClient_Close_Call[CHAIN_ID] {
- _c.Call.Return()
- return _c
-}
-
-func (_c *mockSendOnlyClient_Close_Call[CHAIN_ID]) RunAndReturn(run func()) *mockSendOnlyClient_Close_Call[CHAIN_ID] {
- _c.Run(run)
- return _c
-}
-
-// Dial provides a mock function with given fields: ctx
-func (_m *mockSendOnlyClient[CHAIN_ID]) Dial(ctx context.Context) error {
- ret := _m.Called(ctx)
-
- if len(ret) == 0 {
- panic("no return value specified for Dial")
- }
-
- var r0 error
- if rf, ok := ret.Get(0).(func(context.Context) error); ok {
- r0 = rf(ctx)
- } else {
- r0 = ret.Error(0)
- }
-
- return r0
-}
-
-// mockSendOnlyClient_Dial_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Dial'
-type mockSendOnlyClient_Dial_Call[CHAIN_ID types.ID] struct {
- *mock.Call
-}
-
-// Dial is a helper method to define mock.On call
-// - ctx context.Context
-func (_e *mockSendOnlyClient_Expecter[CHAIN_ID]) Dial(ctx interface{}) *mockSendOnlyClient_Dial_Call[CHAIN_ID] {
- return &mockSendOnlyClient_Dial_Call[CHAIN_ID]{Call: _e.mock.On("Dial", ctx)}
-}
-
-func (_c *mockSendOnlyClient_Dial_Call[CHAIN_ID]) Run(run func(ctx context.Context)) *mockSendOnlyClient_Dial_Call[CHAIN_ID] {
- _c.Call.Run(func(args mock.Arguments) {
- run(args[0].(context.Context))
- })
- return _c
-}
-
-func (_c *mockSendOnlyClient_Dial_Call[CHAIN_ID]) Return(_a0 error) *mockSendOnlyClient_Dial_Call[CHAIN_ID] {
- _c.Call.Return(_a0)
- return _c
-}
-
-func (_c *mockSendOnlyClient_Dial_Call[CHAIN_ID]) RunAndReturn(run func(context.Context) error) *mockSendOnlyClient_Dial_Call[CHAIN_ID] {
- _c.Call.Return(run)
- return _c
-}
-
-// newMockSendOnlyClient creates a new instance of mockSendOnlyClient. 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 newMockSendOnlyClient[CHAIN_ID types.ID](t interface {
- mock.TestingT
- Cleanup(func())
-}) *mockSendOnlyClient[CHAIN_ID] {
- mock := &mockSendOnlyClient[CHAIN_ID]{}
- mock.Mock.Test(t)
-
- t.Cleanup(func() { mock.AssertExpectations(t) })
-
- return mock
-}
diff --git a/common/client/mock_send_only_node_test.go b/common/client/mock_send_only_node_test.go
deleted file mode 100644
index 16d463df3de..00000000000
--- a/common/client/mock_send_only_node_test.go
+++ /dev/null
@@ -1,357 +0,0 @@
-// Code generated by mockery v2.50.0. DO NOT EDIT.
-
-package client
-
-import (
- context "context"
-
- types "github.com/smartcontractkit/chainlink/v2/common/types"
- mock "github.com/stretchr/testify/mock"
-)
-
-// mockSendOnlyNode is an autogenerated mock type for the SendOnlyNode type
-type mockSendOnlyNode[CHAIN_ID types.ID, RPC interface{}] struct {
- mock.Mock
-}
-
-type mockSendOnlyNode_Expecter[CHAIN_ID types.ID, RPC interface{}] struct {
- mock *mock.Mock
-}
-
-func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) EXPECT() *mockSendOnlyNode_Expecter[CHAIN_ID, RPC] {
- return &mockSendOnlyNode_Expecter[CHAIN_ID, RPC]{mock: &_m.Mock}
-}
-
-// Close provides a mock function with no fields
-func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) Close() error {
- ret := _m.Called()
-
- if len(ret) == 0 {
- panic("no return value specified for Close")
- }
-
- var r0 error
- if rf, ok := ret.Get(0).(func() error); ok {
- r0 = rf()
- } else {
- r0 = ret.Error(0)
- }
-
- return r0
-}
-
-// mockSendOnlyNode_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close'
-type mockSendOnlyNode_Close_Call[CHAIN_ID types.ID, RPC interface{}] struct {
- *mock.Call
-}
-
-// Close is a helper method to define mock.On call
-func (_e *mockSendOnlyNode_Expecter[CHAIN_ID, RPC]) Close() *mockSendOnlyNode_Close_Call[CHAIN_ID, RPC] {
- return &mockSendOnlyNode_Close_Call[CHAIN_ID, RPC]{Call: _e.mock.On("Close")}
-}
-
-func (_c *mockSendOnlyNode_Close_Call[CHAIN_ID, RPC]) Run(run func()) *mockSendOnlyNode_Close_Call[CHAIN_ID, RPC] {
- _c.Call.Run(func(args mock.Arguments) {
- run()
- })
- return _c
-}
-
-func (_c *mockSendOnlyNode_Close_Call[CHAIN_ID, RPC]) Return(_a0 error) *mockSendOnlyNode_Close_Call[CHAIN_ID, RPC] {
- _c.Call.Return(_a0)
- return _c
-}
-
-func (_c *mockSendOnlyNode_Close_Call[CHAIN_ID, RPC]) RunAndReturn(run func() error) *mockSendOnlyNode_Close_Call[CHAIN_ID, RPC] {
- _c.Call.Return(run)
- return _c
-}
-
-// ConfiguredChainID provides a mock function with no fields
-func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) ConfiguredChainID() CHAIN_ID {
- ret := _m.Called()
-
- if len(ret) == 0 {
- panic("no return value specified for ConfiguredChainID")
- }
-
- var r0 CHAIN_ID
- if rf, ok := ret.Get(0).(func() CHAIN_ID); ok {
- r0 = rf()
- } else {
- if ret.Get(0) != nil {
- r0 = ret.Get(0).(CHAIN_ID)
- }
- }
-
- return r0
-}
-
-// mockSendOnlyNode_ConfiguredChainID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ConfiguredChainID'
-type mockSendOnlyNode_ConfiguredChainID_Call[CHAIN_ID types.ID, RPC interface{}] struct {
- *mock.Call
-}
-
-// ConfiguredChainID is a helper method to define mock.On call
-func (_e *mockSendOnlyNode_Expecter[CHAIN_ID, RPC]) ConfiguredChainID() *mockSendOnlyNode_ConfiguredChainID_Call[CHAIN_ID, RPC] {
- return &mockSendOnlyNode_ConfiguredChainID_Call[CHAIN_ID, RPC]{Call: _e.mock.On("ConfiguredChainID")}
-}
-
-func (_c *mockSendOnlyNode_ConfiguredChainID_Call[CHAIN_ID, RPC]) Run(run func()) *mockSendOnlyNode_ConfiguredChainID_Call[CHAIN_ID, RPC] {
- _c.Call.Run(func(args mock.Arguments) {
- run()
- })
- return _c
-}
-
-func (_c *mockSendOnlyNode_ConfiguredChainID_Call[CHAIN_ID, RPC]) Return(_a0 CHAIN_ID) *mockSendOnlyNode_ConfiguredChainID_Call[CHAIN_ID, RPC] {
- _c.Call.Return(_a0)
- return _c
-}
-
-func (_c *mockSendOnlyNode_ConfiguredChainID_Call[CHAIN_ID, RPC]) RunAndReturn(run func() CHAIN_ID) *mockSendOnlyNode_ConfiguredChainID_Call[CHAIN_ID, RPC] {
- _c.Call.Return(run)
- return _c
-}
-
-// Name provides a mock function with no fields
-func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) Name() string {
- ret := _m.Called()
-
- if len(ret) == 0 {
- panic("no return value specified for Name")
- }
-
- var r0 string
- if rf, ok := ret.Get(0).(func() string); ok {
- r0 = rf()
- } else {
- r0 = ret.Get(0).(string)
- }
-
- return r0
-}
-
-// mockSendOnlyNode_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Name'
-type mockSendOnlyNode_Name_Call[CHAIN_ID types.ID, RPC interface{}] struct {
- *mock.Call
-}
-
-// Name is a helper method to define mock.On call
-func (_e *mockSendOnlyNode_Expecter[CHAIN_ID, RPC]) Name() *mockSendOnlyNode_Name_Call[CHAIN_ID, RPC] {
- return &mockSendOnlyNode_Name_Call[CHAIN_ID, RPC]{Call: _e.mock.On("Name")}
-}
-
-func (_c *mockSendOnlyNode_Name_Call[CHAIN_ID, RPC]) Run(run func()) *mockSendOnlyNode_Name_Call[CHAIN_ID, RPC] {
- _c.Call.Run(func(args mock.Arguments) {
- run()
- })
- return _c
-}
-
-func (_c *mockSendOnlyNode_Name_Call[CHAIN_ID, RPC]) Return(_a0 string) *mockSendOnlyNode_Name_Call[CHAIN_ID, RPC] {
- _c.Call.Return(_a0)
- return _c
-}
-
-func (_c *mockSendOnlyNode_Name_Call[CHAIN_ID, RPC]) RunAndReturn(run func() string) *mockSendOnlyNode_Name_Call[CHAIN_ID, RPC] {
- _c.Call.Return(run)
- return _c
-}
-
-// RPC provides a mock function with no fields
-func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) RPC() RPC {
- ret := _m.Called()
-
- if len(ret) == 0 {
- panic("no return value specified for RPC")
- }
-
- var r0 RPC
- if rf, ok := ret.Get(0).(func() RPC); ok {
- r0 = rf()
- } else {
- if ret.Get(0) != nil {
- r0 = ret.Get(0).(RPC)
- }
- }
-
- return r0
-}
-
-// mockSendOnlyNode_RPC_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RPC'
-type mockSendOnlyNode_RPC_Call[CHAIN_ID types.ID, RPC interface{}] struct {
- *mock.Call
-}
-
-// RPC is a helper method to define mock.On call
-func (_e *mockSendOnlyNode_Expecter[CHAIN_ID, RPC]) RPC() *mockSendOnlyNode_RPC_Call[CHAIN_ID, RPC] {
- return &mockSendOnlyNode_RPC_Call[CHAIN_ID, RPC]{Call: _e.mock.On("RPC")}
-}
-
-func (_c *mockSendOnlyNode_RPC_Call[CHAIN_ID, RPC]) Run(run func()) *mockSendOnlyNode_RPC_Call[CHAIN_ID, RPC] {
- _c.Call.Run(func(args mock.Arguments) {
- run()
- })
- return _c
-}
-
-func (_c *mockSendOnlyNode_RPC_Call[CHAIN_ID, RPC]) Return(_a0 RPC) *mockSendOnlyNode_RPC_Call[CHAIN_ID, RPC] {
- _c.Call.Return(_a0)
- return _c
-}
-
-func (_c *mockSendOnlyNode_RPC_Call[CHAIN_ID, RPC]) RunAndReturn(run func() RPC) *mockSendOnlyNode_RPC_Call[CHAIN_ID, RPC] {
- _c.Call.Return(run)
- return _c
-}
-
-// Start provides a mock function with given fields: _a0
-func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) Start(_a0 context.Context) error {
- ret := _m.Called(_a0)
-
- if len(ret) == 0 {
- panic("no return value specified for Start")
- }
-
- var r0 error
- if rf, ok := ret.Get(0).(func(context.Context) error); ok {
- r0 = rf(_a0)
- } else {
- r0 = ret.Error(0)
- }
-
- return r0
-}
-
-// mockSendOnlyNode_Start_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Start'
-type mockSendOnlyNode_Start_Call[CHAIN_ID types.ID, RPC interface{}] struct {
- *mock.Call
-}
-
-// Start is a helper method to define mock.On call
-// - _a0 context.Context
-func (_e *mockSendOnlyNode_Expecter[CHAIN_ID, RPC]) Start(_a0 interface{}) *mockSendOnlyNode_Start_Call[CHAIN_ID, RPC] {
- return &mockSendOnlyNode_Start_Call[CHAIN_ID, RPC]{Call: _e.mock.On("Start", _a0)}
-}
-
-func (_c *mockSendOnlyNode_Start_Call[CHAIN_ID, RPC]) Run(run func(_a0 context.Context)) *mockSendOnlyNode_Start_Call[CHAIN_ID, RPC] {
- _c.Call.Run(func(args mock.Arguments) {
- run(args[0].(context.Context))
- })
- return _c
-}
-
-func (_c *mockSendOnlyNode_Start_Call[CHAIN_ID, RPC]) Return(_a0 error) *mockSendOnlyNode_Start_Call[CHAIN_ID, RPC] {
- _c.Call.Return(_a0)
- return _c
-}
-
-func (_c *mockSendOnlyNode_Start_Call[CHAIN_ID, RPC]) RunAndReturn(run func(context.Context) error) *mockSendOnlyNode_Start_Call[CHAIN_ID, RPC] {
- _c.Call.Return(run)
- return _c
-}
-
-// State provides a mock function with no fields
-func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) State() nodeState {
- ret := _m.Called()
-
- if len(ret) == 0 {
- panic("no return value specified for State")
- }
-
- var r0 nodeState
- if rf, ok := ret.Get(0).(func() nodeState); ok {
- r0 = rf()
- } else {
- r0 = ret.Get(0).(nodeState)
- }
-
- return r0
-}
-
-// mockSendOnlyNode_State_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'State'
-type mockSendOnlyNode_State_Call[CHAIN_ID types.ID, RPC interface{}] struct {
- *mock.Call
-}
-
-// State is a helper method to define mock.On call
-func (_e *mockSendOnlyNode_Expecter[CHAIN_ID, RPC]) State() *mockSendOnlyNode_State_Call[CHAIN_ID, RPC] {
- return &mockSendOnlyNode_State_Call[CHAIN_ID, RPC]{Call: _e.mock.On("State")}
-}
-
-func (_c *mockSendOnlyNode_State_Call[CHAIN_ID, RPC]) Run(run func()) *mockSendOnlyNode_State_Call[CHAIN_ID, RPC] {
- _c.Call.Run(func(args mock.Arguments) {
- run()
- })
- return _c
-}
-
-func (_c *mockSendOnlyNode_State_Call[CHAIN_ID, RPC]) Return(_a0 nodeState) *mockSendOnlyNode_State_Call[CHAIN_ID, RPC] {
- _c.Call.Return(_a0)
- return _c
-}
-
-func (_c *mockSendOnlyNode_State_Call[CHAIN_ID, RPC]) RunAndReturn(run func() nodeState) *mockSendOnlyNode_State_Call[CHAIN_ID, RPC] {
- _c.Call.Return(run)
- return _c
-}
-
-// String provides a mock function with no fields
-func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) String() string {
- ret := _m.Called()
-
- if len(ret) == 0 {
- panic("no return value specified for String")
- }
-
- var r0 string
- if rf, ok := ret.Get(0).(func() string); ok {
- r0 = rf()
- } else {
- r0 = ret.Get(0).(string)
- }
-
- return r0
-}
-
-// mockSendOnlyNode_String_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'String'
-type mockSendOnlyNode_String_Call[CHAIN_ID types.ID, RPC interface{}] struct {
- *mock.Call
-}
-
-// String is a helper method to define mock.On call
-func (_e *mockSendOnlyNode_Expecter[CHAIN_ID, RPC]) String() *mockSendOnlyNode_String_Call[CHAIN_ID, RPC] {
- return &mockSendOnlyNode_String_Call[CHAIN_ID, RPC]{Call: _e.mock.On("String")}
-}
-
-func (_c *mockSendOnlyNode_String_Call[CHAIN_ID, RPC]) Run(run func()) *mockSendOnlyNode_String_Call[CHAIN_ID, RPC] {
- _c.Call.Run(func(args mock.Arguments) {
- run()
- })
- return _c
-}
-
-func (_c *mockSendOnlyNode_String_Call[CHAIN_ID, RPC]) Return(_a0 string) *mockSendOnlyNode_String_Call[CHAIN_ID, RPC] {
- _c.Call.Return(_a0)
- return _c
-}
-
-func (_c *mockSendOnlyNode_String_Call[CHAIN_ID, RPC]) RunAndReturn(run func() string) *mockSendOnlyNode_String_Call[CHAIN_ID, RPC] {
- _c.Call.Return(run)
- return _c
-}
-
-// newMockSendOnlyNode creates a new instance of mockSendOnlyNode. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-// The first argument is typically a *testing.T value.
-func newMockSendOnlyNode[CHAIN_ID types.ID, RPC interface{}](t interface {
- mock.TestingT
- Cleanup(func())
-}) *mockSendOnlyNode[CHAIN_ID, RPC] {
- mock := &mockSendOnlyNode[CHAIN_ID, RPC]{}
- mock.Mock.Test(t)
-
- t.Cleanup(func() { mock.AssertExpectations(t) })
-
- return mock
-}
diff --git a/common/client/mocks/config.go b/common/client/mocks/config.go
deleted file mode 100644
index 95b57cce0c3..00000000000
--- a/common/client/mocks/config.go
+++ /dev/null
@@ -1,31 +0,0 @@
-package mocks
-
-import "time"
-
-type ChainConfig struct {
- IsFinalityTagEnabled bool
- FinalityDepthVal uint32
- NoNewHeadsThresholdVal time.Duration
- FinalizedBlockOffsetVal uint32
- NoNewFinalizedHeadsThresholdVal time.Duration
-}
-
-func (t ChainConfig) NodeNoNewHeadsThreshold() time.Duration {
- return t.NoNewHeadsThresholdVal
-}
-
-func (t ChainConfig) FinalityDepth() uint32 {
- return t.FinalityDepthVal
-}
-
-func (t ChainConfig) FinalityTagEnabled() bool {
- return t.IsFinalityTagEnabled
-}
-
-func (t ChainConfig) FinalizedBlockOffset() uint32 {
- return t.FinalizedBlockOffsetVal
-}
-
-func (t ChainConfig) NoNewFinalizedHeadsThreshold() time.Duration {
- return t.NoNewFinalizedHeadsThresholdVal
-}
diff --git a/common/client/models.go b/common/client/models.go
deleted file mode 100644
index 526bb25c887..00000000000
--- a/common/client/models.go
+++ /dev/null
@@ -1,121 +0,0 @@
-package client
-
-import (
- "bytes"
- "fmt"
-)
-
-type SendTxReturnCode int
-
-// SendTxReturnCode is a generalized client error that dictates what should be the next action, depending on the RPC error response.
-const (
- Successful SendTxReturnCode = iota + 1
- Fatal // Unrecoverable error. Most likely the attempt should be thrown away.
- Retryable // The error returned by the RPC indicates that if we retry with the same attempt, the tx will eventually go through.
- Underpriced // Attempt was underpriced. New estimation is needed with bumped gas price.
- Unknown // Tx failed with an error response that is not recognized by the client.
- Unsupported // Attempt failed with an error response that is not supported by the client for the given chain.
- TransactionAlreadyKnown // The transaction that was sent has already been received by the RPC.
- InsufficientFunds // Tx was rejected due to insufficient funds.
- ExceedsMaxFee // Attempt's fee was higher than the node's limit and got rejected.
- FeeOutOfValidRange // This error is returned when we use a fee price suggested from an RPC, but the network rejects the attempt due to an invalid range(mostly used by L2 chains). Retry by requesting a new suggested fee price.
- TerminallyStuck // The error returned when a transaction is or could get terminally stuck in the mempool without any chance of inclusion.
- sendTxReturnCodeLen // tracks the number of errors. Must always be last
-)
-
-// sendTxSevereErrors - error codes which signal that transaction would never be accepted in its current form by the node
-var sendTxSevereErrors = []SendTxReturnCode{Fatal, Underpriced, Unsupported, ExceedsMaxFee, FeeOutOfValidRange, Unknown}
-
-// sendTxSuccessfulCodes - error codes which signal that transaction was accepted by the node
-var sendTxSuccessfulCodes = []SendTxReturnCode{Successful, TransactionAlreadyKnown}
-
-func (c SendTxReturnCode) String() string {
- switch c {
- case Successful:
- return "Successful"
- case Fatal:
- return "Fatal"
- case Retryable:
- return "Retryable"
- case Underpriced:
- return "Underpriced"
- case Unknown:
- return "Unknown"
- case Unsupported:
- return "Unsupported"
- case TransactionAlreadyKnown:
- return "TransactionAlreadyKnown"
- case InsufficientFunds:
- return "InsufficientFunds"
- case ExceedsMaxFee:
- return "ExceedsMaxFee"
- case FeeOutOfValidRange:
- return "FeeOutOfValidRange"
- case TerminallyStuck:
- return "TerminallyStuck"
- default:
- return fmt.Sprintf("SendTxReturnCode(%d)", c)
- }
-}
-
-type NodeTier int
-
-const (
- Primary = NodeTier(iota)
- Secondary
-)
-
-func (n NodeTier) String() string {
- switch n {
- case Primary:
- return "primary"
- case Secondary:
- return "secondary"
- default:
- return fmt.Sprintf("NodeTier(%d)", n)
- }
-}
-
-// syncStatus - defines problems related to RPC's state synchronization. Can be used as a bitmask to define multiple issues
-type syncStatus int
-
-const (
- // syncStatusSynced - RPC is fully synced
- syncStatusSynced = 0
- // syncStatusNotInSyncWithPool - RPC is lagging behind the highest block observed within the pool of RPCs
- syncStatusNotInSyncWithPool syncStatus = 1 << iota
- // syncStatusNoNewHead - RPC failed to produce a new head for too long
- syncStatusNoNewHead
- // syncStatusNoNewFinalizedHead - RPC failed to produce a new finalized head for too long
- syncStatusNoNewFinalizedHead
- syncStatusLen
-)
-
-func (s syncStatus) String() string {
- if s == syncStatusSynced {
- return "Synced"
- }
- var result bytes.Buffer
- for i := syncStatusNotInSyncWithPool; i < syncStatusLen; i = i << 1 {
- if i&s == 0 {
- continue
- }
- result.WriteString(i.string())
- result.WriteString(",")
- }
- result.Truncate(result.Len() - 1)
- return result.String()
-}
-
-func (s syncStatus) string() string {
- switch s {
- case syncStatusNotInSyncWithPool:
- return "NotInSyncWithRPCPool"
- case syncStatusNoNewHead:
- return "NoNewHead"
- case syncStatusNoNewFinalizedHead:
- return "NoNewFinalizedHead"
- default:
- return fmt.Sprintf("syncStatus(%d)", s)
- }
-}
diff --git a/common/client/models_test.go b/common/client/models_test.go
deleted file mode 100644
index a10592c3b68..00000000000
--- a/common/client/models_test.go
+++ /dev/null
@@ -1,50 +0,0 @@
-package client
-
-import (
- "strings"
- "testing"
-
- "github.com/stretchr/testify/assert"
-)
-
-func TestSendTxReturnCode_String(t *testing.T) {
- // ensure all the SendTxReturnCodes have proper name
- for c := 1; c < int(sendTxReturnCodeLen); c++ {
- strC := SendTxReturnCode(c).String()
- if strings.Contains(strC, "SendTxReturnCode(") {
- t.Errorf("Expected %s to have a proper string representation", strC)
- }
- }
-}
-
-func TestSyncStatus_String(t *testing.T) {
- t.Run("All of the statuses have proper string representation", func(t *testing.T) {
- for i := syncStatusNotInSyncWithPool; i < syncStatusLen; i <<= 1 {
- // ensure that i's string representation is not equal to `syncStatus(%d)`
- assert.NotContains(t, i.String(), "syncStatus(")
- }
- })
- t.Run("Unwraps mask", func(t *testing.T) {
- testCases := []struct {
- Mask syncStatus
- ExpectedStr string
- }{
- {
- ExpectedStr: "Synced",
- },
- {
- Mask: syncStatusNotInSyncWithPool | syncStatusNoNewHead,
- ExpectedStr: "NotInSyncWithRPCPool,NoNewHead",
- },
- {
- Mask: syncStatusNotInSyncWithPool | syncStatusNoNewHead | syncStatusNoNewFinalizedHead,
- ExpectedStr: "NotInSyncWithRPCPool,NoNewHead,NoNewFinalizedHead",
- },
- }
- for _, testCase := range testCases {
- t.Run(testCase.ExpectedStr, func(t *testing.T) {
- assert.Equal(t, testCase.ExpectedStr, testCase.Mask.String())
- })
- }
- })
-}
diff --git a/common/client/multi_node.go b/common/client/multi_node.go
deleted file mode 100644
index b946fb8fc2a..00000000000
--- a/common/client/multi_node.go
+++ /dev/null
@@ -1,364 +0,0 @@
-package client
-
-import (
- "context"
- "fmt"
- "math/big"
- "sync"
- "time"
-
- "github.com/prometheus/client_golang/prometheus"
- "github.com/prometheus/client_golang/prometheus/promauto"
-
- "github.com/smartcontractkit/chainlink-common/pkg/logger"
- "github.com/smartcontractkit/chainlink-common/pkg/services"
-
- "github.com/smartcontractkit/chainlink/v2/common/types"
-)
-
-var (
- // PromMultiNodeRPCNodeStates reports current RPC node state
- PromMultiNodeRPCNodeStates = promauto.NewGaugeVec(prometheus.GaugeOpts{
- Name: "multi_node_states",
- Help: "The number of RPC nodes currently in the given state for the given chain",
- }, []string{"network", "chainId", "state"})
- ErroringNodeError = fmt.Errorf("no live nodes available")
-)
-
-// MultiNode is a generalized multi node client interface that includes methods to interact with different chains.
-// It also handles multiple node RPC connections simultaneously.
-type MultiNode[
- CHAIN_ID types.ID,
- RPC any,
-] struct {
- services.Service
- eng *services.Engine
-
- primaryNodes []Node[CHAIN_ID, RPC]
- sendOnlyNodes []SendOnlyNode[CHAIN_ID, RPC]
- chainID CHAIN_ID
- lggr logger.SugaredLogger
- selectionMode string
- nodeSelector NodeSelector[CHAIN_ID, RPC]
- leaseDuration time.Duration
- leaseTicker *time.Ticker
- chainFamily string
- reportInterval time.Duration
- deathDeclarationDelay time.Duration
-
- activeMu sync.RWMutex
- activeNode Node[CHAIN_ID, RPC]
-}
-
-func NewMultiNode[
- CHAIN_ID types.ID,
- RPC any,
-](
- lggr logger.Logger,
- selectionMode string, // type of the "best" RPC selector (e.g HighestHead, RoundRobin, etc.)
- leaseDuration time.Duration, // defines interval on which new "best" RPC should be selected
- primaryNodes []Node[CHAIN_ID, RPC],
- sendOnlyNodes []SendOnlyNode[CHAIN_ID, RPC],
- chainID CHAIN_ID, // configured chain ID (used to verify that passed primaryNodes belong to the same chain)
- chainFamily string, // name of the chain family - used in the metrics
- deathDeclarationDelay time.Duration,
-) *MultiNode[CHAIN_ID, RPC] {
- nodeSelector := newNodeSelector(selectionMode, primaryNodes)
- // Prometheus' default interval is 15s, set this to under 7.5s to avoid
- // aliasing (see: https://en.wikipedia.org/wiki/Nyquist_frequency)
- const reportInterval = 6500 * time.Millisecond
- c := &MultiNode[CHAIN_ID, RPC]{
- primaryNodes: primaryNodes,
- sendOnlyNodes: sendOnlyNodes,
- chainID: chainID,
- selectionMode: selectionMode,
- nodeSelector: nodeSelector,
- leaseDuration: leaseDuration,
- chainFamily: chainFamily,
- reportInterval: reportInterval,
- deathDeclarationDelay: deathDeclarationDelay,
- }
- c.Service, c.eng = services.Config{
- Name: "MultiNode",
- Start: c.start,
- Close: c.close,
- }.NewServiceEngine(logger.With(lggr, "chainID", chainID.String()))
- c.lggr = c.eng.SugaredLogger
-
- c.lggr.Debugf("The MultiNode is configured to use NodeSelectionMode: %s", selectionMode)
-
- return c
-}
-
-func (c *MultiNode[CHAIN_ID, RPC]) ChainID() CHAIN_ID {
- return c.chainID
-}
-
-func (c *MultiNode[CHAIN_ID, RPC]) DoAll(ctx context.Context, do func(ctx context.Context, rpc RPC, isSendOnly bool)) error {
- return c.eng.IfNotStopped(func() error {
- callsCompleted := 0
- for _, n := range c.primaryNodes {
- select {
- case <-ctx.Done():
- return ctx.Err()
- default:
- if n.State() != nodeStateAlive {
- continue
- }
- do(ctx, n.RPC(), false)
- callsCompleted++
- }
- }
-
- for _, n := range c.sendOnlyNodes {
- select {
- case <-ctx.Done():
- return ctx.Err()
- default:
- if n.State() != nodeStateAlive {
- continue
- }
- do(ctx, n.RPC(), true)
- }
- }
- if callsCompleted == 0 {
- return ErroringNodeError
- }
- return nil
- })
-}
-
-func (c *MultiNode[CHAIN_ID, RPC]) NodeStates() map[string]string {
- states := map[string]string{}
- for _, n := range c.primaryNodes {
- states[n.Name()] = n.State().String()
- }
- for _, n := range c.sendOnlyNodes {
- states[n.Name()] = n.State().String()
- }
- return states
-}
-
-// Start starts every node in the pool
-//
-// Nodes handle their own redialing and runloops, so this function does not
-// return any error if the nodes aren't available
-func (c *MultiNode[CHAIN_ID, RPC]) start(ctx context.Context) error {
- if len(c.primaryNodes) == 0 {
- return fmt.Errorf("no available nodes for chain %s", c.chainID.String())
- }
- var ms services.MultiStart
- for _, n := range c.primaryNodes {
- if n.ConfiguredChainID().String() != c.chainID.String() {
- return ms.CloseBecause(fmt.Errorf("node %s has configured chain ID %s which does not match multinode configured chain ID of %s", n.String(), n.ConfiguredChainID().String(), c.chainID.String()))
- }
- n.SetPoolChainInfoProvider(c)
- // node will handle its own redialing and automatic recovery
- if err := ms.Start(ctx, n); err != nil {
- return err
- }
- }
- for _, s := range c.sendOnlyNodes {
- if s.ConfiguredChainID().String() != c.chainID.String() {
- return ms.CloseBecause(fmt.Errorf("sendonly node %s has configured chain ID %s which does not match multinode configured chain ID of %s", s.String(), s.ConfiguredChainID().String(), c.chainID.String()))
- }
- if err := ms.Start(ctx, s); err != nil {
- return err
- }
- }
- c.eng.Go(c.runLoop)
-
- if c.leaseDuration.Seconds() > 0 && c.selectionMode != NodeSelectionModeRoundRobin {
- c.lggr.Infof("The MultiNode will switch to best node every %s", c.leaseDuration.String())
- c.eng.Go(c.checkLeaseLoop)
- } else {
- c.lggr.Info("Best node switching is disabled")
- }
-
- return nil
-}
-
-// Close tears down the MultiNode and closes all nodes
-func (c *MultiNode[CHAIN_ID, RPC]) close() error {
- return services.CloseAll(services.MultiCloser(c.primaryNodes), services.MultiCloser(c.sendOnlyNodes))
-}
-
-// SelectRPC returns an RPC of an active node. If there are no active nodes it returns an error.
-// Call this method from your chain-specific client implementation to access any chain-specific rpc calls.
-func (c *MultiNode[CHAIN_ID, RPC]) SelectRPC() (rpc RPC, err error) {
- n, err := c.selectNode()
- if err != nil {
- return rpc, err
- }
- return n.RPC(), nil
-}
-
-// selectNode returns the active Node, if it is still nodeStateAlive, otherwise it selects a new one from the NodeSelector.
-func (c *MultiNode[CHAIN_ID, RPC]) selectNode() (node Node[CHAIN_ID, RPC], err error) {
- c.activeMu.RLock()
- node = c.activeNode
- c.activeMu.RUnlock()
- if node != nil && node.State() == nodeStateAlive {
- return // still alive
- }
-
- // select a new one
- c.activeMu.Lock()
- defer c.activeMu.Unlock()
- node = c.activeNode
- if node != nil && node.State() == nodeStateAlive {
- return // another goroutine beat us here
- }
-
- var prevNodeName string
- if c.activeNode != nil {
- prevNodeName = c.activeNode.String()
- c.activeNode.UnsubscribeAllExceptAliveLoop()
- }
- c.activeNode = c.nodeSelector.Select()
- if c.activeNode == nil {
- c.lggr.Criticalw("No live RPC nodes available", "NodeSelectionMode", c.nodeSelector.Name())
- c.eng.EmitHealthErr(fmt.Errorf("no live nodes available for chain %s", c.chainID.String()))
- return nil, ErroringNodeError
- }
-
- c.lggr.Debugw("Switched to a new active node due to prev node heath issues", "prevNode", prevNodeName, "newNode", c.activeNode.String())
- return c.activeNode, err
-}
-
-// LatestChainInfo - returns number of live nodes available in the pool, so we can prevent the last alive node in a pool from being marked as out-of-sync.
-// Return highest ChainInfo most recently received by the alive nodes.
-// E.g. If Node A's the most recent block is 10 and highest 15 and for Node B it's - 12 and 14. This method will return 12.
-func (c *MultiNode[CHAIN_ID, RPC]) LatestChainInfo() (int, ChainInfo) {
- var nLiveNodes int
- ch := ChainInfo{
- TotalDifficulty: big.NewInt(0),
- }
- for _, n := range c.primaryNodes {
- if s, nodeChainInfo := n.StateAndLatest(); s == nodeStateAlive {
- nLiveNodes++
- ch.BlockNumber = max(ch.BlockNumber, nodeChainInfo.BlockNumber)
- ch.FinalizedBlockNumber = max(ch.FinalizedBlockNumber, nodeChainInfo.FinalizedBlockNumber)
- ch.TotalDifficulty = MaxTotalDifficulty(ch.TotalDifficulty, nodeChainInfo.TotalDifficulty)
- }
- }
- return nLiveNodes, ch
-}
-
-// HighestUserObservations - returns highest ChainInfo ever observed by any user of the MultiNode
-func (c *MultiNode[CHAIN_ID, RPC]) HighestUserObservations() ChainInfo {
- ch := ChainInfo{
- TotalDifficulty: big.NewInt(0),
- }
- for _, n := range c.primaryNodes {
- nodeChainInfo := n.HighestUserObservations()
- ch.BlockNumber = max(ch.BlockNumber, nodeChainInfo.BlockNumber)
- ch.FinalizedBlockNumber = max(ch.FinalizedBlockNumber, nodeChainInfo.FinalizedBlockNumber)
- ch.TotalDifficulty = MaxTotalDifficulty(ch.TotalDifficulty, nodeChainInfo.TotalDifficulty)
- }
- return ch
-}
-
-func (c *MultiNode[CHAIN_ID, RPC]) checkLease() {
- bestNode := c.nodeSelector.Select()
- for _, n := range c.primaryNodes {
- // Terminate client subscriptions. Services are responsible for reconnecting, which will be routed to the new
- // best node. Only terminate connections with more than 1 subscription to account for the aliveLoop subscription
- if n.State() == nodeStateAlive && n != bestNode {
- c.lggr.Infof("Switching to best node from %q to %q", n.String(), bestNode.String())
- n.UnsubscribeAllExceptAliveLoop()
- }
- }
-
- c.activeMu.Lock()
- defer c.activeMu.Unlock()
- if bestNode != c.activeNode {
- if c.activeNode != nil {
- c.activeNode.UnsubscribeAllExceptAliveLoop()
- }
- c.activeNode = bestNode
- }
-}
-
-func (c *MultiNode[CHAIN_ID, RPC]) checkLeaseLoop(ctx context.Context) {
- c.leaseTicker = time.NewTicker(c.leaseDuration)
- defer c.leaseTicker.Stop()
-
- for {
- select {
- case <-c.leaseTicker.C:
- c.checkLease()
- case <-ctx.Done():
- return
- }
- }
-}
-
-func (c *MultiNode[CHAIN_ID, RPC]) runLoop(ctx context.Context) {
- nodeStates := make([]nodeWithState, len(c.primaryNodes))
- for i, n := range c.primaryNodes {
- nodeStates[i] = nodeWithState{
- Node: n.String(),
- State: n.State().String(),
- DeadSince: nil,
- }
- }
-
- c.report(nodeStates)
-
- monitor := services.NewTicker(c.reportInterval)
- defer monitor.Stop()
-
- for {
- select {
- case <-monitor.C:
- c.report(nodeStates)
- case <-ctx.Done():
- return
- }
- }
-}
-
-type nodeWithState struct {
- Node string
- State string
- DeadSince *time.Time
-}
-
-func (c *MultiNode[CHAIN_ID, RPC]) report(nodesStateInfo []nodeWithState) {
- start := time.Now()
- var dead int
- counts := make(map[nodeState]int)
- for i, n := range c.primaryNodes {
- state := n.State()
- counts[state]++
- nodesStateInfo[i].State = state.String()
- if state == nodeStateAlive {
- nodesStateInfo[i].DeadSince = nil
- continue
- }
-
- if nodesStateInfo[i].DeadSince == nil {
- nodesStateInfo[i].DeadSince = &start
- }
-
- if start.Sub(*nodesStateInfo[i].DeadSince) >= c.deathDeclarationDelay {
- dead++
- }
- }
- for _, state := range allNodeStates {
- count := counts[state]
- PromMultiNodeRPCNodeStates.WithLabelValues(c.chainFamily, c.chainID.String(), state.String()).Set(float64(count))
- }
-
- total := len(c.primaryNodes)
- live := total - dead
- c.lggr.Tracew(fmt.Sprintf("MultiNode state: %d/%d nodes are alive", live, total), "nodeStates", nodesStateInfo)
- if total == dead {
- rerr := fmt.Errorf("no primary nodes available: 0/%d nodes are alive", total)
- c.lggr.Criticalw(rerr.Error(), "nodeStates", nodesStateInfo)
- c.eng.EmitHealthErr(rerr)
- } else if dead > 0 {
- c.lggr.Errorw(fmt.Sprintf("At least one primary node is dead: %d/%d nodes are alive", live, total), "nodeStates", nodesStateInfo)
- }
-}
diff --git a/common/client/multi_node_test.go b/common/client/multi_node_test.go
deleted file mode 100644
index c1636881dd3..00000000000
--- a/common/client/multi_node_test.go
+++ /dev/null
@@ -1,517 +0,0 @@
-package client
-
-import (
- "fmt"
- "math/big"
- "math/rand"
- "testing"
- "time"
-
- "github.com/pkg/errors"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/mock"
- "github.com/stretchr/testify/require"
- "go.uber.org/zap"
-
- "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest"
- "github.com/smartcontractkit/chainlink-common/pkg/utils/tests"
-
- "github.com/smartcontractkit/chainlink-common/pkg/logger"
- "github.com/smartcontractkit/chainlink/v2/common/types"
-)
-
-type multiNodeRPCClient RPCClient[types.ID, types.Head[Hashable]]
-
-type testMultiNode struct {
- *MultiNode[types.ID, multiNodeRPCClient]
-}
-
-type multiNodeOpts struct {
- logger logger.Logger
- selectionMode string
- leaseDuration time.Duration
- nodes []Node[types.ID, multiNodeRPCClient]
- sendonlys []SendOnlyNode[types.ID, multiNodeRPCClient]
- chainID types.ID
- chainFamily string
- deathDeclarationDelay time.Duration
-}
-
-func newTestMultiNode(t *testing.T, opts multiNodeOpts) testMultiNode {
- if opts.logger == nil {
- opts.logger = logger.Test(t)
- }
-
- result := NewMultiNode[types.ID, multiNodeRPCClient](
- opts.logger, opts.selectionMode, opts.leaseDuration, opts.nodes, opts.sendonlys, opts.chainID, opts.chainFamily, opts.deathDeclarationDelay)
- return testMultiNode{
- result,
- }
-}
-
-func newHealthyNode(t *testing.T, chainID types.ID) *mockNode[types.ID, multiNodeRPCClient] {
- return newNodeWithState(t, chainID, nodeStateAlive)
-}
-
-func newNodeWithState(t *testing.T, chainID types.ID, state nodeState) *mockNode[types.ID, multiNodeRPCClient] {
- node := newMockNode[types.ID, multiNodeRPCClient](t)
- node.On("ConfiguredChainID").Return(chainID).Once()
- node.On("Start", mock.Anything).Return(nil).Once()
- node.On("Close").Return(nil).Once()
- node.On("String").Return(fmt.Sprintf("healthy_node_%d", rand.Int())).Maybe()
- node.On("SetPoolChainInfoProvider", mock.Anything).Once()
- node.On("State").Return(state).Maybe()
- return node
-}
-
-func TestMultiNode_Dial(t *testing.T) {
- t.Parallel()
-
- newMockNode := newMockNode[types.ID, multiNodeRPCClient]
- newMockSendOnlyNode := newMockSendOnlyNode[types.ID, multiNodeRPCClient]
-
- t.Run("Fails without nodes", func(t *testing.T) {
- t.Parallel()
- mn := newTestMultiNode(t, multiNodeOpts{
- selectionMode: NodeSelectionModeRoundRobin,
- chainID: types.RandomID(),
- })
- err := mn.Start(tests.Context(t))
- assert.ErrorContains(t, err, fmt.Sprintf("no available nodes for chain %s", mn.chainID))
- })
- t.Run("Fails with wrong node's chainID", func(t *testing.T) {
- t.Parallel()
- node := newMockNode(t)
- multiNodeChainID := types.NewIDFromInt(10)
- nodeChainID := types.NewIDFromInt(11)
- node.On("ConfiguredChainID").Return(nodeChainID).Twice()
- const nodeName = "nodeName"
- node.On("String").Return(nodeName).Once()
- mn := newTestMultiNode(t, multiNodeOpts{
- selectionMode: NodeSelectionModeRoundRobin,
- chainID: multiNodeChainID,
- nodes: []Node[types.ID, multiNodeRPCClient]{node},
- })
- err := mn.Start(tests.Context(t))
- assert.ErrorContains(t, err, fmt.Sprintf("node %s has configured chain ID %s which does not match multinode configured chain ID of %s", nodeName, nodeChainID, mn.chainID))
- })
- t.Run("Fails if node fails", func(t *testing.T) {
- t.Parallel()
- node := newMockNode(t)
- chainID := types.RandomID()
- node.On("ConfiguredChainID").Return(chainID).Once()
- expectedError := errors.New("failed to start node")
- node.On("Start", mock.Anything).Return(expectedError).Once()
- node.On("SetPoolChainInfoProvider", mock.Anything).Once()
- mn := newTestMultiNode(t, multiNodeOpts{
- selectionMode: NodeSelectionModeRoundRobin,
- chainID: chainID,
- nodes: []Node[types.ID, multiNodeRPCClient]{node},
- })
- err := mn.Start(tests.Context(t))
- assert.ErrorIs(t, err, expectedError)
- })
-
- t.Run("Closes started nodes on failure", func(t *testing.T) {
- t.Parallel()
- chainID := types.RandomID()
- node1 := newHealthyNode(t, chainID)
- node2 := newMockNode(t)
- node2.On("ConfiguredChainID").Return(chainID).Once()
- expectedError := errors.New("failed to start node")
- node2.On("Start", mock.Anything).Return(expectedError).Once()
- node2.On("SetPoolChainInfoProvider", mock.Anything).Once()
-
- mn := newTestMultiNode(t, multiNodeOpts{
- selectionMode: NodeSelectionModeRoundRobin,
- chainID: chainID,
- nodes: []Node[types.ID, multiNodeRPCClient]{node1, node2},
- })
- err := mn.Start(tests.Context(t))
- assert.ErrorIs(t, err, expectedError)
- })
- t.Run("Fails with wrong send only node's chainID", func(t *testing.T) {
- t.Parallel()
- multiNodeChainID := types.NewIDFromInt(10)
- node := newHealthyNode(t, multiNodeChainID)
- sendOnly := newMockSendOnlyNode(t)
- sendOnlyChainID := types.NewIDFromInt(11)
- sendOnly.On("ConfiguredChainID").Return(sendOnlyChainID).Twice()
- const sendOnlyName = "sendOnlyNodeName"
- sendOnly.On("String").Return(sendOnlyName).Once()
-
- mn := newTestMultiNode(t, multiNodeOpts{
- selectionMode: NodeSelectionModeRoundRobin,
- chainID: multiNodeChainID,
- nodes: []Node[types.ID, multiNodeRPCClient]{node},
- sendonlys: []SendOnlyNode[types.ID, multiNodeRPCClient]{sendOnly},
- })
- err := mn.Start(tests.Context(t))
- assert.ErrorContains(t, err, fmt.Sprintf("sendonly node %s has configured chain ID %s which does not match multinode configured chain ID of %s", sendOnlyName, sendOnlyChainID, mn.chainID))
- })
-
- newHealthySendOnly := func(t *testing.T, chainID types.ID) *mockSendOnlyNode[types.ID, multiNodeRPCClient] {
- node := newMockSendOnlyNode(t)
- node.On("ConfiguredChainID").Return(chainID).Once()
- node.On("Start", mock.Anything).Return(nil).Once()
- node.On("Close").Return(nil).Once()
- return node
- }
- t.Run("Fails on send only node failure", func(t *testing.T) {
- t.Parallel()
- chainID := types.NewIDFromInt(10)
- node := newHealthyNode(t, chainID)
- sendOnly1 := newHealthySendOnly(t, chainID)
- sendOnly2 := newMockSendOnlyNode(t)
- sendOnly2.On("ConfiguredChainID").Return(chainID).Once()
- expectedError := errors.New("failed to start send only node")
- sendOnly2.On("Start", mock.Anything).Return(expectedError).Once()
-
- mn := newTestMultiNode(t, multiNodeOpts{
- selectionMode: NodeSelectionModeRoundRobin,
- chainID: chainID,
- nodes: []Node[types.ID, multiNodeRPCClient]{node},
- sendonlys: []SendOnlyNode[types.ID, multiNodeRPCClient]{sendOnly1, sendOnly2},
- })
- err := mn.Start(tests.Context(t))
- assert.ErrorIs(t, err, expectedError)
- })
- t.Run("Starts successfully with healthy nodes", func(t *testing.T) {
- t.Parallel()
- chainID := types.NewIDFromInt(10)
- node := newHealthyNode(t, chainID)
- mn := newTestMultiNode(t, multiNodeOpts{
- selectionMode: NodeSelectionModeRoundRobin,
- chainID: chainID,
- nodes: []Node[types.ID, multiNodeRPCClient]{node},
- sendonlys: []SendOnlyNode[types.ID, multiNodeRPCClient]{newHealthySendOnly(t, chainID)},
- })
- servicetest.Run(t, mn)
- selectedNode, err := mn.selectNode()
- require.NoError(t, err)
- assert.Equal(t, node, selectedNode)
- })
-}
-
-func TestMultiNode_Report(t *testing.T) {
- t.Parallel()
- t.Run("Dial starts periodical reporting", func(t *testing.T) {
- t.Parallel()
- chainID := types.RandomID()
- node1 := newHealthyNode(t, chainID)
- node2 := newNodeWithState(t, chainID, nodeStateOutOfSync)
- lggr, observedLogs := logger.TestObserved(t, zap.WarnLevel)
- mn := newTestMultiNode(t, multiNodeOpts{
- selectionMode: NodeSelectionModeRoundRobin,
- chainID: chainID,
- nodes: []Node[types.ID, multiNodeRPCClient]{node1, node2},
- logger: lggr,
- })
- mn.reportInterval = tests.TestInterval
- mn.deathDeclarationDelay = tests.TestInterval
- servicetest.Run(t, mn)
- tests.AssertLogCountEventually(t, observedLogs, "At least one primary node is dead: 1/2 nodes are alive", 2)
- })
- t.Run("Report critical error on all node failure", func(t *testing.T) {
- t.Parallel()
- chainID := types.RandomID()
- node := newNodeWithState(t, chainID, nodeStateOutOfSync)
- lggr, observedLogs := logger.TestObserved(t, zap.WarnLevel)
- mn := newTestMultiNode(t, multiNodeOpts{
- selectionMode: NodeSelectionModeRoundRobin,
- chainID: chainID,
- nodes: []Node[types.ID, multiNodeRPCClient]{node},
- logger: lggr,
- })
- mn.reportInterval = tests.TestInterval
- mn.deathDeclarationDelay = tests.TestInterval
- servicetest.Run(t, mn)
- tests.AssertLogCountEventually(t, observedLogs, "no primary nodes available: 0/1 nodes are alive", 2)
- err := mn.HealthReport()["MultiNode"]
- require.Error(t, err)
- assert.Contains(t, err.Error(), "no primary nodes available: 0/1 nodes are alive")
- })
-}
-
-func TestMultiNode_CheckLease(t *testing.T) {
- t.Parallel()
- t.Run("Round robin disables lease check", func(t *testing.T) {
- t.Parallel()
- chainID := types.RandomID()
- node := newHealthyNode(t, chainID)
- lggr, observedLogs := logger.TestObserved(t, zap.InfoLevel)
- mn := newTestMultiNode(t, multiNodeOpts{
- selectionMode: NodeSelectionModeRoundRobin,
- chainID: chainID,
- logger: lggr,
- nodes: []Node[types.ID, multiNodeRPCClient]{node},
- })
- servicetest.Run(t, mn)
- tests.RequireLogMessage(t, observedLogs, "Best node switching is disabled")
- })
- t.Run("Misconfigured lease check period won't start", func(t *testing.T) {
- t.Parallel()
- chainID := types.RandomID()
- node := newHealthyNode(t, chainID)
- lggr, observedLogs := logger.TestObserved(t, zap.InfoLevel)
- mn := newTestMultiNode(t, multiNodeOpts{
- selectionMode: NodeSelectionModeHighestHead,
- chainID: chainID,
- logger: lggr,
- nodes: []Node[types.ID, multiNodeRPCClient]{node},
- leaseDuration: 0,
- })
- servicetest.Run(t, mn)
- tests.RequireLogMessage(t, observedLogs, "Best node switching is disabled")
- })
- t.Run("Lease check updates active node", func(t *testing.T) {
- t.Parallel()
- chainID := types.RandomID()
- node := newHealthyNode(t, chainID)
- node.On("UnsubscribeAllExceptAliveLoop")
- bestNode := newHealthyNode(t, chainID)
- nodeSelector := newMockNodeSelector[types.ID, multiNodeRPCClient](t)
- nodeSelector.On("Select").Return(bestNode)
- lggr, observedLogs := logger.TestObserved(t, zap.InfoLevel)
- mn := newTestMultiNode(t, multiNodeOpts{
- selectionMode: NodeSelectionModeHighestHead,
- chainID: chainID,
- logger: lggr,
- nodes: []Node[types.ID, multiNodeRPCClient]{node, bestNode},
- leaseDuration: tests.TestInterval,
- })
- mn.nodeSelector = nodeSelector
- servicetest.Run(t, mn)
- tests.AssertLogEventually(t, observedLogs, fmt.Sprintf("Switching to best node from %q to %q", node.String(), bestNode.String()))
- tests.AssertEventually(t, func() bool {
- mn.activeMu.RLock()
- active := mn.activeNode
- mn.activeMu.RUnlock()
- return bestNode == active
- })
- })
- t.Run("NodeStates returns proper states", func(t *testing.T) {
- t.Parallel()
- chainID := types.NewIDFromInt(10)
- nodes := map[string]nodeState{
- "node_1": nodeStateAlive,
- "node_2": nodeStateUnreachable,
- "node_3": nodeStateDialed,
- }
-
- opts := multiNodeOpts{
- selectionMode: NodeSelectionModeRoundRobin,
- chainID: chainID,
- }
-
- expectedResult := map[string]string{}
- for name, state := range nodes {
- node := newMockNode[types.ID, multiNodeRPCClient](t)
- node.On("State").Return(state).Once()
- node.On("Name").Return(name).Once()
- opts.nodes = append(opts.nodes, node)
-
- sendOnly := newMockSendOnlyNode[types.ID, multiNodeRPCClient](t)
- sendOnlyName := "send_only_" + name
- sendOnly.On("State").Return(state).Once()
- sendOnly.On("Name").Return(sendOnlyName).Once()
- opts.sendonlys = append(opts.sendonlys, sendOnly)
-
- expectedResult[name] = state.String()
- expectedResult[sendOnlyName] = state.String()
- }
-
- mn := newTestMultiNode(t, opts)
- states := mn.NodeStates()
- assert.Equal(t, expectedResult, states)
- })
-}
-
-func TestMultiNode_selectNode(t *testing.T) {
- t.Parallel()
- t.Run("Returns same node, if it's still healthy", func(t *testing.T) {
- t.Parallel()
- chainID := types.RandomID()
- node1 := newMockNode[types.ID, multiNodeRPCClient](t)
- node1.On("State").Return(nodeStateAlive).Once()
- node1.On("String").Return("node1").Maybe()
- node2 := newMockNode[types.ID, multiNodeRPCClient](t)
- node2.On("String").Return("node2").Maybe()
- mn := newTestMultiNode(t, multiNodeOpts{
- selectionMode: NodeSelectionModeRoundRobin,
- chainID: chainID,
- nodes: []Node[types.ID, multiNodeRPCClient]{node1, node2},
- })
- nodeSelector := newMockNodeSelector[types.ID, multiNodeRPCClient](t)
- nodeSelector.On("Select").Return(node1).Once()
- mn.nodeSelector = nodeSelector
- prevActiveNode, err := mn.selectNode()
- require.NoError(t, err)
- require.Equal(t, node1.String(), prevActiveNode.String())
- newActiveNode, err := mn.selectNode()
- require.NoError(t, err)
- require.Equal(t, prevActiveNode.String(), newActiveNode.String())
- })
- t.Run("Updates node if active is not healthy", func(t *testing.T) {
- t.Parallel()
- chainID := types.RandomID()
- oldBest := newMockNode[types.ID, multiNodeRPCClient](t)
- oldBest.On("String").Return("oldBest").Maybe()
- oldBest.On("UnsubscribeAllExceptAliveLoop")
- newBest := newMockNode[types.ID, multiNodeRPCClient](t)
- newBest.On("String").Return("newBest").Maybe()
- mn := newTestMultiNode(t, multiNodeOpts{
- selectionMode: NodeSelectionModeRoundRobin,
- chainID: chainID,
- nodes: []Node[types.ID, multiNodeRPCClient]{oldBest, newBest},
- })
- nodeSelector := newMockNodeSelector[types.ID, multiNodeRPCClient](t)
- nodeSelector.On("Select").Return(oldBest).Once()
- mn.nodeSelector = nodeSelector
- activeNode, err := mn.selectNode()
- require.NoError(t, err)
- require.Equal(t, oldBest.String(), activeNode.String())
- // old best died, so we should replace it
- oldBest.On("State").Return(nodeStateOutOfSync).Twice()
- nodeSelector.On("Select").Return(newBest).Once()
- newActiveNode, err := mn.selectNode()
- require.NoError(t, err)
- require.Equal(t, newBest.String(), newActiveNode.String())
- })
- t.Run("No active nodes - reports critical error", func(t *testing.T) {
- t.Parallel()
- chainID := types.RandomID()
- lggr, observedLogs := logger.TestObserved(t, zap.InfoLevel)
- mn := newTestMultiNode(t, multiNodeOpts{
- selectionMode: NodeSelectionModeRoundRobin,
- chainID: chainID,
- logger: lggr,
- })
- nodeSelector := newMockNodeSelector[types.ID, multiNodeRPCClient](t)
- nodeSelector.On("Select").Return(nil).Once()
- nodeSelector.On("Name").Return("MockedNodeSelector").Once()
- mn.nodeSelector = nodeSelector
- node, err := mn.selectNode()
- require.EqualError(t, err, ErroringNodeError.Error())
- require.Nil(t, node)
- tests.RequireLogMessage(t, observedLogs, "No live RPC nodes available")
- })
-}
-
-func TestMultiNode_ChainInfo(t *testing.T) {
- t.Parallel()
- type nodeParams struct {
- LatestChainInfo ChainInfo
- HighestUserObservations ChainInfo
- State nodeState
- }
- testCases := []struct {
- Name string
- ExpectedNLiveNodes int
- ExpectedLatestChainInfo ChainInfo
- ExpectedHighestUserObservations ChainInfo
- NodeParams []nodeParams
- }{
- {
- Name: "no nodes",
- ExpectedLatestChainInfo: ChainInfo{
- TotalDifficulty: big.NewInt(0),
- },
- ExpectedHighestUserObservations: ChainInfo{
- TotalDifficulty: big.NewInt(0),
- },
- },
- {
- Name: "Best node is not healthy",
- ExpectedNLiveNodes: 3,
- ExpectedLatestChainInfo: ChainInfo{
- BlockNumber: 20,
- FinalizedBlockNumber: 10,
- TotalDifficulty: big.NewInt(10),
- },
- ExpectedHighestUserObservations: ChainInfo{
- BlockNumber: 1005,
- FinalizedBlockNumber: 995,
- TotalDifficulty: big.NewInt(2005),
- },
- NodeParams: []nodeParams{
- {
- State: nodeStateOutOfSync,
- LatestChainInfo: ChainInfo{
- BlockNumber: 1000,
- FinalizedBlockNumber: 990,
- TotalDifficulty: big.NewInt(2000),
- },
- HighestUserObservations: ChainInfo{
- BlockNumber: 1005,
- FinalizedBlockNumber: 995,
- TotalDifficulty: big.NewInt(2005),
- },
- },
- {
- State: nodeStateAlive,
- LatestChainInfo: ChainInfo{
- BlockNumber: 20,
- FinalizedBlockNumber: 10,
- TotalDifficulty: big.NewInt(9),
- },
- HighestUserObservations: ChainInfo{
- BlockNumber: 25,
- FinalizedBlockNumber: 15,
- TotalDifficulty: big.NewInt(14),
- },
- },
- {
- State: nodeStateAlive,
- LatestChainInfo: ChainInfo{
- BlockNumber: 19,
- FinalizedBlockNumber: 9,
- TotalDifficulty: big.NewInt(10),
- },
- HighestUserObservations: ChainInfo{
- BlockNumber: 24,
- FinalizedBlockNumber: 14,
- TotalDifficulty: big.NewInt(15),
- },
- },
- {
- State: nodeStateAlive,
- LatestChainInfo: ChainInfo{
- BlockNumber: 11,
- FinalizedBlockNumber: 1,
- TotalDifficulty: nil,
- },
- HighestUserObservations: ChainInfo{
- BlockNumber: 16,
- FinalizedBlockNumber: 6,
- TotalDifficulty: nil,
- },
- },
- },
- },
- }
-
- chainID := types.RandomID()
- mn := newTestMultiNode(t, multiNodeOpts{
- selectionMode: NodeSelectionModeRoundRobin,
- chainID: chainID,
- })
- for i := range testCases {
- tc := testCases[i]
- t.Run(tc.Name, func(t *testing.T) {
- for _, params := range tc.NodeParams {
- node := newMockNode[types.ID, multiNodeRPCClient](t)
- mn.primaryNodes = append(mn.primaryNodes, node)
- node.On("StateAndLatest").Return(params.State, params.LatestChainInfo)
- node.On("HighestUserObservations").Return(params.HighestUserObservations)
- }
-
- nNodes, latestChainInfo := mn.LatestChainInfo()
- assert.Equal(t, tc.ExpectedNLiveNodes, nNodes)
- assert.Equal(t, tc.ExpectedLatestChainInfo, latestChainInfo)
-
- highestChainInfo := mn.HighestUserObservations()
- assert.Equal(t, tc.ExpectedHighestUserObservations, highestChainInfo)
- })
- }
-}
diff --git a/common/client/node.go b/common/client/node.go
deleted file mode 100644
index 66161ac5d5f..00000000000
--- a/common/client/node.go
+++ /dev/null
@@ -1,336 +0,0 @@
-package client
-
-import (
- "context"
- "errors"
- "fmt"
- "net/url"
- "sync"
- "time"
-
- "github.com/prometheus/client_golang/prometheus"
- "github.com/prometheus/client_golang/prometheus/promauto"
-
- "github.com/smartcontractkit/chainlink-common/pkg/logger"
- "github.com/smartcontractkit/chainlink-common/pkg/services"
-
- "github.com/smartcontractkit/chainlink/v2/common/types"
-)
-
-const QueryTimeout = 10 * time.Second
-
-var errInvalidChainID = errors.New("invalid chain id")
-
-var (
- promPoolRPCNodeVerifies = promauto.NewCounterVec(prometheus.CounterOpts{
- Name: "pool_rpc_node_verifies",
- Help: "The total number of chain ID verifications for the given RPC node",
- }, []string{"network", "chainID", "nodeName"})
- promPoolRPCNodeVerifiesFailed = promauto.NewCounterVec(prometheus.CounterOpts{
- Name: "pool_rpc_node_verifies_failed",
- Help: "The total number of failed chain ID verifications for the given RPC node",
- }, []string{"network", "chainID", "nodeName"})
- promPoolRPCNodeVerifiesSuccess = promauto.NewCounterVec(prometheus.CounterOpts{
- Name: "pool_rpc_node_verifies_success",
- Help: "The total number of successful chain ID verifications for the given RPC node",
- }, []string{"network", "chainID", "nodeName"})
-)
-
-type NodeConfig interface {
- PollFailureThreshold() uint32
- PollInterval() time.Duration
- SelectionMode() string
- SyncThreshold() uint32
- NodeIsSyncingEnabled() bool
- FinalizedBlockPollInterval() time.Duration
- EnforceRepeatableRead() bool
- DeathDeclarationDelay() time.Duration
- NewHeadsPollInterval() time.Duration
-}
-
-type ChainConfig interface {
- NodeNoNewHeadsThreshold() time.Duration
- NoNewFinalizedHeadsThreshold() time.Duration
- FinalityDepth() uint32
- FinalityTagEnabled() bool
- FinalizedBlockOffset() uint32
-}
-
-type Node[
- CHAIN_ID types.ID,
- RPC any,
-] interface {
- // State returns most accurate state of the Node on the moment of call.
- // While some of the checks may be performed in the background and State may return cached value, critical, like
- // `FinalizedBlockOutOfSync`, must be executed upon every call.
- State() nodeState
- // StateAndLatest returns nodeState with the latest ChainInfo observed by Node during current lifecycle.
- StateAndLatest() (nodeState, ChainInfo)
- // HighestUserObservations - returns highest ChainInfo ever observed by underlying RPC excluding results of health check requests
- HighestUserObservations() ChainInfo
- SetPoolChainInfoProvider(PoolChainInfoProvider)
- // Name is a unique identifier for this node.
- Name() string
- // String - returns string representation of the node, useful for debugging (name + URLS used to connect to the RPC)
- String() string
- RPC() RPC
- // UnsubscribeAllExceptAliveLoop - closes all subscriptions except the aliveLoop subscription
- UnsubscribeAllExceptAliveLoop()
- ConfiguredChainID() CHAIN_ID
- // Order - returns priority order configured for the RPC
- Order() int32
- // Start - starts health checks
- Start(context.Context) error
- Close() error
-}
-
-type node[
- CHAIN_ID types.ID,
- HEAD Head,
- RPC RPCClient[CHAIN_ID, HEAD],
-] struct {
- services.StateMachine
- lfcLog logger.Logger
- name string
- id int
- chainID CHAIN_ID
- nodePoolCfg NodeConfig
- chainCfg ChainConfig
- order int32
- chainFamily string
-
- ws *url.URL
- http *url.URL
-
- rpc RPC
-
- stateMu sync.RWMutex // protects state* fields
- state nodeState
-
- poolInfoProvider PoolChainInfoProvider
-
- stopCh services.StopChan
- // wg waits for subsidiary goroutines
- wg sync.WaitGroup
-
- healthCheckSubs []types.Subscription
-}
-
-func NewNode[
- CHAIN_ID types.ID,
- HEAD Head,
- RPC RPCClient[CHAIN_ID, HEAD],
-](
- nodeCfg NodeConfig,
- chainCfg ChainConfig,
- lggr logger.Logger,
- wsuri *url.URL,
- httpuri *url.URL,
- name string,
- id int,
- chainID CHAIN_ID,
- nodeOrder int32,
- rpc RPC,
- chainFamily string,
-) Node[CHAIN_ID, RPC] {
- n := new(node[CHAIN_ID, HEAD, RPC])
- n.name = name
- n.id = id
- n.chainID = chainID
- n.nodePoolCfg = nodeCfg
- n.chainCfg = chainCfg
- n.order = nodeOrder
- if wsuri != nil {
- n.ws = wsuri
- }
- if httpuri != nil {
- n.http = httpuri
- }
- n.stopCh = make(services.StopChan)
- lggr = logger.Named(lggr, "Node")
- lggr = logger.With(lggr,
- "nodeTier", Primary.String(),
- "nodeName", name,
- "node", n.String(),
- "chainID", chainID,
- "nodeOrder", n.order,
- )
- n.lfcLog = logger.Named(lggr, "Lifecycle")
- n.rpc = rpc
- n.chainFamily = chainFamily
- return n
-}
-
-func (n *node[CHAIN_ID, HEAD, RPC]) String() string {
- s := fmt.Sprintf("(%s)%s", Primary.String(), n.name)
- if n.ws != nil {
- s = s + fmt.Sprintf(":%s", n.ws.String())
- }
- if n.http != nil {
- s = s + fmt.Sprintf(":%s", n.http.String())
- }
- return s
-}
-
-func (n *node[CHAIN_ID, HEAD, RPC]) ConfiguredChainID() (chainID CHAIN_ID) {
- return n.chainID
-}
-
-func (n *node[CHAIN_ID, HEAD, RPC]) Name() string {
- return n.name
-}
-
-func (n *node[CHAIN_ID, HEAD, RPC]) RPC() RPC {
- return n.rpc
-}
-
-// unsubscribeAllExceptAliveLoop is not thread-safe; it should only be called
-// while holding the stateMu lock.
-func (n *node[CHAIN_ID, HEAD, RPC]) unsubscribeAllExceptAliveLoop() {
- n.rpc.UnsubscribeAllExcept(n.healthCheckSubs...)
-}
-
-func (n *node[CHAIN_ID, HEAD, RPC]) UnsubscribeAllExceptAliveLoop() {
- n.stateMu.Lock()
- defer n.stateMu.Unlock()
- n.unsubscribeAllExceptAliveLoop()
-}
-
-func (n *node[CHAIN_ID, HEAD, RPC]) Close() error {
- return n.StopOnce(n.name, n.close)
-}
-
-func (n *node[CHAIN_ID, HEAD, RPC]) close() error {
- defer func() {
- n.wg.Wait()
- n.rpc.Close()
- }()
-
- n.stateMu.Lock()
- defer n.stateMu.Unlock()
-
- close(n.stopCh)
- n.state = nodeStateClosed
- return nil
-}
-
-// Start dials and verifies the node
-// Should only be called once in a node's lifecycle
-// Return value is necessary to conform to interface but this will never
-// actually return an error.
-func (n *node[CHAIN_ID, HEAD, RPC]) Start(startCtx context.Context) error {
- return n.StartOnce(n.name, func() error {
- n.start(startCtx)
- return nil
- })
-}
-
-// start initially dials the node and verifies chain ID
-// This spins off lifecycle goroutines.
-// Not thread-safe.
-// Node lifecycle is synchronous: only one goroutine should be running at a
-// time.
-func (n *node[CHAIN_ID, HEAD, RPC]) start(startCtx context.Context) {
- if n.state != nodeStateUndialed {
- panic(fmt.Sprintf("cannot dial node with state %v", n.state))
- }
-
- if err := n.rpc.Dial(startCtx); err != nil {
- n.lfcLog.Errorw("Dial failed: Node is unreachable", "err", err)
- n.declareUnreachable()
- return
- }
- n.setState(nodeStateDialed)
-
- state := n.verifyConn(startCtx, n.lfcLog)
- n.declareState(state)
-}
-
-// verifyChainID checks that connection to the node matches the given chain ID
-// Not thread-safe
-// Pure verifyChainID: does not mutate node "state" field.
-func (n *node[CHAIN_ID, HEAD, RPC]) verifyChainID(callerCtx context.Context, lggr logger.Logger) nodeState {
- promPoolRPCNodeVerifies.WithLabelValues(n.chainFamily, n.chainID.String(), n.name).Inc()
- promFailed := func() {
- promPoolRPCNodeVerifiesFailed.WithLabelValues(n.chainFamily, n.chainID.String(), n.name).Inc()
- }
-
- st := n.getCachedState()
- switch st {
- case nodeStateClosed:
- // The node is already closed, and any subsequent transition is invalid.
- // To make spotting such transitions a bit easier, return the invalid node state.
- return nodeStateLen
- case nodeStateDialed, nodeStateOutOfSync, nodeStateInvalidChainID, nodeStateSyncing:
- default:
- panic(fmt.Sprintf("cannot verify node in state %v", st))
- }
-
- var chainID CHAIN_ID
- var err error
- if chainID, err = n.rpc.ChainID(callerCtx); err != nil {
- promFailed()
- lggr.Errorw("Failed to verify chain ID for node", "err", err, "nodeState", n.getCachedState())
- return nodeStateUnreachable
- } else if chainID.String() != n.chainID.String() {
- promFailed()
- err = fmt.Errorf(
- "rpc ChainID doesn't match local chain ID: RPC ID=%s, local ID=%s, node name=%s: %w",
- chainID.String(),
- n.chainID.String(),
- n.name,
- errInvalidChainID,
- )
- lggr.Errorw("Failed to verify RPC node; remote endpoint returned the wrong chain ID", "err", err, "nodeState", n.getCachedState())
- return nodeStateInvalidChainID
- }
-
- promPoolRPCNodeVerifiesSuccess.WithLabelValues(n.chainFamily, n.chainID.String(), n.name).Inc()
-
- return nodeStateAlive
-}
-
-// createVerifiedConn - establishes new connection with the RPC and verifies that it's valid: chainID matches, and it's not syncing.
-// Returns desired state if one of the verifications fails. Otherwise, returns nodeStateAlive.
-func (n *node[CHAIN_ID, HEAD, RPC]) createVerifiedConn(ctx context.Context, lggr logger.Logger) nodeState {
- if err := n.rpc.Dial(ctx); err != nil {
- n.lfcLog.Errorw("Dial failed: Node is unreachable", "err", err, "nodeState", n.getCachedState())
- return nodeStateUnreachable
- }
-
- return n.verifyConn(ctx, lggr)
-}
-
-// verifyConn - verifies that current connection is valid: chainID matches, and it's not syncing.
-// Returns desired state if one of the verifications fails. Otherwise, returns nodeStateAlive.
-func (n *node[CHAIN_ID, HEAD, RPC]) verifyConn(ctx context.Context, lggr logger.Logger) nodeState {
- state := n.verifyChainID(ctx, lggr)
- if state != nodeStateAlive {
- return state
- }
-
- if n.nodePoolCfg.NodeIsSyncingEnabled() {
- isSyncing, err := n.rpc.IsSyncing(ctx)
- if err != nil {
- lggr.Errorw("Unexpected error while verifying RPC node synchronization status", "err", err, "nodeState", n.getCachedState())
- return nodeStateUnreachable
- }
-
- if isSyncing {
- lggr.Errorw("Verification failed: Node is syncing", "nodeState", n.getCachedState())
- return nodeStateSyncing
- }
- }
-
- return nodeStateAlive
-}
-
-func (n *node[CHAIN_ID, HEAD, RPC]) Order() int32 {
- return n.order
-}
-
-func (n *node[CHAIN_ID, HEAD, RPC]) newCtx() (context.Context, context.CancelFunc) {
- ctx, cancel := n.stopCh.NewCtx()
- ctx = CtxAddHealthCheckFlag(ctx)
- return ctx, cancel
-}
diff --git a/common/client/node_fsm.go b/common/client/node_fsm.go
deleted file mode 100644
index b707e9f4375..00000000000
--- a/common/client/node_fsm.go
+++ /dev/null
@@ -1,377 +0,0 @@
-package client
-
-import (
- "fmt"
-
- "github.com/prometheus/client_golang/prometheus"
- "github.com/prometheus/client_golang/prometheus/promauto"
-)
-
-var (
- promPoolRPCNodeTransitionsToAlive = promauto.NewCounterVec(prometheus.CounterOpts{
- Name: "pool_rpc_node_num_transitions_to_alive",
- Help: transitionString(nodeStateAlive),
- }, []string{"chainID", "nodeName"})
- promPoolRPCNodeTransitionsToInSync = promauto.NewCounterVec(prometheus.CounterOpts{
- Name: "pool_rpc_node_num_transitions_to_in_sync",
- Help: fmt.Sprintf("%s to %s", transitionString(nodeStateOutOfSync), nodeStateAlive),
- }, []string{"chainID", "nodeName"})
- promPoolRPCNodeTransitionsToOutOfSync = promauto.NewCounterVec(prometheus.CounterOpts{
- Name: "pool_rpc_node_num_transitions_to_out_of_sync",
- Help: transitionString(nodeStateOutOfSync),
- }, []string{"chainID", "nodeName"})
- promPoolRPCNodeTransitionsToUnreachable = promauto.NewCounterVec(prometheus.CounterOpts{
- Name: "pool_rpc_node_num_transitions_to_unreachable",
- Help: transitionString(nodeStateUnreachable),
- }, []string{"chainID", "nodeName"})
- promPoolRPCNodeTransitionsToInvalidChainID = promauto.NewCounterVec(prometheus.CounterOpts{
- Name: "pool_rpc_node_num_transitions_to_invalid_chain_id",
- Help: transitionString(nodeStateInvalidChainID),
- }, []string{"chainID", "nodeName"})
- promPoolRPCNodeTransitionsToUnusable = promauto.NewCounterVec(prometheus.CounterOpts{
- Name: "pool_rpc_node_num_transitions_to_unusable",
- Help: transitionString(nodeStateUnusable),
- }, []string{"chainID", "nodeName"})
- promPoolRPCNodeTransitionsToSyncing = promauto.NewCounterVec(prometheus.CounterOpts{
- Name: "pool_rpc_node_num_transitions_to_syncing",
- Help: transitionString(nodeStateSyncing),
- }, []string{"chainID", "nodeName"})
-)
-
-// nodeState represents the current state of the node
-// Node is a FSM (finite state machine)
-type nodeState int
-
-func (n nodeState) String() string {
- switch n {
- case nodeStateUndialed:
- return "Undialed"
- case nodeStateDialed:
- return "Dialed"
- case nodeStateInvalidChainID:
- return "InvalidChainID"
- case nodeStateAlive:
- return "Alive"
- case nodeStateUnreachable:
- return "Unreachable"
- case nodeStateUnusable:
- return "Unusable"
- case nodeStateOutOfSync:
- return "OutOfSync"
- case nodeStateClosed:
- return "Closed"
- case nodeStateSyncing:
- return "Syncing"
- case nodeStateFinalizedBlockOutOfSync:
- return "FinalizedBlockOutOfSync"
- default:
- return fmt.Sprintf("nodeState(%d)", n)
- }
-}
-
-// GoString prints a prettier state
-func (n nodeState) GoString() string {
- return fmt.Sprintf("nodeState%s(%d)", n.String(), n)
-}
-
-const (
- // nodeStateUndialed is the first state of a virgin node
- nodeStateUndialed = nodeState(iota)
- // nodeStateDialed is after a node has successfully dialed but before it has verified the correct chain ID
- nodeStateDialed
- // nodeStateInvalidChainID is after chain ID verification failed
- nodeStateInvalidChainID
- // nodeStateAlive is a healthy node after chain ID verification succeeded
- nodeStateAlive
- // nodeStateUnreachable is a node that cannot be dialed or has disconnected
- nodeStateUnreachable
- // nodeStateOutOfSync is a node that is accepting connections but exceeded
- // the failure threshold without sending any new heads. It will be
- // disconnected, then put into a revive loop and re-awakened after redial
- // if a new head arrives
- nodeStateOutOfSync
- // nodeStateUnusable is a sendonly node that has an invalid URL that can never be reached
- nodeStateUnusable
- // nodeStateClosed is after the connection has been closed and the node is at the end of its lifecycle
- nodeStateClosed
- // nodeStateSyncing is a node that is actively back-filling blockchain. Usually, it's a newly set up node that is
- // still syncing the chain. The main difference from `nodeStateOutOfSync` is that it represents state relative
- // to other primary nodes configured in the MultiNode. In contrast, `nodeStateSyncing` represents the internal state of
- // the node (RPC).
- nodeStateSyncing
- // nodeStateFinalizedBlockOutOfSync - node is lagging behind on latest finalized block
- nodeStateFinalizedBlockOutOfSync
- // nodeStateLen tracks the number of states
- nodeStateLen
-)
-
-// allNodeStates represents all possible states a node can be in
-var allNodeStates []nodeState
-
-func init() {
- for s := nodeState(0); s < nodeStateLen; s++ {
- allNodeStates = append(allNodeStates, s)
- }
-}
-
-// FSM methods
-
-// State allows reading the current state of the node.
-func (n *node[CHAIN_ID, HEAD, RPC]) State() nodeState {
- n.stateMu.RLock()
- defer n.stateMu.RUnlock()
- return n.recalculateState()
-}
-
-func (n *node[CHAIN_ID, HEAD, RPC]) getCachedState() nodeState {
- n.stateMu.RLock()
- defer n.stateMu.RUnlock()
- return n.state
-}
-
-func (n *node[CHAIN_ID, HEAD, RPC]) recalculateState() nodeState {
- if n.state != nodeStateAlive {
- return n.state
- }
-
- // double check that node is not lagging on finalized block
- if n.nodePoolCfg.EnforceRepeatableRead() && n.isFinalizedBlockOutOfSync() {
- return nodeStateFinalizedBlockOutOfSync
- }
-
- return nodeStateAlive
-}
-
-func (n *node[CHAIN_ID, HEAD, RPC]) isFinalizedBlockOutOfSync() bool {
- if n.poolInfoProvider == nil {
- return false
- }
-
- highestObservedByCaller := n.poolInfoProvider.HighestUserObservations()
- latest, rpcHighest := n.rpc.GetInterceptedChainInfo()
- isOutOfSync := false
- if n.chainCfg.FinalityTagEnabled() {
- isOutOfSync = latest.FinalizedBlockNumber < highestObservedByCaller.FinalizedBlockNumber-int64(n.chainCfg.FinalizedBlockOffset())
- } else {
- isOutOfSync = latest.BlockNumber < highestObservedByCaller.BlockNumber-int64(n.chainCfg.FinalizedBlockOffset())
- }
-
- if isOutOfSync {
- n.lfcLog.Debugw("finalized block is out of sync", "rpcLatestChainInfo", latest, "rpcHighest", rpcHighest, "highestObservedByCaller", highestObservedByCaller)
- }
-
- return isOutOfSync
-}
-
-// StateAndLatest returns nodeState with the latest ChainInfo observed by Node during current lifecycle.
-func (n *node[CHAIN_ID, HEAD, RPC]) StateAndLatest() (nodeState, ChainInfo) {
- n.stateMu.RLock()
- defer n.stateMu.RUnlock()
- latest, _ := n.rpc.GetInterceptedChainInfo()
- return n.recalculateState(), latest
-}
-
-// HighestUserObservations - returns highest ChainInfo ever observed by external user of the Node
-func (n *node[CHAIN_ID, HEAD, RPC]) HighestUserObservations() ChainInfo {
- _, highestUserObservations := n.rpc.GetInterceptedChainInfo()
- return highestUserObservations
-}
-func (n *node[CHAIN_ID, HEAD, RPC]) SetPoolChainInfoProvider(poolInfoProvider PoolChainInfoProvider) {
- n.poolInfoProvider = poolInfoProvider
-}
-
-// setState is only used by internal state management methods.
-// This is low-level; care should be taken by the caller to ensure the new state is a valid transition.
-// State changes should always be synchronous: only one goroutine at a time should change state.
-// n.stateMu should not be locked for long periods of time because external clients expect a timely response from n.State()
-func (n *node[CHAIN_ID, HEAD, RPC]) setState(s nodeState) {
- n.stateMu.Lock()
- defer n.stateMu.Unlock()
- n.state = s
-}
-
-// declareXXX methods change the state and pass conrol off the new state
-// management goroutine
-
-func (n *node[CHAIN_ID, HEAD, RPC]) declareAlive() {
- n.transitionToAlive(func() {
- n.lfcLog.Infow("RPC Node is online", "nodeState", n.state)
- n.wg.Add(1)
- go n.aliveLoop()
- })
-}
-
-func (n *node[CHAIN_ID, HEAD, RPC]) transitionToAlive(fn func()) {
- promPoolRPCNodeTransitionsToAlive.WithLabelValues(n.chainID.String(), n.name).Inc()
- n.stateMu.Lock()
- defer n.stateMu.Unlock()
- if n.state == nodeStateClosed {
- return
- }
- switch n.state {
- case nodeStateDialed, nodeStateInvalidChainID, nodeStateSyncing:
- n.state = nodeStateAlive
- default:
- panic(transitionFail(n.state, nodeStateAlive))
- }
- fn()
-}
-
-// declareInSync puts a node back into Alive state, allowing it to be used by
-// pool consumers again
-func (n *node[CHAIN_ID, HEAD, RPC]) declareInSync() {
- n.transitionToInSync(func() {
- n.lfcLog.Infow("RPC Node is back in sync", "nodeState", n.state)
- n.wg.Add(1)
- go n.aliveLoop()
- })
-}
-
-func (n *node[CHAIN_ID, HEAD, RPC]) transitionToInSync(fn func()) {
- promPoolRPCNodeTransitionsToAlive.WithLabelValues(n.chainID.String(), n.name).Inc()
- promPoolRPCNodeTransitionsToInSync.WithLabelValues(n.chainID.String(), n.name).Inc()
- n.stateMu.Lock()
- defer n.stateMu.Unlock()
- if n.state == nodeStateClosed {
- return
- }
- switch n.state {
- case nodeStateOutOfSync, nodeStateSyncing:
- n.state = nodeStateAlive
- default:
- panic(transitionFail(n.state, nodeStateAlive))
- }
- fn()
-}
-
-// declareOutOfSync puts a node into OutOfSync state, disconnecting all current
-// clients and making it unavailable for use until back in-sync.
-func (n *node[CHAIN_ID, HEAD, RPC]) declareOutOfSync(syncIssues syncStatus) {
- n.transitionToOutOfSync(func() {
- n.lfcLog.Errorw("RPC Node is out of sync", "nodeState", n.state, "syncIssues", syncIssues)
- n.wg.Add(1)
- go n.outOfSyncLoop(syncIssues)
- })
-}
-
-func (n *node[CHAIN_ID, HEAD, RPC]) transitionToOutOfSync(fn func()) {
- promPoolRPCNodeTransitionsToOutOfSync.WithLabelValues(n.chainID.String(), n.name).Inc()
- n.stateMu.Lock()
- defer n.stateMu.Unlock()
- if n.state == nodeStateClosed {
- return
- }
- switch n.state {
- case nodeStateAlive:
- n.rpc.Close()
- n.state = nodeStateOutOfSync
- default:
- panic(transitionFail(n.state, nodeStateOutOfSync))
- }
- fn()
-}
-
-func (n *node[CHAIN_ID, HEAD, RPC]) declareUnreachable() {
- n.transitionToUnreachable(func() {
- n.lfcLog.Errorw("RPC Node is unreachable", "nodeState", n.state)
- n.wg.Add(1)
- go n.unreachableLoop()
- })
-}
-
-func (n *node[CHAIN_ID, HEAD, RPC]) transitionToUnreachable(fn func()) {
- promPoolRPCNodeTransitionsToUnreachable.WithLabelValues(n.chainID.String(), n.name).Inc()
- n.stateMu.Lock()
- defer n.stateMu.Unlock()
- if n.state == nodeStateClosed {
- return
- }
- switch n.state {
- case nodeStateUndialed, nodeStateDialed, nodeStateAlive, nodeStateOutOfSync, nodeStateInvalidChainID, nodeStateSyncing:
- n.rpc.Close()
- n.state = nodeStateUnreachable
- default:
- panic(transitionFail(n.state, nodeStateUnreachable))
- }
- fn()
-}
-
-func (n *node[CHAIN_ID, HEAD, RPC]) declareState(state nodeState) {
- if n.getCachedState() == nodeStateClosed {
- return
- }
- switch state {
- case nodeStateInvalidChainID:
- n.declareInvalidChainID()
- case nodeStateUnreachable:
- n.declareUnreachable()
- case nodeStateSyncing:
- n.declareSyncing()
- case nodeStateAlive:
- n.declareAlive()
- default:
- panic(fmt.Sprintf("%#v state declaration is not implemented", state))
- }
-}
-
-func (n *node[CHAIN_ID, HEAD, RPC]) declareInvalidChainID() {
- n.transitionToInvalidChainID(func() {
- n.lfcLog.Errorw("RPC Node has the wrong chain ID", "nodeState", n.state)
- n.wg.Add(1)
- go n.invalidChainIDLoop()
- })
-}
-
-func (n *node[CHAIN_ID, HEAD, RPC]) transitionToInvalidChainID(fn func()) {
- promPoolRPCNodeTransitionsToInvalidChainID.WithLabelValues(n.chainID.String(), n.name).Inc()
- n.stateMu.Lock()
- defer n.stateMu.Unlock()
- if n.state == nodeStateClosed {
- return
- }
- switch n.state {
- case nodeStateDialed, nodeStateOutOfSync, nodeStateSyncing:
- n.rpc.Close()
- n.state = nodeStateInvalidChainID
- default:
- panic(transitionFail(n.state, nodeStateInvalidChainID))
- }
- fn()
-}
-
-func (n *node[CHAIN_ID, HEAD, RPC]) declareSyncing() {
- n.transitionToSyncing(func() {
- n.lfcLog.Errorw("RPC Node is syncing", "nodeState", n.state)
- n.wg.Add(1)
- go n.syncingLoop()
- })
-}
-
-func (n *node[CHAIN_ID, HEAD, RPC]) transitionToSyncing(fn func()) {
- promPoolRPCNodeTransitionsToSyncing.WithLabelValues(n.chainID.String(), n.name).Inc()
- n.stateMu.Lock()
- defer n.stateMu.Unlock()
- if n.state == nodeStateClosed {
- return
- }
- switch n.state {
- case nodeStateDialed, nodeStateOutOfSync, nodeStateInvalidChainID:
- n.rpc.Close()
- n.state = nodeStateSyncing
- default:
- panic(transitionFail(n.state, nodeStateSyncing))
- }
-
- if !n.nodePoolCfg.NodeIsSyncingEnabled() {
- panic("unexpected transition to nodeStateSyncing, while it's disabled")
- }
- fn()
-}
-
-func transitionString(state nodeState) string {
- return fmt.Sprintf("Total number of times node has transitioned to %s", state)
-}
-
-func transitionFail(from nodeState, to nodeState) string {
- return fmt.Sprintf("cannot transition from %#v to %#v", from, to)
-}
diff --git a/common/client/node_fsm_test.go b/common/client/node_fsm_test.go
deleted file mode 100644
index 93460d934a3..00000000000
--- a/common/client/node_fsm_test.go
+++ /dev/null
@@ -1,131 +0,0 @@
-package client
-
-import (
- "slices"
- "strconv"
- "testing"
-
- "github.com/stretchr/testify/assert"
-
- "github.com/smartcontractkit/chainlink/v2/common/types"
-)
-
-type fnMock struct{ calls int }
-
-func (fm *fnMock) Fn() {
- fm.calls++
-}
-
-func (fm *fnMock) AssertNotCalled(t *testing.T) {
- assert.Equal(t, 0, fm.calls)
-}
-
-func (fm *fnMock) AssertCalled(t *testing.T) {
- assert.Greater(t, fm.calls, 0)
-}
-
-func TestUnit_Node_StateTransitions(t *testing.T) {
- t.Parallel()
-
- t.Run("setState", func(t *testing.T) {
- n := newTestNode(t, testNodeOpts{rpc: nil, config: testNodeConfig{nodeIsSyncingEnabled: true}})
- assert.Equal(t, nodeStateUndialed, n.State())
- n.setState(nodeStateAlive)
- assert.Equal(t, nodeStateAlive, n.State())
- n.setState(nodeStateUndialed)
- assert.Equal(t, nodeStateUndialed, n.State())
- })
-
- t.Run("transitionToAlive", func(t *testing.T) {
- const destinationState = nodeStateAlive
- allowedStates := []nodeState{nodeStateDialed, nodeStateInvalidChainID, nodeStateSyncing}
- rpc := newMockRPCClient[types.ID, Head](t)
- testTransition(t, rpc, testNode.transitionToAlive, destinationState, allowedStates...)
- })
-
- t.Run("transitionToInSync", func(t *testing.T) {
- const destinationState = nodeStateAlive
- allowedStates := []nodeState{nodeStateOutOfSync, nodeStateSyncing}
- rpc := newMockRPCClient[types.ID, Head](t)
- testTransition(t, rpc, testNode.transitionToInSync, destinationState, allowedStates...)
- })
- t.Run("transitionToOutOfSync", func(t *testing.T) {
- const destinationState = nodeStateOutOfSync
- allowedStates := []nodeState{nodeStateAlive}
- rpc := newMockRPCClient[types.ID, Head](t)
- rpc.On("Close")
- testTransition(t, rpc, testNode.transitionToOutOfSync, destinationState, allowedStates...)
- })
- t.Run("transitionToUnreachable", func(t *testing.T) {
- const destinationState = nodeStateUnreachable
- allowedStates := []nodeState{nodeStateUndialed, nodeStateDialed, nodeStateAlive, nodeStateOutOfSync, nodeStateInvalidChainID, nodeStateSyncing}
- rpc := newMockRPCClient[types.ID, Head](t)
- rpc.On("Close")
- testTransition(t, rpc, testNode.transitionToUnreachable, destinationState, allowedStates...)
- })
- t.Run("transitionToInvalidChain", func(t *testing.T) {
- const destinationState = nodeStateInvalidChainID
- allowedStates := []nodeState{nodeStateDialed, nodeStateOutOfSync, nodeStateSyncing}
- rpc := newMockRPCClient[types.ID, Head](t)
- rpc.On("Close")
- testTransition(t, rpc, testNode.transitionToInvalidChainID, destinationState, allowedStates...)
- })
- t.Run("transitionToSyncing", func(t *testing.T) {
- const destinationState = nodeStateSyncing
- allowedStates := []nodeState{nodeStateDialed, nodeStateOutOfSync, nodeStateInvalidChainID}
- rpc := newMockRPCClient[types.ID, Head](t)
- rpc.On("Close")
- testTransition(t, rpc, testNode.transitionToSyncing, destinationState, allowedStates...)
- })
- t.Run("transitionToSyncing panics if nodeIsSyncing is disabled", func(t *testing.T) {
- rpc := newMockRPCClient[types.ID, Head](t)
- rpc.On("Close")
- node := newTestNode(t, testNodeOpts{rpc: rpc})
- node.setState(nodeStateDialed)
- fn := new(fnMock)
- defer fn.AssertNotCalled(t)
- assert.PanicsWithValue(t, "unexpected transition to nodeStateSyncing, while it's disabled", func() {
- node.transitionToSyncing(fn.Fn)
- })
- })
-}
-
-func testTransition(t *testing.T, rpc *mockRPCClient[types.ID, Head], transition func(node testNode, fn func()), destinationState nodeState, allowedStates ...nodeState) {
- node := newTestNode(t, testNodeOpts{rpc: rpc, config: testNodeConfig{nodeIsSyncingEnabled: true}})
- for _, allowedState := range allowedStates {
- m := new(fnMock)
- node.setState(allowedState)
- transition(node, m.Fn)
- assert.Equal(t, destinationState, node.State(), "Expected node to successfully transition from %s to %s state", allowedState, destinationState)
- m.AssertCalled(t)
- }
- // noop on attempt to transition from Closed state
- m := new(fnMock)
- node.setState(nodeStateClosed)
- transition(node, m.Fn)
- m.AssertNotCalled(t)
- assert.Equal(t, nodeStateClosed, node.State(), "Expected node to remain in closed state on transition attempt")
-
- for _, nodeState := range allNodeStates {
- if slices.Contains(allowedStates, nodeState) || nodeState == nodeStateClosed {
- continue
- }
-
- m := new(fnMock)
- node.setState(nodeState)
- assert.Panics(t, func() {
- transition(node, m.Fn)
- }, "Expected transition from `%s` to `%s` to panic", nodeState, destinationState)
- m.AssertNotCalled(t)
- assert.Equal(t, nodeState, node.State(), "Expected node to remain in initial state on invalid transition")
- }
-}
-
-func TestNodeState_String(t *testing.T) {
- t.Run("Ensure all states are meaningful when converted to string", func(t *testing.T) {
- for _, ns := range allNodeStates {
- // ensure that string representation is not nodeState(%d)
- assert.NotContains(t, ns.String(), strconv.FormatInt(int64(ns), 10), "Expected node state to have readable name")
- }
- })
-}
diff --git a/common/client/node_lifecycle.go b/common/client/node_lifecycle.go
deleted file mode 100644
index 6ec6a598eb2..00000000000
--- a/common/client/node_lifecycle.go
+++ /dev/null
@@ -1,700 +0,0 @@
-package client
-
-import (
- "context"
- "fmt"
- "math"
- "math/big"
- "time"
-
- "github.com/prometheus/client_golang/prometheus"
- "github.com/prometheus/client_golang/prometheus/promauto"
-
- "github.com/smartcontractkit/chainlink-common/pkg/logger"
- "github.com/smartcontractkit/chainlink-common/pkg/utils"
- bigmath "github.com/smartcontractkit/chainlink-common/pkg/utils/big_math"
-
- iutils "github.com/smartcontractkit/chainlink/v2/common/internal/utils"
- "github.com/smartcontractkit/chainlink/v2/common/types"
-)
-
-var (
- promPoolRPCNodeHighestSeenBlock = promauto.NewGaugeVec(prometheus.GaugeOpts{
- Name: "pool_rpc_node_highest_seen_block",
- Help: "The highest seen block for the given RPC node",
- }, []string{"chainID", "nodeName"})
- promPoolRPCNodeHighestFinalizedBlock = promauto.NewGaugeVec(prometheus.GaugeOpts{
- Name: "pool_rpc_node_highest_finalized_block",
- Help: "The highest seen finalized block for the given RPC node",
- }, []string{"chainID", "nodeName"})
- promPoolRPCNodeNumSeenBlocks = promauto.NewCounterVec(prometheus.CounterOpts{
- Name: "pool_rpc_node_num_seen_blocks",
- Help: "The total number of new blocks seen by the given RPC node",
- }, []string{"chainID", "nodeName"})
- promPoolRPCNodePolls = promauto.NewCounterVec(prometheus.CounterOpts{
- Name: "pool_rpc_node_polls_total",
- Help: "The total number of poll checks for the given RPC node",
- }, []string{"chainID", "nodeName"})
- promPoolRPCNodePollsFailed = promauto.NewCounterVec(prometheus.CounterOpts{
- Name: "pool_rpc_node_polls_failed",
- Help: "The total number of failed poll checks for the given RPC node",
- }, []string{"chainID", "nodeName"})
- promPoolRPCNodePollsSuccess = promauto.NewCounterVec(prometheus.CounterOpts{
- Name: "pool_rpc_node_polls_success",
- Help: "The total number of successful poll checks for the given RPC node",
- }, []string{"chainID", "nodeName"})
-)
-
-// zombieNodeCheckInterval controls how often to re-check to see if we need to
-// state change in case we have to force a state transition due to no available
-// nodes.
-// NOTE: This only applies to out-of-sync nodes if they are the last available node
-func zombieNodeCheckInterval(noNewHeadsThreshold time.Duration) time.Duration {
- interval := noNewHeadsThreshold
- if interval <= 0 || interval > QueryTimeout {
- interval = QueryTimeout
- }
- return utils.WithJitter(interval)
-}
-
-const (
- msgCannotDisable = "but cannot disable this connection because there are no other RPC endpoints, or all other RPC endpoints are dead."
- msgDegradedState = "Chainlink is now operating in a degraded state and urgent action is required to resolve the issue"
-)
-
-// Node is a FSM
-// Each state has a loop that goes with it, which monitors the node and moves it into another state as necessary.
-// Only one loop must run at a time.
-// Each loop passes control onto the next loop as it exits, except when the node is Closed which terminates the loop permanently.
-
-// This handles node lifecycle for the ALIVE state
-// Should only be run ONCE per node, after a successful Dial
-func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() {
- defer n.wg.Done()
- ctx, cancel := n.newCtx()
- defer cancel()
-
- {
- // sanity check
- state := n.getCachedState()
- switch state {
- case nodeStateAlive:
- case nodeStateClosed:
- return
- default:
- panic(fmt.Sprintf("aliveLoop can only run for node in Alive state, got: %s", state))
- }
- }
-
- noNewHeadsTimeoutThreshold := n.chainCfg.NodeNoNewHeadsThreshold()
- noNewFinalizedBlocksTimeoutThreshold := n.chainCfg.NoNewFinalizedHeadsThreshold()
- pollFailureThreshold := n.nodePoolCfg.PollFailureThreshold()
- pollInterval := n.nodePoolCfg.PollInterval()
-
- lggr := logger.Sugared(n.lfcLog).Named("Alive").With("noNewHeadsTimeoutThreshold", noNewHeadsTimeoutThreshold, "pollInterval", pollInterval, "pollFailureThreshold", pollFailureThreshold)
- lggr.Tracew("Alive loop starting", "nodeState", n.getCachedState())
-
- headsSub, err := n.registerNewSubscription(ctx, lggr.With("subscriptionType", "heads"),
- n.chainCfg.NodeNoNewHeadsThreshold(), n.rpc.SubscribeToHeads)
- if err != nil {
- lggr.Errorw("Initial subscribe for heads failed", "nodeState", n.getCachedState(), "err", err)
- n.declareUnreachable()
- return
- }
-
- defer n.unsubscribeHealthChecks()
-
- var pollCh <-chan time.Time
- if pollInterval > 0 {
- lggr.Debug("Polling enabled")
- pollT := time.NewTicker(pollInterval)
- defer pollT.Stop()
- pollCh = pollT.C
- if pollFailureThreshold > 0 {
- // polling can be enabled with no threshold to enable polling but
- // the node will not be marked offline regardless of the number of
- // poll failures
- lggr.Debug("Polling liveness checking enabled")
- }
- } else {
- lggr.Debug("Polling disabled")
- }
-
- var finalizedHeadsSub headSubscription[HEAD]
- if n.chainCfg.FinalityTagEnabled() {
- finalizedHeadsSub, err = n.registerNewSubscription(ctx, lggr.With("subscriptionType", "finalizedHeads"),
- n.chainCfg.NoNewFinalizedHeadsThreshold(), n.rpc.SubscribeToFinalizedHeads)
- if err != nil {
- lggr.Errorw("Failed to subscribe to finalized heads", "err", err)
- n.declareUnreachable()
- return
- }
- }
-
- // Get the latest chain info to use as local highest
- localHighestChainInfo, _ := n.rpc.GetInterceptedChainInfo()
- var pollFailures uint32
-
- for {
- select {
- case <-ctx.Done():
- return
- case <-pollCh:
- promPoolRPCNodePolls.WithLabelValues(n.chainID.String(), n.name).Inc()
- lggr.Tracew("Pinging RPC", "nodeState", n.State(), "pollFailures", pollFailures)
- pollCtx, cancel := context.WithTimeout(ctx, pollInterval)
- err = n.RPC().Ping(pollCtx)
- cancel()
- if err != nil {
- // prevent overflow
- if pollFailures < math.MaxUint32 {
- promPoolRPCNodePollsFailed.WithLabelValues(n.chainID.String(), n.name).Inc()
- pollFailures++
- }
- lggr.Warnw(fmt.Sprintf("Poll failure, RPC endpoint %s failed to respond properly", n.String()), "err", err, "pollFailures", pollFailures, "nodeState", n.getCachedState())
- } else {
- lggr.Debugw("Ping successful", "nodeState", n.State())
- promPoolRPCNodePollsSuccess.WithLabelValues(n.chainID.String(), n.name).Inc()
- pollFailures = 0
- }
- if pollFailureThreshold > 0 && pollFailures >= pollFailureThreshold {
- lggr.Errorw(fmt.Sprintf("RPC endpoint failed to respond to %d consecutive polls", pollFailures), "pollFailures", pollFailures, "nodeState", n.getCachedState())
- if n.poolInfoProvider != nil {
- if l, _ := n.poolInfoProvider.LatestChainInfo(); l < 2 {
- lggr.Criticalf("RPC endpoint failed to respond to polls; %s %s", msgCannotDisable, msgDegradedState)
- continue
- }
- }
- n.declareUnreachable()
- return
- }
- if outOfSync, liveNodes := n.isOutOfSyncWithPool(); outOfSync {
- // note: there must be another live node for us to be out of sync
- if liveNodes < 2 {
- lggr.Criticalf("RPC endpoint has fallen behind; %s %s", msgCannotDisable, msgDegradedState)
- continue
- }
- n.declareOutOfSync(syncStatusNotInSyncWithPool)
- return
- }
- case bh, open := <-headsSub.Heads:
- if !open {
- lggr.Errorw("Subscription channel unexpectedly closed", "nodeState", n.getCachedState())
- n.declareUnreachable()
- return
- }
- receivedNewHead := n.onNewHead(lggr, &localHighestChainInfo, bh)
- if receivedNewHead && noNewHeadsTimeoutThreshold > 0 {
- headsSub.ResetTimer(noNewHeadsTimeoutThreshold)
- }
- case err = <-headsSub.Errors:
- lggr.Errorw("Subscription was terminated", "err", err, "nodeState", n.getCachedState())
- n.declareUnreachable()
- return
- case <-headsSub.NoNewHeads:
- // We haven't received a head on the channel for at least the
- // threshold amount of time, mark it broken
- lggr.Errorw(fmt.Sprintf("RPC endpoint detected out of sync; no new heads received for %s (last head received was %v)", noNewHeadsTimeoutThreshold, localHighestChainInfo.BlockNumber), "nodeState", n.getCachedState(), "latestReceivedBlockNumber", localHighestChainInfo.BlockNumber, "noNewHeadsTimeoutThreshold", noNewHeadsTimeoutThreshold)
- if n.poolInfoProvider != nil {
- if l, _ := n.poolInfoProvider.LatestChainInfo(); l < 2 {
- lggr.Criticalf("RPC endpoint detected out of sync; %s %s", msgCannotDisable, msgDegradedState)
- // We don't necessarily want to wait the full timeout to check again, we should
- // check regularly and log noisily in this state
- headsSub.ResetTimer(zombieNodeCheckInterval(noNewHeadsTimeoutThreshold))
- continue
- }
- }
- n.declareOutOfSync(syncStatusNoNewHead)
- return
- case latestFinalized, open := <-finalizedHeadsSub.Heads:
- if !open {
- lggr.Errorw("Finalized heads subscription channel unexpectedly closed")
- n.declareUnreachable()
- return
- }
-
- receivedNewHead := n.onNewFinalizedHead(lggr, &localHighestChainInfo, latestFinalized)
- if receivedNewHead && noNewFinalizedBlocksTimeoutThreshold > 0 {
- finalizedHeadsSub.ResetTimer(noNewFinalizedBlocksTimeoutThreshold)
- }
- case <-finalizedHeadsSub.NoNewHeads:
- // We haven't received a finalized head on the channel for at least the
- // threshold amount of time, mark it broken
- lggr.Errorw(fmt.Sprintf("RPC's finalized state is out of sync; no new finalized heads received for %s (last finalized head received was %v)", noNewFinalizedBlocksTimeoutThreshold, localHighestChainInfo.FinalizedBlockNumber), "latestReceivedBlockNumber", localHighestChainInfo.BlockNumber)
- if n.poolInfoProvider != nil {
- if l, _ := n.poolInfoProvider.LatestChainInfo(); l < 2 {
- lggr.Criticalf("RPC's finalized state is out of sync; %s %s", msgCannotDisable, msgDegradedState)
- // We don't necessarily want to wait the full timeout to check again, we should
- // check regularly and log noisily in this state
- finalizedHeadsSub.ResetTimer(zombieNodeCheckInterval(noNewFinalizedBlocksTimeoutThreshold))
- continue
- }
- }
- n.declareOutOfSync(syncStatusNoNewFinalizedHead)
- return
- case <-finalizedHeadsSub.Errors:
- lggr.Errorw("Finalized heads subscription was terminated", "err", err)
- n.declareUnreachable()
- return
- }
- }
-}
-
-func (n *node[CHAIN_ID, HEAD, RPC]) unsubscribeHealthChecks() {
- n.stateMu.Lock()
- for _, sub := range n.healthCheckSubs {
- sub.Unsubscribe()
- }
- n.healthCheckSubs = []types.Subscription{}
- n.stateMu.Unlock()
-}
-
-type headSubscription[HEAD any] struct {
- Heads <-chan HEAD
- Errors <-chan error
- NoNewHeads <-chan time.Time
-
- noNewHeadsTicker *time.Ticker
- sub types.Subscription
- cleanUpTasks []func()
-}
-
-func (sub *headSubscription[HEAD]) ResetTimer(duration time.Duration) {
- sub.noNewHeadsTicker.Reset(duration)
-}
-
-func (sub *headSubscription[HEAD]) Unsubscribe() {
- for _, doCleanUp := range sub.cleanUpTasks {
- doCleanUp()
- }
-}
-
-func (n *node[CHAIN_ID, HEAD, PRC]) registerNewSubscription(ctx context.Context, lggr logger.SugaredLogger,
- noNewDataThreshold time.Duration, newSub func(ctx context.Context) (<-chan HEAD, types.Subscription, error)) (headSubscription[HEAD], error) {
- result := headSubscription[HEAD]{}
- var err error
- var sub types.Subscription
- result.Heads, sub, err = newSub(ctx)
- if err != nil {
- return result, err
- }
-
- result.Errors = sub.Err()
- lggr.Debug("Successfully subscribed")
-
- result.sub = sub
- n.stateMu.Lock()
- n.healthCheckSubs = append(n.healthCheckSubs, sub)
- n.stateMu.Unlock()
-
- result.cleanUpTasks = append(result.cleanUpTasks, sub.Unsubscribe)
-
- if noNewDataThreshold > 0 {
- lggr.Debugw("Subscription liveness checking enabled")
- result.noNewHeadsTicker = time.NewTicker(noNewDataThreshold)
- result.NoNewHeads = result.noNewHeadsTicker.C
- result.cleanUpTasks = append(result.cleanUpTasks, result.noNewHeadsTicker.Stop)
- } else {
- lggr.Debug("Subscription liveness checking disabled")
- }
-
- return result, nil
-}
-
-func (n *node[CHAIN_ID, HEAD, RPC]) onNewFinalizedHead(lggr logger.SugaredLogger, chainInfo *ChainInfo, latestFinalized HEAD) bool {
- if !latestFinalized.IsValid() {
- lggr.Warn("Latest finalized block is not valid")
- return false
- }
-
- latestFinalizedBN := latestFinalized.BlockNumber()
- lggr.Debugw("Got latest finalized head", "latestFinalized", latestFinalized)
- if latestFinalizedBN <= chainInfo.FinalizedBlockNumber {
- lggr.Debugw("Ignoring previously seen finalized block number")
- return false
- }
-
- promPoolRPCNodeHighestFinalizedBlock.WithLabelValues(n.chainID.String(), n.name).Set(float64(latestFinalizedBN))
- chainInfo.FinalizedBlockNumber = latestFinalizedBN
- return true
-}
-
-func (n *node[CHAIN_ID, HEAD, RPC]) onNewHead(lggr logger.SugaredLogger, chainInfo *ChainInfo, head HEAD) bool {
- if !head.IsValid() {
- lggr.Warn("Latest head is not valid")
- return false
- }
-
- promPoolRPCNodeNumSeenBlocks.WithLabelValues(n.chainID.String(), n.name).Inc()
- lggr.Debugw("Got head", "head", head)
- lggr = lggr.With("latestReceivedBlockNumber", chainInfo.BlockNumber, "blockNumber", head.BlockNumber(), "nodeState", n.getCachedState())
- if head.BlockNumber() <= chainInfo.BlockNumber {
- lggr.Debugw("Ignoring previously seen block number")
- return false
- }
-
- promPoolRPCNodeHighestSeenBlock.WithLabelValues(n.chainID.String(), n.name).Set(float64(head.BlockNumber()))
- chainInfo.BlockNumber = head.BlockNumber()
-
- if !n.chainCfg.FinalityTagEnabled() {
- latestFinalizedBN := max(head.BlockNumber()-int64(n.chainCfg.FinalityDepth()), 0)
- if latestFinalizedBN > chainInfo.FinalizedBlockNumber {
- promPoolRPCNodeHighestFinalizedBlock.WithLabelValues(n.chainID.String(), n.name).Set(float64(latestFinalizedBN))
- chainInfo.FinalizedBlockNumber = latestFinalizedBN
- }
- }
-
- return true
-}
-
-const (
- msgReceivedBlock = "Received block for RPC node, waiting until back in-sync to mark as live again"
- msgReceivedFinalizedBlock = "Received new finalized block for RPC node, waiting until back in-sync to mark as live again"
- msgInSync = "RPC node back in sync"
-)
-
-// isOutOfSyncWithPool returns outOfSync true if num or td is more than SyncThresold behind the best node.
-// Always returns outOfSync false for SyncThreshold 0.
-// liveNodes is only included when outOfSync is true.
-func (n *node[CHAIN_ID, HEAD, RPC]) isOutOfSyncWithPool() (outOfSync bool, liveNodes int) {
- if n.poolInfoProvider == nil {
- n.lfcLog.Warn("skipping sync state against the pool - should only occur in tests")
- return // skip for tests
- }
- threshold := n.nodePoolCfg.SyncThreshold()
- if threshold == 0 {
- return // disabled
- }
- // Check against best node
- ln, ci := n.poolInfoProvider.LatestChainInfo()
- localChainInfo, _ := n.rpc.GetInterceptedChainInfo()
- mode := n.nodePoolCfg.SelectionMode()
- switch mode {
- case NodeSelectionModeHighestHead, NodeSelectionModeRoundRobin, NodeSelectionModePriorityLevel:
- outOfSync = localChainInfo.BlockNumber < ci.BlockNumber-int64(threshold)
- case NodeSelectionModeTotalDifficulty:
- bigThreshold := big.NewInt(int64(threshold))
- outOfSync = localChainInfo.TotalDifficulty.Cmp(bigmath.Sub(ci.TotalDifficulty, bigThreshold)) < 0
- default:
- panic("unrecognized NodeSelectionMode: " + mode)
- }
-
- if outOfSync && n.getCachedState() == nodeStateAlive {
- n.lfcLog.Errorw("RPC endpoint has fallen behind", "blockNumber", localChainInfo.BlockNumber, "bestLatestBlockNumber", ci.BlockNumber, "totalDifficulty", localChainInfo.TotalDifficulty)
- }
- return outOfSync, ln
-}
-
-// outOfSyncLoop takes an OutOfSync node and waits until isOutOfSync returns false to go back to live status
-func (n *node[CHAIN_ID, HEAD, RPC]) outOfSyncLoop(syncIssues syncStatus) {
- defer n.wg.Done()
- ctx, cancel := n.newCtx()
- defer cancel()
-
- {
- // sanity check
- state := n.getCachedState()
- switch state {
- case nodeStateOutOfSync:
- case nodeStateClosed:
- return
- default:
- panic(fmt.Sprintf("outOfSyncLoop can only run for node in OutOfSync state, got: %s", state))
- }
- }
-
- outOfSyncAt := time.Now()
-
- // set logger name to OutOfSync or FinalizedBlockOutOfSync
- lggr := logger.Sugared(logger.Named(n.lfcLog, n.getCachedState().String())).With("nodeState", n.getCachedState())
- lggr.Debugw("Trying to revive out-of-sync RPC node")
-
- // Need to redial since out-of-sync nodes are automatically disconnected
- state := n.createVerifiedConn(ctx, lggr)
- if state != nodeStateAlive {
- n.declareState(state)
- return
- }
-
- noNewHeadsTimeoutThreshold := n.chainCfg.NodeNoNewHeadsThreshold()
- headsSub, err := n.registerNewSubscription(ctx, lggr.With("subscriptionType", "heads"),
- noNewHeadsTimeoutThreshold, n.rpc.SubscribeToHeads)
- if err != nil {
- lggr.Errorw("Failed to subscribe heads on out-of-sync RPC node", "err", err)
- n.declareUnreachable()
- return
- }
-
- defer n.unsubscribeHealthChecks()
-
- lggr.Tracew("Successfully subscribed to heads feed on out-of-sync RPC node")
-
- noNewFinalizedBlocksTimeoutThreshold := n.chainCfg.NoNewFinalizedHeadsThreshold()
- var finalizedHeadsSub headSubscription[HEAD]
- if n.chainCfg.FinalityTagEnabled() {
- finalizedHeadsSub, err = n.registerNewSubscription(ctx, lggr.With("subscriptionType", "finalizedHeads"),
- noNewFinalizedBlocksTimeoutThreshold, n.rpc.SubscribeToFinalizedHeads)
- if err != nil {
- lggr.Errorw("Subscribe to finalized heads failed on out-of-sync RPC node", "err", err)
- n.declareUnreachable()
- return
- }
-
- lggr.Tracew("Successfully subscribed to finalized heads feed on out-of-sync RPC node")
- }
-
- _, localHighestChainInfo := n.rpc.GetInterceptedChainInfo()
- for {
- if syncIssues == syncStatusSynced {
- // back in-sync! flip back into alive loop
- lggr.Infow(fmt.Sprintf("%s: %s. Node was out-of-sync for %s", msgInSync, n.String(), time.Since(outOfSyncAt)))
- n.declareInSync()
- return
- }
-
- select {
- case <-ctx.Done():
- return
- case head, open := <-headsSub.Heads:
- if !open {
- lggr.Errorw("Subscription channel unexpectedly closed", "nodeState", n.getCachedState())
- n.declareUnreachable()
- return
- }
-
- if !n.onNewHead(lggr, &localHighestChainInfo, head) {
- continue
- }
-
- // received a new head - clear NoNewHead flag
- syncIssues &= ^syncStatusNoNewHead
- if outOfSync, _ := n.isOutOfSyncWithPool(); !outOfSync {
- // we caught up with the pool - clear NotInSyncWithPool flag
- syncIssues &= ^syncStatusNotInSyncWithPool
- } else {
- // we've received new head, but lagging behind the pool, add NotInSyncWithPool flag to prevent false transition to alive
- syncIssues |= syncStatusNotInSyncWithPool
- }
-
- if noNewHeadsTimeoutThreshold > 0 {
- headsSub.ResetTimer(noNewHeadsTimeoutThreshold)
- }
-
- lggr.Debugw(msgReceivedBlock, "blockNumber", head.BlockNumber(), "blockDifficulty", head.BlockDifficulty(), "syncIssues", syncIssues)
- case <-time.After(zombieNodeCheckInterval(noNewHeadsTimeoutThreshold)):
- if n.poolInfoProvider != nil {
- if l, _ := n.poolInfoProvider.LatestChainInfo(); l < 1 {
- lggr.Criticalw("RPC endpoint is still out of sync, but there are no other available nodes. This RPC node will be forcibly moved back into the live pool in a degraded state", "syncIssues", syncIssues)
- n.declareInSync()
- return
- }
- }
- case err := <-headsSub.Errors:
- lggr.Errorw("Subscription was terminated", "err", err)
- n.declareUnreachable()
- return
- case <-headsSub.NoNewHeads:
- // we are not resetting the timer, as there is no need to add syncStatusNoNewHead until it's removed on new head.
- syncIssues |= syncStatusNoNewHead
- lggr.Debugw(fmt.Sprintf("No new heads received for %s. Node stays out-of-sync due to sync issues: %s", noNewHeadsTimeoutThreshold, syncIssues))
- case latestFinalized, open := <-finalizedHeadsSub.Heads:
- if !open {
- lggr.Errorw("Finalized heads subscription channel unexpectedly closed")
- n.declareUnreachable()
- return
- }
- if !latestFinalized.IsValid() {
- lggr.Warn("Latest finalized block is not valid")
- continue
- }
-
- receivedNewHead := n.onNewFinalizedHead(lggr, &localHighestChainInfo, latestFinalized)
- if !receivedNewHead {
- continue
- }
-
- // on new finalized head remove NoNewFinalizedHead flag from the mask
- syncIssues &= ^syncStatusNoNewFinalizedHead
- if noNewFinalizedBlocksTimeoutThreshold > 0 {
- finalizedHeadsSub.ResetTimer(noNewFinalizedBlocksTimeoutThreshold)
- }
-
- var highestSeen ChainInfo
- if n.poolInfoProvider != nil {
- highestSeen = n.poolInfoProvider.HighestUserObservations()
- }
-
- lggr.Debugw(msgReceivedFinalizedBlock, "blockNumber", latestFinalized.BlockNumber(), "poolHighestBlockNumber", highestSeen.FinalizedBlockNumber, "syncIssues", syncIssues)
- case err := <-finalizedHeadsSub.Errors:
- lggr.Errorw("Finalized head subscription was terminated", "err", err)
- n.declareUnreachable()
- return
- case <-finalizedHeadsSub.NoNewHeads:
- // we are not resetting the timer, as there is no need to add syncStatusNoNewFinalizedHead until it's removed on new finalized head.
- syncIssues |= syncStatusNoNewFinalizedHead
- lggr.Debugw(fmt.Sprintf("No new finalized heads received for %s. Node stays out-of-sync due to sync issues: %s", noNewFinalizedBlocksTimeoutThreshold, syncIssues))
- }
- }
-}
-
-func (n *node[CHAIN_ID, HEAD, RPC]) unreachableLoop() {
- defer n.wg.Done()
- ctx, cancel := n.newCtx()
- defer cancel()
-
- {
- // sanity check
- state := n.getCachedState()
- switch state {
- case nodeStateUnreachable:
- case nodeStateClosed:
- return
- default:
- panic(fmt.Sprintf("unreachableLoop can only run for node in Unreachable state, got: %s", state))
- }
- }
-
- unreachableAt := time.Now()
-
- lggr := logger.Sugared(logger.Named(n.lfcLog, "Unreachable"))
- lggr.Debugw("Trying to revive unreachable RPC node", "nodeState", n.getCachedState())
-
- dialRetryBackoff := iutils.NewRedialBackoff()
-
- for {
- select {
- case <-ctx.Done():
- return
- case <-time.After(dialRetryBackoff.Duration()):
- lggr.Tracew("Trying to re-dial RPC node", "nodeState", n.getCachedState())
-
- err := n.rpc.Dial(ctx)
- if err != nil {
- lggr.Errorw(fmt.Sprintf("Failed to redial RPC node; still unreachable: %v", err), "err", err, "nodeState", n.getCachedState())
- continue
- }
-
- n.setState(nodeStateDialed)
-
- state := n.verifyConn(ctx, lggr)
- switch state {
- case nodeStateUnreachable:
- n.setState(nodeStateUnreachable)
- continue
- case nodeStateAlive:
- lggr.Infow(fmt.Sprintf("Successfully redialled and verified RPC node %s. Node was offline for %s", n.String(), time.Since(unreachableAt)), "nodeState", n.getCachedState())
- fallthrough
- default:
- n.declareState(state)
- return
- }
- }
- }
-}
-
-func (n *node[CHAIN_ID, HEAD, RPC]) invalidChainIDLoop() {
- defer n.wg.Done()
- ctx, cancel := n.newCtx()
- defer cancel()
-
- {
- // sanity check
- state := n.getCachedState()
- switch state {
- case nodeStateInvalidChainID:
- case nodeStateClosed:
- return
- default:
- panic(fmt.Sprintf("invalidChainIDLoop can only run for node in InvalidChainID state, got: %s", state))
- }
- }
-
- invalidAt := time.Now()
-
- lggr := logger.Named(n.lfcLog, "InvalidChainID")
-
- // Need to redial since invalid chain ID nodes are automatically disconnected
- state := n.createVerifiedConn(ctx, lggr)
- if state != nodeStateInvalidChainID {
- n.declareState(state)
- return
- }
-
- lggr.Debugw(fmt.Sprintf("Periodically re-checking RPC node %s with invalid chain ID", n.String()), "nodeState", n.getCachedState())
-
- chainIDRecheckBackoff := iutils.NewRedialBackoff()
-
- for {
- select {
- case <-ctx.Done():
- return
- case <-time.After(chainIDRecheckBackoff.Duration()):
- state := n.verifyConn(ctx, lggr)
- switch state {
- case nodeStateInvalidChainID:
- continue
- case nodeStateAlive:
- lggr.Infow(fmt.Sprintf("Successfully verified RPC node. Node was offline for %s", time.Since(invalidAt)), "nodeState", n.getCachedState())
- fallthrough
- default:
- n.declareState(state)
- return
- }
- }
- }
-}
-
-func (n *node[CHAIN_ID, HEAD, RPC]) syncingLoop() {
- defer n.wg.Done()
- ctx, cancel := n.newCtx()
- defer cancel()
-
- {
- // sanity check
- state := n.getCachedState()
- switch state {
- case nodeStateSyncing:
- case nodeStateClosed:
- return
- default:
- panic(fmt.Sprintf("syncingLoop can only run for node in NodeStateSyncing state, got: %s", state))
- }
- }
-
- syncingAt := time.Now()
-
- lggr := logger.Sugared(logger.Named(n.lfcLog, "Syncing"))
- lggr.Debugw(fmt.Sprintf("Periodically re-checking RPC node %s with syncing status", n.String()), "nodeState", n.getCachedState())
- // Need to redial since syncing nodes are automatically disconnected
- state := n.createVerifiedConn(ctx, lggr)
- if state != nodeStateSyncing {
- n.declareState(state)
- return
- }
-
- recheckBackoff := iutils.NewRedialBackoff()
-
- for {
- select {
- case <-ctx.Done():
- return
- case <-time.After(recheckBackoff.Duration()):
- lggr.Tracew("Trying to recheck if the node is still syncing", "nodeState", n.getCachedState())
- isSyncing, err := n.rpc.IsSyncing(ctx)
- if err != nil {
- lggr.Errorw("Unexpected error while verifying RPC node synchronization status", "err", err, "nodeState", n.getCachedState())
- n.declareUnreachable()
- return
- }
-
- if isSyncing {
- lggr.Errorw("Verification failed: Node is syncing", "nodeState", n.getCachedState())
- continue
- }
-
- lggr.Infow(fmt.Sprintf("Successfully verified RPC node. Node was syncing for %s", time.Since(syncingAt)), "nodeState", n.getCachedState())
- n.declareAlive()
- return
- }
- }
-}
diff --git a/common/client/node_lifecycle_test.go b/common/client/node_lifecycle_test.go
deleted file mode 100644
index 39c39e318ef..00000000000
--- a/common/client/node_lifecycle_test.go
+++ /dev/null
@@ -1,1983 +0,0 @@
-package client
-
-import (
- "errors"
- "fmt"
- "math/big"
- "sync"
- "sync/atomic"
- "testing"
-
- "github.com/cometbft/cometbft/libs/rand"
- prom "github.com/prometheus/client_model/go"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/mock"
- "github.com/stretchr/testify/require"
- "go.uber.org/zap"
-
- "github.com/smartcontractkit/chainlink-common/pkg/logger"
- "github.com/smartcontractkit/chainlink-common/pkg/utils/tests"
-
- clientMocks "github.com/smartcontractkit/chainlink/v2/common/client/mocks"
- "github.com/smartcontractkit/chainlink/v2/common/types"
- "github.com/smartcontractkit/chainlink/v2/common/types/mocks"
-)
-
-func newSub(t *testing.T) *mocks.Subscription {
- sub := mocks.NewSubscription(t)
- sub.On("Err").Return((<-chan error)(nil)).Maybe()
- sub.On("Unsubscribe")
- return sub
-}
-
-func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) {
- t.Parallel()
-
- newDialedNode := func(t *testing.T, opts testNodeOpts) testNode {
- node := newTestNode(t, opts)
- opts.rpc.On("Close").Return(nil)
-
- node.setState(nodeStateDialed)
- return node
- }
-
- t.Run("returns on closed", func(t *testing.T) {
- node := newTestNode(t, testNodeOpts{})
- node.setState(nodeStateClosed)
- node.wg.Add(1)
- node.aliveLoop()
- })
- t.Run("if initial subscribe fails, transitions to unreachable", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- node := newDialedNode(t, testNodeOpts{
- rpc: rpc,
- })
- defer func() { assert.NoError(t, node.close()) }()
-
- expectedError := errors.New("failed to subscribe to rpc")
- rpc.On("SubscribeToHeads", mock.Anything).Return(nil, nil, expectedError).Once()
- // might be called in unreachable loop
- rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe()
- node.declareAlive()
- tests.AssertEventually(t, func() bool {
- return node.State() == nodeStateUnreachable
- })
- })
- t.Run("if remote RPC connection is closed transitions to unreachable", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
-
- lggr, observedLogs := logger.TestObserved(t, zap.WarnLevel)
- node := newDialedNode(t, testNodeOpts{
- rpc: rpc,
- lggr: lggr,
- })
- rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once()
- defer func() { assert.NoError(t, node.close()) }()
-
- sub := mocks.NewSubscription(t)
- errChan := make(chan error)
- close(errChan)
- sub.On("Err").Return((<-chan error)(errChan)).Once()
- sub.On("Unsubscribe").Once()
- rpc.On("SubscribeToHeads", mock.Anything).Return(nil, sub, nil).Once()
- // might be called in unreachable loop
- rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe()
- node.declareAlive()
- tests.AssertLogEventually(t, observedLogs, "Subscription was terminated")
- assert.Equal(t, nodeStateUnreachable, node.State())
- })
-
- newSubscribedNode := func(t *testing.T, opts testNodeOpts) testNode {
- sub := newSub(t)
- opts.rpc.On("SubscribeToHeads", mock.Anything).Return(make(<-chan Head), sub, nil).Once()
- return newDialedNode(t, opts)
- }
- t.Run("Stays alive and waits for signal", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once()
- lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel)
- node := newSubscribedNode(t, testNodeOpts{
- config: testNodeConfig{},
- rpc: rpc,
- lggr: lggr,
- })
- defer func() { assert.NoError(t, node.close()) }()
- node.declareAlive()
- tests.AssertLogEventually(t, observedLogs, "Subscription liveness checking disabled")
- tests.AssertLogEventually(t, observedLogs, "Polling disabled")
- assert.Equal(t, nodeStateAlive, node.State())
- })
- t.Run("stays alive while below pollFailureThreshold and resets counter on success", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{})
- lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel)
- const pollFailureThreshold = 3
- node := newSubscribedNode(t, testNodeOpts{
- config: testNodeConfig{
- pollFailureThreshold: pollFailureThreshold,
- pollInterval: tests.TestInterval,
- },
- rpc: rpc,
- lggr: lggr,
- })
- defer func() { assert.NoError(t, node.close()) }()
-
- pollError := errors.New("failed to get ClientVersion")
- // 1. Return error several times, but below threshold
- rpc.On("Ping", mock.Anything).Return(pollError).Run(func(_ mock.Arguments) {
- // stays healthy while below threshold
- assert.Equal(t, nodeStateAlive, node.State())
- }).Times(pollFailureThreshold - 1)
- // 2. Successful call that is expected to reset counter
- rpc.On("Ping", mock.Anything).Return(nil).Once()
- // 3. Return error. If we have not reset the timer, we'll transition to nonAliveState
- rpc.On("Ping", mock.Anything).Return(pollError).Once()
- // 4. Once during the call, check if node is alive
- var ensuredAlive atomic.Bool
- rpc.On("Ping", mock.Anything).Return(nil).Run(func(_ mock.Arguments) {
- if ensuredAlive.Load() {
- return
- }
- ensuredAlive.Store(true)
- assert.Equal(t, nodeStateAlive, node.State())
- }).Once()
- // redundant call to stay in alive state
- rpc.On("Ping", mock.Anything).Return(nil)
- node.declareAlive()
- tests.AssertLogCountEventually(t, observedLogs, fmt.Sprintf("Poll failure, RPC endpoint %s failed to respond properly", node.String()), pollFailureThreshold)
- tests.AssertLogCountEventually(t, observedLogs, "Ping successful", 2)
- assert.True(t, ensuredAlive.Load(), "expected to ensure that node was alive")
- })
- t.Run("with threshold poll failures, transitions to unreachable", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{})
- lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel)
- const pollFailureThreshold = 3
- node := newSubscribedNode(t, testNodeOpts{
- config: testNodeConfig{
- pollFailureThreshold: pollFailureThreshold,
- pollInterval: tests.TestInterval,
- },
- rpc: rpc,
- lggr: lggr,
- })
- defer func() { assert.NoError(t, node.close()) }()
- pollError := errors.New("failed to get ClientVersion")
- rpc.On("Ping", mock.Anything).Return(pollError)
- rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe()
- node.declareAlive()
- tests.AssertLogCountEventually(t, observedLogs, fmt.Sprintf("Poll failure, RPC endpoint %s failed to respond properly", node.String()), pollFailureThreshold)
- tests.AssertEventually(t, func() bool {
- return nodeStateUnreachable == node.State()
- })
- })
- t.Run("with threshold poll failures, but we are the last node alive, forcibly keeps it alive", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel)
- const pollFailureThreshold = 3
- node := newSubscribedNode(t, testNodeOpts{
- config: testNodeConfig{
- pollFailureThreshold: pollFailureThreshold,
- pollInterval: tests.TestInterval,
- },
- rpc: rpc,
- lggr: lggr,
- })
- defer func() { assert.NoError(t, node.close()) }()
- poolInfo := newMockPoolChainInfoProvider(t)
- poolInfo.On("LatestChainInfo").Return(1, ChainInfo{
- BlockNumber: 20,
- }).Once()
- node.SetPoolChainInfoProvider(poolInfo)
- rpc.On("GetInterceptedChainInfo").Return(ChainInfo{BlockNumber: 20}, ChainInfo{BlockNumber: 20})
- pollError := errors.New("failed to get ClientVersion")
- rpc.On("Ping", mock.Anything).Return(pollError)
- node.declareAlive()
- tests.AssertLogEventually(t, observedLogs, fmt.Sprintf("RPC endpoint failed to respond to %d consecutive polls", pollFailureThreshold))
- assert.Equal(t, nodeStateAlive, node.State())
- })
- t.Run("when behind more than SyncThreshold, transitions to out of sync", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel)
- const syncThreshold = 10
- node := newSubscribedNode(t, testNodeOpts{
- config: testNodeConfig{
- pollInterval: tests.TestInterval,
- syncThreshold: syncThreshold,
- selectionMode: NodeSelectionModeRoundRobin,
- },
- rpc: rpc,
- lggr: lggr,
- })
- defer func() { assert.NoError(t, node.close()) }()
- rpc.On("Ping", mock.Anything).Return(nil)
- const mostRecentBlock = 20
- rpc.On("GetInterceptedChainInfo").Return(ChainInfo{BlockNumber: mostRecentBlock}, ChainInfo{BlockNumber: 30})
- poolInfo := newMockPoolChainInfoProvider(t)
- poolInfo.On("LatestChainInfo").Return(10, ChainInfo{
- BlockNumber: syncThreshold + mostRecentBlock + 1,
- TotalDifficulty: big.NewInt(10),
- })
- node.SetPoolChainInfoProvider(poolInfo)
- // tries to redial in outOfSync
- rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Run(func(_ mock.Arguments) {
- assert.Equal(t, nodeStateOutOfSync, node.State())
- }).Once()
- rpc.On("Close").Maybe()
- rpc.On("Dial", mock.Anything).Run(func(_ mock.Arguments) {
- require.Equal(t, nodeStateOutOfSync, node.State())
- }).Return(errors.New("failed to dial")).Maybe()
- node.declareAlive()
- tests.AssertLogEventually(t, observedLogs, "Dial failed: Node is unreachable")
- })
- t.Run("when behind more than SyncThreshold but we are the last live node, forcibly stays alive", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel)
- const syncThreshold = 10
- node := newSubscribedNode(t, testNodeOpts{
- config: testNodeConfig{
- pollInterval: tests.TestInterval,
- syncThreshold: syncThreshold,
- selectionMode: NodeSelectionModeRoundRobin,
- },
- rpc: rpc,
- lggr: lggr,
- })
- defer func() { assert.NoError(t, node.close()) }()
- rpc.On("Ping", mock.Anything).Return(nil)
- const mostRecentBlock = 20
- rpc.On("GetInterceptedChainInfo").Return(ChainInfo{BlockNumber: mostRecentBlock}, ChainInfo{BlockNumber: 30})
- poolInfo := newMockPoolChainInfoProvider(t)
- poolInfo.On("LatestChainInfo").Return(1, ChainInfo{
- BlockNumber: syncThreshold + mostRecentBlock + 1,
- TotalDifficulty: big.NewInt(10),
- })
- node.SetPoolChainInfoProvider(poolInfo)
- node.declareAlive()
- tests.AssertLogEventually(t, observedLogs, fmt.Sprintf("RPC endpoint has fallen behind; %s %s", msgCannotDisable, msgDegradedState))
- })
- t.Run("when behind but SyncThreshold=0, stay alive", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel)
- node := newSubscribedNode(t, testNodeOpts{
- config: testNodeConfig{
- pollInterval: tests.TestInterval,
- syncThreshold: 0,
- selectionMode: NodeSelectionModeRoundRobin,
- },
- rpc: rpc,
- lggr: lggr,
- })
- defer func() { assert.NoError(t, node.close()) }()
- rpc.On("Ping", mock.Anything).Return(nil)
- const mostRecentBlock = 20
- rpc.On("GetInterceptedChainInfo").Return(ChainInfo{BlockNumber: mostRecentBlock}, ChainInfo{BlockNumber: 30})
- node.declareAlive()
- tests.AssertLogCountEventually(t, observedLogs, "Ping successful", 2)
- assert.Equal(t, nodeStateAlive, node.State())
- })
- t.Run("when no new heads received for threshold, transitions to out of sync", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{})
- node := newSubscribedNode(t, testNodeOpts{
- config: testNodeConfig{},
- chainConfig: clientMocks.ChainConfig{
- NoNewHeadsThresholdVal: tests.TestInterval,
- },
- rpc: rpc,
- })
- defer func() { assert.NoError(t, node.close()) }()
- // tries to redial in outOfSync
- rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Run(func(_ mock.Arguments) {
- assert.Equal(t, nodeStateOutOfSync, node.State())
- }).Once()
- rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe()
- node.declareAlive()
- tests.AssertEventually(t, func() bool {
- // right after outOfSync we'll transfer to unreachable due to returned error on Dial
- // we check that we were in out of sync state on first Dial call
- return node.State() == nodeStateUnreachable
- })
- })
- t.Run("when no new heads received for threshold but we are the last live node, forcibly stays alive", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{})
- lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel)
- node := newSubscribedNode(t, testNodeOpts{
- config: testNodeConfig{},
- lggr: lggr,
- chainConfig: clientMocks.ChainConfig{
- NoNewHeadsThresholdVal: tests.TestInterval,
- },
- rpc: rpc,
- })
- defer func() { assert.NoError(t, node.close()) }()
- poolInfo := newMockPoolChainInfoProvider(t)
- poolInfo.On("LatestChainInfo").Return(1, ChainInfo{
- BlockNumber: 20,
- TotalDifficulty: big.NewInt(10),
- }).Once()
- node.SetPoolChainInfoProvider(poolInfo)
- node.declareAlive()
- tests.AssertLogEventually(t, observedLogs, fmt.Sprintf("RPC endpoint detected out of sync; %s %s", msgCannotDisable, msgDegradedState))
- assert.Equal(t, nodeStateAlive, node.State())
- })
-
- t.Run("rpc closed head channel", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- sub := newSub(t)
- rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once()
- ch := make(chan Head)
- rpc.On("SubscribeToHeads", mock.Anything).Run(func(args mock.Arguments) {
- close(ch)
- }).Return((<-chan Head)(ch), sub, nil).Once()
- lggr, observedLogs := logger.TestObserved(t, zap.ErrorLevel)
- node := newDialedNode(t, testNodeOpts{
- lggr: lggr,
- config: testNodeConfig{},
- chainConfig: clientMocks.ChainConfig{
- NoNewHeadsThresholdVal: tests.TestInterval,
- },
- rpc: rpc,
- })
- defer func() { assert.NoError(t, node.close()) }()
- rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe()
- node.declareAlive()
- tests.AssertLogEventually(t, observedLogs, "Subscription channel unexpectedly closed")
- assert.Equal(t, nodeStateUnreachable, node.State())
- })
- t.Run("If finality tag is not enabled updates finalized block metric using finality depth and latest head", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- sub := newSub(t)
- const blockNumber = 1000
- const finalityDepth = 10
- const expectedBlock = 990
- ch := make(chan Head)
- rpc.On("SubscribeToHeads", mock.Anything).Run(func(args mock.Arguments) {
- rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once()
- go writeHeads(t, ch, head{BlockNumber: blockNumber - 1}, head{BlockNumber: blockNumber}, head{BlockNumber: blockNumber - 1})
- }).Return((<-chan Head)(ch), sub, nil).Once()
- name := "node-" + rand.Str(5)
- node := newDialedNode(t, testNodeOpts{
- config: testNodeConfig{},
- chainConfig: clientMocks.ChainConfig{FinalityDepthVal: finalityDepth},
- rpc: rpc,
- name: name,
- chainID: big.NewInt(1),
- })
- defer func() { assert.NoError(t, node.close()) }()
- node.declareAlive()
- tests.AssertEventually(t, func() bool {
- metric, err := promPoolRPCNodeHighestFinalizedBlock.GetMetricWithLabelValues(big.NewInt(1).String(), name)
- require.NoError(t, err)
- var m = &prom.Metric{}
- require.NoError(t, metric.Write(m))
- return float64(expectedBlock) == m.Gauge.GetValue()
- })
- })
- t.Run("If fails to subscribe to latest finalized blocks, transitions to unreachable", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- sub := newSub(t)
- rpc.On("SubscribeToHeads", mock.Anything).Return(make(<-chan Head), sub, nil).Once()
- expectedError := errors.New("failed to subscribe to finalized heads")
- rpc.On("SubscribeToFinalizedHeads", mock.Anything).Return(nil, sub, expectedError).Once()
- lggr := logger.Test(t)
- node := newDialedNode(t, testNodeOpts{
- config: testNodeConfig{
- finalizedBlockPollInterval: tests.TestInterval,
- },
- chainConfig: clientMocks.ChainConfig{
- IsFinalityTagEnabled: true,
- },
- rpc: rpc,
- lggr: lggr,
- })
- defer func() { assert.NoError(t, node.close()) }()
- node.declareAlive()
- tests.AssertEventually(t, func() bool {
- return node.State() == nodeStateUnreachable
- })
- })
- t.Run("Logs warning if latest finalized block is not valid", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- sub := newSub(t)
- rpc.On("SubscribeToHeads", mock.Anything).Return(make(<-chan Head), sub, nil).Once()
- ch := make(chan Head, 1)
- head := newMockHead(t)
- head.On("IsValid").Return(false)
- rpc.On("SubscribeToFinalizedHeads", mock.Anything).Run(func(args mock.Arguments) {
- ch <- head
- }).Return((<-chan Head)(ch), sub, nil).Once()
-
- rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once()
- lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel)
- node := newDialedNode(t, testNodeOpts{
- config: testNodeConfig{},
- chainConfig: clientMocks.ChainConfig{
- IsFinalityTagEnabled: true,
- },
- rpc: rpc,
- lggr: lggr,
- })
- defer func() { assert.NoError(t, node.close()) }()
- node.declareAlive()
- tests.AssertLogEventually(t, observedLogs, "Latest finalized block is not valid")
- })
- t.Run("On new finalized block updates corresponding metric", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- const expectedBlock = 1101
- const finalityDepth = 10
- ch := make(chan Head)
- rpc.On("SubscribeToFinalizedHeads", mock.Anything).Return((<-chan Head)(ch), newSub(t), nil).Once()
- rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once()
- name := "node-" + rand.Str(5)
- node := newSubscribedNode(t, testNodeOpts{
- config: testNodeConfig{},
- chainConfig: clientMocks.ChainConfig{
- FinalityDepthVal: finalityDepth,
- IsFinalityTagEnabled: true,
- },
- rpc: rpc,
- name: name,
- chainID: big.NewInt(1),
- })
- defer func() { assert.NoError(t, node.close()) }()
- node.declareAlive()
- var wg sync.WaitGroup
- wg.Add(1)
- go func() {
- defer wg.Done()
- writeHeads(t, ch, head{BlockNumber: expectedBlock - 1}, head{BlockNumber: expectedBlock}, head{BlockNumber: expectedBlock - 1})
- }()
- tests.AssertEventually(t, func() bool {
- metric, err := promPoolRPCNodeHighestFinalizedBlock.GetMetricWithLabelValues(big.NewInt(1).String(), name)
- require.NoError(t, err)
- var m = &prom.Metric{}
- require.NoError(t, metric.Write(m))
- return float64(expectedBlock) == m.Gauge.GetValue()
- })
- })
- t.Run("If finalized heads channel is closed, transitions to unreachable", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once()
- ch := make(chan Head)
- close(ch)
- rpc.On("SubscribeToFinalizedHeads", mock.Anything).Return((<-chan Head)(ch), newSub(t), nil).Once()
- lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel)
- node := newSubscribedNode(t, testNodeOpts{
- chainConfig: clientMocks.ChainConfig{
- IsFinalityTagEnabled: true,
- },
- rpc: rpc,
- lggr: lggr,
- })
- defer func() { assert.NoError(t, node.close()) }()
- rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe()
- node.declareAlive()
- tests.AssertLogEventually(t, observedLogs, "Finalized heads subscription channel unexpectedly closed")
- tests.AssertEventually(t, func() bool {
- return nodeStateUnreachable == node.State()
- })
- })
- t.Run("when no new finalized heads received for threshold, transitions to out of sync", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once()
- ch := make(chan Head, 1)
- ch <- head{BlockNumber: 10}.ToMockHead(t)
- rpc.On("SubscribeToFinalizedHeads", mock.Anything).Return((<-chan Head)(ch), newSub(t), nil).Once()
- lggr, observed := logger.TestObserved(t, zap.DebugLevel)
- noNewFinalizedHeadsThreshold := tests.TestInterval
- node := newSubscribedNode(t, testNodeOpts{
- config: testNodeConfig{},
- chainConfig: clientMocks.ChainConfig{
- NoNewFinalizedHeadsThresholdVal: noNewFinalizedHeadsThreshold,
- IsFinalityTagEnabled: true,
- },
- rpc: rpc,
- lggr: lggr,
- })
- defer func() { assert.NoError(t, node.close()) }()
- // tries to redial in outOfSync
- rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Run(func(_ mock.Arguments) {
- assert.Equal(t, nodeStateOutOfSync, node.State())
- }).Once()
- rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe()
- node.declareAlive()
- tests.AssertLogEventually(t, observed, fmt.Sprintf("RPC's finalized state is out of sync; no new finalized heads received for %s (last finalized head received was 10)", noNewFinalizedHeadsThreshold))
- tests.AssertEventually(t, func() bool {
- // right after outOfSync we'll transfer to unreachable due to returned error on Dial
- // we check that we were in out of sync state on first Dial call
- return node.State() == nodeStateUnreachable
- })
- })
- t.Run("when no new finalized heads received for threshold but we are the last live node, forcibly stays alive", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once()
- rpc.On("SubscribeToFinalizedHeads", mock.Anything).Return(make(<-chan Head), newSub(t), nil).Once()
- lggr, observed := logger.TestObserved(t, zap.DebugLevel)
- noNewFinalizedHeadsThreshold := tests.TestInterval
- node := newSubscribedNode(t, testNodeOpts{
- config: testNodeConfig{},
- chainConfig: clientMocks.ChainConfig{
- NoNewFinalizedHeadsThresholdVal: noNewFinalizedHeadsThreshold,
- IsFinalityTagEnabled: true,
- },
- rpc: rpc,
- lggr: lggr,
- })
- defer func() { assert.NoError(t, node.close()) }()
- poolInfo := newMockPoolChainInfoProvider(t)
- poolInfo.On("LatestChainInfo").Return(1, ChainInfo{
- BlockNumber: 20,
- TotalDifficulty: big.NewInt(10),
- }).Once()
- node.SetPoolChainInfoProvider(poolInfo)
- node.declareAlive()
- tests.AssertLogEventually(t, observed, fmt.Sprintf("RPC's finalized state is out of sync; %s %s", msgCannotDisable, msgDegradedState))
- assert.Equal(t, nodeStateAlive, node.State())
- })
- t.Run("If finalized subscription returns an error, transitions to unreachable", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once()
- sub := mocks.NewSubscription(t)
- errCh := make(chan error, 1)
- errCh <- errors.New("subscription failed")
- sub.On("Err").Return((<-chan error)(errCh))
- sub.On("Unsubscribe").Once()
- rpc.On("SubscribeToFinalizedHeads", mock.Anything).Return((<-chan Head)(nil), sub, nil).Once()
- lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel)
- node := newSubscribedNode(t, testNodeOpts{
- chainConfig: clientMocks.ChainConfig{
- IsFinalityTagEnabled: true,
- },
- rpc: rpc,
- lggr: lggr,
- })
- defer func() { assert.NoError(t, node.close()) }()
- rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe()
- node.declareAlive()
- tests.AssertLogEventually(t, observedLogs, "Finalized heads subscription was terminated")
- tests.AssertEventually(t, func() bool {
- return nodeStateUnreachable == node.State()
- })
- })
-}
-
-type head struct {
- BlockNumber int64
- BlockDifficulty *big.Int
-}
-
-func (h head) ToMockHead(t *testing.T) *mockHead {
- m := newMockHead(t)
- m.On("BlockNumber").Return(h.BlockNumber).Maybe()
- m.On("BlockDifficulty").Return(h.BlockDifficulty).Maybe()
- m.On("IsValid").Return(true).Maybe()
- return m
-}
-
-func writeHeads(t *testing.T, ch chan<- Head, heads ...head) {
- for _, head := range heads {
- h := head.ToMockHead(t)
- select {
- case ch <- h:
- case <-tests.Context(t).Done():
- return
- }
- }
-}
-
-func setupRPCForAliveLoop(t *testing.T, rpc *mockRPCClient[types.ID, Head]) {
- rpc.On("Dial", mock.Anything).Return(nil).Maybe()
- aliveSubscription := mocks.NewSubscription(t)
- aliveSubscription.On("Err").Return(nil).Maybe()
- aliveSubscription.On("Unsubscribe").Maybe()
- rpc.On("SubscribeToHeads", mock.Anything).Return(make(<-chan Head), aliveSubscription, nil).Maybe()
- rpc.On("SubscribeToFinalizedHeads", mock.Anything).Return(make(<-chan Head), aliveSubscription, nil).Maybe()
- rpc.On("SetAliveLoopSub", mock.Anything).Maybe()
- rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Maybe()
-}
-
-func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) {
- t.Parallel()
-
- newAliveNode := func(t *testing.T, opts testNodeOpts) testNode {
- node := newTestNode(t, opts)
- opts.rpc.On("Close").Return(nil)
- node.setState(nodeStateAlive)
- return node
- }
-
- t.Run("returns on closed", func(t *testing.T) {
- t.Parallel()
- node := newTestNode(t, testNodeOpts{})
- node.setState(nodeStateClosed)
- node.wg.Add(1)
- node.outOfSyncLoop(syncStatusNotInSyncWithPool)
- })
- t.Run("on old blocks stays outOfSync and returns on close", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- nodeChainID := types.RandomID()
- lggr := logger.Test(t)
- node := newAliveNode(t, testNodeOpts{
- rpc: rpc,
- chainID: nodeChainID,
- lggr: lggr,
- })
- defer func() { assert.NoError(t, node.close()) }()
-
- rpc.On("Dial", mock.Anything).Return(nil).Once()
- rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once()
-
- rpc.On("GetInterceptedChainInfo").Return(ChainInfo{BlockNumber: 0}, ChainInfo{BlockNumber: 13}).Once()
-
- outOfSyncSubscription := mocks.NewSubscription(t)
- outOfSyncSubscription.On("Err").Return((<-chan error)(nil))
- outOfSyncSubscription.On("Unsubscribe").Once()
- heads := []head{{BlockNumber: 7}, {BlockNumber: 11}, {BlockNumber: 13}}
- ch := make(chan Head)
- var wg sync.WaitGroup
- wg.Add(1)
- rpc.On("SubscribeToHeads", mock.Anything).Run(func(args mock.Arguments) {
- go func() {
- defer wg.Done()
- writeHeads(t, ch, heads...)
- }()
- }).Return((<-chan Head)(ch), outOfSyncSubscription, nil).Once()
-
- rpc.On("Dial", mock.Anything).Return(errors.New("failed to redial")).Maybe()
-
- node.declareOutOfSync(syncStatusNoNewHead)
- // wait until all heads are consumed
- wg.Wait()
- assert.Equal(t, nodeStateOutOfSync, node.State())
- })
- t.Run("if initial dial fails, transitions to unreachable", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- node := newAliveNode(t, testNodeOpts{
- rpc: rpc,
- })
- defer func() { assert.NoError(t, node.close()) }()
-
- expectedError := errors.New("failed to dial rpc")
- // might be called again in unreachable loop, so no need to set once
- rpc.On("Dial", mock.Anything).Return(expectedError)
-
- node.declareOutOfSync(syncStatusNoNewHead)
- tests.AssertEventually(t, func() bool {
- return node.State() == nodeStateUnreachable
- })
- })
- t.Run("if fail to get chainID, transitions to unreachable", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- chainID := types.RandomID()
- node := newAliveNode(t, testNodeOpts{
- rpc: rpc,
- chainID: chainID,
- })
- defer func() { assert.NoError(t, node.close()) }()
-
- rpc.On("ChainID", mock.Anything).Return(chainID, nil)
- // for out-of-sync
- rpc.On("Dial", mock.Anything).Return(nil).Once()
- // for unreachable
- rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe()
- sub := mocks.NewSubscription(t)
- errChan := make(chan error, 1)
- errChan <- errors.New("subscription was terminate")
- sub.On("Err").Return((<-chan error)(errChan))
- sub.On("Unsubscribe").Once()
- rpc.On("SubscribeToHeads", mock.Anything).Return(make(<-chan Head), sub, nil)
- rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{})
-
- expectedError := errors.New("failed to get chain ID")
- // might be called multiple times
- rpc.On("ChainID", mock.Anything).Return(types.NewIDFromInt(0), expectedError)
- node.declareOutOfSync(syncStatusNoNewHead)
- tests.AssertEventually(t, func() bool {
- return node.State() == nodeStateUnreachable
- })
- })
- t.Run("if chainID does not match, transitions to invalidChainID", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- nodeChainID := types.NewIDFromInt(10)
- rpcChainID := types.NewIDFromInt(11)
- node := newAliveNode(t, testNodeOpts{
- rpc: rpc,
- chainID: nodeChainID,
- })
- defer func() { assert.NoError(t, node.close()) }()
-
- // one for out-of-sync & one for invalid chainID
- rpc.On("Dial", mock.Anything).Return(nil).Twice()
-
- // might be called multiple times
- rpc.On("ChainID", mock.Anything).Return(rpcChainID, nil)
- node.declareOutOfSync(syncStatusNoNewHead)
- tests.AssertEventually(t, func() bool {
- return node.State() == nodeStateInvalidChainID
- })
- })
- t.Run("if syncing, transitions to syncing", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- nodeChainID := types.NewIDFromInt(10)
- node := newAliveNode(t, testNodeOpts{
- rpc: rpc,
- chainID: nodeChainID,
- config: testNodeConfig{nodeIsSyncingEnabled: true},
- })
- defer func() { assert.NoError(t, node.close()) }()
-
- rpc.On("Dial", mock.Anything).Return(nil)
- rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil)
-
- // might be called multiple times
- rpc.On("IsSyncing", mock.Anything).Return(true, nil)
- node.declareOutOfSync(syncStatusNoNewHead)
- tests.AssertEventually(t, func() bool {
- return node.State() == nodeStateSyncing
- })
- })
- t.Run("if fails to fetch syncing status, transitions to unreachable", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- nodeChainID := types.NewIDFromInt(10)
- node := newAliveNode(t, testNodeOpts{
- rpc: rpc,
- chainID: nodeChainID,
- config: testNodeConfig{nodeIsSyncingEnabled: true},
- })
- defer func() { assert.NoError(t, node.close()) }()
-
- // one for out-of-sync
- rpc.On("Dial", mock.Anything).Return(nil).Once()
-
- // for unreachable
- rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe()
- rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once()
- // might be called multiple times
- rpc.On("IsSyncing", mock.Anything).Return(false, errors.New("failed to check syncing"))
- node.declareOutOfSync(syncStatusNoNewHead)
- tests.AssertEventually(t, func() bool {
- return node.State() == nodeStateUnreachable
- })
- })
- t.Run("if fails to subscribe, becomes unreachable", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- nodeChainID := types.RandomID()
- node := newAliveNode(t, testNodeOpts{
- rpc: rpc,
- chainID: nodeChainID,
- })
- defer func() { assert.NoError(t, node.close()) }()
-
- rpc.On("Dial", mock.Anything).Return(nil).Once()
- rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once()
- expectedError := errors.New("failed to subscribe")
- rpc.On("SubscribeToHeads", mock.Anything).Return(nil, nil, expectedError).Once()
-
- rpc.On("Dial", mock.Anything).Return(errors.New("failed to redial")).Maybe()
- node.declareOutOfSync(syncStatusNoNewHead)
- tests.AssertEventually(t, func() bool {
- return node.State() == nodeStateUnreachable
- })
- })
- t.Run("on subscription termination becomes unreachable", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- nodeChainID := types.RandomID()
- lggr, observedLogs := logger.TestObserved(t, zap.ErrorLevel)
- node := newAliveNode(t, testNodeOpts{
- rpc: rpc,
- chainID: nodeChainID,
- lggr: lggr,
- })
- defer func() { assert.NoError(t, node.close()) }()
-
- rpc.On("Dial", mock.Anything).Return(nil).Once()
- rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once()
-
- rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once()
- sub := mocks.NewSubscription(t)
- errChan := make(chan error, 1)
- errChan <- errors.New("subscription was terminate")
- sub.On("Err").Return((<-chan error)(errChan))
- sub.On("Unsubscribe").Once()
- rpc.On("SubscribeToHeads", mock.Anything).Return(make(<-chan Head), sub, nil).Once()
- rpc.On("Dial", mock.Anything).Return(errors.New("failed to redial")).Maybe()
- node.declareOutOfSync(syncStatusNoNewHead)
- tests.AssertLogEventually(t, observedLogs, "Subscription was terminated")
- tests.AssertEventually(t, func() bool {
- return node.State() == nodeStateUnreachable
- })
- })
- t.Run("becomes unreachable if head channel is closed", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- nodeChainID := types.RandomID()
- lggr, observedLogs := logger.TestObserved(t, zap.ErrorLevel)
- node := newAliveNode(t, testNodeOpts{
- rpc: rpc,
- chainID: nodeChainID,
- lggr: lggr,
- })
- defer func() { assert.NoError(t, node.close()) }()
-
- rpc.On("Dial", mock.Anything).Return(nil).Once()
- rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once()
-
- rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once()
-
- sub := newSub(t)
- ch := make(chan Head)
- rpc.On("SubscribeToHeads", mock.Anything).Run(func(args mock.Arguments) {
- close(ch)
- }).Return((<-chan Head)(ch), sub, nil).Once()
- rpc.On("Dial", mock.Anything).Return(errors.New("failed to redial")).Maybe()
- node.declareOutOfSync(syncStatusNoNewHead)
- tests.AssertLogEventually(t, observedLogs, "Subscription channel unexpectedly closed")
- tests.AssertEventually(t, func() bool {
- return node.State() == nodeStateUnreachable
- })
- })
- t.Run("becomes alive if it receives a newer head", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- nodeChainID := types.RandomID()
- lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel)
- node := newAliveNode(t, testNodeOpts{
- rpc: rpc,
- chainID: nodeChainID,
- lggr: lggr,
- })
- defer func() { assert.NoError(t, node.close()) }()
-
- rpc.On("Dial", mock.Anything).Return(nil).Once()
- rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once()
-
- outOfSyncSubscription := mocks.NewSubscription(t)
- outOfSyncSubscription.On("Err").Return((<-chan error)(nil))
- outOfSyncSubscription.On("Unsubscribe").Once()
- const highestBlock = 1000
- ch := make(chan Head)
- rpc.On("SubscribeToHeads", mock.Anything).Run(func(args mock.Arguments) {
- go writeHeads(t, ch, head{BlockNumber: highestBlock - 1}, head{BlockNumber: highestBlock}, head{BlockNumber: highestBlock + 1})
- }).Return((<-chan Head)(ch), outOfSyncSubscription, nil).Once()
- rpc.On("GetInterceptedChainInfo").Return(ChainInfo{BlockNumber: highestBlock}, ChainInfo{BlockNumber: highestBlock})
- setupRPCForAliveLoop(t, rpc)
-
- node.declareOutOfSync(syncStatusNoNewHead)
- tests.AssertLogEventually(t, observedLogs, msgReceivedBlock)
- tests.AssertLogEventually(t, observedLogs, msgInSync)
- tests.AssertEventually(t, func() bool {
- return node.State() == nodeStateAlive
- })
- })
- t.Run("becomes alive if there is no other nodes", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- nodeChainID := types.RandomID()
- lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel)
- node := newAliveNode(t, testNodeOpts{
- chainConfig: clientMocks.ChainConfig{
- NoNewHeadsThresholdVal: tests.TestInterval,
- },
- rpc: rpc,
- chainID: nodeChainID,
- lggr: lggr,
- })
- defer func() { assert.NoError(t, node.close()) }()
- poolInfo := newMockPoolChainInfoProvider(t)
- poolInfo.On("LatestChainInfo").Return(0, ChainInfo{
- BlockNumber: 100,
- TotalDifficulty: big.NewInt(200),
- })
- node.SetPoolChainInfoProvider(poolInfo)
- rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{})
-
- rpc.On("Dial", mock.Anything).Return(nil).Once()
- rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once()
-
- outOfSyncSubscription := mocks.NewSubscription(t)
- outOfSyncSubscription.On("Err").Return((<-chan error)(nil))
- outOfSyncSubscription.On("Unsubscribe").Once()
- rpc.On("SubscribeToHeads", mock.Anything).Return(make(<-chan Head), outOfSyncSubscription, nil).Once()
- setupRPCForAliveLoop(t, rpc)
-
- node.declareOutOfSync(syncStatusNoNewHead)
- tests.AssertLogEventually(t, observedLogs, "RPC endpoint is still out of sync, but there are no other available nodes. This RPC node will be forcibly moved back into the live pool in a degraded state")
- tests.AssertEventually(t, func() bool {
- return node.State() == nodeStateAlive
- })
- })
- t.Run("Stays out-of-sync if received new head, but lags behind pool", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- nodeChainID := types.RandomID()
- lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel)
- node := newAliveNode(t, testNodeOpts{
- chainConfig: clientMocks.ChainConfig{
- NoNewHeadsThresholdVal: tests.TestInterval,
- },
- config: testNodeConfig{
- syncThreshold: 1,
- selectionMode: NodeSelectionModeHighestHead,
- },
- rpc: rpc,
- chainID: nodeChainID,
- lggr: lggr,
- })
- defer func() { assert.NoError(t, node.close()) }()
- poolInfo := newMockPoolChainInfoProvider(t)
- const highestBlock = 20
- poolInfo.On("LatestChainInfo").Return(1, ChainInfo{
- BlockNumber: highestBlock * 2,
- TotalDifficulty: big.NewInt(200),
- })
- node.SetPoolChainInfoProvider(poolInfo)
- rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{BlockNumber: highestBlock})
-
- rpc.On("Dial", mock.Anything).Return(nil).Once()
- rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once()
-
- outOfSyncSubscription := mocks.NewSubscription(t)
- outOfSyncSubscription.On("Err").Return((<-chan error)(nil))
- outOfSyncSubscription.On("Unsubscribe").Once()
- ch := make(chan Head)
- rpc.On("SubscribeToHeads", mock.Anything).Run(func(args mock.Arguments) {
- go writeHeads(t, ch, head{BlockNumber: highestBlock - 1}, head{BlockNumber: highestBlock}, head{BlockNumber: highestBlock + 1})
- }).Return((<-chan Head)(ch), outOfSyncSubscription, nil).Once()
-
- node.declareOutOfSync(syncStatusNoNewHead)
- tests.AssertLogEventually(t, observedLogs, msgReceivedBlock)
- tests.AssertLogEventually(t, observedLogs, "No new heads received for")
- tests.AssertEventually(t, func() bool {
- return node.State() == nodeStateOutOfSync
- })
- })
-
- // creates RPC mock with all calls necessary to create heads subscription that won't produce any events
- newRPCWithNoOpHeads := func(t *testing.T, chainID types.ID) *mockRPCClient[types.ID, Head] {
- rpc := newMockRPCClient[types.ID, Head](t)
- rpc.On("Dial", mock.Anything).Return(nil).Once()
- rpc.On("ChainID", mock.Anything).Return(chainID, nil).Once()
- sub := newSub(t)
- rpc.On("SubscribeToHeads", mock.Anything).Return(make(<-chan Head), sub, nil).Once()
- return rpc
- }
-
- t.Run("if fails to subscribe to finalized, becomes unreachable", func(t *testing.T) {
- t.Parallel()
- nodeChainID := types.RandomID()
- rpc := newRPCWithNoOpHeads(t, nodeChainID)
- node := newAliveNode(t, testNodeOpts{
- rpc: rpc,
- chainID: nodeChainID,
- chainConfig: clientMocks.ChainConfig{
- IsFinalityTagEnabled: true,
- },
- })
- defer func() { assert.NoError(t, node.close()) }()
-
- rpc.On("SubscribeToFinalizedHeads", mock.Anything).Return((<-chan Head)(nil), nil, errors.New("failed to subscribe")).Once()
- // unreachable
- rpc.On("Dial", mock.Anything).Return(errors.New("failed to redial")).Maybe()
-
- node.declareOutOfSync(syncStatusNoNewHead)
- tests.AssertEventually(t, func() bool {
- return node.State() == nodeStateUnreachable
- })
- })
- t.Run("on subscription termination becomes unreachable", func(t *testing.T) {
- t.Parallel()
- nodeChainID := types.RandomID()
- rpc := newRPCWithNoOpHeads(t, nodeChainID)
- lggr, observedLogs := logger.TestObserved(t, zap.ErrorLevel)
- node := newAliveNode(t, testNodeOpts{
- rpc: rpc,
- chainID: nodeChainID,
- lggr: lggr,
- chainConfig: clientMocks.ChainConfig{
- IsFinalityTagEnabled: true,
- },
- })
- defer func() { assert.NoError(t, node.close()) }()
-
- sub := mocks.NewSubscription(t)
- errChan := make(chan error, 1)
- errChan <- errors.New("subscription was terminate")
- sub.On("Err").Return((<-chan error)(errChan))
- sub.On("Unsubscribe").Once()
- rpc.On("SubscribeToFinalizedHeads", mock.Anything).Return(make(<-chan Head), sub, nil).Once()
- rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{})
-
- // unreachable
- rpc.On("Dial", mock.Anything).Return(errors.New("failed to redial")).Maybe()
- node.declareOutOfSync(syncStatusNoNewHead)
- tests.AssertLogEventually(t, observedLogs, "Finalized head subscription was terminated")
- tests.AssertEventually(t, func() bool {
- return node.State() == nodeStateUnreachable
- })
- })
- t.Run("becomes unreachable if head channel is closed", func(t *testing.T) {
- t.Parallel()
- nodeChainID := types.RandomID()
- rpc := newRPCWithNoOpHeads(t, nodeChainID)
- lggr, observedLogs := logger.TestObserved(t, zap.ErrorLevel)
- node := newAliveNode(t, testNodeOpts{
- rpc: rpc,
- chainID: nodeChainID,
- lggr: lggr,
- chainConfig: clientMocks.ChainConfig{
- IsFinalityTagEnabled: true,
- },
- })
- defer func() { assert.NoError(t, node.close()) }()
-
- sub := newSub(t)
-
- ch := make(chan Head)
- rpc.On("SubscribeToFinalizedHeads", mock.Anything).Run(func(args mock.Arguments) {
- close(ch)
- }).Return((<-chan Head)(ch), sub, nil).Once()
- rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{})
- // unreachable
- rpc.On("Dial", mock.Anything).Return(errors.New("failed to redial")).Maybe()
- node.declareOutOfSync(syncStatusNoNewHead)
- tests.AssertLogEventually(t, observedLogs, "Finalized heads subscription channel unexpectedly closed")
- tests.AssertEventually(t, func() bool {
- return node.State() == nodeStateUnreachable
- })
- })
- t.Run("becomes alive on new finalized block", func(t *testing.T) {
- t.Parallel()
- nodeChainID := types.RandomID()
- rpc := newRPCWithNoOpHeads(t, nodeChainID)
- lggr := logger.Test(t)
- node := newAliveNode(t, testNodeOpts{
- rpc: rpc,
- chainID: nodeChainID,
- lggr: lggr,
- chainConfig: clientMocks.ChainConfig{
- IsFinalityTagEnabled: true,
- NoNewFinalizedHeadsThresholdVal: tests.TestInterval,
- },
- })
- defer func() { assert.NoError(t, node.close()) }()
-
- const highestBlock = 13
- rpc.On("GetInterceptedChainInfo").Return(ChainInfo{FinalizedBlockNumber: highestBlock}, ChainInfo{FinalizedBlockNumber: highestBlock})
-
- outOfSyncSubscription := mocks.NewSubscription(t)
- outOfSyncSubscription.On("Err").Return((<-chan error)(nil))
- outOfSyncSubscription.On("Unsubscribe").Once()
- ch := make(chan Head)
- rpc.On("SubscribeToFinalizedHeads", mock.Anything).Return((<-chan Head)(ch), outOfSyncSubscription, nil).Once()
-
- setupRPCForAliveLoop(t, rpc)
-
- node.declareOutOfSync(syncStatusNoNewFinalizedHead)
- heads := []head{{BlockNumber: highestBlock - 1}, {BlockNumber: highestBlock}}
- writeHeads(t, ch, heads...)
- assert.Equal(t, nodeStateOutOfSync, node.State())
- writeHeads(t, ch, head{BlockNumber: highestBlock + 1})
- tests.AssertEventually(t, func() bool {
- return node.State() == nodeStateAlive
- })
- })
- t.Run("adds finalized block is not increasing flag, if there is no new finalized heads for too long", func(t *testing.T) {
- t.Parallel()
- nodeChainID := types.RandomID()
- rpc := newRPCWithNoOpHeads(t, nodeChainID)
- lggr, observed := logger.TestObserved(t, zap.DebugLevel)
- const noNewFinalizedHeads = tests.TestInterval
- node := newAliveNode(t, testNodeOpts{
- rpc: rpc,
- chainID: nodeChainID,
- lggr: lggr,
- chainConfig: clientMocks.ChainConfig{
- IsFinalityTagEnabled: true,
- NoNewFinalizedHeadsThresholdVal: noNewFinalizedHeads,
- },
- })
- defer func() { assert.NoError(t, node.close()) }()
-
- const highestBlock = 13
- rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{FinalizedBlockNumber: highestBlock})
-
- outOfSyncSubscription := mocks.NewSubscription(t)
- outOfSyncSubscription.On("Err").Return((<-chan error)(nil))
- outOfSyncSubscription.On("Unsubscribe").Once()
- ch := make(chan Head)
- rpc.On("SubscribeToFinalizedHeads", mock.Anything).Return((<-chan Head)(ch), outOfSyncSubscription, nil).Once()
-
- node.declareOutOfSync(syncStatusNotInSyncWithPool)
- heads := []head{{BlockNumber: highestBlock - 1}, {BlockNumber: highestBlock}}
- writeHeads(t, ch, heads...)
- assert.Equal(t, nodeStateOutOfSync, node.State())
- tests.AssertLogEventually(t, observed, fmt.Sprintf("No new finalized heads received for %s. Node stays "+
- "out-of-sync due to sync issues: NotInSyncWithRPCPool,NoNewFinalizedHead", noNewFinalizedHeads))
- })
-}
-
-func TestUnit_NodeLifecycle_unreachableLoop(t *testing.T) {
- t.Parallel()
-
- newAliveNode := func(t *testing.T, opts testNodeOpts) testNode {
- node := newTestNode(t, opts)
- opts.rpc.On("Close").Return(nil)
-
- node.setState(nodeStateAlive)
- return node
- }
- t.Run("returns on closed", func(t *testing.T) {
- t.Parallel()
- node := newTestNode(t, testNodeOpts{})
- node.setState(nodeStateClosed)
- node.wg.Add(1)
- node.unreachableLoop()
- })
- t.Run("on failed redial, keeps trying", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- nodeChainID := types.RandomID()
- lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel)
- node := newAliveNode(t, testNodeOpts{
- rpc: rpc,
- chainID: nodeChainID,
- lggr: lggr,
- })
- defer func() { assert.NoError(t, node.close()) }()
-
- rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial"))
- node.declareUnreachable()
- tests.AssertLogCountEventually(t, observedLogs, "Failed to redial RPC node; still unreachable", 2)
- })
- t.Run("on failed chainID verification, keep trying", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- nodeChainID := types.RandomID()
- lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel)
- node := newAliveNode(t, testNodeOpts{
- rpc: rpc,
- chainID: nodeChainID,
- lggr: lggr,
- })
- defer func() { assert.NoError(t, node.close()) }()
-
- rpc.On("Dial", mock.Anything).Return(nil)
- rpc.On("ChainID", mock.Anything).Run(func(_ mock.Arguments) {
- assert.Equal(t, nodeStateDialed, node.State())
- }).Return(nodeChainID, errors.New("failed to get chain id"))
- node.declareUnreachable()
- tests.AssertLogCountEventually(t, observedLogs, "Failed to verify chain ID for node", 2)
- })
- t.Run("on chain ID mismatch transitions to invalidChainID", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- nodeChainID := types.NewIDFromInt(10)
- rpcChainID := types.NewIDFromInt(11)
- node := newAliveNode(t, testNodeOpts{
- rpc: rpc,
- chainID: nodeChainID,
- })
- defer func() { assert.NoError(t, node.close()) }()
-
- rpc.On("Dial", mock.Anything).Return(nil)
- rpc.On("ChainID", mock.Anything).Return(rpcChainID, nil)
-
- node.declareUnreachable()
- tests.AssertEventually(t, func() bool {
- return node.State() == nodeStateInvalidChainID
- })
- })
- t.Run("on syncing status check failure, keeps trying", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- nodeChainID := types.RandomID()
- lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel)
- node := newAliveNode(t, testNodeOpts{
- rpc: rpc,
- chainID: nodeChainID,
- lggr: lggr,
- config: testNodeConfig{nodeIsSyncingEnabled: true},
- })
- defer func() { assert.NoError(t, node.close()) }()
-
- rpc.On("Dial", mock.Anything).Return(nil)
- rpc.On("ChainID", mock.Anything).Run(func(_ mock.Arguments) {
- assert.Equal(t, nodeStateDialed, node.State())
- }).Return(nodeChainID, nil)
- rpc.On("IsSyncing", mock.Anything).Return(false, errors.New("failed to check syncing status"))
- node.declareUnreachable()
- tests.AssertLogCountEventually(t, observedLogs, "Unexpected error while verifying RPC node synchronization status", 2)
- })
- t.Run("on syncing, transitions to syncing state", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- nodeChainID := types.RandomID()
- node := newAliveNode(t, testNodeOpts{
- rpc: rpc,
- chainID: nodeChainID,
- config: testNodeConfig{nodeIsSyncingEnabled: true},
- })
- defer func() { assert.NoError(t, node.close()) }()
-
- rpc.On("Dial", mock.Anything).Return(nil)
- rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil)
- rpc.On("IsSyncing", mock.Anything).Return(true, nil)
-
- setupRPCForAliveLoop(t, rpc)
-
- node.declareUnreachable()
- tests.AssertEventually(t, func() bool {
- return node.State() == nodeStateSyncing
- })
- })
- t.Run("on successful verification becomes alive", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- nodeChainID := types.RandomID()
- node := newAliveNode(t, testNodeOpts{
- rpc: rpc,
- chainID: nodeChainID,
- config: testNodeConfig{nodeIsSyncingEnabled: true},
- })
- defer func() { assert.NoError(t, node.close()) }()
-
- rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil)
- rpc.On("IsSyncing", mock.Anything).Return(false, nil)
- setupRPCForAliveLoop(t, rpc)
-
- node.declareUnreachable()
- tests.AssertEventually(t, func() bool {
- return node.State() == nodeStateAlive
- })
- })
- t.Run("on successful verification without isSyncing becomes alive", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- nodeChainID := types.RandomID()
- node := newAliveNode(t, testNodeOpts{
- rpc: rpc,
- chainID: nodeChainID,
- })
- defer func() { assert.NoError(t, node.close()) }()
-
- rpc.On("Dial", mock.Anything).Return(nil)
- rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil)
-
- setupRPCForAliveLoop(t, rpc)
-
- node.declareUnreachable()
- tests.AssertEventually(t, func() bool {
- return node.State() == nodeStateAlive
- })
- })
-}
-
-func TestUnit_NodeLifecycle_invalidChainIDLoop(t *testing.T) {
- t.Parallel()
- newDialedNode := func(t *testing.T, opts testNodeOpts) testNode {
- node := newTestNode(t, opts)
- opts.rpc.On("Close").Return(nil)
-
- node.setState(nodeStateDialed)
- return node
- }
- t.Run("returns on closed", func(t *testing.T) {
- t.Parallel()
- node := newTestNode(t, testNodeOpts{})
- node.setState(nodeStateClosed)
- node.wg.Add(1)
- node.invalidChainIDLoop()
- })
- t.Run("on invalid dial becomes unreachable", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- nodeChainID := types.RandomID()
- node := newDialedNode(t, testNodeOpts{
- rpc: rpc,
- chainID: nodeChainID,
- })
- defer func() { assert.NoError(t, node.close()) }()
-
- rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial"))
- rpc.On("Close")
-
- node.declareInvalidChainID()
- tests.AssertEventually(t, func() bool {
- return node.State() == nodeStateUnreachable
- })
- })
- t.Run("on failed chainID call becomes unreachable", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- nodeChainID := types.RandomID()
- lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel)
- node := newDialedNode(t, testNodeOpts{
- rpc: rpc,
- chainID: nodeChainID,
- lggr: lggr,
- })
- defer func() { assert.NoError(t, node.close()) }()
-
- rpc.On("ChainID", mock.Anything).Return(nodeChainID, errors.New("failed to get chain id"))
- // once for chainID and maybe another one for unreachable
- rpc.On("Dial", mock.Anything).Return(nil).Once()
- rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe()
-
- node.declareInvalidChainID()
- tests.AssertLogEventually(t, observedLogs, "Failed to verify chain ID for node")
- tests.AssertEventually(t, func() bool {
- return node.State() == nodeStateUnreachable
- })
- })
- t.Run("on chainID mismatch keeps trying", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- nodeChainID := types.NewIDFromInt(10)
- rpcChainID := types.NewIDFromInt(11)
- lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel)
- node := newDialedNode(t, testNodeOpts{
- rpc: rpc,
- chainID: nodeChainID,
- lggr: lggr,
- })
- defer func() { assert.NoError(t, node.close()) }()
-
- rpc.On("Dial", mock.Anything).Return(nil).Once()
- rpc.On("ChainID", mock.Anything).Return(rpcChainID, nil)
-
- node.declareInvalidChainID()
- tests.AssertLogCountEventually(t, observedLogs, "Failed to verify RPC node; remote endpoint returned the wrong chain ID", 2)
- tests.AssertEventually(t, func() bool {
- return node.State() == nodeStateInvalidChainID
- })
- })
- t.Run("on successful verification without isSyncing becomes alive", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- nodeChainID := types.NewIDFromInt(10)
- rpcChainID := types.NewIDFromInt(11)
- node := newDialedNode(t, testNodeOpts{
- rpc: rpc,
- chainID: nodeChainID,
- })
- defer func() { assert.NoError(t, node.close()) }()
-
- setupRPCForAliveLoop(t, rpc)
- rpc.On("ChainID", mock.Anything).Return(rpcChainID, nil).Once()
- rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once()
-
- node.declareInvalidChainID()
- tests.AssertEventually(t, func() bool {
- return node.State() == nodeStateAlive
- })
- })
- t.Run("on successful verification becomes alive", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- nodeChainID := types.NewIDFromInt(10)
- rpcChainID := types.NewIDFromInt(11)
- node := newDialedNode(t, testNodeOpts{
- rpc: rpc,
- chainID: nodeChainID,
- config: testNodeConfig{nodeIsSyncingEnabled: true},
- })
- defer func() { assert.NoError(t, node.close()) }()
-
- rpc.On("ChainID", mock.Anything).Return(rpcChainID, nil).Once()
- rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once()
- rpc.On("IsSyncing", mock.Anything).Return(false, nil).Once()
-
- setupRPCForAliveLoop(t, rpc)
-
- node.declareInvalidChainID()
- tests.AssertEventually(t, func() bool {
- return node.State() == nodeStateAlive
- })
- })
-}
-
-func TestUnit_NodeLifecycle_start(t *testing.T) {
- t.Parallel()
-
- newNode := func(t *testing.T, opts testNodeOpts) testNode {
- node := newTestNode(t, opts)
- opts.rpc.On("Close").Return(nil)
-
- return node
- }
- t.Run("if fails on initial dial, becomes unreachable", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- nodeChainID := types.RandomID()
- lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel)
- node := newNode(t, testNodeOpts{
- rpc: rpc,
- chainID: nodeChainID,
- lggr: lggr,
- })
- defer func() { assert.NoError(t, node.close()) }()
-
- rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial"))
- err := node.Start(tests.Context(t))
- assert.NoError(t, err)
- tests.AssertLogEventually(t, observedLogs, "Dial failed: Node is unreachable")
- tests.AssertEventually(t, func() bool {
- return node.State() == nodeStateUnreachable
- })
- })
- t.Run("if chainID verification fails, becomes unreachable", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- nodeChainID := types.RandomID()
- lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel)
- node := newNode(t, testNodeOpts{
- rpc: rpc,
- chainID: nodeChainID,
- lggr: lggr,
- })
- defer func() { assert.NoError(t, node.close()) }()
-
- rpc.On("Dial", mock.Anything).Return(nil)
- rpc.On("ChainID", mock.Anything).Run(func(_ mock.Arguments) {
- assert.Equal(t, nodeStateDialed, node.State())
- }).Return(nodeChainID, errors.New("failed to get chain id"))
- err := node.Start(tests.Context(t))
- assert.NoError(t, err)
- tests.AssertLogEventually(t, observedLogs, "Failed to verify chain ID for node")
- tests.AssertEventually(t, func() bool {
- return node.State() == nodeStateUnreachable
- })
- })
- t.Run("on chain ID mismatch transitions to invalidChainID", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- nodeChainID := types.NewIDFromInt(10)
- rpcChainID := types.NewIDFromInt(11)
- node := newNode(t, testNodeOpts{
- rpc: rpc,
- chainID: nodeChainID,
- })
- defer func() { assert.NoError(t, node.close()) }()
-
- rpc.On("Dial", mock.Anything).Return(nil)
-
- rpc.On("ChainID", mock.Anything).Return(rpcChainID, nil)
- err := node.Start(tests.Context(t))
- assert.NoError(t, err)
- tests.AssertEventually(t, func() bool {
- return node.State() == nodeStateInvalidChainID
- })
- })
- t.Run("if syncing verification fails, becomes unreachable", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- nodeChainID := types.RandomID()
- lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel)
- node := newNode(t, testNodeOpts{
- rpc: rpc,
- chainID: nodeChainID,
- lggr: lggr,
- config: testNodeConfig{nodeIsSyncingEnabled: true},
- })
- defer func() { assert.NoError(t, node.close()) }()
-
- rpc.On("Dial", mock.Anything).Return(nil).Once()
-
- rpc.On("ChainID", mock.Anything).Run(func(_ mock.Arguments) {
- assert.Equal(t, nodeStateDialed, node.State())
- }).Return(nodeChainID, nil).Once()
- rpc.On("IsSyncing", mock.Anything).Return(false, errors.New("failed to check syncing status"))
- rpc.On("Dial", mock.Anything).Return(errors.New("failed to redial"))
- err := node.Start(tests.Context(t))
- assert.NoError(t, err)
- tests.AssertLogEventually(t, observedLogs, "Unexpected error while verifying RPC node synchronization status")
- tests.AssertEventually(t, func() bool {
- return node.State() == nodeStateUnreachable
- })
- })
- t.Run("on isSyncing transitions to syncing", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- nodeChainID := types.NewIDFromInt(10)
- node := newNode(t, testNodeOpts{
- rpc: rpc,
- chainID: nodeChainID,
- config: testNodeConfig{nodeIsSyncingEnabled: true},
- })
- defer func() { assert.NoError(t, node.close()) }()
-
- rpc.On("Dial", mock.Anything).Return(nil)
-
- rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil)
- rpc.On("IsSyncing", mock.Anything).Return(true, nil)
- err := node.Start(tests.Context(t))
- assert.NoError(t, err)
- tests.AssertEventually(t, func() bool {
- return node.State() == nodeStateSyncing
- })
- })
- t.Run("on successful verification becomes alive", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- nodeChainID := types.RandomID()
- node := newNode(t, testNodeOpts{
- rpc: rpc,
- chainID: nodeChainID,
- config: testNodeConfig{nodeIsSyncingEnabled: true},
- })
- defer func() { assert.NoError(t, node.close()) }()
-
- rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil)
- rpc.On("IsSyncing", mock.Anything).Return(false, nil)
- setupRPCForAliveLoop(t, rpc)
-
- err := node.Start(tests.Context(t))
- assert.NoError(t, err)
- tests.AssertEventually(t, func() bool {
- return node.State() == nodeStateAlive
- })
- })
- t.Run("on successful verification without isSyncing becomes alive", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- nodeChainID := types.RandomID()
- node := newNode(t, testNodeOpts{
- rpc: rpc,
- chainID: nodeChainID,
- })
- defer func() { assert.NoError(t, node.close()) }()
-
- rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil)
- setupRPCForAliveLoop(t, rpc)
-
- err := node.Start(tests.Context(t))
- assert.NoError(t, err)
- tests.AssertEventually(t, func() bool {
- return node.State() == nodeStateAlive
- })
- })
-}
-
-func TestUnit_NodeLifecycle_outOfSyncWithPool(t *testing.T) {
- t.Parallel()
- t.Run("skip if nLiveNodes is not configured", func(t *testing.T) {
- node := newTestNode(t, testNodeOpts{})
- outOfSync, liveNodes := node.isOutOfSyncWithPool()
- assert.Equal(t, false, outOfSync)
- assert.Equal(t, 0, liveNodes)
- })
- t.Run("skip if syncThreshold is not configured", func(t *testing.T) {
- node := newTestNode(t, testNodeOpts{})
- poolInfo := newMockPoolChainInfoProvider(t)
- node.SetPoolChainInfoProvider(poolInfo)
- outOfSync, liveNodes := node.isOutOfSyncWithPool()
- assert.Equal(t, false, outOfSync)
- assert.Equal(t, 0, liveNodes)
- })
- t.Run("panics on invalid selection mode", func(t *testing.T) {
- node := newTestNode(t, testNodeOpts{
- config: testNodeConfig{syncThreshold: 1},
- })
- poolInfo := newMockPoolChainInfoProvider(t)
- poolInfo.On("LatestChainInfo").Return(1, ChainInfo{}).Once()
- node.SetPoolChainInfoProvider(poolInfo)
- assert.Panics(t, func() {
- _, _ = node.isOutOfSyncWithPool()
- })
- })
- t.Run("block height selection mode", func(t *testing.T) {
- const syncThreshold = 10
- const highestBlock = 1000
- const nodesNum = 20
- const totalDifficulty = 3000
- testCases := []struct {
- name string
- blockNumber int64
- outOfSync bool
- }{
- {
- name: "below threshold",
- blockNumber: highestBlock - syncThreshold - 1,
- outOfSync: true,
- },
- {
- name: "equal to threshold",
- blockNumber: highestBlock - syncThreshold,
- outOfSync: false,
- },
- {
- name: "equal to highest block",
- blockNumber: highestBlock,
- outOfSync: false,
- },
- {
- name: "higher than highest block",
- blockNumber: highestBlock,
- outOfSync: false,
- },
- }
-
- for _, selectionMode := range []string{NodeSelectionModeHighestHead, NodeSelectionModeRoundRobin, NodeSelectionModePriorityLevel} {
- node := newTestNode(t, testNodeOpts{
- config: testNodeConfig{
- syncThreshold: syncThreshold,
- selectionMode: selectionMode,
- },
- })
- poolInfo := newMockPoolChainInfoProvider(t)
- poolInfo.On("LatestChainInfo").Return(nodesNum, ChainInfo{
- BlockNumber: highestBlock,
- TotalDifficulty: big.NewInt(totalDifficulty),
- })
- node.SetPoolChainInfoProvider(poolInfo)
- for _, td := range []int64{totalDifficulty - syncThreshold - 1, totalDifficulty - syncThreshold, totalDifficulty, totalDifficulty + 1} {
- for _, testCase := range testCases {
- t.Run(fmt.Sprintf("%s: SelectionModeVal: %s: total difficulty: %d", testCase.name, selectionMode, td), func(t *testing.T) {
- chainInfo := ChainInfo{BlockNumber: testCase.blockNumber, TotalDifficulty: big.NewInt(td)}
- rpc := newMockRPCClient[types.ID, Head](t)
- rpc.On("GetInterceptedChainInfo").Return(chainInfo, ChainInfo{}).Once()
- node.rpc = rpc
- outOfSync, liveNodes := node.isOutOfSyncWithPool()
- assert.Equal(t, nodesNum, liveNodes)
- assert.Equal(t, testCase.outOfSync, outOfSync)
- })
- }
- }
- }
- })
- t.Run("total difficulty selection mode", func(t *testing.T) {
- const syncThreshold = 10
- const highestBlock = 1000
- const nodesNum = 20
- const totalDifficulty = 3000
- testCases := []struct {
- name string
- totalDifficulty int64
- outOfSync bool
- }{
- {
- name: "below threshold",
- totalDifficulty: totalDifficulty - syncThreshold - 1,
- outOfSync: true,
- },
- {
- name: "equal to threshold",
- totalDifficulty: totalDifficulty - syncThreshold,
- outOfSync: false,
- },
- {
- name: "equal to highest block",
- totalDifficulty: totalDifficulty,
- outOfSync: false,
- },
- {
- name: "higher than highest block",
- totalDifficulty: totalDifficulty,
- outOfSync: false,
- },
- }
-
- node := newTestNode(t, testNodeOpts{
- config: testNodeConfig{
- syncThreshold: syncThreshold,
- selectionMode: NodeSelectionModeTotalDifficulty,
- },
- })
-
- poolInfo := newMockPoolChainInfoProvider(t)
- poolInfo.On("LatestChainInfo").Return(nodesNum, ChainInfo{
- BlockNumber: highestBlock,
- TotalDifficulty: big.NewInt(totalDifficulty),
- })
- node.SetPoolChainInfoProvider(poolInfo)
- for _, hb := range []int64{highestBlock - syncThreshold - 1, highestBlock - syncThreshold, highestBlock, highestBlock + 1} {
- for _, testCase := range testCases {
- t.Run(fmt.Sprintf("%s: SelectionModeVal: %s: highest block: %d", testCase.name, NodeSelectionModeTotalDifficulty, hb), func(t *testing.T) {
- chainInfo := ChainInfo{BlockNumber: hb, TotalDifficulty: big.NewInt(testCase.totalDifficulty)}
- rpc := newMockRPCClient[types.ID, Head](t)
- rpc.On("GetInterceptedChainInfo").Return(chainInfo, ChainInfo{}).Once()
- node.rpc = rpc
- outOfSync, liveNodes := node.isOutOfSyncWithPool()
- assert.Equal(t, nodesNum, liveNodes)
- assert.Equal(t, testCase.outOfSync, outOfSync)
- })
- }
- }
- })
-}
-
-func TestUnit_NodeLifecycle_SyncingLoop(t *testing.T) {
- t.Parallel()
- newDialedNode := func(t *testing.T, opts testNodeOpts) testNode {
- opts.config.nodeIsSyncingEnabled = true
- node := newTestNode(t, opts)
- opts.rpc.On("Close").Return(nil)
-
- node.setState(nodeStateDialed)
- return node
- }
- t.Run("returns on closed", func(t *testing.T) {
- t.Parallel()
- node := newTestNode(t, testNodeOpts{})
- node.setState(nodeStateClosed)
- node.wg.Add(1)
- node.syncingLoop()
- })
- t.Run("on invalid dial becomes unreachable", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- nodeChainID := types.RandomID()
- node := newDialedNode(t, testNodeOpts{
- rpc: rpc,
- chainID: nodeChainID,
- })
- defer func() { assert.NoError(t, node.close()) }()
-
- rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial"))
-
- node.declareSyncing()
- tests.AssertEventually(t, func() bool {
- return node.State() == nodeStateUnreachable
- })
- })
- t.Run("on failed chainID call becomes unreachable", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- nodeChainID := types.RandomID()
- lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel)
- node := newDialedNode(t, testNodeOpts{
- rpc: rpc,
- chainID: nodeChainID,
- lggr: lggr,
- })
- defer func() { assert.NoError(t, node.close()) }()
-
- rpc.On("ChainID", mock.Anything).Return(nodeChainID, errors.New("failed to get chain id"))
-
- // once for syncing and maybe another one for unreachable
- rpc.On("Dial", mock.Anything).Return(nil).Once()
- rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe()
- node.declareSyncing()
- tests.AssertLogEventually(t, observedLogs, "Failed to verify chain ID for node")
- tests.AssertEventually(t, func() bool {
- return node.State() == nodeStateUnreachable
- })
- })
- t.Run("on chainID mismatch transitions to invalidChainID", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- nodeChainID := types.NewIDFromInt(10)
- rpcChainID := types.NewIDFromInt(11)
- lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel)
- node := newDialedNode(t, testNodeOpts{
- rpc: rpc,
- chainID: nodeChainID,
- lggr: lggr,
- })
- defer func() { assert.NoError(t, node.close()) }()
-
- rpc.On("Dial", mock.Anything).Return(nil).Twice()
-
- rpc.On("ChainID", mock.Anything).Return(rpcChainID, nil)
- node.declareSyncing()
- tests.AssertLogCountEventually(t, observedLogs, "Failed to verify RPC node; remote endpoint returned the wrong chain ID", 2)
- tests.AssertEventually(t, func() bool {
- return node.State() == nodeStateInvalidChainID
- })
- })
- t.Run("on failed Syncing check - becomes unreachable", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- nodeChainID := types.RandomID()
- lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel)
- node := newDialedNode(t, testNodeOpts{
- rpc: rpc,
- chainID: nodeChainID,
- lggr: lggr,
- })
- defer func() { assert.NoError(t, node.close()) }()
-
- rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once()
- // first one is needed to enter internal loop
- rpc.On("IsSyncing", mock.Anything).Return(true, nil).Once()
- rpc.On("IsSyncing", mock.Anything).Return(false, errors.New("failed to check if syncing")).Once()
- rpc.On("Dial", mock.Anything).Return(nil).Once()
- rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe()
-
- node.declareSyncing()
- tests.AssertLogEventually(t, observedLogs, "Unexpected error while verifying RPC node synchronization status")
- tests.AssertEventually(t, func() bool {
- return node.State() == nodeStateUnreachable
- })
- })
- t.Run("on IsSyncing - keeps trying", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- nodeChainID := types.RandomID()
- lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel)
- node := newDialedNode(t, testNodeOpts{
- rpc: rpc,
- chainID: nodeChainID,
- lggr: lggr,
- })
- defer func() { assert.NoError(t, node.close()) }()
-
- rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once()
- rpc.On("IsSyncing", mock.Anything).Return(true, nil)
- rpc.On("Dial", mock.Anything).Return(nil).Once()
-
- node.declareSyncing()
- tests.AssertLogCountEventually(t, observedLogs, "Verification failed: Node is syncing", 2)
- tests.AssertEventually(t, func() bool {
- return node.State() == nodeStateSyncing
- })
- })
- t.Run("on successful verification becomes alive", func(t *testing.T) {
- t.Parallel()
- rpc := newMockRPCClient[types.ID, Head](t)
- nodeChainID := types.RandomID()
- node := newDialedNode(t, testNodeOpts{
- rpc: rpc,
- chainID: nodeChainID,
- })
- defer func() { assert.NoError(t, node.close()) }()
-
- rpc.On("Dial", mock.Anything).Return(nil).Once()
-
- rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once()
- rpc.On("IsSyncing", mock.Anything).Return(true, nil).Once()
- rpc.On("IsSyncing", mock.Anything).Return(false, nil).Once()
-
- setupRPCForAliveLoop(t, rpc)
-
- node.declareSyncing()
- tests.AssertEventually(t, func() bool {
- return node.State() == nodeStateAlive
- })
- })
-}
-
-func TestNode_State(t *testing.T) {
- t.Run("If not Alive, returns as is", func(t *testing.T) {
- for state := nodeState(0); state < nodeStateLen; state++ {
- if state == nodeStateAlive {
- continue
- }
-
- node := newTestNode(t, testNodeOpts{})
- node.setState(state)
- assert.Equal(t, state, node.State())
- }
- })
- t.Run("If repeatable read is not enforced, returns alive", func(t *testing.T) {
- node := newTestNode(t, testNodeOpts{})
- node.setState(nodeStateAlive)
- assert.Equal(t, nodeStateAlive, node.State())
- })
- testCases := []struct {
- Name string
- FinalizedBlockOffsetVal uint32
- IsFinalityTagEnabled bool
- PoolChainInfo ChainInfo
- NodeChainInfo ChainInfo
- ExpectedState nodeState
- }{
- {
- Name: "If finality lag does not exceeds offset, returns alive (FinalityDepth)",
- FinalizedBlockOffsetVal: 15,
- PoolChainInfo: ChainInfo{
- BlockNumber: 20,
- },
- NodeChainInfo: ChainInfo{
- BlockNumber: 5,
- },
- ExpectedState: nodeStateAlive,
- },
- {
- Name: "If finality lag does not exceeds offset, returns alive (FinalityTag)",
- FinalizedBlockOffsetVal: 15,
- IsFinalityTagEnabled: true,
- PoolChainInfo: ChainInfo{
- FinalizedBlockNumber: 20,
- },
- NodeChainInfo: ChainInfo{
- FinalizedBlockNumber: 5,
- },
- ExpectedState: nodeStateAlive,
- },
- {
- Name: "If finality lag exceeds offset, returns nodeStateFinalizedBlockOutOfSync (FinalityDepth)",
- FinalizedBlockOffsetVal: 15,
- PoolChainInfo: ChainInfo{
- BlockNumber: 20,
- },
- NodeChainInfo: ChainInfo{
- BlockNumber: 4,
- },
- ExpectedState: nodeStateFinalizedBlockOutOfSync,
- },
- {
- Name: "If finality lag exceeds offset, returns nodeStateFinalizedBlockOutOfSync (FinalityTag)",
- FinalizedBlockOffsetVal: 15,
- IsFinalityTagEnabled: true,
- PoolChainInfo: ChainInfo{
- FinalizedBlockNumber: 20,
- },
- NodeChainInfo: ChainInfo{
- FinalizedBlockNumber: 4,
- },
- ExpectedState: nodeStateFinalizedBlockOutOfSync,
- },
- }
- for _, tc := range testCases {
- t.Run(tc.Name, func(t *testing.T) {
- rpc := newMockRPCClient[types.ID, Head](t)
- rpc.On("GetInterceptedChainInfo").Return(tc.NodeChainInfo, tc.PoolChainInfo).Once()
- node := newTestNode(t, testNodeOpts{
- config: testNodeConfig{
- enforceRepeatableRead: true,
- },
- chainConfig: clientMocks.ChainConfig{
- FinalizedBlockOffsetVal: tc.FinalizedBlockOffsetVal,
- IsFinalityTagEnabled: tc.IsFinalityTagEnabled,
- },
- rpc: rpc,
- })
- poolInfo := newMockPoolChainInfoProvider(t)
- poolInfo.On("HighestUserObservations").Return(tc.PoolChainInfo).Once()
- node.SetPoolChainInfoProvider(poolInfo)
- node.setState(nodeStateAlive)
- assert.Equal(t, tc.ExpectedState, node.State())
- })
- }
-}
diff --git a/common/client/node_selector.go b/common/client/node_selector.go
deleted file mode 100644
index 372b521bb1c..00000000000
--- a/common/client/node_selector.go
+++ /dev/null
@@ -1,43 +0,0 @@
-package client
-
-import (
- "fmt"
-
- "github.com/smartcontractkit/chainlink/v2/common/types"
-)
-
-const (
- NodeSelectionModeHighestHead = "HighestHead"
- NodeSelectionModeRoundRobin = "RoundRobin"
- NodeSelectionModeTotalDifficulty = "TotalDifficulty"
- NodeSelectionModePriorityLevel = "PriorityLevel"
-)
-
-type NodeSelector[
- CHAIN_ID types.ID,
- RPC any,
-] interface {
- // Select returns a Node, or nil if none can be selected.
- // Implementation must be thread-safe.
- Select() Node[CHAIN_ID, RPC]
- // Name returns the strategy name, e.g. "HighestHead" or "RoundRobin"
- Name() string
-}
-
-func newNodeSelector[
- CHAIN_ID types.ID,
- RPC any,
-](selectionMode string, nodes []Node[CHAIN_ID, RPC]) NodeSelector[CHAIN_ID, RPC] {
- switch selectionMode {
- case NodeSelectionModeHighestHead:
- return NewHighestHeadNodeSelector[CHAIN_ID, RPC](nodes)
- case NodeSelectionModeRoundRobin:
- return NewRoundRobinSelector[CHAIN_ID, RPC](nodes)
- case NodeSelectionModeTotalDifficulty:
- return NewTotalDifficultyNodeSelector[CHAIN_ID, RPC](nodes)
- case NodeSelectionModePriorityLevel:
- return NewPriorityLevelNodeSelector[CHAIN_ID, RPC](nodes)
- default:
- panic(fmt.Sprintf("unsupported NodeSelectionMode: %s", selectionMode))
- }
-}
diff --git a/common/client/node_selector_highest_head.go b/common/client/node_selector_highest_head.go
deleted file mode 100644
index 454584a77e1..00000000000
--- a/common/client/node_selector_highest_head.go
+++ /dev/null
@@ -1,40 +0,0 @@
-package client
-
-import (
- "math"
-
- "github.com/smartcontractkit/chainlink/v2/common/types"
-)
-
-type highestHeadNodeSelector[
- CHAIN_ID types.ID,
- RPC any,
-] []Node[CHAIN_ID, RPC]
-
-func NewHighestHeadNodeSelector[
- CHAIN_ID types.ID,
- RPC any,
-](nodes []Node[CHAIN_ID, RPC]) NodeSelector[CHAIN_ID, RPC] {
- return highestHeadNodeSelector[CHAIN_ID, RPC](nodes)
-}
-
-func (s highestHeadNodeSelector[CHAIN_ID, RPC]) Select() Node[CHAIN_ID, RPC] {
- var highestHeadNumber int64 = math.MinInt64
- var highestHeadNodes []Node[CHAIN_ID, RPC]
- for _, n := range s {
- state, currentChainInfo := n.StateAndLatest()
- currentHeadNumber := currentChainInfo.BlockNumber
- if state == nodeStateAlive && currentHeadNumber >= highestHeadNumber {
- if highestHeadNumber < currentHeadNumber {
- highestHeadNumber = currentHeadNumber
- highestHeadNodes = nil
- }
- highestHeadNodes = append(highestHeadNodes, n)
- }
- }
- return firstOrHighestPriority(highestHeadNodes)
-}
-
-func (s highestHeadNodeSelector[CHAIN_ID, RPC]) Name() string {
- return NodeSelectionModeHighestHead
-}
diff --git a/common/client/node_selector_highest_head_test.go b/common/client/node_selector_highest_head_test.go
deleted file mode 100644
index 9d85c688e05..00000000000
--- a/common/client/node_selector_highest_head_test.go
+++ /dev/null
@@ -1,176 +0,0 @@
-package client
-
-import (
- "testing"
-
- "github.com/stretchr/testify/assert"
-
- "github.com/smartcontractkit/chainlink/v2/common/types"
-)
-
-func TestHighestHeadNodeSelectorName(t *testing.T) {
- selector := newNodeSelector[types.ID, RPCClient[types.ID, Head]](NodeSelectionModeHighestHead, nil)
- assert.Equal(t, selector.Name(), NodeSelectionModeHighestHead)
-}
-
-func TestHighestHeadNodeSelector(t *testing.T) {
- t.Parallel()
-
- type nodeClient RPCClient[types.ID, Head]
-
- var nodes []Node[types.ID, nodeClient]
-
- for i := 0; i < 3; i++ {
- node := newMockNode[types.ID, nodeClient](t)
- if i == 0 {
- // first node is out of sync
- node.On("StateAndLatest").Return(nodeStateOutOfSync, ChainInfo{BlockNumber: int64(-1)})
- } else if i == 1 {
- // second node is alive, LatestReceivedBlockNumber = 1
- node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(1)})
- } else {
- // third node is alive, LatestReceivedBlockNumber = 2 (best node)
- node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(2)})
- }
- node.On("Order").Maybe().Return(int32(1))
- nodes = append(nodes, node)
- }
-
- selector := newNodeSelector[types.ID, nodeClient](NodeSelectionModeHighestHead, nodes)
- assert.Same(t, nodes[2], selector.Select())
-
- t.Run("stick to the same node", func(t *testing.T) {
- node := newMockNode[types.ID, nodeClient](t)
- // fourth node is alive, LatestReceivedBlockNumber = 2 (same as 3rd)
- node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(2)})
- node.On("Order").Return(int32(1))
- nodes = append(nodes, node)
-
- selector := newNodeSelector(NodeSelectionModeHighestHead, nodes)
- assert.Same(t, nodes[2], selector.Select())
- })
-
- t.Run("another best node", func(t *testing.T) {
- node := newMockNode[types.ID, nodeClient](t)
- // fifth node is alive, LatestReceivedBlockNumber = 3 (better than 3rd and 4th)
- node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(3)})
- node.On("Order").Return(int32(1))
- nodes = append(nodes, node)
-
- selector := newNodeSelector(NodeSelectionModeHighestHead, nodes)
- assert.Same(t, nodes[4], selector.Select())
- })
-
- t.Run("nodes never update latest block number", func(t *testing.T) {
- node1 := newMockNode[types.ID, nodeClient](t)
- node1.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(-1)})
- node1.On("Order").Return(int32(1))
- node2 := newMockNode[types.ID, nodeClient](t)
- node2.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(-1)})
- node2.On("Order").Return(int32(1))
- selector := newNodeSelector(NodeSelectionModeHighestHead, []Node[types.ID, nodeClient]{node1, node2})
- assert.Same(t, node1, selector.Select())
- })
-}
-
-func TestHighestHeadNodeSelector_None(t *testing.T) {
- t.Parallel()
-
- type nodeClient RPCClient[types.ID, Head]
- var nodes []Node[types.ID, nodeClient]
-
- for i := 0; i < 3; i++ {
- node := newMockNode[types.ID, nodeClient](t)
- if i == 0 {
- // first node is out of sync
- node.On("StateAndLatest").Return(nodeStateOutOfSync, ChainInfo{BlockNumber: int64(-1)})
- } else {
- // others are unreachable
- node.On("StateAndLatest").Return(nodeStateUnreachable, ChainInfo{BlockNumber: int64(-1)})
- }
- nodes = append(nodes, node)
- }
-
- selector := newNodeSelector(NodeSelectionModeHighestHead, nodes)
- assert.Nil(t, selector.Select())
-}
-
-func TestHighestHeadNodeSelectorWithOrder(t *testing.T) {
- t.Parallel()
-
- type nodeClient RPCClient[types.ID, Head]
- var nodes []Node[types.ID, nodeClient]
-
- t.Run("same head and order", func(t *testing.T) {
- for i := 0; i < 3; i++ {
- node := newMockNode[types.ID, nodeClient](t)
- node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(1)})
- node.On("Order").Return(int32(2))
- nodes = append(nodes, node)
- }
- selector := newNodeSelector(NodeSelectionModeHighestHead, nodes)
- // Should select the first node because all things are equal
- assert.Same(t, nodes[0], selector.Select())
- })
-
- t.Run("same head but different order", func(t *testing.T) {
- node1 := newMockNode[types.ID, nodeClient](t)
- node1.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(3)})
- node1.On("Order").Return(int32(3))
-
- node2 := newMockNode[types.ID, nodeClient](t)
- node2.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(3)})
- node2.On("Order").Return(int32(1))
-
- node3 := newMockNode[types.ID, nodeClient](t)
- node3.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(3)})
- node3.On("Order").Return(int32(2))
-
- nodes := []Node[types.ID, nodeClient]{node1, node2, node3}
- selector := newNodeSelector(NodeSelectionModeHighestHead, nodes)
- // Should select the second node as it has the highest priority
- assert.Same(t, nodes[1], selector.Select())
- })
-
- t.Run("different head but same order", func(t *testing.T) {
- node1 := newMockNode[types.ID, nodeClient](t)
- node1.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(1)})
- node1.On("Order").Maybe().Return(int32(3))
-
- node2 := newMockNode[types.ID, nodeClient](t)
- node2.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(2)})
- node2.On("Order").Maybe().Return(int32(3))
-
- node3 := newMockNode[types.ID, nodeClient](t)
- node3.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(3)})
- node3.On("Order").Return(int32(3))
-
- nodes := []Node[types.ID, nodeClient]{node1, node2, node3}
- selector := newNodeSelector(NodeSelectionModeHighestHead, nodes)
- // Should select the third node as it has the highest head
- assert.Same(t, nodes[2], selector.Select())
- })
-
- t.Run("different head and different order", func(t *testing.T) {
- node1 := newMockNode[types.ID, nodeClient](t)
- node1.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(10)})
- node1.On("Order").Maybe().Return(int32(3))
-
- node2 := newMockNode[types.ID, nodeClient](t)
- node2.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(11)})
- node2.On("Order").Maybe().Return(int32(4))
-
- node3 := newMockNode[types.ID, nodeClient](t)
- node3.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(11)})
- node3.On("Order").Maybe().Return(int32(3))
-
- node4 := newMockNode[types.ID, nodeClient](t)
- node4.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(10)})
- node4.On("Order").Maybe().Return(int32(1))
-
- nodes := []Node[types.ID, nodeClient]{node1, node2, node3, node4}
- selector := newNodeSelector(NodeSelectionModeHighestHead, nodes)
- // Should select the third node as it has the highest head and will win the priority tie-breaker
- assert.Same(t, nodes[2], selector.Select())
- })
-}
diff --git a/common/client/node_selector_priority_level.go b/common/client/node_selector_priority_level.go
deleted file mode 100644
index 6d6784fb216..00000000000
--- a/common/client/node_selector_priority_level.go
+++ /dev/null
@@ -1,123 +0,0 @@
-package client
-
-import (
- "math"
- "sort"
- "sync/atomic"
-
- "github.com/smartcontractkit/chainlink/v2/common/types"
-)
-
-type priorityLevelNodeSelector[
- CHAIN_ID types.ID,
- RPC any,
-] struct {
- nodes []Node[CHAIN_ID, RPC]
- roundRobinCount []atomic.Uint32
-}
-
-type nodeWithPriority[
- CHAIN_ID types.ID,
- RPC any,
-] struct {
- node Node[CHAIN_ID, RPC]
- priority int32
-}
-
-func NewPriorityLevelNodeSelector[
- CHAIN_ID types.ID,
- RPC any,
-](nodes []Node[CHAIN_ID, RPC]) NodeSelector[CHAIN_ID, RPC] {
- return &priorityLevelNodeSelector[CHAIN_ID, RPC]{
- nodes: nodes,
- roundRobinCount: make([]atomic.Uint32, nrOfPriorityTiers(nodes)),
- }
-}
-
-func (s priorityLevelNodeSelector[CHAIN_ID, RPC]) Select() Node[CHAIN_ID, RPC] {
- nodes := s.getHighestPriorityAliveTier()
-
- if len(nodes) == 0 {
- return nil
- }
- priorityLevel := nodes[len(nodes)-1].priority
-
- // NOTE: Inc returns the number after addition, so we must -1 to get the "current" counter
- count := s.roundRobinCount[priorityLevel].Add(1) - 1
- idx := int(count % uint32(len(nodes)))
-
- return nodes[idx].node
-}
-
-func (s priorityLevelNodeSelector[CHAIN_ID, RPC]) Name() string {
- return NodeSelectionModePriorityLevel
-}
-
-// getHighestPriorityAliveTier filters nodes that are not in state nodeStateAlive and
-// returns only the highest tier of alive nodes
-func (s priorityLevelNodeSelector[CHAIN_ID, RPC]) getHighestPriorityAliveTier() []nodeWithPriority[CHAIN_ID, RPC] {
- var nodes []nodeWithPriority[CHAIN_ID, RPC]
- for _, n := range s.nodes {
- if n.State() == nodeStateAlive {
- nodes = append(nodes, nodeWithPriority[CHAIN_ID, RPC]{n, n.Order()})
- }
- }
-
- if len(nodes) == 0 {
- return nil
- }
-
- return removeLowerTiers(nodes)
-}
-
-// removeLowerTiers take a slice of nodeWithPriority[CHAIN_ID, BLOCK_HASH, HEAD, RPC] and keeps only the highest tier
-func removeLowerTiers[
- CHAIN_ID types.ID,
- RPC any,
-](nodes []nodeWithPriority[CHAIN_ID, RPC]) []nodeWithPriority[CHAIN_ID, RPC] {
- sort.SliceStable(nodes, func(i, j int) bool {
- return nodes[i].priority > nodes[j].priority
- })
-
- var nodes2 []nodeWithPriority[CHAIN_ID, RPC]
- currentPriority := nodes[len(nodes)-1].priority
-
- for _, n := range nodes {
- if n.priority == currentPriority {
- nodes2 = append(nodes2, n)
- }
- }
-
- return nodes2
-}
-
-// nrOfPriorityTiers calculates the total number of priority tiers
-func nrOfPriorityTiers[
- CHAIN_ID types.ID,
- RPC any,
-](nodes []Node[CHAIN_ID, RPC]) int32 {
- highestPriority := int32(0)
- for _, n := range nodes {
- priority := n.Order()
- if highestPriority < priority {
- highestPriority = priority
- }
- }
- return highestPriority + 1
-}
-
-// firstOrHighestPriority takes a list of nodes and returns the first one with the highest priority
-func firstOrHighestPriority[
- CHAIN_ID types.ID,
- RPC any,
-](nodes []Node[CHAIN_ID, RPC]) Node[CHAIN_ID, RPC] {
- hp := int32(math.MaxInt32)
- var node Node[CHAIN_ID, RPC]
- for _, n := range nodes {
- if n.Order() < hp {
- hp = n.Order()
- node = n
- }
- }
- return node
-}
diff --git a/common/client/node_selector_priority_level_test.go b/common/client/node_selector_priority_level_test.go
deleted file mode 100644
index 67aac97be1b..00000000000
--- a/common/client/node_selector_priority_level_test.go
+++ /dev/null
@@ -1,91 +0,0 @@
-package client
-
-import (
- "testing"
-
- "github.com/smartcontractkit/chainlink/v2/common/types"
-
- "github.com/stretchr/testify/assert"
-)
-
-func TestPriorityLevelNodeSelectorName(t *testing.T) {
- selector := newNodeSelector[types.ID, RPCClient[types.ID, Head]](NodeSelectionModePriorityLevel, nil)
- assert.Equal(t, selector.Name(), NodeSelectionModePriorityLevel)
-}
-
-func TestPriorityLevelNodeSelector(t *testing.T) {
- t.Parallel()
-
- type nodeClient RPCClient[types.ID, Head]
- type testNode struct {
- order int32
- state nodeState
- }
- type testCase struct {
- name string
- nodes []testNode
- expect []int // indexes of the nodes expected to be returned by Select
- }
-
- testCases := []testCase{
- {
- name: "TwoNodesSameOrder: Highest Allowed Order",
- nodes: []testNode{
- {order: 1, state: nodeStateAlive},
- {order: 1, state: nodeStateAlive},
- },
- expect: []int{0, 1, 0, 1, 0, 1},
- },
- {
- name: "TwoNodesSameOrder: Lowest Allowed Order",
- nodes: []testNode{
- {order: 100, state: nodeStateAlive},
- {order: 100, state: nodeStateAlive},
- },
- expect: []int{0, 1, 0, 1, 0, 1},
- },
- {
- name: "NoneAvailable",
- nodes: []testNode{
- {order: 1, state: nodeStateOutOfSync},
- {order: 1, state: nodeStateUnreachable},
- {order: 1, state: nodeStateUnreachable},
- },
- expect: []int{}, // no nodes should be selected
- },
- {
- name: "DifferentOrder",
- nodes: []testNode{
- {order: 1, state: nodeStateAlive},
- {order: 2, state: nodeStateAlive},
- {order: 3, state: nodeStateAlive},
- },
- expect: []int{0, 0}, // only the highest order node should be selected
- },
- }
-
- for _, tc := range testCases {
- t.Run(tc.name, func(t *testing.T) {
- var nodes []Node[types.ID, nodeClient]
- for _, tn := range tc.nodes {
- node := newMockNode[types.ID, nodeClient](t)
- node.On("State").Return(tn.state)
- node.On("Order").Return(tn.order)
- nodes = append(nodes, node)
- }
-
- selector := newNodeSelector(NodeSelectionModePriorityLevel, nodes)
- for _, idx := range tc.expect {
- if idx >= len(nodes) {
- t.Fatalf("Invalid node index %d in test case '%s'", idx, tc.name)
- }
- assert.Same(t, nodes[idx], selector.Select())
- }
-
- // Check for nil selection if expected slice is empty
- if len(tc.expect) == 0 {
- assert.Nil(t, selector.Select())
- }
- })
- }
-}
diff --git a/common/client/node_selector_round_robin.go b/common/client/node_selector_round_robin.go
deleted file mode 100644
index 18cea03ebd5..00000000000
--- a/common/client/node_selector_round_robin.go
+++ /dev/null
@@ -1,48 +0,0 @@
-package client
-
-import (
- "sync/atomic"
-
- "github.com/smartcontractkit/chainlink/v2/common/types"
-)
-
-type roundRobinSelector[
- CHAIN_ID types.ID,
- RPC any,
-] struct {
- nodes []Node[CHAIN_ID, RPC]
- roundRobinCount atomic.Uint32
-}
-
-func NewRoundRobinSelector[
- CHAIN_ID types.ID,
- RPC any,
-](nodes []Node[CHAIN_ID, RPC]) NodeSelector[CHAIN_ID, RPC] {
- return &roundRobinSelector[CHAIN_ID, RPC]{
- nodes: nodes,
- }
-}
-
-func (s *roundRobinSelector[CHAIN_ID, RPC]) Select() Node[CHAIN_ID, RPC] {
- var liveNodes []Node[CHAIN_ID, RPC]
- for _, n := range s.nodes {
- if n.State() == nodeStateAlive {
- liveNodes = append(liveNodes, n)
- }
- }
-
- nNodes := len(liveNodes)
- if nNodes == 0 {
- return nil
- }
-
- // NOTE: Inc returns the number after addition, so we must -1 to get the "current" counter
- count := s.roundRobinCount.Add(1) - 1
- idx := int(count % uint32(nNodes))
-
- return liveNodes[idx]
-}
-
-func (s *roundRobinSelector[CHAIN_ID, RPC]) Name() string {
- return NodeSelectionModeRoundRobin
-}
diff --git a/common/client/node_selector_round_robin_test.go b/common/client/node_selector_round_robin_test.go
deleted file mode 100644
index 189b58da9ea..00000000000
--- a/common/client/node_selector_round_robin_test.go
+++ /dev/null
@@ -1,61 +0,0 @@
-package client
-
-import (
- "testing"
-
- "github.com/stretchr/testify/assert"
-
- "github.com/smartcontractkit/chainlink/v2/common/types"
-)
-
-func TestRoundRobinNodeSelectorName(t *testing.T) {
- selector := newNodeSelector[types.ID, RPCClient[types.ID, Head]](NodeSelectionModeRoundRobin, nil)
- assert.Equal(t, selector.Name(), NodeSelectionModeRoundRobin)
-}
-
-func TestRoundRobinNodeSelector(t *testing.T) {
- t.Parallel()
-
- type nodeClient RPCClient[types.ID, Head]
- var nodes []Node[types.ID, nodeClient]
-
- for i := 0; i < 3; i++ {
- node := newMockNode[types.ID, nodeClient](t)
- if i == 0 {
- // first node is out of sync
- node.On("State").Return(nodeStateOutOfSync)
- } else {
- // second & third nodes are alive
- node.On("State").Return(nodeStateAlive)
- }
- nodes = append(nodes, node)
- }
-
- selector := newNodeSelector(NodeSelectionModeRoundRobin, nodes)
- assert.Same(t, nodes[1], selector.Select())
- assert.Same(t, nodes[2], selector.Select())
- assert.Same(t, nodes[1], selector.Select())
- assert.Same(t, nodes[2], selector.Select())
-}
-
-func TestRoundRobinNodeSelector_None(t *testing.T) {
- t.Parallel()
-
- type nodeClient RPCClient[types.ID, Head]
- var nodes []Node[types.ID, nodeClient]
-
- for i := 0; i < 3; i++ {
- node := newMockNode[types.ID, nodeClient](t)
- if i == 0 {
- // first node is out of sync
- node.On("State").Return(nodeStateOutOfSync)
- } else {
- // others are unreachable
- node.On("State").Return(nodeStateUnreachable)
- }
- nodes = append(nodes, node)
- }
-
- selector := newNodeSelector(NodeSelectionModeRoundRobin, nodes)
- assert.Nil(t, selector.Select())
-}
diff --git a/common/client/node_selector_test.go b/common/client/node_selector_test.go
deleted file mode 100644
index f652bfc50ad..00000000000
--- a/common/client/node_selector_test.go
+++ /dev/null
@@ -1,18 +0,0 @@
-package client
-
-import (
- "testing"
-
- "github.com/stretchr/testify/assert"
-
- "github.com/smartcontractkit/chainlink/v2/common/types"
-)
-
-func TestNodeSelector(t *testing.T) {
- // rest of the tests are located in specific node selectors tests
- t.Run("panics on unknown type", func(t *testing.T) {
- assert.Panics(t, func() {
- _ = newNodeSelector[types.ID, RPCClient[types.ID, Head]]("unknown", nil)
- })
- })
-}
diff --git a/common/client/node_selector_total_difficulty.go b/common/client/node_selector_total_difficulty.go
deleted file mode 100644
index 3a4b9733f0d..00000000000
--- a/common/client/node_selector_total_difficulty.go
+++ /dev/null
@@ -1,53 +0,0 @@
-package client
-
-import (
- "math/big"
-
- "github.com/smartcontractkit/chainlink/v2/common/types"
-)
-
-type totalDifficultyNodeSelector[
- CHAIN_ID types.ID,
- RPC any,
-] []Node[CHAIN_ID, RPC]
-
-func NewTotalDifficultyNodeSelector[
- CHAIN_ID types.ID,
- RPC any,
-](nodes []Node[CHAIN_ID, RPC]) NodeSelector[CHAIN_ID, RPC] {
- return totalDifficultyNodeSelector[CHAIN_ID, RPC](nodes)
-}
-
-func (s totalDifficultyNodeSelector[CHAIN_ID, RPC]) Select() Node[CHAIN_ID, RPC] {
- // NodeNoNewHeadsThreshold may not be enabled, in this case all nodes have td == nil
- var highestTD *big.Int
- var nodes []Node[CHAIN_ID, RPC]
- var aliveNodes []Node[CHAIN_ID, RPC]
-
- for _, n := range s {
- state, currentChainInfo := n.StateAndLatest()
- if state != nodeStateAlive {
- continue
- }
-
- currentTD := currentChainInfo.TotalDifficulty
- aliveNodes = append(aliveNodes, n)
- if currentTD != nil && (highestTD == nil || currentTD.Cmp(highestTD) >= 0) {
- if highestTD == nil || currentTD.Cmp(highestTD) > 0 {
- highestTD = currentTD
- nodes = nil
- }
- nodes = append(nodes, n)
- }
- }
-
- // If all nodes have td == nil pick one from the nodes that are alive
- if len(nodes) == 0 {
- return firstOrHighestPriority(aliveNodes)
- }
- return firstOrHighestPriority(nodes)
-}
-
-func (s totalDifficultyNodeSelector[CHAIN_ID, RPC]) Name() string {
- return NodeSelectionModeTotalDifficulty
-}
diff --git a/common/client/node_selector_total_difficulty_test.go b/common/client/node_selector_total_difficulty_test.go
deleted file mode 100644
index 7cd5867ddca..00000000000
--- a/common/client/node_selector_total_difficulty_test.go
+++ /dev/null
@@ -1,178 +0,0 @@
-package client
-
-import (
- "math/big"
- "testing"
-
- "github.com/smartcontractkit/chainlink/v2/common/types"
-
- "github.com/stretchr/testify/assert"
-)
-
-func TestTotalDifficultyNodeSelectorName(t *testing.T) {
- selector := newNodeSelector[types.ID, RPCClient[types.ID, Head]](NodeSelectionModeTotalDifficulty, nil)
- assert.Equal(t, selector.Name(), NodeSelectionModeTotalDifficulty)
-}
-
-func TestTotalDifficultyNodeSelector(t *testing.T) {
- t.Parallel()
-
- type nodeClient RPCClient[types.ID, Head]
- var nodes []Node[types.ID, nodeClient]
-
- for i := 0; i < 3; i++ {
- node := newMockNode[types.ID, nodeClient](t)
- if i == 0 {
- // first node is out of sync
- node.On("StateAndLatest").Return(nodeStateOutOfSync, ChainInfo{BlockNumber: -1})
- } else if i == 1 {
- // second node is alive
- node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(7)})
- } else {
- // third node is alive and best
- node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 2, TotalDifficulty: big.NewInt(8)})
- }
- node.On("Order").Maybe().Return(int32(1))
- nodes = append(nodes, node)
- }
-
- selector := newNodeSelector(NodeSelectionModeTotalDifficulty, nodes)
- assert.Same(t, nodes[2], selector.Select())
-
- t.Run("stick to the same node", func(t *testing.T) {
- node := newMockNode[types.ID, nodeClient](t)
- // fourth node is alive (same as 3rd)
- node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 2, TotalDifficulty: big.NewInt(8)})
- node.On("Order").Maybe().Return(int32(1))
- nodes = append(nodes, node)
-
- selector := newNodeSelector(NodeSelectionModeTotalDifficulty, nodes)
- assert.Same(t, nodes[2], selector.Select())
- })
-
- t.Run("another best node", func(t *testing.T) {
- node := newMockNode[types.ID, nodeClient](t)
- // fifth node is alive (better than 3rd and 4th)
- node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 3, TotalDifficulty: big.NewInt(11)})
- node.On("Order").Maybe().Return(int32(1))
- nodes = append(nodes, node)
-
- selector := newNodeSelector(NodeSelectionModeTotalDifficulty, nodes)
- assert.Same(t, nodes[4], selector.Select())
- })
-
- t.Run("nodes never update latest block number", func(t *testing.T) {
- node1 := newMockNode[types.ID, nodeClient](t)
- node1.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: -1, TotalDifficulty: nil})
- node1.On("Order").Maybe().Return(int32(1))
- node2 := newMockNode[types.ID, nodeClient](t)
- node2.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: -1, TotalDifficulty: nil})
- node2.On("Order").Maybe().Return(int32(1))
- nodes := []Node[types.ID, nodeClient]{node1, node2}
-
- selector := newNodeSelector(NodeSelectionModeTotalDifficulty, nodes)
- assert.Same(t, node1, selector.Select())
- })
-}
-
-func TestTotalDifficultyNodeSelector_None(t *testing.T) {
- t.Parallel()
-
- type nodeClient RPCClient[types.ID, Head]
- var nodes []Node[types.ID, nodeClient]
-
- for i := 0; i < 3; i++ {
- node := newMockNode[types.ID, nodeClient](t)
- if i == 0 {
- // first node is out of sync
- node.On("StateAndLatest").Return(nodeStateOutOfSync, ChainInfo{BlockNumber: -1, TotalDifficulty: nil})
- } else {
- // others are unreachable
- node.On("StateAndLatest").Return(nodeStateUnreachable, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(7)})
- }
- nodes = append(nodes, node)
- }
-
- selector := newNodeSelector(NodeSelectionModeTotalDifficulty, nodes)
- assert.Nil(t, selector.Select())
-}
-
-func TestTotalDifficultyNodeSelectorWithOrder(t *testing.T) {
- t.Parallel()
-
- type nodeClient RPCClient[types.ID, Head]
- var nodes []Node[types.ID, nodeClient]
-
- t.Run("same td and order", func(t *testing.T) {
- for i := 0; i < 3; i++ {
- node := newMockNode[types.ID, nodeClient](t)
- node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(10)})
- node.On("Order").Return(int32(2))
- nodes = append(nodes, node)
- }
- selector := newNodeSelector(NodeSelectionModeTotalDifficulty, nodes)
- // Should select the first node because all things are equal
- assert.Same(t, nodes[0], selector.Select())
- })
-
- t.Run("same td but different order", func(t *testing.T) {
- node1 := newMockNode[types.ID, nodeClient](t)
- node1.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 3, TotalDifficulty: big.NewInt(10)})
- node1.On("Order").Return(int32(3))
-
- node2 := newMockNode[types.ID, nodeClient](t)
- node2.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 3, TotalDifficulty: big.NewInt(10)})
- node2.On("Order").Return(int32(1))
-
- node3 := newMockNode[types.ID, nodeClient](t)
- node3.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 3, TotalDifficulty: big.NewInt(10)})
- node3.On("Order").Return(int32(2))
-
- nodes := []Node[types.ID, nodeClient]{node1, node2, node3}
- selector := newNodeSelector(NodeSelectionModeTotalDifficulty, nodes)
- // Should select the second node as it has the highest priority
- assert.Same(t, nodes[1], selector.Select())
- })
-
- t.Run("different td but same order", func(t *testing.T) {
- node1 := newMockNode[types.ID, nodeClient](t)
- node1.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(10)})
- node1.On("Order").Maybe().Return(int32(3))
-
- node2 := newMockNode[types.ID, nodeClient](t)
- node2.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(11)})
- node2.On("Order").Maybe().Return(int32(3))
-
- node3 := newMockNode[types.ID, nodeClient](t)
- node3.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(12)})
- node3.On("Order").Return(int32(3))
-
- nodes := []Node[types.ID, nodeClient]{node1, node2, node3}
- selector := newNodeSelector(NodeSelectionModeTotalDifficulty, nodes)
- // Should select the third node as it has the highest td
- assert.Same(t, nodes[2], selector.Select())
- })
-
- t.Run("different head and different order", func(t *testing.T) {
- node1 := newMockNode[types.ID, nodeClient](t)
- node1.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(100)})
- node1.On("Order").Maybe().Return(int32(4))
-
- node2 := newMockNode[types.ID, nodeClient](t)
- node2.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(110)})
- node2.On("Order").Maybe().Return(int32(5))
-
- node3 := newMockNode[types.ID, nodeClient](t)
- node3.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(110)})
- node3.On("Order").Maybe().Return(int32(1))
-
- node4 := newMockNode[types.ID, nodeClient](t)
- node4.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(105)})
- node4.On("Order").Maybe().Return(int32(2))
-
- nodes := []Node[types.ID, nodeClient]{node1, node2, node3, node4}
- selector := newNodeSelector(NodeSelectionModeTotalDifficulty, nodes)
- // Should select the third node as it has the highest td and will win the priority tie-breaker
- assert.Same(t, nodes[2], selector.Select())
- })
-}
diff --git a/common/client/node_test.go b/common/client/node_test.go
deleted file mode 100644
index 6d98c2d9ea4..00000000000
--- a/common/client/node_test.go
+++ /dev/null
@@ -1,107 +0,0 @@
-package client
-
-import (
- "net/url"
- "testing"
- "time"
-
- "github.com/smartcontractkit/chainlink-common/pkg/logger"
-
- clientMocks "github.com/smartcontractkit/chainlink/v2/common/client/mocks"
- "github.com/smartcontractkit/chainlink/v2/common/types"
-)
-
-type testNodeConfig struct {
- pollFailureThreshold uint32
- pollInterval time.Duration
- selectionMode string
- syncThreshold uint32
- nodeIsSyncingEnabled bool
- enforceRepeatableRead bool
- finalizedBlockPollInterval time.Duration
- deathDeclarationDelay time.Duration
- newHeadsPollInterval time.Duration
-}
-
-func (n testNodeConfig) NewHeadsPollInterval() time.Duration {
- return n.newHeadsPollInterval
-}
-
-func (n testNodeConfig) PollFailureThreshold() uint32 {
- return n.pollFailureThreshold
-}
-
-func (n testNodeConfig) PollInterval() time.Duration {
- return n.pollInterval
-}
-
-func (n testNodeConfig) SelectionMode() string {
- return n.selectionMode
-}
-
-func (n testNodeConfig) SyncThreshold() uint32 {
- return n.syncThreshold
-}
-
-func (n testNodeConfig) NodeIsSyncingEnabled() bool {
- return n.nodeIsSyncingEnabled
-}
-
-func (n testNodeConfig) FinalizedBlockPollInterval() time.Duration {
- return n.finalizedBlockPollInterval
-}
-
-func (n testNodeConfig) EnforceRepeatableRead() bool {
- return n.enforceRepeatableRead
-}
-
-func (n testNodeConfig) DeathDeclarationDelay() time.Duration {
- return n.deathDeclarationDelay
-}
-
-type testNode struct {
- *node[types.ID, Head, RPCClient[types.ID, Head]]
-}
-
-type testNodeOpts struct {
- config testNodeConfig
- chainConfig clientMocks.ChainConfig
- lggr logger.Logger
- wsuri *url.URL
- httpuri *url.URL
- name string
- id int
- chainID types.ID
- nodeOrder int32
- rpc *mockRPCClient[types.ID, Head]
- chainFamily string
-}
-
-func newTestNode(t *testing.T, opts testNodeOpts) testNode {
- if opts.lggr == nil {
- opts.lggr = logger.Test(t)
- }
-
- if opts.name == "" {
- opts.name = "tes node"
- }
-
- if opts.chainFamily == "" {
- opts.chainFamily = "test node chain family"
- }
-
- if opts.chainID == nil {
- opts.chainID = types.RandomID()
- }
-
- if opts.id == 0 {
- opts.id = 42
- }
-
- nodeI := NewNode[types.ID, Head, RPCClient[types.ID, Head]](opts.config, opts.chainConfig, opts.lggr,
- opts.wsuri, opts.httpuri, opts.name, opts.id, opts.chainID, opts.nodeOrder, opts.rpc, opts.chainFamily)
-
- return testNode{
- nodeI.(*node[types.ID, Head, RPCClient[types.ID, Head]]),
- }
-}
diff --git a/common/client/poller.go b/common/client/poller.go
deleted file mode 100644
index bdd3d36f16f..00000000000
--- a/common/client/poller.go
+++ /dev/null
@@ -1,95 +0,0 @@
-package client
-
-import (
- "context"
- "time"
-
- "github.com/smartcontractkit/chainlink-common/pkg/logger"
- "github.com/smartcontractkit/chainlink-common/pkg/services"
-
- "github.com/smartcontractkit/chainlink/v2/common/types"
-)
-
-// Poller is a component that polls a function at a given interval
-// 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.Service
- eng *services.Engine
-
- pollingInterval time.Duration
- pollingFunc func(ctx context.Context) (T, error)
- pollingTimeout time.Duration
- channel chan<- T
- errCh chan error
-}
-
-// 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, lggr logger.Logger) (Poller[T], <-chan T) {
- channel := make(chan T)
- p := Poller[T]{
- pollingInterval: pollingInterval,
- pollingFunc: pollingFunc,
- pollingTimeout: pollingTimeout,
- channel: channel,
- errCh: make(chan error),
- }
- 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(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.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(ctx context.Context) {
- ticker := services.NewTicker(p.pollingInterval) // reduce possibility of sending two exactly the same request at once
- defer ticker.Stop()
-
- for {
- select {
- case <-ctx.Done():
- return
- case <-ticker.C:
- // Set polling timeout
- pollingCtx, cancelPolling := context.WithTimeout(ctx, p.pollingTimeout)
- // Execute polling function
- result, err := p.pollingFunc(pollingCtx)
- cancelPolling()
- if err != nil {
- p.eng.Warnf("polling error: %v", err)
- continue
- }
- // Send result to channel or block if channel is full
- select {
- case p.channel <- result:
- case <-ctx.Done():
- return
- }
- }
- }
-}
diff --git a/common/client/poller_test.go b/common/client/poller_test.go
deleted file mode 100644
index 930b101751c..00000000000
--- a/common/client/poller_test.go
+++ /dev/null
@@ -1,194 +0,0 @@
-package client
-
-import (
- "context"
- "fmt"
- "math/big"
- "sync"
- "testing"
- "time"
-
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
- "go.uber.org/zap"
-
- "github.com/smartcontractkit/chainlink-common/pkg/logger"
- "github.com/smartcontractkit/chainlink-common/pkg/utils/tests"
-)
-
-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(ctx)
- require.NoError(t, err)
-
- 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{}
- pollFunc := func(ctx context.Context) (Head, error) {
- pollLock.Lock()
- defer pollLock.Unlock()
- pollNumber++
- h := head{
- BlockNumber: int64(pollNumber),
- BlockDifficulty: big.NewInt(int64(pollNumber)),
- }
- return h.ToMockHead(t), nil
- }
-
- // Create poller and start to receive data
- poller, channel := NewPoller[Head](time.Millisecond, pollFunc, time.Second, lggr)
- require.NoError(t, poller.Start(ctx))
- defer poller.Unsubscribe()
-
- // Receive updates from the poller
- pollCount := 0
- pollMax := 50
- for ; pollCount < pollMax; pollCount++ {
- h := <-channel
- assert.Equal(t, int64(pollCount+1), h.BlockNumber())
- }
- })
-
- 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{}
- pollFunc := func(ctx context.Context) (Head, error) {
- pollLock.Lock()
- defer pollLock.Unlock()
- pollNumber++
- return nil, fmt.Errorf("polling error %d", pollNumber)
- }
-
- olggr, observedLogs := logger.TestObserved(t, zap.WarnLevel)
-
- // Create poller and subscribe to receive data
- poller, _ := NewPoller[Head](time.Millisecond, pollFunc, time.Second, olggr)
- require.NoError(t, poller.Start(ctx))
- defer poller.Unsubscribe()
-
- // Ensure that all errors were logged as expected
- logsSeen := func() bool {
- for pollCount := 0; pollCount < 50; pollCount++ {
- numLogs := observedLogs.FilterMessage(fmt.Sprintf("polling error: polling error %d", pollCount+1)).Len()
- if numLogs != 1 {
- return false
- }
- }
- return true
- }
- require.Eventually(t, logsSeen, tests.WaitTimeout(t), 100*time.Millisecond)
- })
-
- 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()
- }
- return nil, nil
- }
-
- // Set instant timeout
- pollingTimeout := time.Duration(0)
-
- olggr, observedLogs := logger.TestObserved(t, zap.WarnLevel)
-
- // Create poller and subscribe to receive data
- poller, _ := NewPoller[Head](time.Millisecond, pollFunc, pollingTimeout, olggr)
- require.NoError(t, poller.Start(ctx))
- defer poller.Unsubscribe()
-
- // Ensure that timeout errors were logged as expected
- logsSeen := func() bool {
- return observedLogs.FilterMessage("polling error: context deadline exceeded").Len() >= 1
- }
- require.Eventually(t, logsSeen, tests.WaitTimeout(t), 100*time.Millisecond)
- })
-
- 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) {
- closeOnce()
- // Block in polling function until context is cancelled
- if <-ctx.Done(); true {
- return nil, ctx.Err()
- }
- return nil, nil
- }
-
- // Set long timeout
- pollingTimeout := time.Minute
-
- olggr, observedLogs := logger.TestObserved(t, zap.WarnLevel)
-
- // Create poller and subscribe to receive data
- poller, _ := NewPoller[Head](time.Millisecond, pollFunc, pollingTimeout, olggr)
- require.NoError(t, poller.Start(ctx))
-
- // Unsubscribe while blocked in polling function
- <-wait
- poller.Unsubscribe()
-
- // Ensure error was logged
- logsSeen := func() bool {
- return observedLogs.FilterMessage("polling error: context canceled").Len() >= 1
- }
- require.Eventually(t, logsSeen, tests.WaitTimeout(t), 100*time.Millisecond)
- })
-}
-
-func Test_Poller_Unsubscribe(t *testing.T) {
- lggr := logger.Test(t)
- pollFunc := func(ctx context.Context) (Head, error) {
- select {
- case <-ctx.Done():
- return nil, ctx.Err()
- default:
- h := head{
- BlockNumber: 0,
- BlockDifficulty: big.NewInt(0),
- }
- return h.ToMockHead(t), nil
- }
- }
-
- 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(ctx)
- require.NoError(t, err)
-
- <-channel
- poller.Unsubscribe()
- poller.Unsubscribe()
- })
-
- 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(ctx)
- require.NoError(t, err)
-
- poller.Unsubscribe()
- require.Equal(t, <-channel, nil)
- })
-}
diff --git a/common/client/send_only_node.go b/common/client/send_only_node.go
deleted file mode 100644
index 0a1715fa191..00000000000
--- a/common/client/send_only_node.go
+++ /dev/null
@@ -1,183 +0,0 @@
-package client
-
-import (
- "context"
- "fmt"
- "net/url"
- "sync"
-
- "github.com/smartcontractkit/chainlink-common/pkg/logger"
- "github.com/smartcontractkit/chainlink-common/pkg/services"
-
- "github.com/smartcontractkit/chainlink/v2/common/types"
-)
-
-type sendOnlyClient[
- CHAIN_ID types.ID,
-] interface {
- Close()
- ChainID(context.Context) (CHAIN_ID, error)
- Dial(ctx context.Context) error
-}
-
-// SendOnlyNode represents one node used as a sendonly
-type SendOnlyNode[
- CHAIN_ID types.ID,
- RPC any,
-] interface {
- // Start may attempt to connect to the node, but should only return error for misconfiguration - never for temporary errors.
- Start(context.Context) error
- Close() error
-
- ConfiguredChainID() CHAIN_ID
- RPC() RPC
-
- String() string
- // State returns nodeState
- State() nodeState
- // Name is a unique identifier for this node.
- Name() string
-}
-
-// It only supports sending transactions
-// It must use an http(s) url
-type sendOnlyNode[
- CHAIN_ID types.ID,
- RPC sendOnlyClient[CHAIN_ID],
-] struct {
- services.StateMachine
-
- stateMu sync.RWMutex // protects state* fields
- state nodeState
-
- rpc RPC
- uri url.URL
- log logger.Logger
- name string
- chainID CHAIN_ID
- chStop services.StopChan
- wg sync.WaitGroup
-}
-
-// NewSendOnlyNode returns a new sendonly node
-func NewSendOnlyNode[
- CHAIN_ID types.ID,
- RPC sendOnlyClient[CHAIN_ID],
-](
- lggr logger.Logger,
- httpuri url.URL,
- name string,
- chainID CHAIN_ID,
- rpc RPC,
-) SendOnlyNode[CHAIN_ID, RPC] {
- s := new(sendOnlyNode[CHAIN_ID, RPC])
- s.name = name
- s.log = logger.Named(logger.Named(lggr, "SendOnlyNode"), name)
- s.log = logger.With(s.log,
- "nodeTier", "sendonly",
- )
- s.rpc = rpc
- s.uri = httpuri
- s.chainID = chainID
- s.chStop = make(chan struct{})
- return s
-}
-
-func (s *sendOnlyNode[CHAIN_ID, RPC]) Start(ctx context.Context) error {
- return s.StartOnce(s.name, func() error {
- s.start(ctx)
- return nil
- })
-}
-
-// Start setups up and verifies the sendonly node
-// Should only be called once in a node's lifecycle
-func (s *sendOnlyNode[CHAIN_ID, RPC]) start(startCtx context.Context) {
- if s.State() != nodeStateUndialed {
- panic(fmt.Sprintf("cannot dial node with state %v", s.state))
- }
-
- err := s.rpc.Dial(startCtx)
- if err != nil {
- promPoolRPCNodeTransitionsToUnusable.WithLabelValues(s.chainID.String(), s.name).Inc()
- s.log.Errorw("Dial failed: SendOnly Node is unusable", "err", err)
- s.setState(nodeStateUnusable)
- return
- }
- s.setState(nodeStateDialed)
-
- if s.chainID.String() == "0" {
- // Skip verification if chainID is zero
- s.log.Warn("sendonly rpc ChainID verification skipped")
- } else {
- chainID, err := s.rpc.ChainID(startCtx)
- if err != nil || chainID.String() != s.chainID.String() {
- promPoolRPCNodeTransitionsToUnreachable.WithLabelValues(s.chainID.String(), s.name).Inc()
- if err != nil {
- promPoolRPCNodeTransitionsToUnreachable.WithLabelValues(s.chainID.String(), s.name).Inc()
- s.log.Errorw(fmt.Sprintf("Verify failed: %v", err), "err", err)
- s.setState(nodeStateUnreachable)
- } else {
- promPoolRPCNodeTransitionsToInvalidChainID.WithLabelValues(s.chainID.String(), s.name).Inc()
- s.log.Errorf(
- "sendonly rpc ChainID doesn't match local chain ID: RPC ID=%s, local ID=%s, node name=%s",
- chainID.String(),
- s.chainID.String(),
- s.name,
- )
- s.setState(nodeStateInvalidChainID)
- }
- // Since it has failed, spin up the verifyLoop that will keep
- // retrying until success
- s.wg.Add(1)
- go s.verifyLoop()
- return
- }
- }
-
- promPoolRPCNodeTransitionsToAlive.WithLabelValues(s.chainID.String(), s.name).Inc()
- s.setState(nodeStateAlive)
- s.log.Infow("Sendonly RPC Node is online", "nodeState", s.state)
-}
-
-func (s *sendOnlyNode[CHAIN_ID, RPC]) Close() error {
- return s.StopOnce(s.name, func() error {
- s.rpc.Close()
- close(s.chStop)
- s.wg.Wait()
- s.setState(nodeStateClosed)
- return nil
- })
-}
-
-func (s *sendOnlyNode[CHAIN_ID, RPC]) ConfiguredChainID() CHAIN_ID {
- return s.chainID
-}
-
-func (s *sendOnlyNode[CHAIN_ID, RPC]) RPC() RPC {
- return s.rpc
-}
-
-func (s *sendOnlyNode[CHAIN_ID, RPC]) String() string {
- return fmt.Sprintf("(%s)%s:%s", Secondary.String(), s.name, s.uri.Redacted())
-}
-
-func (s *sendOnlyNode[CHAIN_ID, RPC]) setState(state nodeState) (changed bool) {
- s.stateMu.Lock()
- defer s.stateMu.Unlock()
- if s.state == state {
- return false
- }
- s.state = state
- return true
-}
-
-func (s *sendOnlyNode[CHAIN_ID, RPC]) State() nodeState {
- s.stateMu.RLock()
- defer s.stateMu.RUnlock()
- return s.state
-}
-
-func (s *sendOnlyNode[CHAIN_ID, RPC]) Name() string {
- return s.name
-}
diff --git a/common/client/send_only_node_lifecycle.go b/common/client/send_only_node_lifecycle.go
deleted file mode 100644
index c66d267ed42..00000000000
--- a/common/client/send_only_node_lifecycle.go
+++ /dev/null
@@ -1,67 +0,0 @@
-package client
-
-import (
- "fmt"
- "time"
-
- "github.com/smartcontractkit/chainlink/v2/common/internal/utils"
-)
-
-// verifyLoop may only be triggered once, on Start, if initial chain ID check
-// fails.
-//
-// It will continue checking until success and then exit permanently.
-func (s *sendOnlyNode[CHAIN_ID, RPC]) verifyLoop() {
- defer s.wg.Done()
- ctx, cancel := s.chStop.NewCtx()
- defer cancel()
-
- backoff := utils.NewRedialBackoff()
- for {
- select {
- case <-ctx.Done():
- return
- case <-time.After(backoff.Duration()):
- }
- chainID, err := s.rpc.ChainID(ctx)
- if err != nil {
- ok := s.IfStarted(func() {
- if changed := s.setState(nodeStateUnreachable); changed {
- promPoolRPCNodeTransitionsToUnreachable.WithLabelValues(s.chainID.String(), s.name).Inc()
- }
- })
- if !ok {
- return
- }
- s.log.Errorw(fmt.Sprintf("Verify failed: %v", err), "err", err)
- continue
- } else if chainID.String() != s.chainID.String() {
- ok := s.IfStarted(func() {
- if changed := s.setState(nodeStateInvalidChainID); changed {
- promPoolRPCNodeTransitionsToInvalidChainID.WithLabelValues(s.chainID.String(), s.name).Inc()
- }
- })
- if !ok {
- return
- }
- s.log.Errorf(
- "sendonly rpc ChainID doesn't match local chain ID: RPC ID=%s, local ID=%s, node name=%s",
- chainID.String(),
- s.chainID.String(),
- s.name,
- )
-
- continue
- }
- ok := s.IfStarted(func() {
- if changed := s.setState(nodeStateAlive); changed {
- promPoolRPCNodeTransitionsToAlive.WithLabelValues(s.chainID.String(), s.name).Inc()
- }
- })
- if !ok {
- return
- }
- s.log.Infow("Sendonly RPC Node is online", "nodeState", s.state)
- return
- }
-}
diff --git a/common/client/send_only_node_test.go b/common/client/send_only_node_test.go
deleted file mode 100644
index 532946da48f..00000000000
--- a/common/client/send_only_node_test.go
+++ /dev/null
@@ -1,139 +0,0 @@
-package client
-
-import (
- "errors"
- "fmt"
- "net/url"
- "testing"
-
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/mock"
- "github.com/stretchr/testify/require"
- "go.uber.org/zap"
-
- "github.com/smartcontractkit/chainlink-common/pkg/logger"
- "github.com/smartcontractkit/chainlink-common/pkg/utils/tests"
-
- "github.com/smartcontractkit/chainlink/v2/common/types"
-)
-
-func TestNewSendOnlyNode(t *testing.T) {
- t.Parallel()
-
- urlFormat := "http://user:%s@testurl.com"
- password := "pass"
- u, err := url.Parse(fmt.Sprintf(urlFormat, password))
- require.NoError(t, err)
- redacted := fmt.Sprintf(urlFormat, "xxxxx")
- lggr := logger.Test(t)
- name := "TestNewSendOnlyNode"
- chainID := types.RandomID()
- client := newMockSendOnlyClient[types.ID](t)
-
- node := NewSendOnlyNode(lggr, *u, name, chainID, client)
- assert.NotNil(t, node)
-
- // Must contain name & url with redacted password
- assert.Contains(t, node.String(), fmt.Sprintf("%s:%s", name, redacted))
- assert.Equal(t, node.ConfiguredChainID(), chainID)
-}
-
-func TestStartSendOnlyNode(t *testing.T) {
- t.Parallel()
- t.Run("becomes unusable if initial dial fails", func(t *testing.T) {
- t.Parallel()
- lggr, observedLogs := logger.TestObserved(t, zap.WarnLevel)
- client := newMockSendOnlyClient[types.ID](t)
- client.On("Close").Once()
- expectedError := errors.New("some http error")
- client.On("Dial", mock.Anything).Return(expectedError).Once()
- s := NewSendOnlyNode(lggr, url.URL{}, t.Name(), types.RandomID(), client)
-
- defer func() { assert.NoError(t, s.Close()) }()
- err := s.Start(tests.Context(t))
- require.NoError(t, err)
-
- assert.Equal(t, nodeStateUnusable, s.State())
- tests.RequireLogMessage(t, observedLogs, "Dial failed: SendOnly Node is unusable")
- })
- t.Run("Default ChainID(0) produces warn and skips checks", func(t *testing.T) {
- t.Parallel()
- lggr, observedLogs := logger.TestObserved(t, zap.WarnLevel)
- client := newMockSendOnlyClient[types.ID](t)
- client.On("Close").Once()
- client.On("Dial", mock.Anything).Return(nil).Once()
- s := NewSendOnlyNode(lggr, url.URL{}, t.Name(), types.NewIDFromInt(0), client)
-
- defer func() { assert.NoError(t, s.Close()) }()
- err := s.Start(tests.Context(t))
- require.NoError(t, err)
-
- assert.Equal(t, nodeStateAlive, s.State())
- tests.RequireLogMessage(t, observedLogs, "sendonly rpc ChainID verification skipped")
- })
- t.Run("Can recover from chainID verification failure", func(t *testing.T) {
- t.Parallel()
- lggr, observedLogs := logger.TestObserved(t, zap.WarnLevel)
- client := newMockSendOnlyClient[types.ID](t)
- client.On("Close").Once()
- client.On("Dial", mock.Anything).Return(nil)
- expectedError := errors.New("failed to get chain ID")
- chainID := types.RandomID()
- const failuresCount = 2
- client.On("ChainID", mock.Anything).Return(types.RandomID(), expectedError).Times(failuresCount)
- client.On("ChainID", mock.Anything).Return(chainID, nil)
-
- s := NewSendOnlyNode(lggr, url.URL{}, t.Name(), chainID, client)
-
- defer func() { assert.NoError(t, s.Close()) }()
- err := s.Start(tests.Context(t))
- require.NoError(t, err)
-
- assert.Equal(t, nodeStateUnreachable, s.State())
- tests.AssertLogCountEventually(t, observedLogs, fmt.Sprintf("Verify failed: %v", expectedError), failuresCount)
- tests.AssertEventually(t, func() bool {
- return s.State() == nodeStateAlive
- })
- })
- t.Run("Can recover from chainID mismatch", func(t *testing.T) {
- t.Parallel()
- lggr, observedLogs := logger.TestObserved(t, zap.WarnLevel)
- client := newMockSendOnlyClient[types.ID](t)
- client.On("Close").Once()
- client.On("Dial", mock.Anything).Return(nil).Once()
- configuredChainID := types.NewIDFromInt(11)
- rpcChainID := types.NewIDFromInt(20)
- const failuresCount = 2
- client.On("ChainID", mock.Anything).Return(rpcChainID, nil).Times(failuresCount)
- client.On("ChainID", mock.Anything).Return(configuredChainID, nil)
- s := NewSendOnlyNode(lggr, url.URL{}, t.Name(), configuredChainID, client)
-
- defer func() { assert.NoError(t, s.Close()) }()
- err := s.Start(tests.Context(t))
- require.NoError(t, err)
-
- assert.Equal(t, nodeStateInvalidChainID, s.State())
- tests.AssertLogCountEventually(t, observedLogs, "sendonly rpc ChainID doesn't match local chain ID", failuresCount)
- tests.AssertEventually(t, func() bool {
- return s.State() == nodeStateAlive
- })
- })
- t.Run("Start with Random ChainID", func(t *testing.T) {
- t.Parallel()
- lggr, observedLogs := logger.TestObserved(t, zap.WarnLevel)
- client := newMockSendOnlyClient[types.ID](t)
- client.On("Close").Once()
- client.On("Dial", mock.Anything).Return(nil).Once()
- configuredChainID := types.RandomID()
- client.On("ChainID", mock.Anything).Return(configuredChainID, nil)
- s := NewSendOnlyNode(lggr, url.URL{}, t.Name(), configuredChainID, client)
-
- defer func() { assert.NoError(t, s.Close()) }()
- err := s.Start(tests.Context(t))
- assert.NoError(t, err)
- tests.AssertEventually(t, func() bool {
- return s.State() == nodeStateAlive
- })
- assert.Equal(t, 0, observedLogs.Len()) // No warnings expected
- })
-}
diff --git a/common/client/timeout.go b/common/client/timeout.go
new file mode 100644
index 00000000000..0827b812962
--- /dev/null
+++ b/common/client/timeout.go
@@ -0,0 +1,5 @@
+package client
+
+import "time"
+
+const QueryTimeout = 10 * time.Second
diff --git a/common/client/transaction_sender.go b/common/client/transaction_sender.go
deleted file mode 100644
index 5f58682142f..00000000000
--- a/common/client/transaction_sender.go
+++ /dev/null
@@ -1,301 +0,0 @@
-package client
-
-import (
- "context"
- "errors"
- "math"
- "slices"
- "sync"
- "time"
-
- "github.com/prometheus/client_golang/prometheus"
- "github.com/prometheus/client_golang/prometheus/promauto"
-
- "github.com/smartcontractkit/chainlink-common/pkg/logger"
- "github.com/smartcontractkit/chainlink-common/pkg/services"
-
- "github.com/smartcontractkit/chainlink/v2/common/types"
-)
-
-var (
- // PromMultiNodeInvariantViolations reports violation of our assumptions
- PromMultiNodeInvariantViolations = promauto.NewCounterVec(prometheus.CounterOpts{
- Name: "multi_node_invariant_violations",
- Help: "The number of invariant violations",
- }, []string{"network", "chainId", "invariant"})
-)
-
-type SendTxResult interface {
- Code() SendTxReturnCode
- Error() error
-}
-
-const sendTxQuorum = 0.7
-
-// SendTxRPCClient - defines interface of an RPC used by TransactionSender to broadcast transaction
-type SendTxRPCClient[TX any, RESULT SendTxResult] interface {
- // SendTransaction errors returned should include name or other unique identifier of the RPC
- SendTransaction(ctx context.Context, tx TX) RESULT
-}
-
-func NewTransactionSender[TX any, RESULT SendTxResult, CHAIN_ID types.ID, RPC SendTxRPCClient[TX, RESULT]](
- lggr logger.Logger,
- chainID CHAIN_ID,
- chainFamily string,
- multiNode *MultiNode[CHAIN_ID, RPC],
- newResult func(err error) RESULT,
- sendTxSoftTimeout time.Duration,
-) *TransactionSender[TX, RESULT, CHAIN_ID, RPC] {
- if sendTxSoftTimeout == 0 {
- sendTxSoftTimeout = QueryTimeout / 2
- }
- return &TransactionSender[TX, RESULT, CHAIN_ID, RPC]{
- chainID: chainID,
- chainFamily: chainFamily,
- lggr: logger.Sugared(lggr).Named("TransactionSender").With("chainID", chainID.String()),
- multiNode: multiNode,
- newResult: newResult,
- sendTxSoftTimeout: sendTxSoftTimeout,
- chStop: make(services.StopChan),
- }
-}
-
-type TransactionSender[TX any, RESULT SendTxResult, CHAIN_ID types.ID, RPC SendTxRPCClient[TX, RESULT]] struct {
- services.StateMachine
- chainID CHAIN_ID
- chainFamily string
- lggr logger.SugaredLogger
- multiNode *MultiNode[CHAIN_ID, RPC]
- newResult func(err error) RESULT
- sendTxSoftTimeout time.Duration // defines max waiting time from first response til responses evaluation
-
- wg sync.WaitGroup // waits for all reporting goroutines to finish
- chStop services.StopChan
-}
-
-// SendTransaction - broadcasts transaction to all the send-only and primary nodes in MultiNode.
-// A returned nil or error does not guarantee that the transaction will or won't be included. Additional checks must be
-// performed to determine the final state.
-//
-// Send-only nodes' results are ignored as they tend to return false-positive responses. Broadcast to them is necessary
-// to speed up the propagation of TX in the network.
-//
-// Handling of primary nodes' results consists of collection and aggregation.
-// In the collection step, we gather as many results as possible while minimizing waiting time. This operation succeeds
-// on one of the following conditions:
-// * Received at least one success
-// * Received at least one result and `sendTxSoftTimeout` expired
-// * Received results from the sufficient number of nodes defined by sendTxQuorum.
-// The aggregation is based on the following conditions:
-// * If there is at least one success - returns success
-// * If there is at least one terminal error - returns terminal error
-// * If there is both success and terminal error - returns success and reports invariant violation
-// * Otherwise, returns any (effectively random) of the errors.
-func (txSender *TransactionSender[TX, RESULT, CHAIN_ID, RPC]) SendTransaction(ctx context.Context, tx TX) RESULT {
- var result RESULT
- ctx, cancel := txSender.chStop.Ctx(ctx)
- defer cancel()
- if !txSender.IfStarted(func() {
- txResults := make(chan RESULT)
- txResultsToReport := make(chan RESULT)
- primaryNodeWg := sync.WaitGroup{}
-
- healthyNodesNum := 0
- err := txSender.multiNode.DoAll(ctx, func(ctx context.Context, rpc RPC, isSendOnly bool) {
- if isSendOnly {
- txSender.wg.Add(1)
- go func(ctx context.Context) {
- defer txSender.wg.Done()
- // Send-only nodes' results are ignored as they tend to return false-positive responses.
- // Broadcast to them is necessary to speed up the propagation of TX in the network.
- _ = txSender.broadcastTxAsync(ctx, rpc, tx)
- }(ctx)
- return
- }
-
- // Primary Nodes
- healthyNodesNum++
- primaryNodeWg.Add(1)
- go func(ctx context.Context) {
- // Broadcasting transaction and results reporting for invariant detection are background jobs that must be detached from
- // callers cancellation.
- // Results reporting to SendTransaction caller must respect caller's context to avoid goroutine leak.
- defer primaryNodeWg.Done()
- r := txSender.broadcastTxAsync(ctx, rpc, tx)
- select {
- case <-ctx.Done():
- txSender.lggr.Debugw("Failed to send tx results", "err", ctx.Err())
- return
- case txResults <- r:
- }
-
- ctx, cancel := txSender.chStop.Ctx(context.WithoutCancel(ctx))
- defer cancel()
- select {
- case <-ctx.Done():
- txSender.lggr.Debugw("Failed to send tx results to report", "err", ctx.Err())
- return
- case txResultsToReport <- r:
- }
- }(ctx)
- })
-
- // This needs to be done in parallel so the reporting knows when it's done (when the channel is closed)
- txSender.wg.Add(1)
- go func() {
- defer txSender.wg.Done()
- primaryNodeWg.Wait()
- close(txResultsToReport)
- close(txResults)
- }()
-
- if err != nil {
- result = txSender.newResult(err)
- return
- }
-
- if healthyNodesNum == 0 {
- result = txSender.newResult(ErroringNodeError)
- return
- }
-
- txSender.wg.Add(1)
- go txSender.reportSendTxAnomalies(tx, txResultsToReport)
-
- result = txSender.collectTxResults(ctx, tx, healthyNodesNum, txResults)
- }) {
- result = txSender.newResult(errors.New("TransactionSender not started"))
- }
-
- return result
-}
-
-func (txSender *TransactionSender[TX, RESULT, CHAIN_ID, RPC]) broadcastTxAsync(ctx context.Context, rpc RPC, tx TX) RESULT {
- // broadcast is a background job, so always detach from caller's cancellation
- ctx, cancel := txSender.chStop.Ctx(context.WithoutCancel(ctx))
- defer cancel()
- result := rpc.SendTransaction(ctx, tx)
- txSender.lggr.Debugw("Node sent transaction", "tx", tx, "err", result.Error())
- if !slices.Contains(sendTxSuccessfulCodes, result.Code()) && ctx.Err() == nil {
- txSender.lggr.Warnw("RPC returned error", "tx", tx, "err", result.Error())
- }
- return result
-}
-
-func (txSender *TransactionSender[TX, RESULT, CHAIN_ID, RPC]) reportSendTxAnomalies(tx TX, txResults <-chan RESULT) {
- defer txSender.wg.Done()
- resultsByCode := sendTxResults[RESULT]{}
- // txResults eventually will be closed
- for txResult := range txResults {
- resultsByCode[txResult.Code()] = append(resultsByCode[txResult.Code()], txResult)
- }
-
- select {
- case <-txSender.chStop:
- // it's ok to receive no results if txSender is closing. Return early to prevent false reporting of invariant violation.
- if len(resultsByCode) == 0 {
- return
- }
- default:
- }
-
- _, criticalErr := aggregateTxResults[RESULT](resultsByCode)
- if criticalErr != nil {
- txSender.lggr.Criticalw("observed invariant violation on SendTransaction", "tx", tx, "resultsByCode", resultsByCode, "err", criticalErr)
- PromMultiNodeInvariantViolations.WithLabelValues(txSender.chainFamily, txSender.chainID.String(), criticalErr.Error()).Inc()
- }
-}
-
-type sendTxResults[RESULT any] map[SendTxReturnCode][]RESULT
-
-func aggregateTxResults[RESULT any](resultsByCode sendTxResults[RESULT]) (result RESULT, criticalErr error) {
- severeErrors, hasSevereErrors := findFirstIn(resultsByCode, sendTxSevereErrors)
- successResults, hasSuccess := findFirstIn(resultsByCode, sendTxSuccessfulCodes)
- if hasSuccess {
- // We assume that primary node would never report false positive txResult for a transaction.
- // Thus, if such case occurs it's probably due to misconfiguration or a bug and requires manual intervention.
- if hasSevereErrors {
- const errMsg = "found contradictions in nodes replies on SendTransaction: got success and severe error"
- // return success, since at least 1 node has accepted our broadcasted Tx, and thus it can now be included onchain
- return successResults[0], errors.New(errMsg)
- }
-
- // other errors are temporary - we are safe to return success
- return successResults[0], nil
- }
-
- if hasSevereErrors {
- return severeErrors[0], nil
- }
-
- // return temporary error
- for _, r := range resultsByCode {
- return r[0], nil
- }
-
- criticalErr = errors.New("expected at least one response on SendTransaction")
- return result, criticalErr
-}
-
-func (txSender *TransactionSender[TX, RESULT, CHAIN_ID, RPC]) collectTxResults(ctx context.Context, tx TX, healthyNodesNum int, txResults <-chan RESULT) RESULT {
- requiredResults := int(math.Ceil(float64(healthyNodesNum) * sendTxQuorum))
- errorsByCode := sendTxResults[RESULT]{}
- var softTimeoutChan <-chan time.Time
- var resultsCount int
-loop:
- for {
- select {
- case <-ctx.Done():
- txSender.lggr.Debugw("Failed to collect of the results before context was done", "tx", tx, "errorsByCode", errorsByCode)
- return txSender.newResult(ctx.Err())
- case r := <-txResults:
- errorsByCode[r.Code()] = append(errorsByCode[r.Code()], r)
- resultsCount++
- if slices.Contains(sendTxSuccessfulCodes, r.Code()) || resultsCount >= requiredResults {
- break loop
- }
- case <-softTimeoutChan:
- txSender.lggr.Debugw("Send Tx soft timeout expired - returning responses we've collected so far", "tx", tx, "resultsCount", resultsCount, "requiredResults", requiredResults)
- break loop
- }
-
- if softTimeoutChan == nil {
- tm := time.NewTimer(txSender.sendTxSoftTimeout)
- softTimeoutChan = tm.C
- // we are fine with stopping timer at the end of function
- //nolint
- defer tm.Stop()
- }
- }
-
- // ignore critical error as it's reported in reportSendTxAnomalies
- result, _ := aggregateTxResults(errorsByCode)
- txSender.lggr.Debugw("Collected results", "errorsByCode", errorsByCode, "result", result)
- return result
-}
-
-func (txSender *TransactionSender[TX, RESULT, CHAIN_ID, RPC]) Start(ctx context.Context) error {
- return txSender.StartOnce("TransactionSender", func() error {
- return nil
- })
-}
-
-func (txSender *TransactionSender[TX, RESULT, CHAIN_ID, RPC]) Close() error {
- return txSender.StopOnce("TransactionSender", func() error {
- txSender.lggr.Debug("Closing TransactionSender")
- close(txSender.chStop)
- txSender.wg.Wait()
- return nil
- })
-}
-
-// findFirstIn - returns the first existing key and value for the slice of keys
-func findFirstIn[K comparable, V any](set map[K]V, keys []K) (V, bool) {
- for _, k := range keys {
- if v, ok := set[k]; ok {
- return v, true
- }
- }
- var zeroV V
- return zeroV, false
-}
diff --git a/common/client/transaction_sender_test.go b/common/client/transaction_sender_test.go
deleted file mode 100644
index 14dc0799a6a..00000000000
--- a/common/client/transaction_sender_test.go
+++ /dev/null
@@ -1,419 +0,0 @@
-package client
-
-import (
- "context"
- "fmt"
- "testing"
-
- "github.com/pkg/errors"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/mock"
- "github.com/stretchr/testify/require"
- "go.uber.org/zap"
-
- "github.com/smartcontractkit/chainlink-common/pkg/logger"
- "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest"
- "github.com/smartcontractkit/chainlink-common/pkg/utils/tests"
-
- "github.com/smartcontractkit/chainlink/v2/common/types"
-)
-
-type TestSendTxRPCClient SendTxRPCClient[any, *sendTxResult]
-
-type sendTxMultiNode struct {
- *MultiNode[types.ID, TestSendTxRPCClient]
-}
-
-type sendTxRPC struct {
- sendTxRun func(args mock.Arguments)
- sendTxErr error
-}
-
-type sendTxResult struct {
- err error
- code SendTxReturnCode
-}
-
-var _ SendTxResult = (*sendTxResult)(nil)
-
-func NewSendTxResult(err error) *sendTxResult {
- result := &sendTxResult{
- err: err,
- }
- return result
-}
-
-func (r *sendTxResult) Error() error {
- return r.err
-}
-
-func (r *sendTxResult) Code() SendTxReturnCode {
- return r.code
-}
-
-var _ TestSendTxRPCClient = (*sendTxRPC)(nil)
-
-func newSendTxRPC(sendTxErr error, sendTxRun func(args mock.Arguments)) *sendTxRPC {
- return &sendTxRPC{sendTxErr: sendTxErr, sendTxRun: sendTxRun}
-}
-
-func (rpc *sendTxRPC) SendTransaction(ctx context.Context, _ any) *sendTxResult {
- if rpc.sendTxRun != nil {
- rpc.sendTxRun(mock.Arguments{ctx})
- }
- return &sendTxResult{err: rpc.sendTxErr, code: classifySendTxError(nil, rpc.sendTxErr)}
-}
-
-// newTestTransactionSender returns a sendTxMultiNode and TransactionSender.
-// Only the TransactionSender is run via Start/Close.
-func newTestTransactionSender(t *testing.T, chainID types.ID, lggr logger.Logger,
- nodes []Node[types.ID, TestSendTxRPCClient],
- sendOnlyNodes []SendOnlyNode[types.ID, TestSendTxRPCClient],
-) (*sendTxMultiNode, *TransactionSender[any, *sendTxResult, types.ID, TestSendTxRPCClient]) {
- mn := sendTxMultiNode{NewMultiNode[types.ID, TestSendTxRPCClient](
- lggr, NodeSelectionModeRoundRobin, 0, nodes, sendOnlyNodes, chainID, "chainFamily", 0)}
-
- txSender := NewTransactionSender[any, *sendTxResult, types.ID, TestSendTxRPCClient](lggr, chainID, mn.chainFamily, mn.MultiNode, NewSendTxResult, tests.TestInterval)
- servicetest.Run(t, txSender)
- return &mn, txSender
-}
-
-func classifySendTxError(_ any, err error) SendTxReturnCode {
- if err != nil {
- return Fatal
- }
- return Successful
-}
-
-func TestTransactionSender_SendTransaction(t *testing.T) {
- t.Parallel()
-
- newNodeWithState := func(t *testing.T, state nodeState, txErr error, sendTxRun func(args mock.Arguments)) *mockNode[types.ID, TestSendTxRPCClient] {
- rpc := newSendTxRPC(txErr, sendTxRun)
- node := newMockNode[types.ID, TestSendTxRPCClient](t)
- node.On("String").Return("node name").Maybe()
- node.On("RPC").Return(rpc).Maybe()
- node.On("State").Return(state).Maybe()
- node.On("Start", mock.Anything).Return(nil).Maybe()
- node.On("Close").Return(nil).Maybe()
- node.On("SetPoolChainInfoProvider", mock.Anything).Return(nil).Maybe()
- return node
- }
-
- newNode := func(t *testing.T, txErr error, sendTxRun func(args mock.Arguments)) *mockNode[types.ID, TestSendTxRPCClient] {
- return newNodeWithState(t, nodeStateAlive, txErr, sendTxRun)
- }
-
- t.Run("Fails if there is no nodes available", func(t *testing.T) {
- lggr := logger.Test(t)
- _, txSender := newTestTransactionSender(t, types.RandomID(), lggr, nil, nil)
- result := txSender.SendTransaction(tests.Context(t), nil)
- assert.EqualError(t, result.Error(), ErroringNodeError.Error())
- })
-
- t.Run("Transaction failure happy path", func(t *testing.T) {
- expectedError := errors.New("transaction failed")
- mainNode := newNode(t, expectedError, nil)
- lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel)
-
- _, txSender := newTestTransactionSender(t, types.RandomID(), lggr,
- []Node[types.ID, TestSendTxRPCClient]{mainNode},
- []SendOnlyNode[types.ID, TestSendTxRPCClient]{newNode(t, errors.New("unexpected error"), nil)})
-
- result := txSender.SendTransaction(tests.Context(t), nil)
- require.ErrorIs(t, result.Error(), expectedError)
- require.Equal(t, Fatal, result.Code())
- tests.AssertLogCountEventually(t, observedLogs, "Node sent transaction", 2)
- tests.AssertLogCountEventually(t, observedLogs, "RPC returned error", 2)
- })
-
- t.Run("Transaction success happy path", func(t *testing.T) {
- mainNode := newNode(t, nil, nil)
-
- lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel)
- _, txSender := newTestTransactionSender(t, types.RandomID(), lggr,
- []Node[types.ID, TestSendTxRPCClient]{mainNode},
- []SendOnlyNode[types.ID, TestSendTxRPCClient]{newNode(t, errors.New("unexpected error"), nil)})
-
- result := txSender.SendTransaction(tests.Context(t), nil)
- require.NoError(t, result.Error())
- require.Equal(t, Successful, result.Code())
- tests.AssertLogCountEventually(t, observedLogs, "Node sent transaction", 2)
- tests.AssertLogCountEventually(t, observedLogs, "RPC returned error", 1)
- })
-
- t.Run("Context expired before collecting sufficient results", func(t *testing.T) {
- testContext, testCancel := context.WithCancel(tests.Context(t))
- defer testCancel()
-
- mainNode := newNode(t, nil, func(_ mock.Arguments) {
- // block caller til end of the test
- <-testContext.Done()
- })
-
- lggr := logger.Test(t)
-
- _, txSender := newTestTransactionSender(t, types.RandomID(), lggr,
- []Node[types.ID, TestSendTxRPCClient]{mainNode}, nil)
-
- requestContext, cancel := context.WithCancel(tests.Context(t))
- cancel()
- result := txSender.SendTransaction(requestContext, nil)
- require.EqualError(t, result.Error(), "context canceled")
- })
-
- t.Run("Soft timeout stops results collection", func(t *testing.T) {
- chainID := types.RandomID()
- expectedError := errors.New("transaction failed")
- fastNode := newNode(t, expectedError, nil)
-
- // hold reply from the node till end of the test
- testContext, testCancel := context.WithCancel(tests.Context(t))
- defer testCancel()
- slowNode := newNode(t, errors.New("transaction failed"), func(_ mock.Arguments) {
- // block caller til end of the test
- <-testContext.Done()
- })
-
- lggr := logger.Test(t)
-
- _, txSender := newTestTransactionSender(t, chainID, lggr, []Node[types.ID, TestSendTxRPCClient]{fastNode, slowNode}, nil)
- result := txSender.SendTransaction(tests.Context(t), nil)
- require.EqualError(t, result.Error(), expectedError.Error())
- })
- t.Run("Returns success without waiting for the rest of the nodes", func(t *testing.T) {
- chainID := types.RandomID()
- fastNode := newNode(t, nil, nil)
- // hold reply from the node till end of the test
- testContext, testCancel := context.WithCancel(tests.Context(t))
- defer testCancel()
- slowNode := newNode(t, errors.New("transaction failed"), func(_ mock.Arguments) {
- // block caller til end of the test
- <-testContext.Done()
- })
- slowSendOnly := newNode(t, errors.New("send only failed"), func(_ mock.Arguments) {
- // block caller til end of the test
- <-testContext.Done()
- })
- lggr, _ := logger.TestObserved(t, zap.WarnLevel)
- _, txSender := newTestTransactionSender(t, chainID, lggr,
- []Node[types.ID, TestSendTxRPCClient]{fastNode, slowNode},
- []SendOnlyNode[types.ID, TestSendTxRPCClient]{slowSendOnly})
-
- result := txSender.SendTransaction(tests.Context(t), nil)
- require.NoError(t, result.Error())
- require.Equal(t, Successful, result.Code())
- })
- t.Run("Fails when multinode is closed", func(t *testing.T) {
- chainID := types.RandomID()
- fastNode := newNode(t, nil, nil)
- fastNode.On("ConfiguredChainID").Return(chainID).Maybe()
- // hold reply from the node till end of the test
- testContext, testCancel := context.WithCancel(tests.Context(t))
- defer testCancel()
- slowNode := newNode(t, errors.New("transaction failed"), func(_ mock.Arguments) {
- // block caller til end of the test
- <-testContext.Done()
- })
- slowNode.On("ConfiguredChainID").Return(chainID).Maybe()
- slowSendOnly := newNode(t, errors.New("send only failed"), func(_ mock.Arguments) {
- // block caller til end of the test
- <-testContext.Done()
- })
- slowSendOnly.On("ConfiguredChainID").Return(chainID).Maybe()
-
- lggr, _ := logger.TestObserved(t, zap.DebugLevel)
-
- mn, txSender := newTestTransactionSender(t, chainID, lggr,
- []Node[types.ID, TestSendTxRPCClient]{fastNode, slowNode},
- []SendOnlyNode[types.ID, TestSendTxRPCClient]{slowSendOnly})
-
- require.NoError(t, mn.Start(tests.Context(t)))
- require.NoError(t, mn.Close())
- result := txSender.SendTransaction(tests.Context(t), nil)
- require.EqualError(t, result.Error(), "service is stopped")
- })
- t.Run("Fails when closed", func(t *testing.T) {
- chainID := types.RandomID()
- fastNode := newNode(t, nil, nil)
- // hold reply from the node till end of the test
- testContext, testCancel := context.WithCancel(tests.Context(t))
- defer testCancel()
- slowNode := newNode(t, errors.New("transaction failed"), func(_ mock.Arguments) {
- // block caller til end of the test
- <-testContext.Done()
- })
- slowSendOnly := newNode(t, errors.New("send only failed"), func(_ mock.Arguments) {
- // block caller til end of the test
- <-testContext.Done()
- })
-
- var txSender *TransactionSender[any, *sendTxResult, types.ID, TestSendTxRPCClient]
-
- t.Cleanup(func() { // after txSender.Close()
- result := txSender.SendTransaction(tests.Context(t), nil)
- assert.EqualError(t, result.err, "TransactionSender not started")
- })
-
- _, txSender = newTestTransactionSender(t, chainID, logger.Test(t),
- []Node[types.ID, TestSendTxRPCClient]{fastNode, slowNode},
- []SendOnlyNode[types.ID, TestSendTxRPCClient]{slowSendOnly})
- })
- t.Run("Returns error if there is no healthy primary nodes", func(t *testing.T) {
- chainID := types.RandomID()
- primary := newNodeWithState(t, nodeStateUnreachable, nil, nil)
- sendOnly := newNodeWithState(t, nodeStateUnreachable, nil, nil)
-
- lggr := logger.Test(t)
-
- _, txSender := newTestTransactionSender(t, chainID, lggr,
- []Node[types.ID, TestSendTxRPCClient]{primary},
- []SendOnlyNode[types.ID, TestSendTxRPCClient]{sendOnly})
-
- result := txSender.SendTransaction(tests.Context(t), nil)
- assert.EqualError(t, result.Error(), ErroringNodeError.Error())
- })
-
- t.Run("Transaction success even if one of the nodes is unhealthy", func(t *testing.T) {
- chainID := types.RandomID()
- mainNode := newNode(t, nil, nil)
- unexpectedCall := func(args mock.Arguments) {
- panic("SendTx must not be called for unhealthy node")
- }
- unhealthyNode := newNodeWithState(t, nodeStateUnreachable, nil, unexpectedCall)
- unhealthySendOnlyNode := newNodeWithState(t, nodeStateUnreachable, nil, unexpectedCall)
-
- lggr := logger.Test(t)
-
- _, txSender := newTestTransactionSender(t, chainID, lggr,
- []Node[types.ID, TestSendTxRPCClient]{mainNode, unhealthyNode},
- []SendOnlyNode[types.ID, TestSendTxRPCClient]{unhealthySendOnlyNode})
-
- result := txSender.SendTransaction(tests.Context(t), nil)
- require.NoError(t, result.Error())
- require.Equal(t, Successful, result.Code())
- })
- t.Run("All background jobs stop even if RPC returns result after soft timeout", func(t *testing.T) {
- chainID := types.RandomID()
- expectedError := errors.New("transaction failed")
- fastNode := newNode(t, expectedError, nil)
-
- // hold reply from the node till SendTransaction returns result
- sendTxContext, sendTxCancel := context.WithCancel(tests.Context(t))
- slowNode := newNode(t, errors.New("transaction failed"), func(_ mock.Arguments) {
- <-sendTxContext.Done()
- })
-
- lggr := logger.Test(t)
-
- _, txSender := newTestTransactionSender(t, chainID, lggr, []Node[types.ID, TestSendTxRPCClient]{fastNode, slowNode}, nil)
- result := txSender.SendTransaction(sendTxContext, nil)
- sendTxCancel()
- require.EqualError(t, result.Error(), expectedError.Error())
- // TxSender should stop all background go routines after SendTransaction is done and before test is done.
- // Otherwise, it signals that we have a goroutine leak.
- txSender.wg.Wait()
- })
-}
-
-func TestTransactionSender_SendTransaction_aggregateTxResults(t *testing.T) {
- t.Parallel()
- // ensure failure on new SendTxReturnCode
- codesToCover := map[SendTxReturnCode]struct{}{}
- for code := Successful; code < sendTxReturnCodeLen; code++ {
- codesToCover[code] = struct{}{}
- }
-
- testCases := []struct {
- Name string
- ExpectedTxResult string
- ExpectedCriticalErr string
- ResultsByCode sendTxResults[*sendTxResult]
- }{
- {
- Name: "Returns success and logs critical error on success and Fatal",
- ExpectedTxResult: "success",
- ExpectedCriticalErr: "found contradictions in nodes replies on SendTransaction: got success and severe error",
- ResultsByCode: sendTxResults[*sendTxResult]{
- Successful: {NewSendTxResult(errors.New("success"))},
- Fatal: {NewSendTxResult(errors.New("fatal"))},
- },
- },
- {
- Name: "Returns TransactionAlreadyKnown and logs critical error on TransactionAlreadyKnown and Fatal",
- ExpectedTxResult: "tx_already_known",
- ExpectedCriticalErr: "found contradictions in nodes replies on SendTransaction: got success and severe error",
- ResultsByCode: sendTxResults[*sendTxResult]{
- TransactionAlreadyKnown: {NewSendTxResult(errors.New("tx_already_known"))},
- Unsupported: {NewSendTxResult(errors.New("unsupported"))},
- },
- },
- {
- Name: "Prefers sever error to temporary",
- ExpectedTxResult: "underpriced",
- ExpectedCriticalErr: "",
- ResultsByCode: sendTxResults[*sendTxResult]{
- Retryable: {NewSendTxResult(errors.New("retryable"))},
- Underpriced: {NewSendTxResult(errors.New("underpriced"))},
- },
- },
- {
- Name: "Returns temporary error",
- ExpectedTxResult: "retryable",
- ExpectedCriticalErr: "",
- ResultsByCode: sendTxResults[*sendTxResult]{
- Retryable: {NewSendTxResult(errors.New("retryable"))},
- },
- },
- {
- Name: "Insufficient funds is treated as error",
- ExpectedTxResult: "insufficientFunds",
- ExpectedCriticalErr: "",
- ResultsByCode: sendTxResults[*sendTxResult]{
- InsufficientFunds: {NewSendTxResult(errors.New("insufficientFunds"))},
- },
- },
- {
- Name: "Logs critical error on empty ResultsByCode",
- ExpectedCriticalErr: "expected at least one response on SendTransaction",
- ResultsByCode: sendTxResults[*sendTxResult]{},
- },
- {
- Name: "Zk terminally stuck",
- ExpectedTxResult: "not enough keccak counters to continue the execution",
- ExpectedCriticalErr: "",
- ResultsByCode: sendTxResults[*sendTxResult]{
- TerminallyStuck: {NewSendTxResult(errors.New("not enough keccak counters to continue the execution"))},
- },
- },
- }
-
- for _, testCase := range testCases {
- for code := range testCase.ResultsByCode {
- delete(codesToCover, code)
- }
-
- t.Run(testCase.Name, func(t *testing.T) {
- txResult, err := aggregateTxResults(testCase.ResultsByCode)
- if testCase.ExpectedTxResult != "" {
- require.EqualError(t, txResult.Error(), testCase.ExpectedTxResult)
- }
-
- logger.Sugared(logger.Test(t)).Info("Map: " + fmt.Sprint(testCase.ResultsByCode))
- logger.Sugared(logger.Test(t)).Criticalw("observed invariant violation on SendTransaction", "resultsByCode", testCase.ResultsByCode, "err", err)
-
- if testCase.ExpectedCriticalErr == "" {
- require.NoError(t, err)
- } else {
- require.EqualError(t, err, testCase.ExpectedCriticalErr)
- }
- })
- }
-
- // explicitly signal that following codes are properly handled in aggregateTxResults,
- // but dedicated test cases won't be beneficial
- for _, codeToIgnore := range []SendTxReturnCode{Unknown, ExceedsMaxFee, FeeOutOfValidRange} {
- delete(codesToCover, codeToIgnore)
- }
- assert.Empty(t, codesToCover, "all of the SendTxReturnCode must be covered by this test")
-}
diff --git a/common/client/types.go b/common/client/types.go
deleted file mode 100644
index 38880397442..00000000000
--- a/common/client/types.go
+++ /dev/null
@@ -1,83 +0,0 @@
-package client
-
-import (
- "context"
- "math/big"
-
- "github.com/smartcontractkit/chainlink/v2/common/types"
-)
-
-// RPCClient includes all the necessary generalized RPC methods used by Node to perform health checks
-type RPCClient[
- CHAIN_ID types.ID,
- HEAD Head,
-] interface {
- // ChainID - fetches ChainID from the RPC to verify that it matches config
- ChainID(ctx context.Context) (CHAIN_ID, error)
- // Dial - prepares the RPC for usage. Can be called on fresh or closed RPC
- Dial(ctx context.Context) error
- // SubscribeToHeads - returns channel and subscription for new heads.
- SubscribeToHeads(ctx context.Context) (<-chan HEAD, types.Subscription, error)
- // SubscribeToFinalizedHeads - returns channel and subscription for finalized heads.
- SubscribeToFinalizedHeads(ctx context.Context) (<-chan HEAD, types.Subscription, error)
- // Ping - returns error if RPC is not reachable
- Ping(context.Context) error
- // IsSyncing - returns true if the RPC is in Syncing state and can not process calls
- IsSyncing(ctx context.Context) (bool, error)
- // UnsubscribeAllExcept - close all subscriptions except `subs`
- UnsubscribeAllExcept(subs ...types.Subscription)
- // Close - closes all subscriptions and aborts all RPC calls
- Close()
- // GetInterceptedChainInfo - returns latest and highest observed by application layer ChainInfo.
- // latest ChainInfo is the most recent value received within a NodeClient's current lifecycle between Dial and DisconnectAll.
- // highestUserObservations ChainInfo is the highest ChainInfo observed excluding health checks calls.
- // Its values must not be reset.
- // The results of corresponding calls, to get the most recent head and the latest finalized head, must be
- // intercepted and reflected in ChainInfo before being returned to a caller. Otherwise, MultiNode is not able to
- // provide repeatable read guarantee.
- // DisconnectAll must reset latest ChainInfo to default value.
- // Ensure implementation does not have a race condition when values are reset before request completion and as
- // a result latest ChainInfo contains information from the previous cycle.
- GetInterceptedChainInfo() (latest, highestUserObservations ChainInfo)
-}
-
-// Head is the interface required by the NodeClient
-type Head interface {
- BlockNumber() int64
- BlockDifficulty() *big.Int
- IsValid() bool
-}
-
-// PoolChainInfoProvider - provides aggregation of nodes pool ChainInfo
-type PoolChainInfoProvider interface {
- // LatestChainInfo - returns number of live nodes available in the pool, so we can prevent the last alive node in a pool from being
- // moved to out-of-sync state. It is better to have one out-of-sync node than no nodes at all.
- // Returns highest latest ChainInfo within the alive nodes. E.g. most recent block number and highest block number
- // observed by Node A are 10 and 15; Node B - 12 and 14. This method will return 12.
- LatestChainInfo() (int, ChainInfo)
- // HighestUserObservations - returns highest ChainInfo ever observed by any user of MultiNode.
- HighestUserObservations() ChainInfo
-}
-
-// ChainInfo - defines RPC's or MultiNode's view on the chain
-type ChainInfo struct {
- BlockNumber int64
- FinalizedBlockNumber int64
- TotalDifficulty *big.Int
-}
-
-func MaxTotalDifficulty(a, b *big.Int) *big.Int {
- if a == nil {
- if b == nil {
- return nil
- }
-
- return big.NewInt(0).Set(b)
- }
-
- if b == nil || a.Cmp(b) >= 0 {
- return big.NewInt(0).Set(a)
- }
-
- return big.NewInt(0).Set(b)
-}
diff --git a/common/client/types_test.go b/common/client/types_test.go
deleted file mode 100644
index 68d7a3fe78e..00000000000
--- a/common/client/types_test.go
+++ /dev/null
@@ -1,34 +0,0 @@
-package client
-
-import (
- "math/big"
- "testing"
-
- "github.com/stretchr/testify/assert"
-)
-
-func TestMaxDifficulty(t *testing.T) {
- cases := []struct {
- A, B, Result *big.Int
- }{
- {
- A: nil, B: nil, Result: nil,
- },
- {
- A: nil, B: big.NewInt(1), Result: big.NewInt(1),
- },
- {
- A: big.NewInt(1), B: big.NewInt(1), Result: big.NewInt(1),
- },
- {
- A: big.NewInt(1), B: big.NewInt(2), Result: big.NewInt(2),
- },
- }
-
- for _, test := range cases {
- actualResult := MaxTotalDifficulty(test.A, test.B)
- assert.Equal(t, test.Result, actualResult, "expected max(%v, %v) to produce %v", test.A, test.B, test.Result)
- inverted := MaxTotalDifficulty(test.B, test.A)
- assert.Equal(t, actualResult, inverted, "expected max(%v, %v) == max(%v, %v)", test.A, test.B, test.B, test.A)
- }
-}
diff --git a/common/txmgr/broadcaster.go b/common/txmgr/broadcaster.go
index 14e959c39ae..41ec0f644b4 100644
--- a/common/txmgr/broadcaster.go
+++ b/common/txmgr/broadcaster.go
@@ -18,8 +18,8 @@ import (
"github.com/smartcontractkit/chainlink-common/pkg/logger"
"github.com/smartcontractkit/chainlink-common/pkg/services"
"github.com/smartcontractkit/chainlink-common/pkg/utils"
+ "github.com/smartcontractkit/chainlink-framework/multinode"
- "github.com/smartcontractkit/chainlink/v2/common/client"
commonfee "github.com/smartcontractkit/chainlink/v2/common/fee"
feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types"
txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types"
@@ -498,7 +498,7 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) hand
errType, err = eb.validateOnChainSequence(ctx, lgr, errType, err, etx, retryCount)
}
- if errType == client.Fatal || errType == client.TerminallyStuck {
+ if errType == multinode.Fatal || errType == multinode.TerminallyStuck {
eb.SvcErrBuffer.Append(err)
etx.Error = null.StringFrom(err.Error())
return eb.saveFatallyErroredTransaction(lgr, &etx), true
@@ -508,9 +508,9 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) hand
etx.BroadcastAt = &initialBroadcastAt
switch errType {
- case client.TransactionAlreadyKnown:
+ case multinode.TransactionAlreadyKnown:
fallthrough
- case client.Successful:
+ case multinode.Successful:
// Either the transaction was successful or one of the following four scenarios happened:
//
// SCENARIO 1
@@ -557,14 +557,14 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) hand
// Increment sequence if successfully broadcasted
eb.sequenceTracker.GenerateNextSequence(etx.FromAddress, *etx.Sequence)
return err, true
- case client.Underpriced:
+ case multinode.Underpriced:
bumpedAttempt, retryable, replaceErr := eb.replaceAttemptWithBumpedGas(ctx, lgr, err, etx, attempt)
if replaceErr != nil {
return replaceErr, retryable
}
return eb.handleInProgressTx(ctx, etx, bumpedAttempt, initialBroadcastAt, retryCount+1)
- case client.InsufficientFunds:
+ case multinode.InsufficientFunds:
// NOTE: This can occur due to either insufficient funds or a gas spike
// combined with a high gas limit. Regardless of the cause, we need to obtain a new estimate,
// replace the current attempt, and retry after the backoff duration.
@@ -574,9 +574,9 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) hand
return replaceErr, true
}
return err, true
- case client.Retryable:
+ case multinode.Retryable:
return err, true
- case client.FeeOutOfValidRange:
+ case multinode.FeeOutOfValidRange:
replacementAttempt, retryable, replaceErr := eb.replaceAttemptWithNewEstimation(ctx, lgr, etx, attempt)
if replaceErr != nil {
return replaceErr, retryable
@@ -585,9 +585,9 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) hand
lgr.Warnw("L2 rejected transaction due to incorrect fee, re-estimated and will try again",
"etxID", etx.ID, "err", err, "newGasPrice", replacementAttempt.TxFee, "newGasLimit", replacementAttempt.ChainSpecificFeeLimit)
return eb.handleInProgressTx(ctx, etx, *replacementAttempt, initialBroadcastAt, 0)
- case client.Unsupported:
+ case multinode.Unsupported:
return err, false
- case client.ExceedsMaxFee:
+ case multinode.ExceedsMaxFee:
// Broadcaster: Note that we may have broadcast to multiple nodes and had it
// accepted by one of them! It is not guaranteed that all nodes share
// the same tx fee cap. That is why we must treat this as an unknown
@@ -600,7 +600,7 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) hand
default:
// Every error that doesn't fall under one of the above categories will be treated as Unknown.
fallthrough
- case client.Unknown:
+ case multinode.Unknown:
eb.SvcErrBuffer.Append(err)
lgr.Criticalw(`Unknown error occurred while handling tx queue in ProcessUnstartedTxs. This chain/RPC client may not be supported. `+
`Urgent resolution required, Chainlink is currently operating in a degraded state and may miss transactions`, "attempt", attempt, "err", err)
@@ -632,9 +632,9 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) hand
}
}
-func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) validateOnChainSequence(ctx context.Context, lgr logger.SugaredLogger, errType client.SendTxReturnCode, err error, etx txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], retryCount int) (client.SendTxReturnCode, error) {
+func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) validateOnChainSequence(ctx context.Context, lgr logger.SugaredLogger, errType multinode.SendTxReturnCode, err error, etx txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], retryCount int) (multinode.SendTxReturnCode, error) {
// Only check if sequence was incremented if broadcast was successful, otherwise return the existing err type
- if errType != client.Successful {
+ if errType != multinode.Successful {
return errType, err
}
// Transaction sequence cannot be nil here since a sequence is required to broadcast
@@ -649,7 +649,7 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) vali
// Insufficient transaction fee is a common scenario in which the sequence is not incremented by the chain even though we got a successful response
// If the sequence failed to increment and hasn't reached the max retries, return the Underpriced error to try again with a bumped attempt
if nextSeqOnChain.Int64() == txSeq.Int64() && retryCount < maxHederaBroadcastRetries {
- return client.Underpriced, nil
+ return multinode.Underpriced, nil
}
// If the transaction reaches the retry limit and fails to get included, mark it as fatally errored
@@ -657,17 +657,17 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) vali
if nextSeqOnChain.Int64() == txSeq.Int64() && retryCount >= maxHederaBroadcastRetries {
err := fmt.Errorf("failed to broadcast transaction on %s after %d retries", hederaChainType, retryCount)
lgr.Error(err.Error())
- return client.Fatal, err
+ return multinode.Fatal, err
}
// Belts and braces approach to detect and handle sqeuence gaps if the broadcast is considered successful
if nextSeqOnChain.Int64() < txSeq.Int64() {
err := fmt.Errorf("next expected sequence on-chain (%s) is less than the broadcasted transaction's sequence (%s)", nextSeqOnChain.String(), txSeq.String())
lgr.Criticalw("Sequence gap has been detected and needs to be filled", "error", err)
- return client.Fatal, err
+ return multinode.Fatal, err
}
- return client.Successful, nil
+ return multinode.Successful, nil
}
// Finds next transaction in the queue, assigns a sequence, and moves it to "in_progress" state ready for broadcast.
diff --git a/common/txmgr/confirmer.go b/common/txmgr/confirmer.go
index 7c5ba798cf2..7b5a90e70a6 100644
--- a/common/txmgr/confirmer.go
+++ b/common/txmgr/confirmer.go
@@ -20,8 +20,8 @@ import (
"github.com/smartcontractkit/chainlink-common/pkg/logger"
"github.com/smartcontractkit/chainlink-common/pkg/services"
"github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox"
+ "github.com/smartcontractkit/chainlink-framework/multinode"
- "github.com/smartcontractkit/chainlink/v2/common/client"
commonfee "github.com/smartcontractkit/chainlink/v2/common/fee"
feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types"
iutils "github.com/smartcontractkit/chainlink/v2/common/internal/utils"
@@ -696,7 +696,7 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) han
errType, sendError := ec.client.SendTransactionReturnCode(ctx, etx, attempt, lggr)
switch errType {
- case client.Underpriced:
+ case multinode.Underpriced:
// This should really not ever happen in normal operation since we
// already bumped above the required minimum in broadcaster.
ec.lggr.Warnw("Got terminally underpriced error for gas bump, this should never happen unless the remote RPC node changed its configuration on the fly, or you are using multiple RPC nodes with different minimum gas price requirements. This is not recommended", "attempt", attempt)
@@ -731,7 +731,7 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) han
return fmt.Errorf("saveReplacementInProgressAttempt failed: %w", err)
}
return ec.handleInProgressAttempt(ctx, lggr, etx, replacementAttempt, blockHeight)
- case client.ExceedsMaxFee:
+ case multinode.ExceedsMaxFee:
// Confirmer: Note it is not guaranteed that all nodes share the same tx fee cap.
// So it is very likely that this attempt was successful on another node since
// it was already successfully broadcasted. So we assume it is successful and
@@ -745,7 +745,7 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) han
lggr.Criticalw(`RPC node rejected this tx as outside Fee Cap but it may have been accepted by another Node`, "attempt", attempt)
timeout := ec.dbConfig.DefaultQueryTimeout()
return ec.txStore.SaveSentAttempt(ctx, timeout, &attempt, now)
- case client.Fatal:
+ case multinode.Fatal:
// WARNING: This should never happen!
// Should NEVER be fatal this is an invariant violation. The
// Broadcaster can never create a TxAttempt that will
@@ -760,7 +760,7 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) han
ec.SvcErrBuffer.Append(sendError)
// This will loop continuously on every new head so it must be handled manually by the node operator!
return ec.txStore.DeleteInProgressAttempt(ctx, attempt)
- case client.TerminallyStuck:
+ case multinode.TerminallyStuck:
// A transaction could broadcast successfully but then be considered terminally stuck on another attempt
// Even though the transaction can succeed under different circumstances, we want to purge this transaction as soon as we get this error
lggr.Warnw("terminally stuck transaction detected", "err", sendError.Error())
@@ -775,20 +775,20 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) han
return fmt.Errorf("saveReplacementInProgressAttempt failed: %w", err)
}
return ec.handleInProgressAttempt(ctx, lggr, etx, purgeAttempt, blockHeight)
- case client.TransactionAlreadyKnown:
+ case multinode.TransactionAlreadyKnown:
// Sequence too low indicated that a transaction at this sequence was confirmed already.
// Mark confirmed_missing_receipt and wait for the next cycle to try to get a receipt
lggr.Debugw("Sequence already used", "txAttemptID", attempt.ID, "txHash", attempt.Hash.String())
timeout := ec.dbConfig.DefaultQueryTimeout()
return ec.txStore.SaveConfirmedAttempt(ctx, timeout, &attempt, now)
- case client.InsufficientFunds:
+ case multinode.InsufficientFunds:
timeout := ec.dbConfig.DefaultQueryTimeout()
return ec.txStore.SaveInsufficientFundsAttempt(ctx, timeout, &attempt, now)
- case client.Successful:
+ case multinode.Successful:
lggr.Debugw("Successfully broadcast transaction", "txAttemptID", attempt.ID, "txHash", attempt.Hash.String())
timeout := ec.dbConfig.DefaultQueryTimeout()
return ec.txStore.SaveSentAttempt(ctx, timeout, &attempt, now)
- case client.Unknown:
+ case multinode.Unknown:
// Every error that doesn't fall under one of the above categories will be treated as Unknown.
fallthrough
default:
@@ -838,7 +838,7 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) For
}
attempt.Tx = *etx // for logging
ec.lggr.Debugw("Sending transaction", "txAttemptID", attempt.ID, "txHash", attempt.Hash, "err", err, "meta", etx.Meta, "feeLimit", attempt.ChainSpecificFeeLimit, "callerProvidedFeeLimit", etx.FeeLimit, "attempt", attempt)
- if errCode, err := ec.client.SendTransactionReturnCode(ctx, *etx, attempt, ec.lggr); errCode != client.Successful && err != nil {
+ if errCode, err := ec.client.SendTransactionReturnCode(ctx, *etx, attempt, ec.lggr); errCode != multinode.Successful && err != nil {
ec.lggr.Errorw(fmt.Sprintf("ForceRebroadcast: failed to rebroadcast tx %v with sequence %v, gas limit %v, and caller provided fee Limit %v : %s", etx.ID, *etx.Sequence, attempt.ChainSpecificFeeLimit, etx.FeeLimit, err.Error()), "err", err, "fee", attempt.TxFee)
continue
}
diff --git a/common/txmgr/resender.go b/common/txmgr/resender.go
index d4f1b4275a1..6b65ca05923 100644
--- a/common/txmgr/resender.go
+++ b/common/txmgr/resender.go
@@ -9,7 +9,7 @@ import (
"github.com/smartcontractkit/chainlink-common/pkg/chains/label"
"github.com/smartcontractkit/chainlink-common/pkg/logger"
"github.com/smartcontractkit/chainlink-common/pkg/services"
- "github.com/smartcontractkit/chainlink/v2/common/client"
+ "github.com/smartcontractkit/chainlink-framework/multinode"
feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types"
txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types"
"github.com/smartcontractkit/chainlink/v2/common/types"
@@ -184,13 +184,13 @@ func (er *Resender[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) resendUnco
return nil
}
-func logResendResult(lggr logger.Logger, codes []client.SendTxReturnCode) {
+func logResendResult(lggr logger.Logger, codes []multinode.SendTxReturnCode) {
var nNew int
var nFatal int
for _, c := range codes {
- if c == client.Successful {
+ if c == multinode.Successful {
nNew++
- } else if c == client.Fatal {
+ } else if c == multinode.Fatal {
nFatal++
}
}
diff --git a/common/txmgr/types/client.go b/common/txmgr/types/client.go
index 759b15d6162..67426437c8a 100644
--- a/common/txmgr/types/client.go
+++ b/common/txmgr/types/client.go
@@ -7,7 +7,8 @@ import (
"time"
"github.com/smartcontractkit/chainlink-common/pkg/logger"
- "github.com/smartcontractkit/chainlink/v2/common/client"
+ "github.com/smartcontractkit/chainlink-framework/multinode"
+
feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types"
"github.com/smartcontractkit/chainlink/v2/common/types"
)
@@ -49,7 +50,7 @@ type TransactionClient[
bathSize int,
lggr logger.SugaredLogger,
) (
- txCodes []client.SendTxReturnCode,
+ txCodes []multinode.SendTxReturnCode,
txErrs []error,
broadcastTime time.Time,
successfulTxIDs []int64,
@@ -59,7 +60,7 @@ type TransactionClient[
tx Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE],
attempt TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE],
lggr logger.SugaredLogger,
- ) (client.SendTxReturnCode, error)
+ ) (multinode.SendTxReturnCode, error)
SendEmptyTransaction(
ctx context.Context,
newTxAttempt func(ctx context.Context, seq SEQ, feeLimit uint64, fee FEE, fromAddress ADDR) (attempt TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], err error),
diff --git a/common/types/mocks/head.go b/common/types/mocks/head.go
deleted file mode 100644
index b00c155ccc7..00000000000
--- a/common/types/mocks/head.go
+++ /dev/null
@@ -1,607 +0,0 @@
-// Code generated by mockery v2.50.0. DO NOT EDIT.
-
-package mocks
-
-import (
- big "math/big"
- time "time"
-
- mock "github.com/stretchr/testify/mock"
-
- types "github.com/smartcontractkit/chainlink/v2/common/types"
-)
-
-// Head is an autogenerated mock type for the Head type
-type Head[BLOCK_HASH types.Hashable] struct {
- mock.Mock
-}
-
-type Head_Expecter[BLOCK_HASH types.Hashable] struct {
- mock *mock.Mock
-}
-
-func (_m *Head[BLOCK_HASH]) EXPECT() *Head_Expecter[BLOCK_HASH] {
- return &Head_Expecter[BLOCK_HASH]{mock: &_m.Mock}
-}
-
-// BlockDifficulty provides a mock function with no fields
-func (_m *Head[BLOCK_HASH]) BlockDifficulty() *big.Int {
- ret := _m.Called()
-
- if len(ret) == 0 {
- panic("no return value specified for BlockDifficulty")
- }
-
- var r0 *big.Int
- if rf, ok := ret.Get(0).(func() *big.Int); ok {
- r0 = rf()
- } else {
- if ret.Get(0) != nil {
- r0 = ret.Get(0).(*big.Int)
- }
- }
-
- return r0
-}
-
-// Head_BlockDifficulty_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BlockDifficulty'
-type Head_BlockDifficulty_Call[BLOCK_HASH types.Hashable] struct {
- *mock.Call
-}
-
-// BlockDifficulty is a helper method to define mock.On call
-func (_e *Head_Expecter[BLOCK_HASH]) BlockDifficulty() *Head_BlockDifficulty_Call[BLOCK_HASH] {
- return &Head_BlockDifficulty_Call[BLOCK_HASH]{Call: _e.mock.On("BlockDifficulty")}
-}
-
-func (_c *Head_BlockDifficulty_Call[BLOCK_HASH]) Run(run func()) *Head_BlockDifficulty_Call[BLOCK_HASH] {
- _c.Call.Run(func(args mock.Arguments) {
- run()
- })
- return _c
-}
-
-func (_c *Head_BlockDifficulty_Call[BLOCK_HASH]) Return(_a0 *big.Int) *Head_BlockDifficulty_Call[BLOCK_HASH] {
- _c.Call.Return(_a0)
- return _c
-}
-
-func (_c *Head_BlockDifficulty_Call[BLOCK_HASH]) RunAndReturn(run func() *big.Int) *Head_BlockDifficulty_Call[BLOCK_HASH] {
- _c.Call.Return(run)
- return _c
-}
-
-// BlockHash provides a mock function with no fields
-func (_m *Head[BLOCK_HASH]) BlockHash() BLOCK_HASH {
- ret := _m.Called()
-
- if len(ret) == 0 {
- panic("no return value specified for BlockHash")
- }
-
- var r0 BLOCK_HASH
- if rf, ok := ret.Get(0).(func() BLOCK_HASH); ok {
- r0 = rf()
- } else {
- if ret.Get(0) != nil {
- r0 = ret.Get(0).(BLOCK_HASH)
- }
- }
-
- return r0
-}
-
-// Head_BlockHash_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BlockHash'
-type Head_BlockHash_Call[BLOCK_HASH types.Hashable] struct {
- *mock.Call
-}
-
-// BlockHash is a helper method to define mock.On call
-func (_e *Head_Expecter[BLOCK_HASH]) BlockHash() *Head_BlockHash_Call[BLOCK_HASH] {
- return &Head_BlockHash_Call[BLOCK_HASH]{Call: _e.mock.On("BlockHash")}
-}
-
-func (_c *Head_BlockHash_Call[BLOCK_HASH]) Run(run func()) *Head_BlockHash_Call[BLOCK_HASH] {
- _c.Call.Run(func(args mock.Arguments) {
- run()
- })
- return _c
-}
-
-func (_c *Head_BlockHash_Call[BLOCK_HASH]) Return(_a0 BLOCK_HASH) *Head_BlockHash_Call[BLOCK_HASH] {
- _c.Call.Return(_a0)
- return _c
-}
-
-func (_c *Head_BlockHash_Call[BLOCK_HASH]) RunAndReturn(run func() BLOCK_HASH) *Head_BlockHash_Call[BLOCK_HASH] {
- _c.Call.Return(run)
- return _c
-}
-
-// BlockNumber provides a mock function with no fields
-func (_m *Head[BLOCK_HASH]) BlockNumber() int64 {
- ret := _m.Called()
-
- if len(ret) == 0 {
- panic("no return value specified for BlockNumber")
- }
-
- var r0 int64
- if rf, ok := ret.Get(0).(func() int64); ok {
- r0 = rf()
- } else {
- r0 = ret.Get(0).(int64)
- }
-
- return r0
-}
-
-// Head_BlockNumber_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BlockNumber'
-type Head_BlockNumber_Call[BLOCK_HASH types.Hashable] struct {
- *mock.Call
-}
-
-// BlockNumber is a helper method to define mock.On call
-func (_e *Head_Expecter[BLOCK_HASH]) BlockNumber() *Head_BlockNumber_Call[BLOCK_HASH] {
- return &Head_BlockNumber_Call[BLOCK_HASH]{Call: _e.mock.On("BlockNumber")}
-}
-
-func (_c *Head_BlockNumber_Call[BLOCK_HASH]) Run(run func()) *Head_BlockNumber_Call[BLOCK_HASH] {
- _c.Call.Run(func(args mock.Arguments) {
- run()
- })
- return _c
-}
-
-func (_c *Head_BlockNumber_Call[BLOCK_HASH]) Return(_a0 int64) *Head_BlockNumber_Call[BLOCK_HASH] {
- _c.Call.Return(_a0)
- return _c
-}
-
-func (_c *Head_BlockNumber_Call[BLOCK_HASH]) RunAndReturn(run func() int64) *Head_BlockNumber_Call[BLOCK_HASH] {
- _c.Call.Return(run)
- return _c
-}
-
-// ChainLength provides a mock function with no fields
-func (_m *Head[BLOCK_HASH]) ChainLength() uint32 {
- ret := _m.Called()
-
- if len(ret) == 0 {
- panic("no return value specified for ChainLength")
- }
-
- var r0 uint32
- if rf, ok := ret.Get(0).(func() uint32); ok {
- r0 = rf()
- } else {
- r0 = ret.Get(0).(uint32)
- }
-
- return r0
-}
-
-// Head_ChainLength_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ChainLength'
-type Head_ChainLength_Call[BLOCK_HASH types.Hashable] struct {
- *mock.Call
-}
-
-// ChainLength is a helper method to define mock.On call
-func (_e *Head_Expecter[BLOCK_HASH]) ChainLength() *Head_ChainLength_Call[BLOCK_HASH] {
- return &Head_ChainLength_Call[BLOCK_HASH]{Call: _e.mock.On("ChainLength")}
-}
-
-func (_c *Head_ChainLength_Call[BLOCK_HASH]) Run(run func()) *Head_ChainLength_Call[BLOCK_HASH] {
- _c.Call.Run(func(args mock.Arguments) {
- run()
- })
- return _c
-}
-
-func (_c *Head_ChainLength_Call[BLOCK_HASH]) Return(_a0 uint32) *Head_ChainLength_Call[BLOCK_HASH] {
- _c.Call.Return(_a0)
- return _c
-}
-
-func (_c *Head_ChainLength_Call[BLOCK_HASH]) RunAndReturn(run func() uint32) *Head_ChainLength_Call[BLOCK_HASH] {
- _c.Call.Return(run)
- return _c
-}
-
-// EarliestHeadInChain provides a mock function with no fields
-func (_m *Head[BLOCK_HASH]) EarliestHeadInChain() types.Head[BLOCK_HASH] {
- ret := _m.Called()
-
- if len(ret) == 0 {
- panic("no return value specified for EarliestHeadInChain")
- }
-
- var r0 types.Head[BLOCK_HASH]
- if rf, ok := ret.Get(0).(func() types.Head[BLOCK_HASH]); ok {
- r0 = rf()
- } else {
- if ret.Get(0) != nil {
- r0 = ret.Get(0).(types.Head[BLOCK_HASH])
- }
- }
-
- return r0
-}
-
-// Head_EarliestHeadInChain_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'EarliestHeadInChain'
-type Head_EarliestHeadInChain_Call[BLOCK_HASH types.Hashable] struct {
- *mock.Call
-}
-
-// EarliestHeadInChain is a helper method to define mock.On call
-func (_e *Head_Expecter[BLOCK_HASH]) EarliestHeadInChain() *Head_EarliestHeadInChain_Call[BLOCK_HASH] {
- return &Head_EarliestHeadInChain_Call[BLOCK_HASH]{Call: _e.mock.On("EarliestHeadInChain")}
-}
-
-func (_c *Head_EarliestHeadInChain_Call[BLOCK_HASH]) Run(run func()) *Head_EarliestHeadInChain_Call[BLOCK_HASH] {
- _c.Call.Run(func(args mock.Arguments) {
- run()
- })
- return _c
-}
-
-func (_c *Head_EarliestHeadInChain_Call[BLOCK_HASH]) Return(_a0 types.Head[BLOCK_HASH]) *Head_EarliestHeadInChain_Call[BLOCK_HASH] {
- _c.Call.Return(_a0)
- return _c
-}
-
-func (_c *Head_EarliestHeadInChain_Call[BLOCK_HASH]) RunAndReturn(run func() types.Head[BLOCK_HASH]) *Head_EarliestHeadInChain_Call[BLOCK_HASH] {
- _c.Call.Return(run)
- return _c
-}
-
-// GetParent provides a mock function with no fields
-func (_m *Head[BLOCK_HASH]) GetParent() types.Head[BLOCK_HASH] {
- ret := _m.Called()
-
- if len(ret) == 0 {
- panic("no return value specified for GetParent")
- }
-
- var r0 types.Head[BLOCK_HASH]
- if rf, ok := ret.Get(0).(func() types.Head[BLOCK_HASH]); ok {
- r0 = rf()
- } else {
- if ret.Get(0) != nil {
- r0 = ret.Get(0).(types.Head[BLOCK_HASH])
- }
- }
-
- return r0
-}
-
-// Head_GetParent_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetParent'
-type Head_GetParent_Call[BLOCK_HASH types.Hashable] struct {
- *mock.Call
-}
-
-// GetParent is a helper method to define mock.On call
-func (_e *Head_Expecter[BLOCK_HASH]) GetParent() *Head_GetParent_Call[BLOCK_HASH] {
- return &Head_GetParent_Call[BLOCK_HASH]{Call: _e.mock.On("GetParent")}
-}
-
-func (_c *Head_GetParent_Call[BLOCK_HASH]) Run(run func()) *Head_GetParent_Call[BLOCK_HASH] {
- _c.Call.Run(func(args mock.Arguments) {
- run()
- })
- return _c
-}
-
-func (_c *Head_GetParent_Call[BLOCK_HASH]) Return(_a0 types.Head[BLOCK_HASH]) *Head_GetParent_Call[BLOCK_HASH] {
- _c.Call.Return(_a0)
- return _c
-}
-
-func (_c *Head_GetParent_Call[BLOCK_HASH]) RunAndReturn(run func() types.Head[BLOCK_HASH]) *Head_GetParent_Call[BLOCK_HASH] {
- _c.Call.Return(run)
- return _c
-}
-
-// GetParentHash provides a mock function with no fields
-func (_m *Head[BLOCK_HASH]) GetParentHash() BLOCK_HASH {
- ret := _m.Called()
-
- if len(ret) == 0 {
- panic("no return value specified for GetParentHash")
- }
-
- var r0 BLOCK_HASH
- if rf, ok := ret.Get(0).(func() BLOCK_HASH); ok {
- r0 = rf()
- } else {
- if ret.Get(0) != nil {
- r0 = ret.Get(0).(BLOCK_HASH)
- }
- }
-
- return r0
-}
-
-// Head_GetParentHash_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetParentHash'
-type Head_GetParentHash_Call[BLOCK_HASH types.Hashable] struct {
- *mock.Call
-}
-
-// GetParentHash is a helper method to define mock.On call
-func (_e *Head_Expecter[BLOCK_HASH]) GetParentHash() *Head_GetParentHash_Call[BLOCK_HASH] {
- return &Head_GetParentHash_Call[BLOCK_HASH]{Call: _e.mock.On("GetParentHash")}
-}
-
-func (_c *Head_GetParentHash_Call[BLOCK_HASH]) Run(run func()) *Head_GetParentHash_Call[BLOCK_HASH] {
- _c.Call.Run(func(args mock.Arguments) {
- run()
- })
- return _c
-}
-
-func (_c *Head_GetParentHash_Call[BLOCK_HASH]) Return(_a0 BLOCK_HASH) *Head_GetParentHash_Call[BLOCK_HASH] {
- _c.Call.Return(_a0)
- return _c
-}
-
-func (_c *Head_GetParentHash_Call[BLOCK_HASH]) RunAndReturn(run func() BLOCK_HASH) *Head_GetParentHash_Call[BLOCK_HASH] {
- _c.Call.Return(run)
- return _c
-}
-
-// GetTimestamp provides a mock function with no fields
-func (_m *Head[BLOCK_HASH]) GetTimestamp() time.Time {
- ret := _m.Called()
-
- if len(ret) == 0 {
- panic("no return value specified for GetTimestamp")
- }
-
- var r0 time.Time
- if rf, ok := ret.Get(0).(func() time.Time); ok {
- r0 = rf()
- } else {
- r0 = ret.Get(0).(time.Time)
- }
-
- return r0
-}
-
-// Head_GetTimestamp_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetTimestamp'
-type Head_GetTimestamp_Call[BLOCK_HASH types.Hashable] struct {
- *mock.Call
-}
-
-// GetTimestamp is a helper method to define mock.On call
-func (_e *Head_Expecter[BLOCK_HASH]) GetTimestamp() *Head_GetTimestamp_Call[BLOCK_HASH] {
- return &Head_GetTimestamp_Call[BLOCK_HASH]{Call: _e.mock.On("GetTimestamp")}
-}
-
-func (_c *Head_GetTimestamp_Call[BLOCK_HASH]) Run(run func()) *Head_GetTimestamp_Call[BLOCK_HASH] {
- _c.Call.Run(func(args mock.Arguments) {
- run()
- })
- return _c
-}
-
-func (_c *Head_GetTimestamp_Call[BLOCK_HASH]) Return(_a0 time.Time) *Head_GetTimestamp_Call[BLOCK_HASH] {
- _c.Call.Return(_a0)
- return _c
-}
-
-func (_c *Head_GetTimestamp_Call[BLOCK_HASH]) RunAndReturn(run func() time.Time) *Head_GetTimestamp_Call[BLOCK_HASH] {
- _c.Call.Return(run)
- return _c
-}
-
-// HashAtHeight provides a mock function with given fields: blockNum
-func (_m *Head[BLOCK_HASH]) HashAtHeight(blockNum int64) BLOCK_HASH {
- ret := _m.Called(blockNum)
-
- if len(ret) == 0 {
- panic("no return value specified for HashAtHeight")
- }
-
- var r0 BLOCK_HASH
- if rf, ok := ret.Get(0).(func(int64) BLOCK_HASH); ok {
- r0 = rf(blockNum)
- } else {
- if ret.Get(0) != nil {
- r0 = ret.Get(0).(BLOCK_HASH)
- }
- }
-
- return r0
-}
-
-// Head_HashAtHeight_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'HashAtHeight'
-type Head_HashAtHeight_Call[BLOCK_HASH types.Hashable] struct {
- *mock.Call
-}
-
-// HashAtHeight is a helper method to define mock.On call
-// - blockNum int64
-func (_e *Head_Expecter[BLOCK_HASH]) HashAtHeight(blockNum interface{}) *Head_HashAtHeight_Call[BLOCK_HASH] {
- return &Head_HashAtHeight_Call[BLOCK_HASH]{Call: _e.mock.On("HashAtHeight", blockNum)}
-}
-
-func (_c *Head_HashAtHeight_Call[BLOCK_HASH]) Run(run func(blockNum int64)) *Head_HashAtHeight_Call[BLOCK_HASH] {
- _c.Call.Run(func(args mock.Arguments) {
- run(args[0].(int64))
- })
- return _c
-}
-
-func (_c *Head_HashAtHeight_Call[BLOCK_HASH]) Return(_a0 BLOCK_HASH) *Head_HashAtHeight_Call[BLOCK_HASH] {
- _c.Call.Return(_a0)
- return _c
-}
-
-func (_c *Head_HashAtHeight_Call[BLOCK_HASH]) RunAndReturn(run func(int64) BLOCK_HASH) *Head_HashAtHeight_Call[BLOCK_HASH] {
- _c.Call.Return(run)
- return _c
-}
-
-// HeadAtHeight provides a mock function with given fields: blockNum
-func (_m *Head[BLOCK_HASH]) HeadAtHeight(blockNum int64) (types.Head[BLOCK_HASH], error) {
- ret := _m.Called(blockNum)
-
- if len(ret) == 0 {
- panic("no return value specified for HeadAtHeight")
- }
-
- var r0 types.Head[BLOCK_HASH]
- var r1 error
- if rf, ok := ret.Get(0).(func(int64) (types.Head[BLOCK_HASH], error)); ok {
- return rf(blockNum)
- }
- if rf, ok := ret.Get(0).(func(int64) types.Head[BLOCK_HASH]); ok {
- r0 = rf(blockNum)
- } else {
- if ret.Get(0) != nil {
- r0 = ret.Get(0).(types.Head[BLOCK_HASH])
- }
- }
-
- if rf, ok := ret.Get(1).(func(int64) error); ok {
- r1 = rf(blockNum)
- } else {
- r1 = ret.Error(1)
- }
-
- return r0, r1
-}
-
-// Head_HeadAtHeight_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'HeadAtHeight'
-type Head_HeadAtHeight_Call[BLOCK_HASH types.Hashable] struct {
- *mock.Call
-}
-
-// HeadAtHeight is a helper method to define mock.On call
-// - blockNum int64
-func (_e *Head_Expecter[BLOCK_HASH]) HeadAtHeight(blockNum interface{}) *Head_HeadAtHeight_Call[BLOCK_HASH] {
- return &Head_HeadAtHeight_Call[BLOCK_HASH]{Call: _e.mock.On("HeadAtHeight", blockNum)}
-}
-
-func (_c *Head_HeadAtHeight_Call[BLOCK_HASH]) Run(run func(blockNum int64)) *Head_HeadAtHeight_Call[BLOCK_HASH] {
- _c.Call.Run(func(args mock.Arguments) {
- run(args[0].(int64))
- })
- return _c
-}
-
-func (_c *Head_HeadAtHeight_Call[BLOCK_HASH]) Return(_a0 types.Head[BLOCK_HASH], _a1 error) *Head_HeadAtHeight_Call[BLOCK_HASH] {
- _c.Call.Return(_a0, _a1)
- return _c
-}
-
-func (_c *Head_HeadAtHeight_Call[BLOCK_HASH]) RunAndReturn(run func(int64) (types.Head[BLOCK_HASH], error)) *Head_HeadAtHeight_Call[BLOCK_HASH] {
- _c.Call.Return(run)
- return _c
-}
-
-// IsValid provides a mock function with no fields
-func (_m *Head[BLOCK_HASH]) IsValid() bool {
- ret := _m.Called()
-
- if len(ret) == 0 {
- panic("no return value specified for IsValid")
- }
-
- var r0 bool
- if rf, ok := ret.Get(0).(func() bool); ok {
- r0 = rf()
- } else {
- r0 = ret.Get(0).(bool)
- }
-
- return r0
-}
-
-// Head_IsValid_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsValid'
-type Head_IsValid_Call[BLOCK_HASH types.Hashable] struct {
- *mock.Call
-}
-
-// IsValid is a helper method to define mock.On call
-func (_e *Head_Expecter[BLOCK_HASH]) IsValid() *Head_IsValid_Call[BLOCK_HASH] {
- return &Head_IsValid_Call[BLOCK_HASH]{Call: _e.mock.On("IsValid")}
-}
-
-func (_c *Head_IsValid_Call[BLOCK_HASH]) Run(run func()) *Head_IsValid_Call[BLOCK_HASH] {
- _c.Call.Run(func(args mock.Arguments) {
- run()
- })
- return _c
-}
-
-func (_c *Head_IsValid_Call[BLOCK_HASH]) Return(_a0 bool) *Head_IsValid_Call[BLOCK_HASH] {
- _c.Call.Return(_a0)
- return _c
-}
-
-func (_c *Head_IsValid_Call[BLOCK_HASH]) RunAndReturn(run func() bool) *Head_IsValid_Call[BLOCK_HASH] {
- _c.Call.Return(run)
- return _c
-}
-
-// LatestFinalizedHead provides a mock function with no fields
-func (_m *Head[BLOCK_HASH]) LatestFinalizedHead() types.Head[BLOCK_HASH] {
- ret := _m.Called()
-
- if len(ret) == 0 {
- panic("no return value specified for LatestFinalizedHead")
- }
-
- var r0 types.Head[BLOCK_HASH]
- if rf, ok := ret.Get(0).(func() types.Head[BLOCK_HASH]); ok {
- r0 = rf()
- } else {
- if ret.Get(0) != nil {
- r0 = ret.Get(0).(types.Head[BLOCK_HASH])
- }
- }
-
- return r0
-}
-
-// Head_LatestFinalizedHead_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LatestFinalizedHead'
-type Head_LatestFinalizedHead_Call[BLOCK_HASH types.Hashable] struct {
- *mock.Call
-}
-
-// LatestFinalizedHead is a helper method to define mock.On call
-func (_e *Head_Expecter[BLOCK_HASH]) LatestFinalizedHead() *Head_LatestFinalizedHead_Call[BLOCK_HASH] {
- return &Head_LatestFinalizedHead_Call[BLOCK_HASH]{Call: _e.mock.On("LatestFinalizedHead")}
-}
-
-func (_c *Head_LatestFinalizedHead_Call[BLOCK_HASH]) Run(run func()) *Head_LatestFinalizedHead_Call[BLOCK_HASH] {
- _c.Call.Run(func(args mock.Arguments) {
- run()
- })
- return _c
-}
-
-func (_c *Head_LatestFinalizedHead_Call[BLOCK_HASH]) Return(_a0 types.Head[BLOCK_HASH]) *Head_LatestFinalizedHead_Call[BLOCK_HASH] {
- _c.Call.Return(_a0)
- return _c
-}
-
-func (_c *Head_LatestFinalizedHead_Call[BLOCK_HASH]) RunAndReturn(run func() types.Head[BLOCK_HASH]) *Head_LatestFinalizedHead_Call[BLOCK_HASH] {
- _c.Call.Return(run)
- return _c
-}
-
-// NewHead creates a new instance of Head. 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 NewHead[BLOCK_HASH types.Hashable](t interface {
- mock.TestingT
- Cleanup(func())
-}) *Head[BLOCK_HASH] {
- mock := &Head[BLOCK_HASH]{}
- mock.Mock.Test(t)
-
- t.Cleanup(func() { mock.AssertExpectations(t) })
-
- return mock
-}
diff --git a/core/chains/evm/client/chain_client.go b/core/chains/evm/client/chain_client.go
index 0835b4c0ed8..612a95b7461 100644
--- a/core/chains/evm/client/chain_client.go
+++ b/core/chains/evm/client/chain_client.go
@@ -14,7 +14,7 @@ import (
commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets"
"github.com/smartcontractkit/chainlink-common/pkg/logger"
- commonclient "github.com/smartcontractkit/chainlink/v2/common/client"
+ "github.com/smartcontractkit/chainlink-framework/multinode"
evmconfig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/chaintype"
evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types"
@@ -60,7 +60,7 @@ type Client interface {
// to use HeadTracker to get latest finalized block.
LatestFinalizedBlock(ctx context.Context) (head *evmtypes.Head, err error)
- SendTransactionReturnCode(ctx context.Context, tx *types.Transaction, fromAddress common.Address) (commonclient.SendTxReturnCode, error)
+ SendTransactionReturnCode(ctx context.Context, tx *types.Transaction, fromAddress common.Address) (multinode.SendTxReturnCode, error)
// Wrapped Geth client methods
// blockNumber can be specified as `nil` to imply latest block
@@ -97,11 +97,11 @@ type Client interface {
}
type chainClient struct {
- multiNode *commonclient.MultiNode[
+ multiNode *multinode.MultiNode[
*big.Int,
*RPCClient,
]
- txSender *commonclient.TransactionSender[*types.Transaction, *SendTxResult, *big.Int, *RPCClient]
+ txSender *multinode.TransactionSender[*types.Transaction, *SendTxResult, *big.Int, *RPCClient]
logger logger.SugaredLogger
chainType chaintype.ChainType
clientErrors evmconfig.ClientErrors
@@ -111,15 +111,15 @@ func NewChainClient(
lggr logger.Logger,
selectionMode string,
leaseDuration time.Duration,
- nodes []commonclient.Node[*big.Int, *RPCClient],
- sendonlys []commonclient.SendOnlyNode[*big.Int, *RPCClient],
+ nodes []multinode.Node[*big.Int, *RPCClient],
+ sendonlys []multinode.SendOnlyNode[*big.Int, *RPCClient],
chainID *big.Int,
clientErrors evmconfig.ClientErrors,
deathDeclarationDelay time.Duration,
chainType chaintype.ChainType,
) Client {
chainFamily := "EVM"
- multiNode := commonclient.NewMultiNode[*big.Int, *RPCClient](
+ multiNode := multinode.NewMultiNode[*big.Int, *RPCClient](
lggr,
selectionMode,
leaseDuration,
@@ -130,7 +130,7 @@ func NewChainClient(
deathDeclarationDelay,
)
- txSender := commonclient.NewTransactionSender[*types.Transaction, *SendTxResult, *big.Int, *RPCClient](
+ txSender := multinode.NewTransactionSender[*types.Transaction, *SendTxResult, *big.Int, *RPCClient](
lggr,
chainID,
chainFamily,
@@ -389,7 +389,7 @@ func (c *chainClient) SendTransaction(ctx context.Context, tx *types.Transaction
return result.Error()
}
-func (c *chainClient) SendTransactionReturnCode(ctx context.Context, tx *types.Transaction, fromAddress common.Address) (commonclient.SendTxReturnCode, error) {
+func (c *chainClient) SendTransactionReturnCode(ctx context.Context, tx *types.Transaction, fromAddress common.Address) (multinode.SendTxReturnCode, error) {
err := c.SendTransaction(ctx, tx)
returnCode := ClassifySendError(err, c.clientErrors, c.logger, tx, fromAddress, c.IsL2())
return returnCode, err
diff --git a/core/chains/evm/client/chain_client_test.go b/core/chains/evm/client/chain_client_test.go
index 77e11db7a90..e775feb1434 100644
--- a/core/chains/evm/client/chain_client_test.go
+++ b/core/chains/evm/client/chain_client_test.go
@@ -25,8 +25,8 @@ import (
"github.com/tidwall/gjson"
"github.com/smartcontractkit/chainlink-common/pkg/utils/tests"
+ "github.com/smartcontractkit/chainlink-framework/multinode"
- commonclient "github.com/smartcontractkit/chainlink/v2/common/client"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/client"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/testutils"
evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types"
@@ -39,7 +39,7 @@ func mustNewChainClient(t *testing.T, wsURL string, sendonlys ...url.URL) client
func mustNewChainClientWithChainID(t *testing.T, wsURL string, chainID *big.Int, sendonlys ...url.URL) client.Client {
cfg := client.TestNodePoolConfig{
- NodeSelectionMode: commonclient.NodeSelectionModeRoundRobin,
+ NodeSelectionMode: multinode.NodeSelectionModeRoundRobin,
}
c, err := client.NewChainClientWithTestNode(t, cfg, time.Second*0, cfg.NodeLeaseDuration, wsURL, nil, sendonlys, 42, chainID)
require.NoError(t, err)
@@ -491,7 +491,7 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) {
errType, err := ethClient.SendTransactionReturnCode(tests.Context(t), tx, fromAddress)
assert.Error(t, err)
- assert.Equal(t, errType, commonclient.Fatal)
+ assert.Equal(t, multinode.Fatal, errType)
})
t.Run("returns TransactionAlreadyKnown error type when error message is nonce too low", func(t *testing.T) {
@@ -517,7 +517,7 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) {
errType, err := ethClient.SendTransactionReturnCode(tests.Context(t), tx, fromAddress)
assert.Error(t, err)
- assert.Equal(t, errType, commonclient.TransactionAlreadyKnown)
+ assert.Equal(t, multinode.TransactionAlreadyKnown, errType)
})
t.Run("returns Successful error type when there is no error message", func(t *testing.T) {
@@ -542,7 +542,7 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) {
errType, err := ethClient.SendTransactionReturnCode(tests.Context(t), tx, fromAddress)
assert.NoError(t, err)
- assert.Equal(t, errType, commonclient.Successful)
+ assert.Equal(t, multinode.Successful, errType)
})
t.Run("returns Underpriced error type when transaction is terminally underpriced", func(t *testing.T) {
@@ -568,7 +568,7 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) {
errType, err := ethClient.SendTransactionReturnCode(tests.Context(t), tx, fromAddress)
assert.Error(t, err)
- assert.Equal(t, errType, commonclient.Underpriced)
+ assert.Equal(t, multinode.Underpriced, errType)
})
t.Run("returns Unsupported error type when error message is queue full", func(t *testing.T) {
@@ -594,7 +594,7 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) {
errType, err := ethClient.SendTransactionReturnCode(tests.Context(t), tx, fromAddress)
assert.Error(t, err)
- assert.Equal(t, errType, commonclient.Unsupported)
+ assert.Equal(t, multinode.Unsupported, errType)
})
t.Run("returns Retryable error type when there is a transaction gap", func(t *testing.T) {
@@ -620,7 +620,7 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) {
errType, err := ethClient.SendTransactionReturnCode(tests.Context(t), tx, fromAddress)
assert.Error(t, err)
- assert.Equal(t, errType, commonclient.Retryable)
+ assert.Equal(t, multinode.Retryable, errType)
})
t.Run("returns InsufficientFunds error type when the sender address doesn't have enough funds", func(t *testing.T) {
@@ -646,7 +646,7 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) {
errType, err := ethClient.SendTransactionReturnCode(tests.Context(t), tx, fromAddress)
assert.Error(t, err)
- assert.Equal(t, errType, commonclient.InsufficientFunds)
+ assert.Equal(t, multinode.InsufficientFunds, errType)
})
t.Run("returns ExceedsFeeCap error type when gas price is too high for the node", func(t *testing.T) {
@@ -672,7 +672,7 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) {
errType, err := ethClient.SendTransactionReturnCode(tests.Context(t), tx, fromAddress)
assert.Error(t, err)
- assert.Equal(t, errType, commonclient.ExceedsMaxFee)
+ assert.Equal(t, multinode.ExceedsMaxFee, errType)
})
t.Run("returns Unknown error type when the error can't be categorized", func(t *testing.T) {
@@ -698,7 +698,7 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) {
errType, err := ethClient.SendTransactionReturnCode(tests.Context(t), tx, fromAddress)
assert.Error(t, err)
- assert.Equal(t, errType, commonclient.Unknown)
+ assert.Equal(t, multinode.Unknown, errType)
})
}
@@ -793,34 +793,34 @@ func TestEthClient_ErroringClient(t *testing.T) {
ctx := tests.Context(t)
// Empty node means there are no active nodes to select from, causing client to always return error.
- erroringClient := client.NewChainClientWithEmptyNode(t, commonclient.NodeSelectionModeRoundRobin, time.Second*0, time.Second*0, testutils.FixtureChainID)
+ erroringClient := client.NewChainClientWithEmptyNode(t, multinode.NodeSelectionModeRoundRobin, time.Second*0, time.Second*0, testutils.FixtureChainID)
_, err := erroringClient.BalanceAt(ctx, common.Address{}, nil)
- require.Equal(t, err, commonclient.ErroringNodeError)
+ require.Equal(t, multinode.ErrNodeError, err)
err = erroringClient.BatchCallContext(ctx, nil)
- require.Equal(t, err, commonclient.ErroringNodeError)
+ require.Equal(t, multinode.ErrNodeError, err)
err = erroringClient.BatchCallContextAll(ctx, nil)
- require.Equal(t, err, commonclient.ErroringNodeError)
+ require.Equal(t, multinode.ErrNodeError, err)
_, err = erroringClient.BlockByHash(ctx, common.Hash{})
- require.Equal(t, err, commonclient.ErroringNodeError)
+ require.Equal(t, multinode.ErrNodeError, err)
_, err = erroringClient.BlockByNumber(ctx, nil)
- require.Equal(t, err, commonclient.ErroringNodeError)
+ require.Equal(t, multinode.ErrNodeError, err)
err = erroringClient.CallContext(ctx, nil, "")
- require.Equal(t, err, commonclient.ErroringNodeError)
+ require.Equal(t, multinode.ErrNodeError, err)
_, err = erroringClient.CallContract(ctx, ethereum.CallMsg{}, nil)
- require.Equal(t, err, commonclient.ErroringNodeError)
+ require.Equal(t, multinode.ErrNodeError, err)
id := erroringClient.ConfiguredChainID()
require.Equal(t, id, big.NewInt(0))
_, err = erroringClient.CodeAt(ctx, common.Address{}, nil)
- require.Equal(t, err, commonclient.ErroringNodeError)
+ require.Equal(t, multinode.ErrNodeError, err)
id = erroringClient.ConfiguredChainID()
require.Equal(t, id, testutils.FixtureChainID)
@@ -829,67 +829,67 @@ func TestEthClient_ErroringClient(t *testing.T) {
require.ErrorContains(t, err, "no available nodes for chain")
_, err = erroringClient.EstimateGas(ctx, ethereum.CallMsg{})
- require.Equal(t, err, commonclient.ErroringNodeError)
+ require.Equal(t, multinode.ErrNodeError, err)
_, err = erroringClient.FilterLogs(ctx, ethereum.FilterQuery{})
- require.Equal(t, err, commonclient.ErroringNodeError)
+ require.Equal(t, multinode.ErrNodeError, err)
_, err = erroringClient.HeaderByHash(ctx, common.Hash{})
- require.Equal(t, err, commonclient.ErroringNodeError)
+ require.Equal(t, multinode.ErrNodeError, err)
_, err = erroringClient.HeaderByNumber(ctx, nil)
- require.Equal(t, err, commonclient.ErroringNodeError)
+ require.Equal(t, multinode.ErrNodeError, err)
_, err = erroringClient.HeadByHash(ctx, common.Hash{})
- require.Equal(t, err, commonclient.ErroringNodeError)
+ require.Equal(t, multinode.ErrNodeError, err)
_, err = erroringClient.HeadByNumber(ctx, nil)
- require.Equal(t, err, commonclient.ErroringNodeError)
+ require.Equal(t, multinode.ErrNodeError, err)
_, err = erroringClient.LINKBalance(ctx, common.Address{}, common.Address{})
- require.Equal(t, err, commonclient.ErroringNodeError)
+ require.Equal(t, multinode.ErrNodeError, err)
_, err = erroringClient.LatestBlockHeight(ctx)
- require.Equal(t, err, commonclient.ErroringNodeError)
+ require.Equal(t, multinode.ErrNodeError, err)
_, err = erroringClient.PendingCodeAt(ctx, common.Address{})
- require.Equal(t, err, commonclient.ErroringNodeError)
+ require.Equal(t, multinode.ErrNodeError, err)
_, err = erroringClient.PendingNonceAt(ctx, common.Address{})
- require.Equal(t, err, commonclient.ErroringNodeError)
+ require.Equal(t, multinode.ErrNodeError, err)
txSenderNotStarted := errors.New("TransactionSender not started")
err = erroringClient.SendTransaction(ctx, nil)
- require.Equal(t, err, txSenderNotStarted)
+ require.Equal(t, txSenderNotStarted, err)
tx := testutils.NewLegacyTransaction(uint64(42), testutils.NewAddress(), big.NewInt(142), 242, big.NewInt(342), []byte{1, 2, 3})
code, err := erroringClient.SendTransactionReturnCode(ctx, tx, common.Address{})
- require.Equal(t, code, commonclient.Unknown)
- require.Equal(t, err, txSenderNotStarted)
+ require.Equal(t, multinode.Unknown, code)
+ require.Equal(t, txSenderNotStarted, err)
_, err = erroringClient.NonceAt(ctx, common.Address{}, nil)
- require.Equal(t, err, commonclient.ErroringNodeError)
+ require.Equal(t, multinode.ErrNodeError, err)
_, err = erroringClient.SubscribeFilterLogs(ctx, ethereum.FilterQuery{}, nil)
- require.Equal(t, err, commonclient.ErroringNodeError)
+ require.Equal(t, multinode.ErrNodeError, err)
_, _, err = erroringClient.SubscribeToHeads(ctx)
- require.Equal(t, err, commonclient.ErroringNodeError)
+ require.Equal(t, multinode.ErrNodeError, err)
_, err = erroringClient.SuggestGasPrice(ctx)
- require.Equal(t, err, commonclient.ErroringNodeError)
+ require.Equal(t, multinode.ErrNodeError, err)
_, err = erroringClient.SuggestGasTipCap(ctx)
- require.Equal(t, err, commonclient.ErroringNodeError)
+ require.Equal(t, multinode.ErrNodeError, err)
_, err = erroringClient.TokenBalance(ctx, common.Address{}, common.Address{})
- require.Equal(t, err, commonclient.ErroringNodeError)
+ require.Equal(t, multinode.ErrNodeError, err)
_, err = erroringClient.TransactionByHash(ctx, common.Hash{})
- require.Equal(t, err, commonclient.ErroringNodeError)
+ require.Equal(t, multinode.ErrNodeError, err)
_, err = erroringClient.TransactionReceipt(ctx, common.Hash{})
- require.Equal(t, err, commonclient.ErroringNodeError)
+ require.Equal(t, multinode.ErrNodeError, err)
}
const headResult = client.HeadResult
diff --git a/core/chains/evm/client/config_builder.go b/core/chains/evm/client/config_builder.go
index 66bdfc2614f..7b412815557 100644
--- a/core/chains/evm/client/config_builder.go
+++ b/core/chains/evm/client/config_builder.go
@@ -8,8 +8,8 @@ import (
"go.uber.org/multierr"
commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config"
+ "github.com/smartcontractkit/chainlink-framework/multinode"
- commonclient "github.com/smartcontractkit/chainlink/v2/common/client"
evmconfig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/chaintype"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml"
@@ -44,7 +44,7 @@ func NewClientConfigs(
noNewFinalizedHeadsThreshold time.Duration,
finalizedBlockPollInterval time.Duration,
newHeadsPollInterval time.Duration,
-) (commonclient.ChainConfig, evmconfig.NodePool, []*toml.Node, error) {
+) (multinode.ChainConfig, evmconfig.NodePool, []*toml.Node, error) {
nodes, err := parseNodeConfigs(nodeCfgs)
if err != nil {
return nil, nil, nil, err
diff --git a/core/chains/evm/client/errors.go b/core/chains/evm/client/errors.go
index eaa33f041ac..871e574aea2 100644
--- a/core/chains/evm/client/errors.go
+++ b/core/chains/evm/client/errors.go
@@ -14,8 +14,8 @@ import (
pkgerrors "github.com/pkg/errors"
"github.com/smartcontractkit/chainlink-common/pkg/logger"
+ "github.com/smartcontractkit/chainlink-framework/multinode"
- commonclient "github.com/smartcontractkit/chainlink/v2/common/client"
commontypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/label"
)
@@ -407,7 +407,7 @@ func (s *SendError) IsServiceUnavailable(configErrors *ClientErrors) bool {
return false
}
- return s.is(ServiceUnavailable, configErrors) || pkgerrors.Is(s.err, commonclient.ErroringNodeError)
+ return s.is(ServiceUnavailable, configErrors) || pkgerrors.Is(s.err, multinode.ErrNodeError)
}
// IsServiceTimeout indicates if the error was caused by a service timeout
@@ -571,10 +571,10 @@ func ExtractRPCError(baseErr error) (*JsonError, error) {
return &jErr, nil
}
-func ClassifySendError(err error, clientErrors config.ClientErrors, lggr logger.SugaredLogger, tx *types.Transaction, fromAddress common.Address, isL2 bool) commonclient.SendTxReturnCode {
+func ClassifySendError(err error, clientErrors config.ClientErrors, lggr logger.SugaredLogger, tx *types.Transaction, fromAddress common.Address, isL2 bool) multinode.SendTxReturnCode {
sendError := NewSendError(err)
if sendError == nil {
- return commonclient.Successful
+ return multinode.Successful
}
configErrors := ClientErrorRegexes(clientErrors)
@@ -582,13 +582,13 @@ func ClassifySendError(err error, clientErrors config.ClientErrors, lggr logger.
if sendError.Fatal(configErrors) {
lggr.Criticalw("Fatal error sending transaction", "err", sendError, "etx", tx)
// Attempt is thrown away in this case; we don't need it since it never got accepted by a node
- return commonclient.Fatal
+ return multinode.Fatal
}
if sendError.IsNonceTooLowError(configErrors) || sendError.IsTransactionAlreadyMined(configErrors) {
lggr.Debugw(fmt.Sprintf("Transaction already confirmed for this nonce: %d", tx.Nonce()), "err", sendError, "etx", tx)
// Nonce too low indicated that a transaction at this nonce was confirmed already.
// Mark it as TransactionAlreadyKnown.
- return commonclient.TransactionAlreadyKnown
+ return multinode.TransactionAlreadyKnown
}
if sendError.IsReplacementUnderpriced(configErrors) {
lggr.Errorw(fmt.Sprintf("Replacement transaction underpriced for eth_tx %x. "+
@@ -596,57 +596,57 @@ func ClassifySendError(err error, clientErrors config.ClientErrors, lggr logger.
tx.Hash()), "gasPrice", tx.GasPrice, "gasTipCap", tx.GasTipCap, "gasFeeCap", tx.GasFeeCap, "err", sendError, "etx", tx)
// Assume success and hand off to the next cycle.
- return commonclient.Successful
+ return multinode.Successful
}
if sendError.IsTransactionAlreadyInMempool(configErrors) {
lggr.Debugw("Transaction already in mempool", "etx", tx, "err", sendError)
- return commonclient.Successful
+ return multinode.Successful
}
if sendError.IsTemporarilyUnderpriced(configErrors) {
lggr.Infow("Transaction temporarily underpriced", "err", sendError)
- return commonclient.Successful
+ return multinode.Successful
}
if sendError.IsTerminallyUnderpriced(configErrors) {
lggr.Errorw("Transaction terminally underpriced", "etx", tx, "err", sendError)
- return commonclient.Underpriced
+ return multinode.Underpriced
}
if sendError.L2FeeTooLow(configErrors) || sendError.IsL2FeeTooHigh(configErrors) || sendError.IsL2Full(configErrors) {
if isL2 {
lggr.Errorw("Transaction fee out of range", "err", sendError, "etx", tx)
- return commonclient.FeeOutOfValidRange
+ return multinode.FeeOutOfValidRange
}
lggr.Errorw("this error type only handled for L2s", "err", sendError, "etx", tx)
- return commonclient.Unsupported
+ return multinode.Unsupported
}
if sendError.IsNonceTooHighError(configErrors) {
// This error occurs when the tx nonce is greater than current_nonce + tx_count_in_mempool,
// instead of keeping the tx in mempool. This can happen if previous transactions haven't
// reached the client yet. The correct thing to do is to mark it as retryable.
lggr.Warnw("Transaction has a nonce gap.", "err", sendError, "etx", tx)
- return commonclient.Retryable
+ return multinode.Retryable
}
if sendError.IsInsufficientEth(configErrors) {
lggr.Criticalw(fmt.Sprintf("Tx %x with type 0x%d was rejected due to insufficient eth: %s\n"+
"ACTION REQUIRED: Chainlink wallet with address 0x%x is OUT OF FUNDS",
tx.Hash(), tx.Type(), sendError.Error(), fromAddress,
), "err", sendError, "etx", tx)
- return commonclient.InsufficientFunds
+ return multinode.InsufficientFunds
}
if sendError.IsServiceUnavailable(configErrors) {
lggr.Errorw(fmt.Sprintf("service unavailable while sending transaction %x", tx.Hash()), "err", sendError, "etx", tx)
- return commonclient.Retryable
+ return multinode.Retryable
}
if sendError.IsServiceTimeout(configErrors) {
lggr.Errorw(fmt.Sprintf("service timed out while sending transaction %x", tx.Hash()), "err", sendError, "etx", tx)
- return commonclient.Retryable
+ return multinode.Retryable
}
if sendError.IsTimeout() {
lggr.Errorw(fmt.Sprintf("timeout while sending transaction %x", tx.Hash()), "err", sendError, "etx", tx)
- return commonclient.Retryable
+ return multinode.Retryable
}
if sendError.IsCanceled() {
lggr.Errorw(fmt.Sprintf("context was canceled while sending transaction %x", tx.Hash()), "err", sendError, "etx", tx)
- return commonclient.Retryable
+ return multinode.Retryable
}
if sendError.IsTxFeeExceedsCap(configErrors) {
lggr.Criticalw(fmt.Sprintf("Sending transaction failed: %s", label.RPCTxFeeCapConfiguredIncorrectlyWarning),
@@ -654,15 +654,15 @@ func ClassifySendError(err error, clientErrors config.ClientErrors, lggr logger.
"err", sendError,
"id", "RPCTxFeeCapExceeded",
)
- return commonclient.ExceedsMaxFee
+ return multinode.ExceedsMaxFee
}
if sendError.IsTerminallyStuckConfigError(configErrors) {
lggr.Warnw("Transaction that would have been terminally stuck in the mempool detected on send. Marking as fatal error.", "err", sendError, "etx", tx)
// Attempt is thrown away in this case; we don't need it since it never got accepted by a node
- return commonclient.TerminallyStuck
+ return multinode.TerminallyStuck
}
lggr.Criticalw("Unknown error encountered when sending transaction", "err", err, "etx", tx)
- return commonclient.Unknown
+ return multinode.Unknown
}
var infura = ClientErrors{
diff --git a/core/chains/evm/client/errors_test.go b/core/chains/evm/client/errors_test.go
index 7ba042ab5c6..9a046922abb 100644
--- a/core/chains/evm/client/errors_test.go
+++ b/core/chains/evm/client/errors_test.go
@@ -9,7 +9,8 @@ import (
pkgerrors "github.com/pkg/errors"
"github.com/stretchr/testify/assert"
- commonclient "github.com/smartcontractkit/chainlink/v2/common/client"
+ "github.com/smartcontractkit/chainlink-framework/multinode"
+
evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client"
)
@@ -255,9 +256,9 @@ func Test_Eth_Errors(t *testing.T) {
assert.Equal(t, err.IsServiceUnavailable(clientErrors), test.expect)
}
{
- err = evmclient.NewSendError(commonclient.ErroringNodeError)
+ err = evmclient.NewSendError(multinode.ErrNodeError)
assert.True(t, err.IsServiceUnavailable(clientErrors))
- err = evmclient.NewSendError(fmt.Errorf("failed to send transaction: %w", commonclient.ErroringNodeError))
+ err = evmclient.NewSendError(fmt.Errorf("failed to send transaction: %w", multinode.ErrNodeError))
assert.True(t, err.IsServiceUnavailable(clientErrors))
}
})
diff --git a/core/chains/evm/client/evm_client.go b/core/chains/evm/client/evm_client.go
index 18206265fd7..bf985bdfa28 100644
--- a/core/chains/evm/client/evm_client.go
+++ b/core/chains/evm/client/evm_client.go
@@ -6,6 +6,7 @@ import (
"time"
"github.com/smartcontractkit/chainlink-common/pkg/logger"
+ "github.com/smartcontractkit/chainlink-framework/multinode"
commonclient "github.com/smartcontractkit/chainlink/v2/common/client"
evmconfig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config"
@@ -13,22 +14,22 @@ import (
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml"
)
-func NewEvmClient(cfg evmconfig.NodePool, chainCfg commonclient.ChainConfig, clientErrors evmconfig.ClientErrors, lggr logger.Logger, chainID *big.Int, nodes []*toml.Node, chainType chaintype.ChainType) (Client, error) {
- var primaries []commonclient.Node[*big.Int, *RPCClient]
- var sendonlys []commonclient.SendOnlyNode[*big.Int, *RPCClient]
+func NewEvmClient(cfg evmconfig.NodePool, chainCfg multinode.ChainConfig, clientErrors evmconfig.ClientErrors, lggr logger.Logger, chainID *big.Int, nodes []*toml.Node, chainType chaintype.ChainType) (Client, error) {
+ var primaries []multinode.Node[*big.Int, *RPCClient]
+ var sendonlys []multinode.SendOnlyNode[*big.Int, *RPCClient]
largePayloadRPCTimeout, defaultRPCTimeout := getRPCTimeouts(chainType)
for i, node := range nodes {
if node.SendOnly != nil && *node.SendOnly {
rpc := NewRPCClient(cfg, lggr, nil, node.HTTPURL.URL(), *node.Name, i, chainID,
- commonclient.Secondary, largePayloadRPCTimeout, defaultRPCTimeout, chainType)
- sendonly := commonclient.NewSendOnlyNode(lggr, (url.URL)(*node.HTTPURL),
+ multinode.Secondary, largePayloadRPCTimeout, defaultRPCTimeout, chainType)
+ sendonly := multinode.NewSendOnlyNode(lggr, (url.URL)(*node.HTTPURL),
*node.Name, chainID, rpc)
sendonlys = append(sendonlys, sendonly)
} else {
rpc := NewRPCClient(cfg, lggr, node.WSURL.URL(), node.HTTPURL.URL(), *node.Name, i,
- chainID, commonclient.Primary, largePayloadRPCTimeout, defaultRPCTimeout, chainType)
- primaryNode := commonclient.NewNode(cfg, chainCfg,
+ chainID, multinode.Primary, largePayloadRPCTimeout, defaultRPCTimeout, chainType)
+ primaryNode := multinode.NewNode(cfg, chainCfg,
lggr, node.WSURL.URL(), node.HTTPURL.URL(), *node.Name, i, chainID, *node.Order,
rpc, "EVM")
primaries = append(primaries, primaryNode)
diff --git a/core/chains/evm/client/helpers_test.go b/core/chains/evm/client/helpers_test.go
index 6369c9dca12..2a68870e2e5 100644
--- a/core/chains/evm/client/helpers_test.go
+++ b/core/chains/evm/client/helpers_test.go
@@ -12,8 +12,9 @@ import (
"github.com/smartcontractkit/chainlink-common/pkg/logger"
+ "github.com/smartcontractkit/chainlink-framework/multinode"
+ "github.com/smartcontractkit/chainlink-framework/multinode/mocks"
commonclient "github.com/smartcontractkit/chainlink/v2/common/client"
- clientMocks "github.com/smartcontractkit/chainlink/v2/common/client/mocks"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/config"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml"
evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types"
@@ -129,7 +130,7 @@ func (tc TestNodePoolConfig) DeathDeclarationDelay() time.Duration {
func NewChainClientWithTestNode(
t *testing.T,
- nodeCfg commonclient.NodeConfig,
+ nodeCfg multinode.NodeConfig,
noNewHeadsThreshold time.Duration,
leaseDuration time.Duration,
rpcUrl string,
@@ -151,21 +152,21 @@ func NewChainClientWithTestNode(
nodePoolCfg := TestNodePoolConfig{
NodeFinalizedBlockPollInterval: 1 * time.Second,
}
- rpc := NewRPCClient(nodePoolCfg, lggr, parsed, rpcHTTPURL, "eth-primary-rpc-0", id, chainID, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "")
+ rpc := NewRPCClient(nodePoolCfg, lggr, parsed, rpcHTTPURL, "eth-primary-rpc-0", id, chainID, multinode.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "")
- n := commonclient.NewNode[*big.Int, *evmtypes.Head, *RPCClient](
- nodeCfg, clientMocks.ChainConfig{NoNewHeadsThresholdVal: noNewHeadsThreshold}, lggr, parsed, rpcHTTPURL, "eth-primary-node-0", id, chainID, 1, rpc, "EVM")
- primaries := []commonclient.Node[*big.Int, *RPCClient]{n}
+ n := multinode.NewNode[*big.Int, *evmtypes.Head, *RPCClient](
+ nodeCfg, mocks.ChainConfig{NoNewHeadsThresholdVal: noNewHeadsThreshold}, lggr, parsed, rpcHTTPURL, "eth-primary-node-0", id, chainID, 1, rpc, "EVM")
+ primaries := []multinode.Node[*big.Int, *RPCClient]{n}
- var sendonlys []commonclient.SendOnlyNode[*big.Int, *RPCClient]
+ sendonlys := make([]multinode.SendOnlyNode[*big.Int, *RPCClient], len(sendonlyRPCURLs))
for i, u := range sendonlyRPCURLs {
if u.Scheme != "http" && u.Scheme != "https" {
return nil, pkgerrors.Errorf("sendonly ethereum rpc url scheme must be http(s): %s", u.String())
}
- rpc := NewRPCClient(nodePoolCfg, lggr, nil, &sendonlyRPCURLs[i], fmt.Sprintf("eth-sendonly-rpc-%d", i), id, chainID, commonclient.Secondary, commonclient.QueryTimeout, commonclient.QueryTimeout, "")
- s := commonclient.NewSendOnlyNode[*big.Int, *RPCClient](
+ rpc := NewRPCClient(nodePoolCfg, lggr, nil, &sendonlyRPCURLs[i], fmt.Sprintf("eth-sendonly-rpc-%d", i), id, chainID, multinode.Secondary, commonclient.QueryTimeout, commonclient.QueryTimeout, "")
+ s := multinode.NewSendOnlyNode[*big.Int, *RPCClient](
lggr, u, fmt.Sprintf("eth-sendonly-%d", i), chainID, rpc)
- sendonlys = append(sendonlys, s)
+ sendonlys[i] = s
}
clientErrors := NewTestClientErrors()
@@ -199,13 +200,13 @@ func NewChainClientWithMockedRpc(
lggr := logger.Test(t)
cfg := TestNodePoolConfig{
- NodeSelectionMode: commonclient.NodeSelectionModeRoundRobin,
+ NodeSelectionMode: multinode.NodeSelectionModeRoundRobin,
}
parsed, _ := url.ParseRequestURI("ws://test")
- n := commonclient.NewNode[*big.Int, *evmtypes.Head, *RPCClient](
- cfg, clientMocks.ChainConfig{NoNewHeadsThresholdVal: noNewHeadsThreshold}, lggr, parsed, nil, "eth-primary-node-0", 1, chainID, 1, rpc, "EVM")
- primaries := []commonclient.Node[*big.Int, *RPCClient]{n}
+ n := multinode.NewNode[*big.Int, *evmtypes.Head, *RPCClient](
+ cfg, mocks.ChainConfig{NoNewHeadsThresholdVal: noNewHeadsThreshold}, lggr, parsed, nil, "eth-primary-node-0", 1, chainID, 1, rpc, "EVM")
+ primaries := []multinode.Node[*big.Int, *RPCClient]{n}
clientErrors := NewTestClientErrors()
c := NewChainClient(lggr, selectionMode, leaseDuration, primaries, nil, chainID, &clientErrors, 0, "")
t.Cleanup(c.Close)
diff --git a/core/chains/evm/client/mocks/client.go b/core/chains/evm/client/mocks/client.go
index b55c608a590..b712c297c18 100644
--- a/core/chains/evm/client/mocks/client.go
+++ b/core/chains/evm/client/mocks/client.go
@@ -11,8 +11,6 @@ import (
common "github.com/ethereum/go-ethereum/common"
- commonclient "github.com/smartcontractkit/chainlink/v2/common/client"
-
context "context"
ethereum "github.com/ethereum/go-ethereum"
@@ -21,6 +19,8 @@ import (
mock "github.com/stretchr/testify/mock"
+ multinode "github.com/smartcontractkit/chainlink-framework/multinode"
+
rpc "github.com/ethereum/go-ethereum/rpc"
types "github.com/ethereum/go-ethereum/core/types"
@@ -1628,22 +1628,22 @@ func (_c *Client_SendTransaction_Call) RunAndReturn(run func(context.Context, *t
}
// SendTransactionReturnCode provides a mock function with given fields: ctx, tx, fromAddress
-func (_m *Client) SendTransactionReturnCode(ctx context.Context, tx *types.Transaction, fromAddress common.Address) (commonclient.SendTxReturnCode, error) {
+func (_m *Client) SendTransactionReturnCode(ctx context.Context, tx *types.Transaction, fromAddress common.Address) (multinode.SendTxReturnCode, error) {
ret := _m.Called(ctx, tx, fromAddress)
if len(ret) == 0 {
panic("no return value specified for SendTransactionReturnCode")
}
- var r0 commonclient.SendTxReturnCode
+ var r0 multinode.SendTxReturnCode
var r1 error
- if rf, ok := ret.Get(0).(func(context.Context, *types.Transaction, common.Address) (commonclient.SendTxReturnCode, error)); ok {
+ if rf, ok := ret.Get(0).(func(context.Context, *types.Transaction, common.Address) (multinode.SendTxReturnCode, error)); ok {
return rf(ctx, tx, fromAddress)
}
- if rf, ok := ret.Get(0).(func(context.Context, *types.Transaction, common.Address) commonclient.SendTxReturnCode); ok {
+ if rf, ok := ret.Get(0).(func(context.Context, *types.Transaction, common.Address) multinode.SendTxReturnCode); ok {
r0 = rf(ctx, tx, fromAddress)
} else {
- r0 = ret.Get(0).(commonclient.SendTxReturnCode)
+ r0 = ret.Get(0).(multinode.SendTxReturnCode)
}
if rf, ok := ret.Get(1).(func(context.Context, *types.Transaction, common.Address) error); ok {
@@ -1675,12 +1675,12 @@ func (_c *Client_SendTransactionReturnCode_Call) Run(run func(ctx context.Contex
return _c
}
-func (_c *Client_SendTransactionReturnCode_Call) Return(_a0 commonclient.SendTxReturnCode, _a1 error) *Client_SendTransactionReturnCode_Call {
+func (_c *Client_SendTransactionReturnCode_Call) Return(_a0 multinode.SendTxReturnCode, _a1 error) *Client_SendTransactionReturnCode_Call {
_c.Call.Return(_a0, _a1)
return _c
}
-func (_c *Client_SendTransactionReturnCode_Call) RunAndReturn(run func(context.Context, *types.Transaction, common.Address) (commonclient.SendTxReturnCode, error)) *Client_SendTransactionReturnCode_Call {
+func (_c *Client_SendTransactionReturnCode_Call) RunAndReturn(run func(context.Context, *types.Transaction, common.Address) (multinode.SendTxReturnCode, error)) *Client_SendTransactionReturnCode_Call {
_c.Call.Return(run)
return _c
}
diff --git a/core/chains/evm/client/null_client.go b/core/chains/evm/client/null_client.go
index b1dedd3f74a..6b40ba93eb7 100644
--- a/core/chains/evm/client/null_client.go
+++ b/core/chains/evm/client/null_client.go
@@ -11,8 +11,8 @@ import (
"github.com/smartcontractkit/chainlink-common/pkg/assets"
"github.com/smartcontractkit/chainlink-common/pkg/logger"
+ "github.com/smartcontractkit/chainlink-framework/multinode"
- commonclient "github.com/smartcontractkit/chainlink/v2/common/client"
evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types"
)
@@ -122,9 +122,9 @@ func (nc *NullClient) HeaderByHash(ctx context.Context, h common.Hash) (*types.H
return nil, nil
}
-func (nc *NullClient) SendTransactionReturnCode(ctx context.Context, tx *types.Transaction, sender common.Address) (commonclient.SendTxReturnCode, error) {
+func (nc *NullClient) SendTransactionReturnCode(ctx context.Context, tx *types.Transaction, sender common.Address) (multinode.SendTxReturnCode, error) {
nc.lggr.Debug("SendTransactionReturnCode")
- return commonclient.Successful, nil
+ return multinode.Successful, nil
}
func (nc *NullClient) SendTransaction(ctx context.Context, tx *types.Transaction) error {
diff --git a/core/chains/evm/client/rpc_client.go b/core/chains/evm/client/rpc_client.go
index 35d2a6dcd6b..f560a26dda6 100644
--- a/core/chains/evm/client/rpc_client.go
+++ b/core/chains/evm/client/rpc_client.go
@@ -26,9 +26,8 @@ import (
commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets"
"github.com/smartcontractkit/chainlink-common/pkg/logger"
"github.com/smartcontractkit/chainlink-common/pkg/services"
+ "github.com/smartcontractkit/chainlink-framework/multinode"
- commonclient "github.com/smartcontractkit/chainlink/v2/common/client"
- commontypes "github.com/smartcontractkit/chainlink/v2/common/types"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/config"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/chaintype"
@@ -93,7 +92,7 @@ type RPCClient struct {
name string
id int
chainID *big.Int
- tier commonclient.NodeTier
+ tier multinode.NodeTier
largePayloadRPCTimeout time.Duration
finalizedBlockPollInterval time.Duration
newHeadsPollInterval time.Duration
@@ -118,13 +117,13 @@ type RPCClient struct {
chainInfoLock sync.RWMutex
// intercepted values seen by callers of the RPCClient excluding health check calls. Need to ensure MultiNode provides repeatable read guarantee
- highestUserObservations commonclient.ChainInfo
+ highestUserObservations multinode.ChainInfo
// most recent chain info observed during current lifecycle (reseted on DisconnectAll)
- latestChainInfo commonclient.ChainInfo
+ latestChainInfo multinode.ChainInfo
}
-var _ commonclient.RPCClient[*big.Int, *evmtypes.Head] = (*RPCClient)(nil)
-var _ commonclient.SendTxRPCClient[*types.Transaction, *SendTxResult] = (*RPCClient)(nil)
+var _ multinode.RPCClient[*big.Int, *evmtypes.Head] = (*RPCClient)(nil)
+var _ multinode.SendTxRPCClient[*types.Transaction, *SendTxResult] = (*RPCClient)(nil)
func NewRPCClient(
cfg config.NodePool,
@@ -134,7 +133,7 @@ func NewRPCClient(
name string,
id int,
chainID *big.Int,
- tier commonclient.NodeTier,
+ tier multinode.NodeTier,
largePayloadRPCTimeout time.Duration,
rpcTimeout time.Duration,
chainType chaintype.ChainType,
@@ -181,11 +180,11 @@ func (r *RPCClient) Ping(ctx context.Context) error {
return err
}
-func (r *RPCClient) UnsubscribeAllExcept(subs ...commontypes.Subscription) {
+func (r *RPCClient) UnsubscribeAllExcept(subs ...multinode.Subscription) {
r.subsSliceMu.Lock()
defer r.subsSliceMu.Unlock()
- keepSubs := map[commontypes.Subscription]struct{}{}
+ keepSubs := map[multinode.Subscription]struct{}{}
for _, sub := range subs {
keepSubs[sub] = struct{}{}
}
@@ -265,7 +264,7 @@ func (r *RPCClient) Close() {
r.cancelInflightRequests()
r.UnsubscribeAllExcept()
r.chainInfoLock.Lock()
- r.latestChainInfo = commonclient.ChainInfo{}
+ r.latestChainInfo = multinode.ChainInfo{}
r.chainInfoLock.Unlock()
}
@@ -452,7 +451,7 @@ func isRequestingFinalizedBlock(el rpc.BatchElem) bool {
}
}
-func (r *RPCClient) SubscribeToHeads(ctx context.Context) (ch <-chan *evmtypes.Head, sub commontypes.Subscription, err error) {
+func (r *RPCClient) SubscribeToHeads(ctx context.Context) (ch <-chan *evmtypes.Head, sub multinode.Subscription, err error) {
ctx, cancel, chStopInFlight, ws, _ := r.acquireQueryCtx(ctx, r.rpcTimeout)
defer cancel()
args := []interface{}{rpcSubscriptionMethodNewHeads}
@@ -463,10 +462,10 @@ func (r *RPCClient) SubscribeToHeads(ctx context.Context) (ch <-chan *evmtypes.H
if r.newHeadsPollInterval > 0 {
interval := r.newHeadsPollInterval
timeout := interval
- isHealthCheckRequest := commonclient.CtxIsHeathCheckRequest(ctx)
- poller, channel := commonclient.NewPoller[*evmtypes.Head](interval, func(ctx context.Context) (*evmtypes.Head, error) {
+ isHealthCheckRequest := multinode.CtxIsHeathCheckRequest(ctx)
+ poller, channel := multinode.NewPoller[*evmtypes.Head](interval, func(ctx context.Context) (*evmtypes.Head, error) {
if isHealthCheckRequest {
- ctx = commonclient.CtxAddHealthCheckFlag(ctx)
+ ctx = multinode.CtxAddHealthCheckFlag(ctx)
}
return r.latestBlock(ctx)
}, timeout, r.rpcLog)
@@ -514,7 +513,7 @@ func (r *RPCClient) SubscribeToHeads(ctx context.Context) (ch <-chan *evmtypes.H
return channel, forwarder, err
}
-func (r *RPCClient) SubscribeToFinalizedHeads(ctx context.Context) (<-chan *evmtypes.Head, commontypes.Subscription, error) {
+func (r *RPCClient) SubscribeToFinalizedHeads(ctx context.Context) (<-chan *evmtypes.Head, multinode.Subscription, error) {
ctx, cancel, chStopInFlight, _, _ := r.acquireQueryCtx(ctx, r.rpcTimeout)
defer cancel()
@@ -523,10 +522,10 @@ func (r *RPCClient) SubscribeToFinalizedHeads(ctx context.Context) (<-chan *evmt
return nil, nil, errors.New("FinalizedBlockPollInterval is 0")
}
timeout := interval
- isHealthCheckRequest := commonclient.CtxIsHeathCheckRequest(ctx)
- poller, channel := commonclient.NewPoller[*evmtypes.Head](interval, func(ctx context.Context) (*evmtypes.Head, error) {
+ isHealthCheckRequest := multinode.CtxIsHeathCheckRequest(ctx)
+ poller, channel := multinode.NewPoller[*evmtypes.Head](interval, func(ctx context.Context) (*evmtypes.Head, error) {
if isHealthCheckRequest {
- ctx = commonclient.CtxAddHealthCheckFlag(ctx)
+ ctx = multinode.CtxAddHealthCheckFlag(ctx)
}
return r.LatestFinalizedBlock(ctx)
}, timeout, r.rpcLog)
@@ -811,10 +810,10 @@ func (r *RPCClient) BlockByNumberGeth(ctx context.Context, number *big.Int) (blo
type SendTxResult struct {
err error
- code commonclient.SendTxReturnCode
+ code multinode.SendTxReturnCode
}
-var _ commonclient.SendTxResult = (*SendTxResult)(nil)
+var _ multinode.SendTxResult = (*SendTxResult)(nil)
func NewSendTxResult(err error) *SendTxResult {
result := &SendTxResult{
@@ -827,7 +826,7 @@ func (r *SendTxResult) Error() error {
return r.err
}
-func (r *SendTxResult) Code() commonclient.SendTxReturnCode {
+func (r *SendTxResult) Code() multinode.SendTxReturnCode {
return r.code
}
@@ -1418,9 +1417,9 @@ func (r *RPCClient) onNewHead(ctx context.Context, requestCh <-chan struct{}, he
r.chainInfoLock.Lock()
defer r.chainInfoLock.Unlock()
- if !commonclient.CtxIsHeathCheckRequest(ctx) {
+ if !multinode.CtxIsHeathCheckRequest(ctx) {
r.highestUserObservations.BlockNumber = max(r.highestUserObservations.BlockNumber, head.Number)
- r.highestUserObservations.TotalDifficulty = commonclient.MaxTotalDifficulty(r.highestUserObservations.TotalDifficulty, head.TotalDifficulty)
+ r.highestUserObservations.TotalDifficulty = multinode.MaxTotalDifficulty(r.highestUserObservations.TotalDifficulty, head.TotalDifficulty)
}
select {
case <-requestCh: // no need to update latestChainInfo, as RPCClient already started new life cycle
@@ -1437,7 +1436,7 @@ func (r *RPCClient) onNewFinalizedHead(ctx context.Context, requestCh <-chan str
}
r.chainInfoLock.Lock()
defer r.chainInfoLock.Unlock()
- if !commonclient.CtxIsHeathCheckRequest(ctx) {
+ if !multinode.CtxIsHeathCheckRequest(ctx) {
r.highestUserObservations.FinalizedBlockNumber = max(r.highestUserObservations.FinalizedBlockNumber, head.Number)
}
select {
@@ -1448,7 +1447,7 @@ func (r *RPCClient) onNewFinalizedHead(ctx context.Context, requestCh <-chan str
}
}
-func (r *RPCClient) GetInterceptedChainInfo() (latest, highestUserObservations commonclient.ChainInfo) {
+func (r *RPCClient) GetInterceptedChainInfo() (latest, highestUserObservations multinode.ChainInfo) {
r.chainInfoLock.Lock()
defer r.chainInfoLock.Unlock()
return r.latestChainInfo, r.highestUserObservations
diff --git a/core/chains/evm/client/rpc_client_internal_test.go b/core/chains/evm/client/rpc_client_internal_test.go
index ef321645fc2..ec1a89886cd 100644
--- a/core/chains/evm/client/rpc_client_internal_test.go
+++ b/core/chains/evm/client/rpc_client_internal_test.go
@@ -8,6 +8,7 @@ import (
ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/stretchr/testify/require"
+ "github.com/smartcontractkit/chainlink-framework/multinode"
commonclient "github.com/smartcontractkit/chainlink/v2/common/client"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/chaintype"
"github.com/smartcontractkit/chainlink/v2/core/logger"
@@ -73,13 +74,13 @@ func TestRPCClient_MakeLogsValid(t *testing.T) {
}
for _, tc := range testCases {
t.Run(tc.Name, func(t *testing.T) {
- rpc := NewRPCClient(TestNodePoolConfig{}, logger.TestLogger(t), nil, nil, "eth-primary-rpc-0", 0, nil, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "")
+ rpc := NewRPCClient(TestNodePoolConfig{}, logger.TestLogger(t), nil, nil, "eth-primary-rpc-0", 0, nil, multinode.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "")
log, err := rpc.makeLogValid(ethtypes.Log{TxIndex: tc.TxIndex, Index: tc.LogIndex})
// non sei should return as is
require.NoError(t, err)
require.Equal(t, tc.TxIndex, log.TxIndex)
require.Equal(t, tc.LogIndex, log.Index)
- seiRPC := NewRPCClient(TestNodePoolConfig{}, logger.TestLogger(t), nil, nil, "eth-primary-rpc-0", 0, nil, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, chaintype.ChainSei)
+ seiRPC := NewRPCClient(TestNodePoolConfig{}, logger.TestLogger(t), nil, nil, "eth-primary-rpc-0", 0, nil, multinode.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, chaintype.ChainSei)
log, err = seiRPC.makeLogValid(ethtypes.Log{TxIndex: tc.TxIndex, Index: tc.LogIndex})
if tc.ExpectedError != nil {
require.EqualError(t, err, tc.ExpectedError.Error())
diff --git a/core/chains/evm/client/rpc_client_test.go b/core/chains/evm/client/rpc_client_test.go
index f6e7f9ee338..6fc02d3b2c1 100644
--- a/core/chains/evm/client/rpc_client_test.go
+++ b/core/chains/evm/client/rpc_client_test.go
@@ -25,8 +25,8 @@ import (
"github.com/smartcontractkit/chainlink-common/pkg/logger"
"github.com/smartcontractkit/chainlink-common/pkg/utils/tests"
+ "github.com/smartcontractkit/chainlink-framework/multinode"
commonclient "github.com/smartcontractkit/chainlink/v2/common/client"
- commontypes "github.com/smartcontractkit/chainlink/v2/common/types"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/client"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/chaintype"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/testutils"
@@ -74,7 +74,7 @@ func TestRPCClient_SubscribeToHeads(t *testing.T) {
return
}
- checkClosedRPCClientShouldRemoveExistingSub := func(t tests.TestingT, ctx context.Context, sub commontypes.Subscription, rpcClient *client.RPCClient) {
+ checkClosedRPCClientShouldRemoveExistingSub := func(t tests.TestingT, ctx context.Context, sub multinode.Subscription, rpcClient *client.RPCClient) {
errCh := sub.Err()
rpcClient.UnsubscribeAllExcept()
@@ -92,7 +92,7 @@ func TestRPCClient_SubscribeToHeads(t *testing.T) {
t.Run("WS and HTTP URL cannot be both empty", func(t *testing.T) {
// ws is optional when LogBroadcaster is disabled, however SubscribeFilterLogs will return error if ws is missing
observedLggr := logger.Test(t)
- rpcClient := client.NewRPCClient(nodePoolCfgHeadPolling, observedLggr, nil, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "")
+ rpcClient := client.NewRPCClient(nodePoolCfgHeadPolling, observedLggr, nil, nil, "rpc", 1, chainId, multinode.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "")
require.Equal(t, errors.New("cannot dial rpc client when both ws and http info are missing"), rpcClient.Dial(ctx))
})
@@ -100,7 +100,7 @@ func TestRPCClient_SubscribeToHeads(t *testing.T) {
server := testutils.NewWSServer(t, chainId, serverCallBack)
wsURL := server.WSURL()
- rpc := client.NewRPCClient(nodePoolCfgWSSub, lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "")
+ rpc := client.NewRPCClient(nodePoolCfgWSSub, lggr, wsURL, nil, "rpc", 1, chainId, multinode.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "")
defer rpc.Close()
require.NoError(t, rpc.Dial(ctx))
// set to default values
@@ -127,7 +127,7 @@ func TestRPCClient_SubscribeToHeads(t *testing.T) {
assert.Equal(t, int64(0), latest.FinalizedBlockNumber)
assert.Equal(t, big.NewInt(500), latest.TotalDifficulty)
- assertHighestUserObservations := func(highestUserObservations commonclient.ChainInfo) {
+ assertHighestUserObservations := func(highestUserObservations multinode.ChainInfo) {
assert.Equal(t, int64(256), highestUserObservations.BlockNumber)
assert.Equal(t, int64(0), highestUserObservations.FinalizedBlockNumber)
assert.Equal(t, big.NewInt(1000), highestUserObservations.TotalDifficulty)
@@ -149,11 +149,11 @@ func TestRPCClient_SubscribeToHeads(t *testing.T) {
server := testutils.NewWSServer(t, chainId, serverCallBack)
wsURL := server.WSURL()
- rpc := client.NewRPCClient(nodePoolCfgWSSub, lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "")
+ rpc := client.NewRPCClient(nodePoolCfgWSSub, lggr, wsURL, nil, "rpc", 1, chainId, multinode.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "")
defer rpc.Close()
require.NoError(t, rpc.Dial(ctx))
- ch, sub, err := rpc.SubscribeToHeads(commonclient.CtxAddHealthCheckFlag(tests.Context(t)))
+ ch, sub, err := rpc.SubscribeToHeads(multinode.CtxAddHealthCheckFlag(tests.Context(t)))
require.NoError(t, err)
defer sub.Unsubscribe()
go server.MustWriteBinaryMessageSync(t, makeNewHeadWSMessage(&evmtypes.Head{Number: 256, TotalDifficulty: big.NewInt(1000)}))
@@ -192,7 +192,7 @@ func TestRPCClient_SubscribeToHeads(t *testing.T) {
}
server := createRPCServer()
- rpc := client.NewRPCClient(nodePoolCfgHeadPolling, lggr, server.URL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "")
+ rpc := client.NewRPCClient(nodePoolCfgHeadPolling, lggr, server.URL, nil, "rpc", 1, chainId, multinode.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "")
defer rpc.Close()
require.NoError(t, rpc.Dial(ctx))
latest, highestUserObservations := rpc.GetInterceptedChainInfo()
@@ -215,7 +215,7 @@ func TestRPCClient_SubscribeToHeads(t *testing.T) {
// subscription with health check flag won't affect user observations
sub.Unsubscribe() // stop prev subscription
server.Head = &evmtypes.Head{Number: 256}
- headCh, sub, err = rpc.SubscribeToHeads(commonclient.CtxAddHealthCheckFlag(tests.Context(t)))
+ headCh, sub, err = rpc.SubscribeToHeads(multinode.CtxAddHealthCheckFlag(tests.Context(t)))
require.NoError(t, err)
defer sub.Unsubscribe()
@@ -231,7 +231,7 @@ func TestRPCClient_SubscribeToHeads(t *testing.T) {
server := testutils.NewWSServer(t, chainId, serverCallBack)
wsURL := server.WSURL()
- rpc := client.NewRPCClient(nodePoolCfgWSSub, lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "")
+ rpc := client.NewRPCClient(nodePoolCfgWSSub, lggr, wsURL, nil, "rpc", 1, chainId, multinode.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "")
defer rpc.Close()
require.NoError(t, rpc.Dial(ctx))
var wg sync.WaitGroup
@@ -254,7 +254,7 @@ func TestRPCClient_SubscribeToHeads(t *testing.T) {
t.Run("Block's chain ID matched configured", func(t *testing.T) {
server := testutils.NewWSServer(t, chainId, serverCallBack)
wsURL := server.WSURL()
- rpc := client.NewRPCClient(nodePoolCfgWSSub, lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "")
+ rpc := client.NewRPCClient(nodePoolCfgWSSub, lggr, wsURL, nil, "rpc", 1, chainId, multinode.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "")
defer rpc.Close()
require.NoError(t, rpc.Dial(ctx))
ch, sub, err := rpc.SubscribeToHeads(tests.Context(t))
@@ -270,7 +270,7 @@ func TestRPCClient_SubscribeToHeads(t *testing.T) {
})
wsURL := server.WSURL()
observedLggr, observed := logger.TestObserved(t, zap.DebugLevel)
- rpc := client.NewRPCClient(nodePoolCfgWSSub, observedLggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "")
+ rpc := client.NewRPCClient(nodePoolCfgWSSub, observedLggr, wsURL, nil, "rpc", 1, chainId, multinode.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "")
require.NoError(t, rpc.Dial(ctx))
server.Close()
_, _, err := rpc.SubscribeToHeads(ctx)
@@ -280,7 +280,7 @@ func TestRPCClient_SubscribeToHeads(t *testing.T) {
t.Run("Closed rpc client should remove existing SubscribeToHeads subscription with WS", func(t *testing.T) {
server := testutils.NewWSServer(t, chainId, serverCallBack)
wsURL := server.WSURL()
- rpc := client.NewRPCClient(nodePoolCfgWSSub, lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "")
+ rpc := client.NewRPCClient(nodePoolCfgWSSub, lggr, wsURL, nil, "rpc", 1, chainId, multinode.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "")
defer rpc.Close()
require.NoError(t, rpc.Dial(ctx))
@@ -292,7 +292,7 @@ func TestRPCClient_SubscribeToHeads(t *testing.T) {
server := testutils.NewWSServer(t, chainId, serverCallBack)
wsURL := server.WSURL()
- rpc := client.NewRPCClient(nodePoolCfgHeadPolling, lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "")
+ rpc := client.NewRPCClient(nodePoolCfgHeadPolling, lggr, wsURL, nil, "rpc", 1, chainId, multinode.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "")
defer rpc.Close()
require.NoError(t, rpc.Dial(ctx))
@@ -304,7 +304,7 @@ func TestRPCClient_SubscribeToHeads(t *testing.T) {
server := testutils.NewWSServer(t, chainId, serverCallBack)
wsURL := server.WSURL()
- rpc := client.NewRPCClient(nodePoolCfgHeadPolling, lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "")
+ rpc := client.NewRPCClient(nodePoolCfgHeadPolling, lggr, wsURL, nil, "rpc", 1, chainId, multinode.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "")
defer rpc.Close()
require.NoError(t, rpc.Dial(ctx))
@@ -315,7 +315,7 @@ func TestRPCClient_SubscribeToHeads(t *testing.T) {
t.Run("Subscription error is properly wrapper", func(t *testing.T) {
server := testutils.NewWSServer(t, chainId, serverCallBack)
wsURL := server.WSURL()
- rpc := client.NewRPCClient(nodePoolCfgWSSub, lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "")
+ rpc := client.NewRPCClient(nodePoolCfgWSSub, lggr, wsURL, nil, "rpc", 1, chainId, multinode.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "")
defer rpc.Close()
require.NoError(t, rpc.Dial(ctx))
_, sub, err := rpc.SubscribeToHeads(ctx)
@@ -345,7 +345,7 @@ func TestRPCClient_SubscribeFilterLogs(t *testing.T) {
t.Run("Failed SubscribeFilterLogs when WSURL is empty", func(t *testing.T) {
// ws is optional when LogBroadcaster is disabled, however SubscribeFilterLogs will return error if ws is missing
observedLggr := logger.Test(t)
- rpcClient := client.NewRPCClient(nodePoolCfg, observedLggr, nil, &url.URL{}, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "")
+ rpcClient := client.NewRPCClient(nodePoolCfg, observedLggr, nil, &url.URL{}, "rpc", 1, chainId, multinode.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "")
require.Nil(t, rpcClient.Dial(ctx))
_, err := rpcClient.SubscribeFilterLogs(ctx, ethereum.FilterQuery{}, make(chan types.Log))
@@ -357,7 +357,7 @@ func TestRPCClient_SubscribeFilterLogs(t *testing.T) {
})
wsURL := server.WSURL()
observedLggr, observed := logger.TestObserved(t, zap.DebugLevel)
- rpc := client.NewRPCClient(nodePoolCfg, observedLggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "")
+ rpc := client.NewRPCClient(nodePoolCfg, observedLggr, wsURL, nil, "rpc", 1, chainId, multinode.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "")
require.NoError(t, rpc.Dial(ctx))
server.Close()
_, err := rpc.SubscribeFilterLogs(ctx, ethereum.FilterQuery{}, make(chan types.Log))
@@ -374,7 +374,7 @@ func TestRPCClient_SubscribeFilterLogs(t *testing.T) {
return resp
})
wsURL := server.WSURL()
- rpc := client.NewRPCClient(nodePoolCfg, lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "")
+ rpc := client.NewRPCClient(nodePoolCfg, lggr, wsURL, nil, "rpc", 1, chainId, multinode.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "")
defer rpc.Close()
require.NoError(t, rpc.Dial(ctx))
sub, err := rpc.SubscribeFilterLogs(ctx, ethereum.FilterQuery{}, make(chan types.Log))
@@ -403,7 +403,7 @@ func TestRPCClient_SubscribeFilterLogs(t *testing.T) {
return
})
wsURL := server.WSURL()
- rpc := client.NewRPCClient(nodePoolCfg, lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, chaintype.ChainSei)
+ rpc := client.NewRPCClient(nodePoolCfg, lggr, wsURL, nil, "rpc", 1, chainId, multinode.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, chaintype.ChainSei)
defer rpc.Close()
require.NoError(t, rpc.Dial(ctx))
ch := make(chan types.Log)
@@ -498,7 +498,7 @@ func TestRPCClientFilterLogs(t *testing.T) {
return
})
wsURL := server.WSURL()
- seiRPC := client.NewRPCClient(nodePoolCfg, lggr, wsURL, nil, "rpc", 1, chainID, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, chaintype.ChainSei)
+ seiRPC := client.NewRPCClient(nodePoolCfg, lggr, wsURL, nil, "rpc", 1, chainID, multinode.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, chaintype.ChainSei)
defer seiRPC.Close()
require.NoError(t, seiRPC.Dial(ctx))
logs, err := seiRPC.FilterLogs(ctx, ethereum.FilterQuery{})
@@ -508,7 +508,7 @@ func TestRPCClientFilterLogs(t *testing.T) {
}
// non sei should return index as is
- rpc := client.NewRPCClient(nodePoolCfg, lggr, wsURL, nil, "rpc", 1, chainID, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "")
+ rpc := client.NewRPCClient(nodePoolCfg, lggr, wsURL, nil, "rpc", 1, chainID, multinode.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "")
defer rpc.Close()
require.NoError(t, rpc.Dial(ctx))
logs, err = rpc.FilterLogs(ctx, ethereum.FilterQuery{})
@@ -557,7 +557,7 @@ func TestRPCClient_LatestFinalizedBlock(t *testing.T) {
}
server := createRPCServer()
- rpc := client.NewRPCClient(nodePoolCfg, lggr, server.URL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "")
+ rpc := client.NewRPCClient(nodePoolCfg, lggr, server.URL, nil, "rpc", 1, chainId, multinode.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "")
require.NoError(t, rpc.Dial(ctx))
defer rpc.Close()
server.Head.Store(&evmtypes.Head{Number: 128})
@@ -586,7 +586,7 @@ func TestRPCClient_LatestFinalizedBlock(t *testing.T) {
// health check flg prevents change in highestUserObservations
server.Head.Store(&evmtypes.Head{Number: 256})
- _, err = rpc.LatestFinalizedBlock(commonclient.CtxAddHealthCheckFlag(ctx))
+ _, err = rpc.LatestFinalizedBlock(multinode.CtxAddHealthCheckFlag(ctx))
require.NoError(t, err)
latest, highestUserObservations = rpc.GetInterceptedChainInfo()
@@ -614,7 +614,7 @@ func TestRPCClient_LatestFinalizedBlock(t *testing.T) {
// health check subscription only updates latest
sub.Unsubscribe() // close previous one
server.Head.Store(&evmtypes.Head{Number: 1024})
- ch, sub, err = rpc.SubscribeToFinalizedHeads(commonclient.CtxAddHealthCheckFlag(ctx))
+ ch, sub, err = rpc.SubscribeToFinalizedHeads(multinode.CtxAddHealthCheckFlag(ctx))
require.NoError(t, err)
defer sub.Unsubscribe()
head = <-ch
@@ -704,7 +704,7 @@ func TestRpcClientLargePayloadTimeout(t *testing.T) {
// use something unreasonably large for RPC timeout to ensure that we use largePayloadRPCTimeout
const rpcTimeout = time.Hour
const largePayloadRPCTimeout = tests.TestInterval
- rpc := client.NewRPCClient(nodePoolCfg, logger.Test(t), rpcURL, nil, "rpc", 1, chainId, commonclient.Primary, largePayloadRPCTimeout, rpcTimeout, "")
+ rpc := client.NewRPCClient(nodePoolCfg, logger.Test(t), rpcURL, nil, "rpc", 1, chainId, multinode.Primary, largePayloadRPCTimeout, rpcTimeout, "")
require.NoError(t, rpc.Dial(ctx))
defer rpc.Close()
err := testCase.Fn(ctx, rpc)
@@ -749,7 +749,7 @@ func TestAstarCustomFinality(t *testing.T) {
const expectedFinalizedBlockNumber = int64(4)
const expectedFinalizedBlockHash = "0x7441e97acf83f555e0deefef86db636bc8a37eb84747603412884e4df4d22804"
- rpcClient := client.NewRPCClient(nodePoolCfg, logger.Test(t), wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, chaintype.ChainAstar)
+ rpcClient := client.NewRPCClient(nodePoolCfg, logger.Test(t), wsURL, nil, "rpc", 1, chainId, multinode.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, chaintype.ChainAstar)
defer rpcClient.Close()
err := rpcClient.Dial(tests.Context(t))
require.NoError(t, err)
diff --git a/core/chains/evm/client/simulated_backend_client.go b/core/chains/evm/client/simulated_backend_client.go
index fd645203856..7b325e861a5 100644
--- a/core/chains/evm/client/simulated_backend_client.go
+++ b/core/chains/evm/client/simulated_backend_client.go
@@ -24,8 +24,8 @@ import (
"github.com/smartcontractkit/chainlink-common/pkg/logger"
"github.com/smartcontractkit/chainlink-common/pkg/utils/hex"
"github.com/smartcontractkit/chainlink-common/pkg/utils/tests"
+ "github.com/smartcontractkit/chainlink-framework/multinode"
- commonclient "github.com/smartcontractkit/chainlink/v2/common/client"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/chaintype"
evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types"
ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big"
@@ -399,16 +399,16 @@ func (c *SimulatedBackendClient) HeaderByHash(ctx context.Context, h common.Hash
return c.client.HeaderByHash(ctx, h)
}
-func (c *SimulatedBackendClient) SendTransactionReturnCode(ctx context.Context, tx *types.Transaction, fromAddress common.Address) (commonclient.SendTxReturnCode, error) {
+func (c *SimulatedBackendClient) SendTransactionReturnCode(ctx context.Context, tx *types.Transaction, fromAddress common.Address) (multinode.SendTxReturnCode, error) {
err := c.SendTransaction(ctx, tx)
if err == nil {
- return commonclient.Successful, nil
+ return multinode.Successful, nil
}
if strings.Contains(err.Error(), "could not fetch parent") || strings.Contains(err.Error(), "invalid transaction") {
- return commonclient.Fatal, err
+ return multinode.Fatal, err
}
// All remaining error messages returned from SendTransaction are considered Unknown.
- return commonclient.Unknown, err
+ return multinode.Unknown, err
}
// SendTransaction sends a transaction.
diff --git a/core/chains/evm/txmgr/broadcaster_test.go b/core/chains/evm/txmgr/broadcaster_test.go
index 311f1aae648..4ed6729466b 100644
--- a/core/chains/evm/txmgr/broadcaster_test.go
+++ b/core/chains/evm/txmgr/broadcaster_test.go
@@ -27,8 +27,8 @@ import (
"github.com/smartcontractkit/chainlink-common/pkg/sqlutil"
commonutils "github.com/smartcontractkit/chainlink-common/pkg/utils"
"github.com/smartcontractkit/chainlink-common/pkg/utils/tests"
+ "github.com/smartcontractkit/chainlink-framework/multinode"
- commonclient "github.com/smartcontractkit/chainlink/v2/common/client"
commmonfee "github.com/smartcontractkit/chainlink/v2/common/fee"
txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr"
txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types"
@@ -261,7 +261,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Success(t *testing.T) {
}
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool {
return tx.Nonce() == uint64(2) && tx.Value().Cmp(big.NewInt(242)) == 0
- }), fromAddress).Return(commonclient.Successful, nil).Once()
+ }), fromAddress).Return(multinode.Successful, nil).Once()
// Earlier
tr := int32(99)
@@ -289,7 +289,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Success(t *testing.T) {
require.Equal(t, value.String(), tx.Value().String())
require.Equal(t, earlierEthTx.EncodedPayload, tx.Data())
return true
- }), fromAddress).Return(commonclient.Successful, nil).Once()
+ }), fromAddress).Return(multinode.Successful, nil).Once()
// Later
laterEthTx := txmgr.Tx{
@@ -312,7 +312,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Success(t *testing.T) {
require.Equal(t, value.String(), tx.Value().String())
require.Equal(t, laterEthTx.EncodedPayload, tx.Data())
return true
- }), fromAddress).Return(commonclient.Successful, nil).Once()
+ }), fromAddress).Return(multinode.Successful, nil).Once()
// Insertion order deliberately reversed to test ordering
require.NoError(t, txStore.InsertTx(ctx, &expensiveEthTx))
@@ -394,7 +394,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Success(t *testing.T) {
t.Run("sends transactions with type 0x2 in EIP-1559 mode", func(t *testing.T) {
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool {
return tx.Nonce() == uint64(343) && tx.Value().Cmp(big.NewInt(242)) == 0
- }), fromAddress).Return(commonclient.Successful, nil).Once()
+ }), fromAddress).Return(multinode.Successful, nil).Once()
etx := mustCreateUnstartedTx(t, txStore, fromAddress, toAddress, []byte{42, 42, 0}, gasLimit, big.Int(assets.NewEthValue(242)), testutils.FixtureChainID)
// Do the thing
@@ -445,7 +445,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Success(t *testing.T) {
}
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool {
return tx.Nonce() == uint64(344) && tx.Value().Cmp(big.NewInt(442)) == 0
- }), fromAddress).Return(commonclient.Successful, nil).Once()
+ }), fromAddress).Return(multinode.Successful, nil).Once()
ethClient.On("CallContext", mock.Anything, mock.AnythingOfType("*hexutil.Bytes"), "eth_call", mock.MatchedBy(func(callarg map[string]interface{}) bool {
if fmt.Sprintf("%s", callarg["value"]) == "0x1ba" { // 442
assert.Equal(t, txRequest.FromAddress, callarg["from"])
@@ -478,7 +478,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Success(t *testing.T) {
t.Run("with unknown error, sends tx as normal", func(t *testing.T) {
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool {
return tx.Nonce() == uint64(345) && tx.Value().Cmp(big.NewInt(542)) == 0
- }), fromAddress).Return(commonclient.Successful, nil).Once()
+ }), fromAddress).Return(multinode.Successful, nil).Once()
ethClient.On("CallContext", mock.Anything, mock.AnythingOfType("*hexutil.Bytes"), "eth_call", mock.MatchedBy(func(callarg map[string]interface{}) bool {
return fmt.Sprintf("%s", callarg["value"]) == "0x21e" // 542
}), "latest").Return(errors.New("this is not a revert, something unexpected went wrong")).Once()
@@ -529,7 +529,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Success(t *testing.T) {
etx := mustCreateUnstartedTx(t, txStore, fromAddress, toAddress, []byte{42, 42, 0}, gasLimit, big.Int(assets.NewEthValue(243)), testutils.FixtureChainID)
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool {
return tx.Nonce() == uint64(346) && tx.Value().Cmp(big.NewInt(243)) == 0
- }), fromAddress).Return(commonclient.Fatal, errors.New(terminallyStuckError)).Once()
+ }), fromAddress).Return(multinode.Fatal, errors.New(terminallyStuckError)).Once()
// Start processing unstarted transactions
retryable, err := eb.ProcessUnstartedTxs(tests.Context(t), fromAddress)
@@ -569,7 +569,7 @@ func TestEthBroadcaster_TransmitChecking(t *testing.T) {
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool {
return tx.Nonce() == 0 && tx.Value().Cmp(big.NewInt(442)) == 0
- }), fromAddress).Return(commonclient.Successful, nil).Once()
+ }), fromAddress).Return(multinode.Successful, nil).Once()
ethTx := mustCreateUnstartedGeneratedTx(t, txStore, fromAddress, testutils.FixtureChainID,
txRequestWithValue(big.Int(assets.NewEthValue(442))),
@@ -592,7 +592,7 @@ func TestEthBroadcaster_TransmitChecking(t *testing.T) {
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool {
return tx.Nonce() == 1 && tx.Value().Cmp(big.NewInt(442)) == 0
- }), fromAddress).Return(commonclient.Successful, nil).Once()
+ }), fromAddress).Return(multinode.Successful, nil).Once()
ethTx := mustCreateUnstartedGeneratedTx(t, txStore, fromAddress, testutils.FixtureChainID,
txRequestWithValue(big.Int(assets.NewEthValue(442))),
@@ -713,7 +713,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Success_WithMultiplier(t *testing
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool {
assert.Equal(t, int(1600), int(tx.Gas()))
return true
- }), fromAddress).Return(commonclient.Successful, nil).Once()
+ }), fromAddress).Return(multinode.Successful, nil).Once()
txRequest := txmgr.TxRequest{
FromAddress: fromAddress,
@@ -799,7 +799,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_ResumingFromCrash(t *testing.T) {
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool {
return tx.Nonce() == uint64(firstNonce)
- }), fromAddress).Return(commonclient.Successful, nil).Once()
+ }), fromAddress).Return(multinode.Successful, nil).Once()
// Do the thing
{
@@ -836,7 +836,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_ResumingFromCrash(t *testing.T) {
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool {
return tx.Nonce() == uint64(firstNonce)
- }), fromAddress).Return(commonclient.Fatal, errors.New("exceeds block gas limit")).Once()
+ }), fromAddress).Return(multinode.Fatal, errors.New("exceeds block gas limit")).Once()
// Do the thing
{
@@ -873,7 +873,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_ResumingFromCrash(t *testing.T) {
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool {
return tx.Nonce() == uint64(firstNonce)
- }), fromAddress).Return(commonclient.Successful, errors.New("known transaction: a1313bd99a81fb4d8ad1d2e90b67c6b3fa77545c990d6251444b83b70b6f8980")).Once()
+ }), fromAddress).Return(multinode.Successful, errors.New("known transaction: a1313bd99a81fb4d8ad1d2e90b67c6b3fa77545c990d6251444b83b70b6f8980")).Once()
// Do the thing
{
@@ -909,7 +909,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_ResumingFromCrash(t *testing.T) {
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool {
return tx.Nonce() == uint64(firstNonce)
- }), fromAddress).Return(commonclient.TransactionAlreadyKnown, errors.New("nonce too low")).Once()
+ }), fromAddress).Return(multinode.TransactionAlreadyKnown, errors.New("nonce too low")).Once()
// Do the thing
{
@@ -947,7 +947,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_ResumingFromCrash(t *testing.T) {
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool {
return tx.Nonce() == uint64(firstNonce)
- }), fromAddress).Return(commonclient.Retryable, failedToReachNodeError).Once()
+ }), fromAddress).Return(multinode.Retryable, failedToReachNodeError).Once()
// Do the thing
retryable, err := eb.ProcessUnstartedTxs(tests.Context(t), fromAddress)
@@ -994,7 +994,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_ResumingFromCrash(t *testing.T) {
s, e := txmgr.GetGethSignedTx(attempt.SignedRawTx)
require.NoError(t, e)
return tx.Nonce() == uint64(firstNonce) && tx.GasPrice().Int64() == s.GasPrice().Int64()
- }), fromAddress).Return(commonclient.Successful, errors.New("known transaction: a1313bd99a81fb4d8ad1d2e90b67c6b3fa77545c990d6251444b83b70b6f8980")).Once()
+ }), fromAddress).Return(multinode.Successful, errors.New("known transaction: a1313bd99a81fb4d8ad1d2e90b67c6b3fa77545c990d6251444b83b70b6f8980")).Once()
// Do the thing
{
@@ -1059,7 +1059,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) {
// First send, replacement underpriced
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool {
return tx.Nonce() == uint64(0)
- }), fromAddress).Return(commonclient.Successful, errors.New("replacement transaction underpriced")).Once()
+ }), fromAddress).Return(multinode.Successful, errors.New("replacement transaction underpriced")).Once()
// Do the thing
retryable, err := eb.ProcessUnstartedTxs(ctx, fromAddress)
@@ -1096,7 +1096,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) {
etx := mustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, testutils.FixtureChainID)
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool {
return tx.Nonce() == localNextNonce
- }), fromAddress).Return(commonclient.Fatal, errors.New(fatalErrorExample)).Once()
+ }), fromAddress).Return(multinode.Fatal, errors.New(fatalErrorExample)).Once()
retryable, err := eb.ProcessUnstartedTxs(ctx, fromAddress)
assert.NoError(t, err)
@@ -1146,7 +1146,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) {
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool {
return tx.Nonce() == localNextNonce
- }), fromAddress).Return(commonclient.Fatal, errors.New(fatalErrorExample)).Once()
+ }), fromAddress).Return(multinode.Fatal, errors.New(fatalErrorExample)).Once()
retryable, err := eb.ProcessUnstartedTxs(ctx, fromAddress)
require.Error(t, err)
@@ -1167,7 +1167,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) {
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool {
return tx.Nonce() == localNextNonce
- }), fromAddress).Return(commonclient.Fatal, errors.New(fatalErrorExample)).Once()
+ }), fromAddress).Return(multinode.Fatal, errors.New(fatalErrorExample)).Once()
{
retryable, err := eb.ProcessUnstartedTxs(ctx, fromAddress)
@@ -1200,7 +1200,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) {
etx := mustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, testutils.FixtureChainID)
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool {
return tx.Nonce() == localNextNonce
- }), fromAddress).Return(commonclient.ExceedsMaxFee, errors.New(TxFeeExceedsCapError)).Twice()
+ }), fromAddress).Return(multinode.ExceedsMaxFee, errors.New(TxFeeExceedsCapError)).Twice()
// In the first case, the tx was NOT accepted into the mempool. In the case
// of multiple RPC nodes, it is possible that it can be accepted by
// another node even if the primary one returns "exceeds the configured
@@ -1258,7 +1258,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) {
etx := mustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, testutils.FixtureChainID)
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool {
return tx.Nonce() == localNextNonce
- }), fromAddress).Return(commonclient.Unknown, errors.New(retryableErrorExample)).Once()
+ }), fromAddress).Return(multinode.Unknown, errors.New(retryableErrorExample)).Once()
// Nonce is the same as localNextNonce, implying that this sent transaction has not been accepted
ethClient.On("PendingNonceAt", mock.Anything, fromAddress).Return(localNextNonce, nil).Once()
@@ -1284,7 +1284,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) {
// Now on the second run, it is successful
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool {
return tx.Nonce() == localNextNonce
- }), fromAddress).Return(commonclient.Successful, nil).Once()
+ }), fromAddress).Return(multinode.Successful, nil).Once()
retryable, err = eb.ProcessUnstartedTxs(ctx, fromAddress)
assert.NoError(t, err)
@@ -1310,7 +1310,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) {
etx := mustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, testutils.FixtureChainID)
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool {
return tx.Nonce() == localNextNonce
- }), fromAddress).Return(commonclient.Unknown, errors.New(retryableErrorExample)).Once()
+ }), fromAddress).Return(multinode.Unknown, errors.New(retryableErrorExample)).Once()
ethClient.On("PendingNonceAt", mock.Anything, fromAddress).Return(uint64(0), errors.New("pending nonce fetch failed")).Once()
// Do the thing
@@ -1336,7 +1336,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) {
// Now on the second run, it is successful
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool {
return tx.Nonce() == localNextNonce
- }), fromAddress).Return(commonclient.Successful, nil).Once()
+ }), fromAddress).Return(multinode.Successful, nil).Once()
retryable, err = eb.ProcessUnstartedTxs(ctx, fromAddress)
assert.NoError(t, err)
@@ -1362,7 +1362,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) {
etx := mustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, testutils.FixtureChainID)
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool {
return tx.Nonce() == localNextNonce
- }), fromAddress).Return(commonclient.Unknown, errors.New(retryableErrorExample)).Once()
+ }), fromAddress).Return(multinode.Unknown, errors.New(retryableErrorExample)).Once()
// Nonce is one higher than localNextNonce, implying that despite the error, this sent transaction has been accepted into the mempool
ethClient.On("PendingNonceAt", mock.Anything, fromAddress).Return(localNextNonce+1, nil).Once()
@@ -1396,17 +1396,17 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) {
// First was underpriced
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool {
return tx.Nonce() == localNextNonce && tx.GasPrice().Cmp(evmcfg.EVM().GasEstimator().PriceDefault().ToInt()) == 0
- }), fromAddress).Return(commonclient.Underpriced, errors.New(underpricedError)).Once()
+ }), fromAddress).Return(multinode.Underpriced, errors.New(underpricedError)).Once()
// Second with gas bump was still underpriced
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool {
return tx.Nonce() == localNextNonce && tx.GasPrice().Cmp(big.NewInt(25000000000)) == 0
- }), fromAddress).Return(commonclient.Underpriced, errors.New(underpricedError)).Once()
+ }), fromAddress).Return(multinode.Underpriced, errors.New(underpricedError)).Once()
// Third succeeded
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool {
return tx.Nonce() == localNextNonce && tx.GasPrice().Cmp(big.NewInt(30000000000)) == 0
- }), fromAddress).Return(commonclient.Successful, nil).Once()
+ }), fromAddress).Return(multinode.Successful, nil).Once()
// Do the thing
retryable, err := eb.ProcessUnstartedTxs(ctx, fromAddress)
@@ -1442,7 +1442,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) {
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool {
return tx.Nonce() == localNextNonce
- }), fromAddress).Return(commonclient.Retryable, failedToReachNodeError).Once()
+ }), fromAddress).Return(multinode.Retryable, failedToReachNodeError).Once()
// Do the thing
retryable, err := eb.ProcessUnstartedTxs(ctx, fromAddress)
@@ -1473,7 +1473,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) {
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool {
return tx.Nonce() == localNextNonce
- }), fromAddress).Return(commonclient.Successful, errors.New(temporarilyUnderpricedError)).Once()
+ }), fromAddress).Return(multinode.Successful, errors.New(temporarilyUnderpricedError)).Once()
// Do the thing
retryable, err := eb.ProcessUnstartedTxs(ctx, fromAddress)
@@ -1512,7 +1512,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) {
// First was underpriced
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool {
return tx.Nonce() == localNextNonce && tx.GasPrice().Cmp(evmcfg2.EVM().GasEstimator().PriceDefault().ToInt()) == 0
- }), fromAddress).Return(commonclient.Underpriced, errors.New(underpricedError)).Once()
+ }), fromAddress).Return(multinode.Underpriced, errors.New(underpricedError)).Once()
// Do the thing
retryable, err := eb2.ProcessUnstartedTxs(ctx, fromAddress)
@@ -1530,7 +1530,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) {
etx := mustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, testutils.FixtureChainID)
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool {
return tx.Nonce() == localNextNonce
- }), fromAddress).Return(commonclient.InsufficientFunds, errors.New(insufficientEthError)).Once()
+ }), fromAddress).Return(multinode.InsufficientFunds, errors.New(insufficientEthError)).Once()
retryable, err := eb.ProcessUnstartedTxs(ctx, fromAddress)
require.Error(t, err)
@@ -1560,7 +1560,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) {
etx := mustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, testutils.FixtureChainID)
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool {
return tx.Nonce() == localNextNonce
- }), fromAddress).Return(commonclient.Retryable, errors.New(nonceGapError)).Once()
+ }), fromAddress).Return(multinode.Retryable, errors.New(nonceGapError)).Once()
retryable, err := eb.ProcessUnstartedTxs(ctx, fromAddress)
require.Error(t, err)
@@ -1604,7 +1604,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) {
localNextNonce = getLocalNextNonce(t, nonceTracker, fromAddress)
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool {
return tx.Nonce() == localNextNonce && tx.GasTipCap().Cmp(big.NewInt(1)) == 0
- }), fromAddress).Return(commonclient.Underpriced, errors.New(underpricedError)).Once()
+ }), fromAddress).Return(multinode.Underpriced, errors.New(underpricedError)).Once()
// Check gas tip cap verification
retryable, err := eb2.ProcessUnstartedTxs(ctx, fromAddress)
@@ -1635,15 +1635,15 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) {
// Second was underpriced but above minimum
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool {
return tx.Nonce() == localNextNonce && tx.GasTipCap().Cmp(gasTipCapDefault.ToInt()) == 0
- }), fromAddress).Return(commonclient.Underpriced, errors.New(underpricedError)).Once()
+ }), fromAddress).Return(multinode.Underpriced, errors.New(underpricedError)).Once()
// Resend at the bumped price
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool {
return tx.Nonce() == localNextNonce && tx.GasTipCap().Cmp(big.NewInt(0).Add(gasTipCapDefault.ToInt(), evmcfg2.EVM().GasEstimator().BumpMin().ToInt())) == 0
- }), fromAddress).Return(commonclient.Underpriced, errors.New(underpricedError)).Once()
+ }), fromAddress).Return(multinode.Underpriced, errors.New(underpricedError)).Once()
// Final bump succeeds
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool {
return tx.Nonce() == localNextNonce && tx.GasTipCap().Cmp(big.NewInt(0).Add(gasTipCapDefault.ToInt(), big.NewInt(0).Mul(evmcfg2.EVM().GasEstimator().BumpMin().ToInt(), big.NewInt(2)))) == 0
- }), fromAddress).Return(commonclient.Successful, nil).Once()
+ }), fromAddress).Return(multinode.Successful, nil).Once()
retryable, err := eb2.ProcessUnstartedTxs(ctx, fromAddress)
require.NoError(t, err)
@@ -1693,7 +1693,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_GasEstimationError(t *testing.T)
ethClient.On("EstimateGas", mock.Anything, mock.Anything).Return(estimatedGasLimit, nil).Once()
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool {
return tx.Nonce() == uint64(0)
- }), fromAddress).Return(commonclient.Successful, nil).Once()
+ }), fromAddress).Return(multinode.Successful, nil).Once()
// Do the thing
retryable, err := eb.ProcessUnstartedTxs(ctx, fromAddress)
@@ -1860,7 +1860,7 @@ func TestEthBroadcaster_NonceTracker_InProgressTx(t *testing.T) {
inProgressTxNonce := uint64(0)
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool {
return tx.Nonce() == inProgressTxNonce
- }), fromAddress).Return(commonclient.Successful, nil).Once()
+ }), fromAddress).Return(multinode.Successful, nil).Once()
// Tx with nonce 0 in DB will set local nonce map to value to 1
mustInsertInProgressEthTxWithAttempt(t, txStore, evmtypes.Nonce(inProgressTxNonce), fromAddress)
@@ -1903,7 +1903,7 @@ func TestEthBroadcaster_HederaBroadcastValidation(t *testing.T) {
localNonce := uint64(0)
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool {
return tx.Nonce() == localNonce
- }), fromAddress).Return(commonclient.Successful, nil).Once()
+ }), fromAddress).Return(multinode.Successful, nil).Once()
ethClient.On("NonceAt", mock.Anything, fromAddress, mock.Anything).Return(uint64(1), nil).Once()
mustInsertInProgressEthTxWithAttempt(t, txStore, evmtypes.Nonce(localNonce), fromAddress)
@@ -1923,7 +1923,7 @@ func TestEthBroadcaster_HederaBroadcastValidation(t *testing.T) {
localNonce := uint64(0)
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool {
return tx.Nonce() == localNonce
- }), fromAddress).Return(commonclient.Successful, nil).Twice()
+ }), fromAddress).Return(multinode.Successful, nil).Twice()
ethClient.On("NonceAt", mock.Anything, fromAddress, mock.Anything).Return(uint64(0), nil).Once()
ethClient.On("NonceAt", mock.Anything, fromAddress, mock.Anything).Return(uint64(1), nil).Once()
@@ -1945,7 +1945,7 @@ func TestEthBroadcaster_HederaBroadcastValidation(t *testing.T) {
localNonce := uint64(0)
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool {
return tx.Nonce() == localNonce
- }), fromAddress).Return(commonclient.Successful, nil).Times(4)
+ }), fromAddress).Return(multinode.Successful, nil).Times(4)
ethClient.On("NonceAt", mock.Anything, fromAddress, mock.Anything).Return(uint64(0), nil).Times(4)
etx := mustInsertInProgressEthTxWithAttempt(t, txStore, evmtypes.Nonce(localNonce), fromAddress)
diff --git a/core/chains/evm/txmgr/client.go b/core/chains/evm/txmgr/client.go
index 9ec175048d3..adb70b8c9ef 100644
--- a/core/chains/evm/txmgr/client.go
+++ b/core/chains/evm/txmgr/client.go
@@ -15,8 +15,8 @@ import (
"github.com/smartcontractkit/chainlink-common/pkg/logger"
"github.com/smartcontractkit/chainlink-common/pkg/utils"
+ "github.com/smartcontractkit/chainlink-framework/multinode"
- commonclient "github.com/smartcontractkit/chainlink/v2/common/client"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/client"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/config"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas"
@@ -48,14 +48,14 @@ func (c *evmTxmClient) BatchSendTransactions(
batchSize int,
lggr logger.SugaredLogger,
) (
- codes []commonclient.SendTxReturnCode,
+ codes []multinode.SendTxReturnCode,
txErrs []error,
broadcastTime time.Time,
successfulTxIDs []int64,
err error,
) {
// preallocate
- codes = make([]commonclient.SendTxReturnCode, len(attempts))
+ codes = make([]multinode.SendTxReturnCode, len(attempts))
txErrs = make([]error, len(attempts))
reqs, broadcastTime, successfulTxIDs, batchErr := batchSendTransactions(ctx, attempts, batchSize, lggr, c.client)
@@ -95,11 +95,11 @@ func (c *evmTxmClient) BatchSendTransactions(
return
}
-func (c *evmTxmClient) SendTransactionReturnCode(ctx context.Context, etx Tx, attempt TxAttempt, lggr logger.SugaredLogger) (commonclient.SendTxReturnCode, error) {
+func (c *evmTxmClient) SendTransactionReturnCode(ctx context.Context, etx Tx, attempt TxAttempt, lggr logger.SugaredLogger) (multinode.SendTxReturnCode, error) {
signedTx, err := GetGethSignedTx(attempt.SignedRawTx)
if err != nil {
lggr.Criticalw("Fatal error signing transaction", "err", err, "etx", etx)
- return commonclient.Fatal, err
+ return multinode.Fatal, err
}
return c.client.SendTransactionReturnCode(ctx, signedTx, etx.FromAddress)
}
diff --git a/core/chains/evm/txmgr/confirmer_test.go b/core/chains/evm/txmgr/confirmer_test.go
index ea251971860..a35765272bb 100644
--- a/core/chains/evm/txmgr/confirmer_test.go
+++ b/core/chains/evm/txmgr/confirmer_test.go
@@ -21,8 +21,8 @@ import (
"github.com/smartcontractkit/chainlink-common/pkg/logger"
"github.com/smartcontractkit/chainlink-common/pkg/services/servicetest"
"github.com/smartcontractkit/chainlink-common/pkg/utils/tests"
+ "github.com/smartcontractkit/chainlink-framework/multinode"
- commonclient "github.com/smartcontractkit/chainlink/v2/common/client"
commonfee "github.com/smartcontractkit/chainlink/v2/common/fee"
txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr"
txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types"
@@ -751,7 +751,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary_WithConnectivityCheck(t *testing
require.NoError(t, db.Get(&dbAttempt, `UPDATE evm.tx_attempts SET broadcast_before_block_num=$1 WHERE id=$2 RETURNING *`, oldEnough, attempt1.ID))
// Send transaction and assume success.
- ethClient.On("SendTransactionReturnCode", mock.Anything, mock.Anything, fromAddress).Return(commonclient.Successful, nil).Once()
+ ethClient.On("SendTransactionReturnCode", mock.Anything, mock.Anything, fromAddress).Return(multinode.Successful, nil).Once()
err := ec.RebroadcastWhereNecessary(tests.Context(t), currentHead)
require.NoError(t, err)
@@ -800,7 +800,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary_WithConnectivityCheck(t *testing
require.NoError(t, db.Get(&dbAttempt, `UPDATE evm.tx_attempts SET broadcast_before_block_num=$1 WHERE id=$2 RETURNING *`, oldEnough, attempt1.ID))
// Send transaction and assume success.
- ethClient.On("SendTransactionReturnCode", mock.Anything, mock.Anything, fromAddress).Return(commonclient.Successful, nil).Once()
+ ethClient.On("SendTransactionReturnCode", mock.Anything, mock.Anything, fromAddress).Return(multinode.Successful, nil).Once()
err := ec.RebroadcastWhereNecessary(tests.Context(t), currentHead)
require.NoError(t, err)
@@ -862,7 +862,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary_MaxFeeScenario(t *testing.T) {
// Once for the bumped attempt which exceeds limit
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool {
return tx.GasPrice().Int64() == int64(20000000000) && tx.Nonce() == uint64(*etx.Sequence) //nolint:gosec // disable G115
- }), fromAddress).Return(commonclient.ExceedsMaxFee, errors.New("tx fee (1.10 ether) exceeds the configured cap (1.00 ether)")).Once()
+ }), fromAddress).Return(multinode.ExceedsMaxFee, errors.New("tx fee (1.10 ether) exceeds the configured cap (1.00 ether)")).Once()
// Do the thing
require.NoError(t, ec.RebroadcastWhereNecessary(tests.Context(t), currentHead))
@@ -940,7 +940,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary(t *testing.T) {
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool {
return tx.Nonce() == uint64(*etx.Sequence) //nolint:gosec // disable G115
- }), fromAddress).Return(commonclient.Fatal, errors.New("exceeds block gas limit")).Once()
+ }), fromAddress).Return(multinode.Fatal, errors.New("exceeds block gas limit")).Once()
require.NoError(t, ec.RebroadcastWhereNecessary(ctx, currentHead))
var err error
@@ -960,7 +960,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary(t *testing.T) {
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool {
return tx.Nonce() == uint64(*etx.Sequence) //nolint:gosec // disable G115
- }), fromAddress).Return(commonclient.Successful, nil).Once()
+ }), fromAddress).Return(multinode.Successful, nil).Once()
require.NoError(t, ec.RebroadcastWhereNecessary(ctx, currentHead))
var err error
@@ -1003,7 +1003,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary(t *testing.T) {
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool {
return tx.Nonce() == uint64(*etx.Sequence) //nolint:gosec // disable G115
- }), fromAddress).Return(commonclient.Successful, fmt.Errorf("known transaction: %s", etx.TxAttempts[0].Hash.Hex())).Once()
+ }), fromAddress).Return(multinode.Successful, fmt.Errorf("known transaction: %s", etx.TxAttempts[0].Hash.Hex())).Once()
require.NoError(t, ec.RebroadcastWhereNecessary(ctx, currentHead))
var err error
@@ -1030,7 +1030,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary(t *testing.T) {
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool {
return tx.Nonce() == uint64(*etx.Sequence) //nolint:gosec // disable G115
- }), fromAddress).Return(commonclient.TransactionAlreadyKnown, errors.New("nonce too low")).Once()
+ }), fromAddress).Return(multinode.TransactionAlreadyKnown, errors.New("nonce too low")).Once()
require.NoError(t, ec.RebroadcastWhereNecessary(ctx, currentHead))
var err error
@@ -1061,7 +1061,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary(t *testing.T) {
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool {
return tx.Nonce() == uint64(*etx.Sequence) //nolint:gosec // disable G115
- }), fromAddress).Return(commonclient.Unknown, errors.New("some network error")).Once()
+ }), fromAddress).Return(multinode.Unknown, errors.New("some network error")).Once()
err := ec.RebroadcastWhereNecessary(ctx, currentHead)
require.Error(t, err)
@@ -1085,7 +1085,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary(t *testing.T) {
// Try again and move the attempt into "broadcast"
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool {
return tx.Nonce() == uint64(*etx.Sequence) //nolint:gosec // disable G115
- }), fromAddress).Return(commonclient.Successful, nil).Once()
+ }), fromAddress).Return(multinode.Successful, nil).Once()
require.NoError(t, ec.RebroadcastWhereNecessary(ctx, currentHead))
@@ -1112,10 +1112,10 @@ func TestEthConfirmer_RebroadcastWhereNecessary(t *testing.T) {
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool {
return tx.Nonce() == uint64(*etx.Sequence) //nolint:gosec // disable G115
- }), fromAddress).Return(commonclient.Underpriced, errors.New("replacement transaction underpriced")).Once()
+ }), fromAddress).Return(multinode.Underpriced, errors.New("replacement transaction underpriced")).Once()
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool {
return tx.Nonce() == uint64(*etx.Sequence) //nolint:gosec // disable G115
- }), fromAddress).Return(commonclient.Successful, nil).Once()
+ }), fromAddress).Return(multinode.Successful, nil).Once()
// Do the thing
require.NoError(t, ec.RebroadcastWhereNecessary(ctx, currentHead))
@@ -1148,7 +1148,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary(t *testing.T) {
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool {
return tx.Nonce() == uint64(*etx.Sequence) //nolint:gosec // disable G115
- }), fromAddress).Return(commonclient.Underpriced, errors.New("underpriced")).Once() // we already submitted at this price, now it's time to bump and submit again but since we simply resubmitted rather than increasing gas price, geth already knows about this tx
+ }), fromAddress).Return(multinode.Underpriced, errors.New("underpriced")).Once() // we already submitted at this price, now it's time to bump and submit again but since we simply resubmitted rather than increasing gas price, geth already knows about this tx
// Do the thing
require.Error(t, ec.RebroadcastWhereNecessary(ctx, currentHead))
@@ -1179,7 +1179,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary(t *testing.T) {
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool {
return tx.Nonce() == uint64(*etx.Sequence) //nolint:gosec // disable G115
- }), fromAddress).Return(commonclient.Underpriced, errors.New("underpriced")).Once() // we already submitted at this price, now it's time to bump and submit again but since we simply resubmitted rather than increasing gas price, geth already knows about this tx
+ }), fromAddress).Return(multinode.Underpriced, errors.New("underpriced")).Once() // we already submitted at this price, now it's time to bump and submit again but since we simply resubmitted rather than increasing gas price, geth already knows about this tx
// Do the thing
require.Error(t, ec.RebroadcastWhereNecessary(ctx, currentHead))
@@ -1210,7 +1210,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary(t *testing.T) {
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool {
return tx.Nonce() == uint64(*etx.Sequence) //nolint:gosec // disable G115
- }), fromAddress).Return(commonclient.Successful, nil).Once()
+ }), fromAddress).Return(multinode.Successful, nil).Once()
require.NoError(t, ec.RebroadcastWhereNecessary(ctx, currentHead))
etx, err = txStore.FindTxWithAttempts(ctx, etx.ID)
require.NoError(t, err)
@@ -1242,7 +1242,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary(t *testing.T) {
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool {
return tx.Nonce() == uint64(*etx.Sequence) //nolint:gosec // disable G115
- }), fromAddress).Return(commonclient.Underpriced, errors.New("underpriced")).Once()
+ }), fromAddress).Return(multinode.Underpriced, errors.New("underpriced")).Once()
require.Error(t, ec.RebroadcastWhereNecessary(ctx, currentHead))
etx, err = txStore.FindTxWithAttempts(ctx, etx.ID)
@@ -1273,10 +1273,10 @@ func TestEthConfirmer_RebroadcastWhereNecessary(t *testing.T) {
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool {
return tx.Nonce() == uint64(*etx.Sequence) //nolint:gosec // disable G115
- }), fromAddress).Return(commonclient.Underpriced, errors.New("replacement transaction underpriced")).Once()
+ }), fromAddress).Return(multinode.Underpriced, errors.New("replacement transaction underpriced")).Once()
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool {
return tx.Nonce() == uint64(*etx.Sequence) //nolint:gosec // disable G115
- }), fromAddress).Return(commonclient.Successful, nil).Once()
+ }), fromAddress).Return(multinode.Successful, nil).Once()
// Do it
require.NoError(t, ec.RebroadcastWhereNecessary(ctx, currentHead))
@@ -1330,10 +1330,10 @@ func TestEthConfirmer_RebroadcastWhereNecessary_TerminallyUnderpriced_ThenGoesTh
// Fail the first time with terminally underpriced.
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.Anything, fromAddress).Return(
- commonclient.Underpriced, errors.New("Transaction gas price is too low. It does not satisfy your node's minimal gas price")).Once()
+ multinode.Underpriced, errors.New("Transaction gas price is too low. It does not satisfy your node's minimal gas price")).Once()
// Succeed the second time after bumping gas.
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.Anything, fromAddress).Return(
- commonclient.Successful, nil).Once()
+ multinode.Successful, nil).Once()
kst.On("SignTx", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(
signedTx, nil,
).Once()
@@ -1353,10 +1353,10 @@ func TestEthConfirmer_RebroadcastWhereNecessary_TerminallyUnderpriced_ThenGoesTh
// Fail a few times with terminally underpriced
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.Anything, fromAddress).Return(
- commonclient.Underpriced, errors.New("Transaction gas price is too low. It does not satisfy your node's minimal gas price")).Times(3)
+ multinode.Underpriced, errors.New("Transaction gas price is too low. It does not satisfy your node's minimal gas price")).Times(3)
// Succeed the second time after bumping gas.
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.Anything, fromAddress).Return(
- commonclient.Successful, nil).Once()
+ multinode.Successful, nil).Once()
signedLegacyTx := new(types.Transaction)
kst.On("SignTx", mock.Anything, mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool {
return tx.Type() == 0x0 && tx.Nonce() == uint64(*etx.Sequence) //nolint:gosec // disable G115
@@ -1385,10 +1385,10 @@ func TestEthConfirmer_RebroadcastWhereNecessary_TerminallyUnderpriced_ThenGoesTh
// Fail a few times with terminally underpriced
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.Anything, fromAddress).Return(
- commonclient.Underpriced, errors.New("transaction underpriced")).Times(3)
+ multinode.Underpriced, errors.New("transaction underpriced")).Times(3)
// Succeed the second time after bumping gas.
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.Anything, fromAddress).Return(
- commonclient.Successful, nil).Once()
+ multinode.Successful, nil).Once()
signedDxFeeTx := new(types.Transaction)
kst.On("SignTx", mock.Anything, mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool {
return tx.Type() == 0x2 && tx.Nonce() == uint64(*etx.Sequence) //nolint:gosec // disable G115
@@ -1445,7 +1445,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary_WhenOutOfEth(t *testing.T) {
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool {
return expectedBumpedGasPrice.Cmp(tx.GasPrice()) == 0
- }), fromAddress).Return(commonclient.InsufficientFunds, insufficientEthError).Once()
+ }), fromAddress).Return(multinode.InsufficientFunds, insufficientEthError).Once()
// Do the thing
require.NoError(t, ec.RebroadcastWhereNecessary(tests.Context(t), currentHead))
@@ -1471,7 +1471,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary_WhenOutOfEth(t *testing.T) {
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool {
return expectedBumpedGasPrice.Cmp(tx.GasPrice()) == 0
- }), fromAddress).Return(commonclient.InsufficientFunds, insufficientEthError).Once()
+ }), fromAddress).Return(multinode.InsufficientFunds, insufficientEthError).Once()
// Do the thing
require.NoError(t, ec.RebroadcastWhereNecessary(tests.Context(t), currentHead))
@@ -1496,7 +1496,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary_WhenOutOfEth(t *testing.T) {
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool {
return expectedBumpedGasPrice.Cmp(tx.GasPrice()) == 0
- }), fromAddress).Return(commonclient.Successful, nil).Once()
+ }), fromAddress).Return(multinode.Successful, nil).Once()
// Do the thing
require.NoError(t, ec.RebroadcastWhereNecessary(tests.Context(t), currentHead))
@@ -1528,7 +1528,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary_WhenOutOfEth(t *testing.T) {
mustInsertUnconfirmedEthTxWithInsufficientEthAttempt(t, txStore, nonce, fromAddress)
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool {
return tx.Nonce() == uint64(n)
- }), fromAddress).Return(commonclient.Successful, nil).Once()
+ }), fromAddress).Return(multinode.Successful, nil).Once()
nonce++
}
@@ -1576,11 +1576,11 @@ func TestEthConfirmer_RebroadcastWhereNecessary_TerminallyStuckError(t *testing.
// Return terminally stuck error on first rebroadcast
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool {
return tx.Nonce() == uint64(*etx.Sequence) //nolint:gosec // disable G115
- }), fromAddress).Return(commonclient.TerminallyStuck, errors.New(terminallyStuckError)).Once()
+ }), fromAddress).Return(multinode.TerminallyStuck, errors.New(terminallyStuckError)).Once()
// Return successful for purge attempt
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool {
return tx.Nonce() == uint64(*etx.Sequence) //nolint:gosec // disable G115
- }), fromAddress).Return(commonclient.Successful, nil).Once()
+ }), fromAddress).Return(multinode.Successful, nil).Once()
// Start processing transactions for rebroadcast
require.NoError(t, ec.RebroadcastWhereNecessary(tests.Context(t), currentHead))
@@ -1622,7 +1622,7 @@ func TestEthConfirmer_ForceRebroadcast(t *testing.T) {
tx.Gas() == overrideGasLimit &&
reflect.DeepEqual(tx.Data(), etx1.EncodedPayload) &&
tx.To().String() == etx1.ToAddress.String()
- }), mock.Anything).Return(commonclient.Successful, nil).Once()
+ }), mock.Anything).Return(multinode.Successful, nil).Once()
require.NoError(t, ec.ForceRebroadcast(tests.Context(t), []evmtypes.Nonce{1}, gasPriceWei, fromAddress, overrideGasLimit))
})
@@ -1637,7 +1637,7 @@ func TestEthConfirmer_ForceRebroadcast(t *testing.T) {
tx.Gas() == etx1.FeeLimit &&
reflect.DeepEqual(tx.Data(), etx1.EncodedPayload) &&
tx.To().String() == etx1.ToAddress.String()
- }), mock.Anything).Return(commonclient.Successful, nil).Once()
+ }), mock.Anything).Return(multinode.Successful, nil).Once()
require.NoError(t, ec.ForceRebroadcast(tests.Context(t), []evmtypes.Nonce{(1)}, gasPriceWei, fromAddress, 0))
})
@@ -1648,10 +1648,10 @@ func TestEthConfirmer_ForceRebroadcast(t *testing.T) {
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool {
return tx.Nonce() == uint64(*etx1.Sequence) && tx.GasPrice().Int64() == gasPriceWei.GasPrice.Int64() && tx.Gas() == overrideGasLimit
- }), mock.Anything).Return(commonclient.Successful, nil).Once()
+ }), mock.Anything).Return(multinode.Successful, nil).Once()
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool {
return tx.Nonce() == uint64(*etx2.Sequence) && tx.GasPrice().Int64() == gasPriceWei.GasPrice.Int64() && tx.Gas() == overrideGasLimit
- }), mock.Anything).Return(commonclient.Successful, nil).Once()
+ }), mock.Anything).Return(multinode.Successful, nil).Once()
require.NoError(t, ec.ForceRebroadcast(tests.Context(t), []evmtypes.Nonce{(1), (2)}, gasPriceWei, fromAddress, overrideGasLimit))
})
@@ -1662,10 +1662,10 @@ func TestEthConfirmer_ForceRebroadcast(t *testing.T) {
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool {
return tx.Nonce() == uint64(1)
- }), mock.Anything).Return(commonclient.Successful, nil).Once()
+ }), mock.Anything).Return(multinode.Successful, nil).Once()
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool {
return tx.Nonce() == uint64(2)
- }), mock.Anything).Return(commonclient.Successful, nil).Once()
+ }), mock.Anything).Return(multinode.Successful, nil).Once()
for i := 3; i <= 5; i++ {
nonce := i
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool {
@@ -1675,7 +1675,7 @@ func TestEthConfirmer_ForceRebroadcast(t *testing.T) {
*tx.To() == fromAddress &&
tx.Value().Cmp(big.NewInt(0)) == 0 &&
len(tx.Data()) == 0
- }), mock.Anything).Return(commonclient.Successful, nil).Once()
+ }), mock.Anything).Return(multinode.Successful, nil).Once()
}
nonces := []evmtypes.Nonce{(1), (2), (3), (4), (5)}
@@ -1688,7 +1688,7 @@ func TestEthConfirmer_ForceRebroadcast(t *testing.T) {
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool {
return tx.Nonce() == uint64(0) && tx.GasPrice().Int64() == gasPriceWei.GasPrice.Int64() && tx.Gas() == config.EVM().GasEstimator().LimitDefault()
- }), mock.Anything).Return(commonclient.Successful, nil).Once()
+ }), mock.Anything).Return(multinode.Successful, nil).Once()
require.NoError(t, ec.ForceRebroadcast(tests.Context(t), []evmtypes.Nonce{(0)}, gasPriceWei, fromAddress, 0))
})
@@ -1702,7 +1702,7 @@ func TestEthConfirmer_ProcessStuckTransactions(t *testing.T) {
ethKeyStore := cltest.NewKeyStore(t, db).Eth()
_, fromAddress := cltest.MustInsertRandomKey(t, ethKeyStore)
ethClient := testutils.NewEthClientMockWithDefaultChain(t)
- ethClient.On("SendTransactionReturnCode", mock.Anything, mock.Anything, fromAddress).Return(commonclient.Successful, nil).Once()
+ ethClient.On("SendTransactionReturnCode", mock.Anything, mock.Anything, fromAddress).Return(multinode.Successful, nil).Once()
lggr := logger.Test(t)
feeEstimator := gasmocks.NewEvmFeeEstimator(t)
diff --git a/core/cmd/shell_local_test.go b/core/cmd/shell_local_test.go
index 7cdc8c21840..5c9449b2107 100644
--- a/core/cmd/shell_local_test.go
+++ b/core/cmd/shell_local_test.go
@@ -12,8 +12,8 @@ import (
commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config"
pgcommon "github.com/smartcontractkit/chainlink-common/pkg/sqlutil/pg"
"github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox"
+ "github.com/smartcontractkit/chainlink-framework/multinode"
- "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"
@@ -337,7 +337,7 @@ func TestShell_RebroadcastTransactions_Txm(t *testing.T) {
n := i
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool {
return tx.Nonce() == n
- }), mock.Anything).Once().Return(client.Successful, nil)
+ }), mock.Anything).Once().Return(multinode.Successful, nil)
}
assert.NoError(t, c.RebroadcastTransactions(ctx))
@@ -417,7 +417,7 @@ func TestShell_RebroadcastTransactions_OutsideRange_Txm(t *testing.T) {
n := i
ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool {
return uint(tx.Nonce()) == n
- }), mock.Anything).Once().Return(client.Successful, nil)
+ }), mock.Anything).Once().Return(multinode.Successful, nil)
}
assert.NoError(t, c.RebroadcastTransactions(ctx))
@@ -469,7 +469,7 @@ func TestShell_RebroadcastTransactions_AddressCheck(t *testing.T) {
mockRelayerChainInteroperators := &chainlinkmocks.FakeRelayerChainInteroperators{EVMChains: legacy}
app.On("GetRelayers").Return(mockRelayerChainInteroperators).Maybe()
- ethClient.On("SendTransactionReturnCode", mock.Anything, mock.Anything, mock.Anything).Maybe().Return(client.Successful, nil)
+ ethClient.On("SendTransactionReturnCode", mock.Anything, mock.Anything, mock.Anything).Maybe().Return(multinode.Successful, nil)
client := cmd.Shell{
Config: config,
diff --git a/core/internal/cltest/cltest.go b/core/internal/cltest/cltest.go
index a55c57cc9a2..85faad8e5fe 100644
--- a/core/internal/cltest/cltest.go
+++ b/core/internal/cltest/cltest.go
@@ -48,8 +48,8 @@ import (
"github.com/smartcontractkit/chainlink-common/pkg/sqlutil"
"github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox"
"github.com/smartcontractkit/chainlink-common/pkg/utils/tests"
+ "github.com/smartcontractkit/chainlink-framework/multinode"
- "github.com/smartcontractkit/chainlink/v2/common/client"
commonmocks "github.com/smartcontractkit/chainlink/v2/common/types/mocks"
"github.com/smartcontractkit/chainlink/v2/core/auth"
"github.com/smartcontractkit/chainlink/v2/core/bridges"
@@ -561,7 +561,7 @@ func NewEthMocksWithTransactionsOnBlocksAssertions(t testing.TB) *evmclimocks.Cl
c.On("Dial", mock.Anything).Maybe().Return(nil)
c.On("SubscribeToHeads", mock.Anything).Maybe().Return(chHead, EmptyMockSubscription(t), nil)
c.On("SendTransaction", mock.Anything, mock.Anything).Maybe().Return(nil)
- c.On("SendTransactionReturnCode", mock.Anything, mock.Anything, mock.Anything).Maybe().Return(client.Successful, nil)
+ c.On("SendTransactionReturnCode", mock.Anything, mock.Anything, mock.Anything).Maybe().Return(multinode.Successful, nil)
// Construct chain
h2 := Head(2)
h1 := HeadWithHash(1, h2.ParentHash)
diff --git a/core/scripts/go.mod b/core/scripts/go.mod
index a9e5130db2c..91924b9b681 100644
--- a/core/scripts/go.mod
+++ b/core/scripts/go.mod
@@ -37,7 +37,7 @@ require (
github.com/smartcontractkit/libocr v0.0.0-20241223215956-e5b78d8e3919
github.com/spf13/cobra v1.8.1
github.com/spf13/viper v1.19.0
- github.com/stretchr/testify v1.9.0
+ github.com/stretchr/testify v1.10.0
github.com/umbracle/ethgo v0.1.3
github.com/umbracle/fastrlp v0.0.0-20220527094140-59d5dd30e722
github.com/urfave/cli v1.22.14
@@ -277,13 +277,13 @@ require (
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
- github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 // indirect
+ github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/pressly/goose/v3 v3.21.1 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
- github.com/prometheus/common v0.60.0 // indirect
+ github.com/prometheus/common v0.60.1 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/prometheus/prometheus v0.54.1 // indirect
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
@@ -296,7 +296,7 @@ require (
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sanity-io/litter v1.5.5 // indirect
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 // indirect
- github.com/sasha-s/go-deadlock v0.3.1 // indirect
+ github.com/sasha-s/go-deadlock v0.3.5 // indirect
github.com/scylladb/go-reflectx v1.0.1 // indirect
github.com/sethvargo/go-retry v0.2.4 // indirect
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
@@ -307,6 +307,7 @@ require (
github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e // indirect
github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241216163550-fa030d178ba3 // indirect
github.com/smartcontractkit/chainlink-feeds v0.1.1 // indirect
+ github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20241220173418-09e17ddbeb20 // indirect
github.com/smartcontractkit/chainlink-protos/job-distributor v0.6.0 // indirect
github.com/smartcontractkit/chainlink-protos/orchestrator v0.4.0 // indirect
github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241223151630-eac4f1508dce // indirect
diff --git a/core/scripts/go.sum b/core/scripts/go.sum
index 7b004d6eae7..b39eed9a91b 100644
--- a/core/scripts/go.sum
+++ b/core/scripts/go.sum
@@ -1031,9 +1031,8 @@ github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCko
github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
-github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o=
-github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 h1:hDSdbBuw3Lefr6R18ax0tZ2BJeNB3NehB3trOwYBsdU=
-github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
+github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 h1:Dx7Ovyv/SFnMFw3fD4oEoeorXc6saIiQ23LrGLth0Gw=
+github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
@@ -1071,8 +1070,8 @@ github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7q
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
-github.com/prometheus/common v0.60.0 h1:+V9PAREWNvJMAuJ1x1BaWl9dewMW4YrHZQbx0sJNllA=
-github.com/prometheus/common v0.60.0/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw=
+github.com/prometheus/common v0.60.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc=
+github.com/prometheus/common v0.60.1/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
@@ -1125,8 +1124,8 @@ github.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQ
github.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4=
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY=
-github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71efZx0=
-github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM=
+github.com/sasha-s/go-deadlock v0.3.5 h1:tNCOEEDG6tBqrNDOX35j/7hL5FcFViG6awUGROb2NsU=
+github.com/sasha-s/go-deadlock v0.3.5/go.mod h1:bugP6EGbdGYObIlx7pUZtWqlvo8k9H6vCBBsiChJQ5U=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/scylladb/go-reflectx v1.0.1 h1:b917wZM7189pZdlND9PbIJ6NQxfDPfBvUaQ7cjj1iZQ=
github.com/scylladb/go-reflectx v1.0.1/go.mod h1:rWnOfDIRWBGN0miMLIcoPt/Dhi2doCMZqwMCJ3KupFc=
@@ -1170,6 +1169,8 @@ github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241216163550-fa030
github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241216163550-fa030d178ba3/go.mod h1:AS6zY2BkcRwfiGzNabGbHhfrLSrXrcI/GmjnT4jQ5/s=
github.com/smartcontractkit/chainlink-feeds v0.1.1 h1:JzvUOM/OgGQA1sOqTXXl52R6AnNt+Wg64sVG+XSA49c=
github.com/smartcontractkit/chainlink-feeds v0.1.1/go.mod h1:55EZ94HlKCfAsUiKUTNI7QlE/3d3IwTlsU3YNa/nBb4=
+github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20241220173418-09e17ddbeb20 h1:p/gzWpEf8alodCXm2Gtx2kWI/O9ZLdWZOdNnv5ZGO6c=
+github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20241220173418-09e17ddbeb20/go.mod h1:4JqpgFy01LaqG1yM2iFTzwX3ZgcAvW9WdstBZQgPHzU=
github.com/smartcontractkit/chainlink-protos/job-distributor v0.6.0 h1:0ewLMbAz3rZrovdRUCgd028yOXX8KigB4FndAUdI2kM=
github.com/smartcontractkit/chainlink-protos/job-distributor v0.6.0/go.mod h1:/dVVLXrsp+V0AbcYGJo3XMzKg3CkELsweA/TTopCsKE=
github.com/smartcontractkit/chainlink-protos/orchestrator v0.4.0 h1:ZBat8EBvE2LpSQR9U1gEbRV6PfAkiFdINmQ8nVnXIAQ=
@@ -1247,8 +1248,9 @@ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1F
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
-github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
+github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
diff --git a/core/services/chainlink/config_test.go b/core/services/chainlink/config_test.go
index 9a08b356c66..7d5b8628651 100644
--- a/core/services/chainlink/config_test.go
+++ b/core/services/chainlink/config_test.go
@@ -22,10 +22,10 @@ import (
commoncfg "github.com/smartcontractkit/chainlink-common/pkg/config"
"github.com/smartcontractkit/chainlink-common/pkg/utils/hex"
coscfg "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/config"
+ "github.com/smartcontractkit/chainlink-framework/multinode"
solcfg "github.com/smartcontractkit/chainlink-solana/pkg/solana/config"
stkcfg "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/config"
- "github.com/smartcontractkit/chainlink/v2/common/client"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/chaintype"
@@ -49,7 +49,7 @@ var (
second = *commoncfg.MustNewDuration(time.Second)
minute = *commoncfg.MustNewDuration(time.Minute)
- selectionMode = client.NodeSelectionModeHighestHead
+ selectionMode = multinode.NodeSelectionModeHighestHead
multiChain = Config{
Core: toml.Core{
@@ -258,7 +258,7 @@ func TestConfig_Marshal(t *testing.T) {
require.NoError(t, err)
return &a
}
- selectionMode := client.NodeSelectionModeHighestHead
+ selectionMode := multinode.NodeSelectionModeHighestHead
global := Config{
Core: toml.Core{
diff --git a/deployment/go.mod b/deployment/go.mod
index 34fbc7fd21d..5261299679f 100644
--- a/deployment/go.mod
+++ b/deployment/go.mod
@@ -33,7 +33,7 @@ require (
github.com/smartcontractkit/chainlink-protos/job-distributor v0.6.0
github.com/smartcontractkit/chainlink-testing-framework/lib v1.50.13
github.com/smartcontractkit/libocr v0.0.0-20241223215956-e5b78d8e3919
- github.com/stretchr/testify v1.9.0
+ github.com/stretchr/testify v1.10.0
github.com/test-go/testify v1.1.4
github.com/testcontainers/testcontainers-go v0.34.0
go.uber.org/multierr v1.11.0
@@ -375,14 +375,14 @@ require (
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
- github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 // indirect
+ github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/prometheus/alertmanager v0.27.0 // indirect
github.com/prometheus/client_golang v1.20.5 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
- github.com/prometheus/common v0.60.0 // indirect
+ github.com/prometheus/common v0.60.1 // indirect
github.com/prometheus/common/sigv4 v0.1.0 // indirect
github.com/prometheus/exporter-toolkit v0.11.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
@@ -397,7 +397,7 @@ require (
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sanity-io/litter v1.5.5 // indirect
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 // indirect
- github.com/sasha-s/go-deadlock v0.3.1 // indirect
+ github.com/sasha-s/go-deadlock v0.3.5 // indirect
github.com/scylladb/go-reflectx v1.0.1 // indirect
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect
github.com/sercand/kuberesolver/v5 v5.1.1 // indirect
@@ -410,6 +410,7 @@ require (
github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e // indirect
github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241216163550-fa030d178ba3 // indirect
github.com/smartcontractkit/chainlink-feeds v0.1.1 // indirect
+ github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20241220173418-09e17ddbeb20 // indirect
github.com/smartcontractkit/chainlink-protos/orchestrator v0.4.0 // indirect
github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241223151630-eac4f1508dce // indirect
github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241202202529-2033490e77b8 // indirect
diff --git a/deployment/go.sum b/deployment/go.sum
index a364fc9b1ec..5f3ef4e5776 100644
--- a/deployment/go.sum
+++ b/deployment/go.sum
@@ -1273,9 +1273,8 @@ github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNH
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
-github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o=
-github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 h1:hDSdbBuw3Lefr6R18ax0tZ2BJeNB3NehB3trOwYBsdU=
-github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
+github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 h1:Dx7Ovyv/SFnMFw3fD4oEoeorXc6saIiQ23LrGLth0Gw=
+github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
@@ -1323,8 +1322,8 @@ github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8b
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
github.com/prometheus/common v0.29.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
-github.com/prometheus/common v0.60.0 h1:+V9PAREWNvJMAuJ1x1BaWl9dewMW4YrHZQbx0sJNllA=
-github.com/prometheus/common v0.60.0/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw=
+github.com/prometheus/common v0.60.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc=
+github.com/prometheus/common v0.60.1/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw=
github.com/prometheus/common/sigv4 v0.1.0 h1:qoVebwtwwEhS85Czm2dSROY5fTo2PAPEVdDeppTwGX4=
github.com/prometheus/common/sigv4 v0.1.0/go.mod h1:2Jkxxk9yYvCkE5G1sQT7GuEXm57JrvHu9k5YwTjsNtI=
github.com/prometheus/exporter-toolkit v0.11.0 h1:yNTsuZ0aNCNFQ3aFTD2uhPOvr4iD7fdBvKPAEGkNf+g=
@@ -1380,8 +1379,8 @@ github.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQ
github.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4=
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY=
-github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71efZx0=
-github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM=
+github.com/sasha-s/go-deadlock v0.3.5 h1:tNCOEEDG6tBqrNDOX35j/7hL5FcFViG6awUGROb2NsU=
+github.com/sasha-s/go-deadlock v0.3.5/go.mod h1:bugP6EGbdGYObIlx7pUZtWqlvo8k9H6vCBBsiChJQ5U=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.29 h1:BkTk4gynLjguayxrYxZoMZjBnAOh7ntQvUkOFmkMqPU=
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.29/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg=
@@ -1435,6 +1434,8 @@ github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241216163550-fa030
github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241216163550-fa030d178ba3/go.mod h1:AS6zY2BkcRwfiGzNabGbHhfrLSrXrcI/GmjnT4jQ5/s=
github.com/smartcontractkit/chainlink-feeds v0.1.1 h1:JzvUOM/OgGQA1sOqTXXl52R6AnNt+Wg64sVG+XSA49c=
github.com/smartcontractkit/chainlink-feeds v0.1.1/go.mod h1:55EZ94HlKCfAsUiKUTNI7QlE/3d3IwTlsU3YNa/nBb4=
+github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20241220173418-09e17ddbeb20 h1:p/gzWpEf8alodCXm2Gtx2kWI/O9ZLdWZOdNnv5ZGO6c=
+github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20241220173418-09e17ddbeb20/go.mod h1:4JqpgFy01LaqG1yM2iFTzwX3ZgcAvW9WdstBZQgPHzU=
github.com/smartcontractkit/chainlink-protos/job-distributor v0.6.0 h1:0ewLMbAz3rZrovdRUCgd028yOXX8KigB4FndAUdI2kM=
github.com/smartcontractkit/chainlink-protos/job-distributor v0.6.0/go.mod h1:/dVVLXrsp+V0AbcYGJo3XMzKg3CkELsweA/TTopCsKE=
github.com/smartcontractkit/chainlink-protos/orchestrator v0.4.0 h1:ZBat8EBvE2LpSQR9U1gEbRV6PfAkiFdINmQ8nVnXIAQ=
@@ -1522,8 +1523,9 @@ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1F
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
-github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
+github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
diff --git a/go.md b/go.md
index 9f51ecd4c81..c2d3e050b1e 100644
--- a/go.md
+++ b/go.md
@@ -37,6 +37,8 @@ flowchart LR
click chainlink-data-streams href "https://github.com/smartcontractkit/chainlink-data-streams"
chainlink-feeds --> chainlink-common
click chainlink-feeds href "https://github.com/smartcontractkit/chainlink-feeds"
+ chainlink-framework/multinode --> chainlink-common
+ click chainlink-framework/multinode href "https://github.com/smartcontractkit/chainlink-framework"
chainlink-protos/orchestrator --> wsrpc
click chainlink-protos/orchestrator href "https://github.com/smartcontractkit/chainlink-protos"
chainlink-solana --> chainlink-common
@@ -48,6 +50,7 @@ flowchart LR
chainlink/v2 --> chainlink-cosmos
chainlink/v2 --> chainlink-data-streams
chainlink/v2 --> chainlink-feeds
+ chainlink/v2 --> chainlink-framework/multinode
chainlink/v2 --> chainlink-protos/orchestrator
chainlink/v2 --> chainlink-solana
chainlink/v2 --> chainlink-starknet/relayer
@@ -114,6 +117,8 @@ flowchart LR
click chainlink-data-streams href "https://github.com/smartcontractkit/chainlink-data-streams"
chainlink-feeds --> chainlink-common
click chainlink-feeds href "https://github.com/smartcontractkit/chainlink-feeds"
+ chainlink-framework/multinode --> chainlink-common
+ click chainlink-framework/multinode href "https://github.com/smartcontractkit/chainlink-framework"
chainlink-protos/job-distributor
click chainlink-protos/job-distributor href "https://github.com/smartcontractkit/chainlink-protos"
chainlink-protos/orchestrator --> wsrpc
@@ -150,6 +155,7 @@ flowchart LR
chainlink/v2 --> chainlink-cosmos
chainlink/v2 --> chainlink-data-streams
chainlink/v2 --> chainlink-feeds
+ chainlink/v2 --> chainlink-framework/multinode
chainlink/v2 --> chainlink-protos/orchestrator
chainlink/v2 --> chainlink-solana
chainlink/v2 --> chainlink-starknet/relayer
diff --git a/go.mod b/go.mod
index a0b4bca1a87..c12790b12c7 100644
--- a/go.mod
+++ b/go.mod
@@ -68,7 +68,7 @@ require (
github.com/pressly/goose/v3 v3.21.1
github.com/prometheus/client_golang v1.20.5
github.com/prometheus/client_model v0.6.1
- github.com/prometheus/common v0.60.0
+ github.com/prometheus/common v0.60.1
github.com/prometheus/prometheus v0.54.1
github.com/robfig/cron/v3 v3.0.1
github.com/rogpeppe/go-internal v1.13.1
@@ -83,6 +83,7 @@ require (
github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e
github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241216163550-fa030d178ba3
github.com/smartcontractkit/chainlink-feeds v0.1.1
+ github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20241220173418-09e17ddbeb20
github.com/smartcontractkit/chainlink-protos/orchestrator v0.4.0
github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241223151630-eac4f1508dce
github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241202202529-2033490e77b8
@@ -91,7 +92,7 @@ require (
github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20241009055228-33d0c0bf38de
github.com/smartcontractkit/wsrpc v0.8.2
github.com/spf13/cast v1.6.0
- github.com/stretchr/testify v1.9.0
+ github.com/stretchr/testify v1.10.0
github.com/test-go/testify v1.1.4
github.com/theodesp/go-heaps v0.0.0-20190520121037-88e35354fe0a
github.com/tidwall/gjson v1.17.0
@@ -305,7 +306,7 @@ require (
github.com/opencontainers/image-spec v1.1.0 // indirect
github.com/opencontainers/runc v1.1.10 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
- github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 // indirect
+ github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 // indirect
github.com/pierrec/lz4/v4 v4.1.21 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
@@ -318,7 +319,7 @@ require (
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sanity-io/litter v1.5.5 // indirect
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 // indirect
- github.com/sasha-s/go-deadlock v0.3.1 // indirect
+ github.com/sasha-s/go-deadlock v0.3.5 // indirect
github.com/sethvargo/go-retry v0.2.4 // indirect
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7 // indirect
diff --git a/go.sum b/go.sum
index 49520fd1845..c8778ab3532 100644
--- a/go.sum
+++ b/go.sum
@@ -1021,9 +1021,8 @@ github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCko
github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
-github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o=
-github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 h1:hDSdbBuw3Lefr6R18ax0tZ2BJeNB3NehB3trOwYBsdU=
-github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
+github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 h1:Dx7Ovyv/SFnMFw3fD4oEoeorXc6saIiQ23LrGLth0Gw=
+github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
@@ -1061,8 +1060,8 @@ github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7q
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
-github.com/prometheus/common v0.60.0 h1:+V9PAREWNvJMAuJ1x1BaWl9dewMW4YrHZQbx0sJNllA=
-github.com/prometheus/common v0.60.0/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw=
+github.com/prometheus/common v0.60.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc=
+github.com/prometheus/common v0.60.1/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
@@ -1116,8 +1115,8 @@ github.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQ
github.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4=
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY=
-github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71efZx0=
-github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM=
+github.com/sasha-s/go-deadlock v0.3.5 h1:tNCOEEDG6tBqrNDOX35j/7hL5FcFViG6awUGROb2NsU=
+github.com/sasha-s/go-deadlock v0.3.5/go.mod h1:bugP6EGbdGYObIlx7pUZtWqlvo8k9H6vCBBsiChJQ5U=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/scylladb/go-reflectx v1.0.1 h1:b917wZM7189pZdlND9PbIJ6NQxfDPfBvUaQ7cjj1iZQ=
github.com/scylladb/go-reflectx v1.0.1/go.mod h1:rWnOfDIRWBGN0miMLIcoPt/Dhi2doCMZqwMCJ3KupFc=
@@ -1159,6 +1158,8 @@ github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241216163550-fa030
github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241216163550-fa030d178ba3/go.mod h1:AS6zY2BkcRwfiGzNabGbHhfrLSrXrcI/GmjnT4jQ5/s=
github.com/smartcontractkit/chainlink-feeds v0.1.1 h1:JzvUOM/OgGQA1sOqTXXl52R6AnNt+Wg64sVG+XSA49c=
github.com/smartcontractkit/chainlink-feeds v0.1.1/go.mod h1:55EZ94HlKCfAsUiKUTNI7QlE/3d3IwTlsU3YNa/nBb4=
+github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20241220173418-09e17ddbeb20 h1:p/gzWpEf8alodCXm2Gtx2kWI/O9ZLdWZOdNnv5ZGO6c=
+github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20241220173418-09e17ddbeb20/go.mod h1:4JqpgFy01LaqG1yM2iFTzwX3ZgcAvW9WdstBZQgPHzU=
github.com/smartcontractkit/chainlink-protos/orchestrator v0.4.0 h1:ZBat8EBvE2LpSQR9U1gEbRV6PfAkiFdINmQ8nVnXIAQ=
github.com/smartcontractkit/chainlink-protos/orchestrator v0.4.0/go.mod h1:m/A3lqD7ms/RsQ9BT5P2uceYY0QX5mIt4KQxT2G6qEo=
github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241223151630-eac4f1508dce h1:Mvpbr/Fi2IdU2EcmqCxhlCzs5ncnx+BwV80YweA4DZs=
@@ -1232,8 +1233,9 @@ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1F
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
-github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
+github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
diff --git a/integration-tests/go.mod b/integration-tests/go.mod
index 1c63ac6738e..e19f4dbe60d 100644
--- a/integration-tests/go.mod
+++ b/integration-tests/go.mod
@@ -38,7 +38,7 @@ require (
github.com/onsi/gomega v1.34.2
github.com/pelletier/go-toml/v2 v2.2.3
github.com/pkg/errors v0.9.1
- github.com/prometheus/common v0.60.0
+ github.com/prometheus/common v0.60.1
github.com/rs/zerolog v1.33.0
github.com/scylladb/go-reflectx v1.0.1
github.com/segmentio/ksuid v1.0.4
@@ -56,7 +56,7 @@ require (
github.com/smartcontractkit/chainlink-testing-framework/wasp v1.50.2
github.com/smartcontractkit/libocr v0.0.0-20241223215956-e5b78d8e3919
github.com/spf13/cobra v1.8.1
- github.com/stretchr/testify v1.9.0
+ github.com/stretchr/testify v1.10.0
github.com/subosito/gotenv v1.6.0
github.com/test-go/testify v1.1.4
github.com/testcontainers/testcontainers-go v0.34.0
@@ -394,7 +394,7 @@ require (
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
- github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 // indirect
+ github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
@@ -415,7 +415,7 @@ require (
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sanity-io/litter v1.5.5 // indirect
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 // indirect
- github.com/sasha-s/go-deadlock v0.3.1 // indirect
+ github.com/sasha-s/go-deadlock v0.3.5 // indirect
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect
github.com/sercand/kuberesolver/v5 v5.1.1 // indirect
github.com/sethvargo/go-retry v0.2.4 // indirect
@@ -427,6 +427,7 @@ require (
github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e // indirect
github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241216163550-fa030d178ba3 // indirect
github.com/smartcontractkit/chainlink-feeds v0.1.1 // indirect
+ github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20241220173418-09e17ddbeb20 // indirect
github.com/smartcontractkit/chainlink-protos/orchestrator v0.4.0 // indirect
github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241223151630-eac4f1508dce // indirect
github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241202202529-2033490e77b8 // indirect
diff --git a/integration-tests/go.sum b/integration-tests/go.sum
index 1baff05f3f1..6c71b224f8e 100644
--- a/integration-tests/go.sum
+++ b/integration-tests/go.sum
@@ -1290,9 +1290,8 @@ github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNH
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
-github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o=
-github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 h1:hDSdbBuw3Lefr6R18ax0tZ2BJeNB3NehB3trOwYBsdU=
-github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
+github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 h1:Dx7Ovyv/SFnMFw3fD4oEoeorXc6saIiQ23LrGLth0Gw=
+github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
@@ -1340,8 +1339,8 @@ github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8b
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
github.com/prometheus/common v0.29.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
-github.com/prometheus/common v0.60.0 h1:+V9PAREWNvJMAuJ1x1BaWl9dewMW4YrHZQbx0sJNllA=
-github.com/prometheus/common v0.60.0/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw=
+github.com/prometheus/common v0.60.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc=
+github.com/prometheus/common v0.60.1/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw=
github.com/prometheus/common/sigv4 v0.1.0 h1:qoVebwtwwEhS85Czm2dSROY5fTo2PAPEVdDeppTwGX4=
github.com/prometheus/common/sigv4 v0.1.0/go.mod h1:2Jkxxk9yYvCkE5G1sQT7GuEXm57JrvHu9k5YwTjsNtI=
github.com/prometheus/exporter-toolkit v0.11.0 h1:yNTsuZ0aNCNFQ3aFTD2uhPOvr4iD7fdBvKPAEGkNf+g=
@@ -1397,8 +1396,8 @@ github.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQ
github.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4=
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY=
-github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71efZx0=
-github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM=
+github.com/sasha-s/go-deadlock v0.3.5 h1:tNCOEEDG6tBqrNDOX35j/7hL5FcFViG6awUGROb2NsU=
+github.com/sasha-s/go-deadlock v0.3.5/go.mod h1:bugP6EGbdGYObIlx7pUZtWqlvo8k9H6vCBBsiChJQ5U=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.29 h1:BkTk4gynLjguayxrYxZoMZjBnAOh7ntQvUkOFmkMqPU=
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.29/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg=
@@ -1456,6 +1455,8 @@ github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241216163550-fa030
github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241216163550-fa030d178ba3/go.mod h1:AS6zY2BkcRwfiGzNabGbHhfrLSrXrcI/GmjnT4jQ5/s=
github.com/smartcontractkit/chainlink-feeds v0.1.1 h1:JzvUOM/OgGQA1sOqTXXl52R6AnNt+Wg64sVG+XSA49c=
github.com/smartcontractkit/chainlink-feeds v0.1.1/go.mod h1:55EZ94HlKCfAsUiKUTNI7QlE/3d3IwTlsU3YNa/nBb4=
+github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20241220173418-09e17ddbeb20 h1:p/gzWpEf8alodCXm2Gtx2kWI/O9ZLdWZOdNnv5ZGO6c=
+github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20241220173418-09e17ddbeb20/go.mod h1:4JqpgFy01LaqG1yM2iFTzwX3ZgcAvW9WdstBZQgPHzU=
github.com/smartcontractkit/chainlink-protos/job-distributor v0.6.0 h1:0ewLMbAz3rZrovdRUCgd028yOXX8KigB4FndAUdI2kM=
github.com/smartcontractkit/chainlink-protos/job-distributor v0.6.0/go.mod h1:/dVVLXrsp+V0AbcYGJo3XMzKg3CkELsweA/TTopCsKE=
github.com/smartcontractkit/chainlink-protos/orchestrator v0.4.0 h1:ZBat8EBvE2LpSQR9U1gEbRV6PfAkiFdINmQ8nVnXIAQ=
@@ -1545,8 +1546,9 @@ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1F
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
-github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
+github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
diff --git a/integration-tests/load/go.mod b/integration-tests/load/go.mod
index 04f9b07f93b..5c6be37328c 100644
--- a/integration-tests/load/go.mod
+++ b/integration-tests/load/go.mod
@@ -32,7 +32,7 @@ require (
github.com/smartcontractkit/chainlink-testing-framework/seth v1.50.9
github.com/smartcontractkit/chainlink-testing-framework/wasp v1.50.2
github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20241009055228-33d0c0bf38de
- github.com/stretchr/testify v1.9.0
+ github.com/stretchr/testify v1.10.0
github.com/wiremock/go-wiremock v1.9.0
go.uber.org/ratelimit v0.3.1
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c
@@ -371,14 +371,14 @@ require (
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
- github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 // indirect
+ github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/prometheus/alertmanager v0.27.0 // indirect
github.com/prometheus/client_golang v1.20.5 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
- github.com/prometheus/common v0.60.0 // indirect
+ github.com/prometheus/common v0.60.1 // indirect
github.com/prometheus/common/sigv4 v0.1.0 // indirect
github.com/prometheus/exporter-toolkit v0.11.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
@@ -393,7 +393,7 @@ require (
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sanity-io/litter v1.5.5 // indirect
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 // indirect
- github.com/sasha-s/go-deadlock v0.3.1 // indirect
+ github.com/sasha-s/go-deadlock v0.3.5 // indirect
github.com/scylladb/go-reflectx v1.0.1 // indirect
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect
github.com/segmentio/ksuid v1.0.4 // indirect
@@ -411,6 +411,7 @@ require (
github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e // indirect
github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241216163550-fa030d178ba3 // indirect
github.com/smartcontractkit/chainlink-feeds v0.1.1 // indirect
+ github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20241220173418-09e17ddbeb20 // indirect
github.com/smartcontractkit/chainlink-protos/orchestrator v0.4.0 // indirect
github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241223151630-eac4f1508dce // indirect
github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241202202529-2033490e77b8 // indirect
diff --git a/integration-tests/load/go.sum b/integration-tests/load/go.sum
index 48bdf1cb5b1..75a961d80a8 100644
--- a/integration-tests/load/go.sum
+++ b/integration-tests/load/go.sum
@@ -1280,9 +1280,8 @@ github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNH
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
-github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o=
-github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 h1:hDSdbBuw3Lefr6R18ax0tZ2BJeNB3NehB3trOwYBsdU=
-github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
+github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 h1:Dx7Ovyv/SFnMFw3fD4oEoeorXc6saIiQ23LrGLth0Gw=
+github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
@@ -1330,8 +1329,8 @@ github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8b
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
github.com/prometheus/common v0.29.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
-github.com/prometheus/common v0.60.0 h1:+V9PAREWNvJMAuJ1x1BaWl9dewMW4YrHZQbx0sJNllA=
-github.com/prometheus/common v0.60.0/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw=
+github.com/prometheus/common v0.60.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc=
+github.com/prometheus/common v0.60.1/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw=
github.com/prometheus/common/sigv4 v0.1.0 h1:qoVebwtwwEhS85Czm2dSROY5fTo2PAPEVdDeppTwGX4=
github.com/prometheus/common/sigv4 v0.1.0/go.mod h1:2Jkxxk9yYvCkE5G1sQT7GuEXm57JrvHu9k5YwTjsNtI=
github.com/prometheus/exporter-toolkit v0.11.0 h1:yNTsuZ0aNCNFQ3aFTD2uhPOvr4iD7fdBvKPAEGkNf+g=
@@ -1387,8 +1386,8 @@ github.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQ
github.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4=
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY=
-github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71efZx0=
-github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM=
+github.com/sasha-s/go-deadlock v0.3.5 h1:tNCOEEDG6tBqrNDOX35j/7hL5FcFViG6awUGROb2NsU=
+github.com/sasha-s/go-deadlock v0.3.5/go.mod h1:bugP6EGbdGYObIlx7pUZtWqlvo8k9H6vCBBsiChJQ5U=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.29 h1:BkTk4gynLjguayxrYxZoMZjBnAOh7ntQvUkOFmkMqPU=
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.29/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg=
@@ -1447,6 +1446,8 @@ github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241216163550-fa030
github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241216163550-fa030d178ba3/go.mod h1:AS6zY2BkcRwfiGzNabGbHhfrLSrXrcI/GmjnT4jQ5/s=
github.com/smartcontractkit/chainlink-feeds v0.1.1 h1:JzvUOM/OgGQA1sOqTXXl52R6AnNt+Wg64sVG+XSA49c=
github.com/smartcontractkit/chainlink-feeds v0.1.1/go.mod h1:55EZ94HlKCfAsUiKUTNI7QlE/3d3IwTlsU3YNa/nBb4=
+github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20241220173418-09e17ddbeb20 h1:p/gzWpEf8alodCXm2Gtx2kWI/O9ZLdWZOdNnv5ZGO6c=
+github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20241220173418-09e17ddbeb20/go.mod h1:4JqpgFy01LaqG1yM2iFTzwX3ZgcAvW9WdstBZQgPHzU=
github.com/smartcontractkit/chainlink-protos/job-distributor v0.6.0 h1:0ewLMbAz3rZrovdRUCgd028yOXX8KigB4FndAUdI2kM=
github.com/smartcontractkit/chainlink-protos/job-distributor v0.6.0/go.mod h1:/dVVLXrsp+V0AbcYGJo3XMzKg3CkELsweA/TTopCsKE=
github.com/smartcontractkit/chainlink-protos/orchestrator v0.4.0 h1:ZBat8EBvE2LpSQR9U1gEbRV6PfAkiFdINmQ8nVnXIAQ=
@@ -1536,8 +1537,9 @@ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1F
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
-github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
+github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
From 5f5e552d587223bd817f7d0f62a6a2eda53a3079 Mon Sep 17 00:00:00 2001
From: dimitris
Date: Thu, 9 Jan 2025 12:05:47 +0200
Subject: [PATCH 17/91] improve peer group dialer sync function logs (#15867)
---
.../capabilities/ccip/oraclecreator/bootstrap.go | 16 +++++++++++++---
1 file changed, 13 insertions(+), 3 deletions(-)
diff --git a/core/capabilities/ccip/oraclecreator/bootstrap.go b/core/capabilities/ccip/oraclecreator/bootstrap.go
index 8dfe3e99ffb..40b3727640d 100644
--- a/core/capabilities/ccip/oraclecreator/bootstrap.go
+++ b/core/capabilities/ccip/oraclecreator/bootstrap.go
@@ -12,6 +12,7 @@ import (
"time"
mapset "github.com/deckarep/golang-set/v2"
+ logger2 "github.com/smartcontractkit/chainlink-common/pkg/logger"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/smartcontractkit/libocr/networking"
@@ -373,13 +374,22 @@ func (d *peerGroupDialer) sync() {
defer d.mu.Unlock()
activeDigest, candidateDigest := d.rmnHomeReader.GetAllConfigDigests()
+
+ lggr := logger2.With(
+ d.lggr,
+ "method", "sync",
+ "activeDigest", activeDigest,
+ "candidateDigest", candidateDigest,
+ "activeConfigDigests", d.activeConfigDigests,
+ )
+
actions := calculateSyncActions(d.activeConfigDigests, activeDigest, candidateDigest)
if len(actions) == 0 {
- d.lggr.Debugw("No peer group actions needed")
+ lggr.Debugw("No peer group actions needed")
return
}
- d.lggr.Infow("Syncing peer groups", "actions", actions)
+ lggr.Infof("Syncing peer groups by applying the actions: %v", actions)
// Handle each action
for _, action := range actions {
@@ -388,7 +398,7 @@ func (d *peerGroupDialer) sync() {
d.closePeerGroup(action.configDigest)
case ActionCreate:
if err := d.createPeerGroup(action.configDigest); err != nil {
- d.lggr.Errorw("Failed to create peer group",
+ lggr.Errorw("Failed to create peer group",
"configDigest", action.configDigest,
"err", err)
// Consider closing all groups on error
From 8270318279ab992328288973f2b831aa9440f6b1 Mon Sep 17 00:00:00 2001
From: Yashvardhan Nevatia
Date: Thu, 9 Jan 2025 10:09:11 +0000
Subject: [PATCH 18/91] Solana devnet spin up in memory env (A) (#15831)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* Adding solchains in NewEnv
* Revert "Adding solchains in NewEnv"
This reverts commit aaab52e01412ea4f1de4f748cadf0c127682418c.
* adding sol chains to newenv
* newEnv needs to send nil
* adding test env setup
* adding nil for crib sol chains
* adding chain selectors commit
* go mod tidy
* linting
* chain sel update
* update core/scripts go files
* again
* add changeset
* go imports
* go mod tidy
* Update modgraph
---------
Co-authored-by: Terry Tata
Co-authored-by: Blaž Hrastnik
---
.changeset/cuddly-turtles-arrive.md | 5 +
core/scripts/go.mod | 24 ++--
core/scripts/go.sum | 125 +++----------------
deployment/environment.go | 13 ++
deployment/environment/crib/types.go | 1 +
deployment/environment/devenv/environment.go | 1 +
deployment/environment/memory/chain.go | 41 +++++-
deployment/environment/memory/environment.go | 35 ++++++
deployment/go.mod | 23 ++--
deployment/go.sum | 118 +++--------------
deployment/solana_chain.go | 38 ++++++
go.md | 11 +-
integration-tests/go.mod | 23 ++--
integration-tests/go.sum | 117 +++--------------
integration-tests/load/go.mod | 22 ++--
integration-tests/load/go.sum | 117 +++--------------
16 files changed, 262 insertions(+), 452 deletions(-)
create mode 100644 .changeset/cuddly-turtles-arrive.md
diff --git a/.changeset/cuddly-turtles-arrive.md b/.changeset/cuddly-turtles-arrive.md
new file mode 100644
index 00000000000..81ceed3e8ff
--- /dev/null
+++ b/.changeset/cuddly-turtles-arrive.md
@@ -0,0 +1,5 @@
+---
+"chainlink": patch
+---
+
+#internal adding solana devnet to ccip deployment
diff --git a/core/scripts/go.mod b/core/scripts/go.mod
index 91924b9b681..e016559d6cf 100644
--- a/core/scripts/go.mod
+++ b/core/scripts/go.mod
@@ -48,7 +48,6 @@ require (
)
require (
- contrib.go.opencensus.io/exporter/stackdriver v0.13.5 // indirect
cosmossdk.io/api v0.3.1 // indirect
cosmossdk.io/core v0.5.1 // indirect
cosmossdk.io/depinject v1.0.0-alpha.4 // indirect
@@ -118,8 +117,7 @@ require (
github.com/danielkov/gin-helmet v0.0.0-20171108135313-1387e224435e // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/deckarep/golang-set/v2 v2.6.0 // indirect
- github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
- github.com/dfuse-io/logging v0.0.0-20210109005628-b97a57253f70 // indirect
+ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
github.com/dgraph-io/badger/v2 v2.2007.4 // indirect
github.com/dgraph-io/ristretto v0.1.1 // indirect
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect
@@ -138,8 +136,8 @@ require (
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
- github.com/gagliardetto/binary v0.7.7 // indirect
- github.com/gagliardetto/solana-go v1.8.4 // indirect
+ github.com/gagliardetto/binary v0.8.0 // indirect
+ github.com/gagliardetto/solana-go v1.12.0 // indirect
github.com/gagliardetto/treeout v0.1.4 // indirect
github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect
github.com/gedex/inflector v0.0.0-20170307190818-16278e9db813 // indirect
@@ -178,7 +176,6 @@ require (
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
github.com/golang/glog v1.2.2 // indirect
- github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect
github.com/google/btree v1.1.2 // indirect
@@ -302,8 +299,9 @@ require (
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
github.com/shirou/gopsutil/v3 v3.24.3 // indirect
github.com/smartcontractkit/ccip-owner-contracts v0.0.0-salt-fix // indirect
- github.com/smartcontractkit/chain-selectors v1.0.34 // indirect
+ github.com/smartcontractkit/chain-selectors v1.0.36 // indirect
github.com/smartcontractkit/chainlink-ccip v0.0.0-20241218114855-f74219171000 // indirect
+ github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250103152858-8973fd0c912b // indirect
github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e // indirect
github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241216163550-fa030d178ba3 // indirect
github.com/smartcontractkit/chainlink-feeds v0.1.1 // indirect
@@ -322,13 +320,12 @@ require (
github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/status-im/keycard-go v0.2.0 // indirect
- github.com/streamingfast/logging v0.0.0-20220405224725-2755dab2ce75 // indirect
+ github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/supranational/blst v0.3.13 // indirect
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect
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/theodesp/go-heaps v0.0.0-20190520121037-88e35354fe0a // indirect
github.com/tidwall/btree v1.6.0 // indirect
@@ -355,7 +352,6 @@ require (
go.dedis.ch/kyber/v3 v3.1.0 // indirect
go.etcd.io/bbolt v1.3.9 // indirect
go.mongodb.org/mongo-driver v1.15.0 // indirect
- go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.49.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 // indirect
@@ -382,16 +378,16 @@ require (
go.uber.org/zap v1.27.0 // indirect
golang.org/x/arch v0.11.0 // indirect
golang.org/x/crypto v0.31.0 // indirect
- golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect
- golang.org/x/mod v0.21.0 // indirect
- golang.org/x/net v0.30.0 // indirect
+ golang.org/x/exp v0.0.0-20241210194714-1829a127f884 // indirect
+ golang.org/x/mod v0.22.0 // indirect
+ golang.org/x/net v0.32.0 // indirect
golang.org/x/oauth2 v0.23.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/term v0.27.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/time v0.7.0 // indirect
- golang.org/x/tools v0.26.0 // indirect
+ golang.org/x/tools v0.28.0 // indirect
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect
gonum.org/v1/gonum v0.15.1 // indirect
google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38 // indirect
diff --git a/core/scripts/go.sum b/core/scripts/go.sum
index b39eed9a91b..b716caa9ec3 100644
--- a/core/scripts/go.sum
+++ b/core/scripts/go.sum
@@ -3,7 +3,6 @@ cel.dev/expr v0.17.0/go.mod h1:HCwbrn+qQoHPXgfz6cl2J0hDybnX2N1sQQkl9EggXx8=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
-cloud.google.com/go v0.43.0/go.mod h1:BOSR3VbTLkk6FDC/TcffxP4NF/FFBGA5ku+jvKOP7pg=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
@@ -54,10 +53,6 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.45.0 h1:5av0QcIVj77t+44mV4gffFC/LscFRUhto6UBMB5SimM=
cloud.google.com/go/storage v1.45.0/go.mod h1:wpPblkIuMP5jCB/E48Pz9zIo2S/zD8g+ITmxKkPCITE=
-contrib.go.opencensus.io/exporter/stackdriver v0.12.6/go.mod h1:8x999/OcIPy5ivx/wDiV7Gx4D+VUPODf0mWRGRc5kSk=
-contrib.go.opencensus.io/exporter/stackdriver v0.13.4/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc=
-contrib.go.opencensus.io/exporter/stackdriver v0.13.5 h1:TNaexHK16gPUoc7uzELKOU7JULqccn1NDuqUxmxSqfo=
-contrib.go.opencensus.io/exporter/stackdriver v0.13.5/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc=
cosmossdk.io/api v0.3.1 h1:NNiOclKRR0AOlO4KIqeaG6PS6kswOMhHD0ir0SscNXE=
cosmossdk.io/api v0.3.1/go.mod h1:DfHfMkiNA2Uhy8fj0JJlOCYOBp4eWUUJ1te5zBGNyIw=
cosmossdk.io/core v0.5.1 h1:vQVtFrIYOQJDV3f7rw4pjjVqc1id4+mE0L9hHP66pyI=
@@ -73,7 +68,6 @@ cosmossdk.io/math v1.3.0/go.mod h1:vnRTxewy+M7BtXBNFybkuhSH4WfedVAAnERHgVFhp3k=
cosmossdk.io/tools/rosetta v0.2.1 h1:ddOMatOH+pbxWbrGJKRAawdBkPYLfKXutK9IETnjYxw=
cosmossdk.io/tools/rosetta v0.2.1/go.mod h1:Pqdc1FdvkNV3LcNIkYWt2RQY6IP1ge6YWZk8MhhO9Hw=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
-filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs=
@@ -100,8 +94,6 @@ github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8=
github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
github.com/Depado/ginprom v1.8.0 h1:zaaibRLNI1dMiiuj1MKzatm8qrcHzikMlCc1anqOdyo=
github.com/Depado/ginprom v1.8.0/go.mod h1:XBaKzeNBqPF4vxJpNLincSQZeMDnZp1tIbU0FU0UKgg=
-github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0=
-github.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtixis4Tib9if0=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.24.3 h1:cb3br57K508pQEFgBxn9GDhPS9HefpyMPK1RzmtMNzk=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.24.3/go.mod h1:itPGVDKf9cC/ov4MdvJ2QZ0khw4bfoo9jzwTJlaxy2k=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.3 h1:xir5X8TS8UBVPWg2jHL+cSTf0jZgqYQSA54TscSt1/0=
@@ -128,7 +120,6 @@ github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrd
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
github.com/XSAM/otelsql v0.27.0 h1:i9xtxtdcqXV768a5C6SoT/RkG+ue3JTOgkYInzlTOqs=
github.com/XSAM/otelsql v0.27.0/go.mod h1:0mFB3TvLa7NCuhm/2nU7/b2wEtsczkj8Rey8ygO7V+A=
-github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
@@ -138,7 +129,6 @@ github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74/go.mod h1:cEWa1L
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
github.com/allegro/bigcache v1.2.1 h1:hg1sY1raCwic3Vnsvje6TT7/pnZba83LeFck5NrFKSc=
github.com/allegro/bigcache v1.2.1/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
-github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129/go.mod h1:rFgpPQZYZ8vdbc+48xibu8ALc3yeyd64IhHS+PU6Yyg=
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
@@ -156,8 +146,6 @@ github.com/atombender/go-jsonschema v0.16.1-0.20240916205339-a74cd4e2851c h1:cxQ
github.com/atombender/go-jsonschema v0.16.1-0.20240916205339-a74cd4e2851c/go.mod h1:3XzxudkrYVUvbduN/uI2fl4lSrMSzU0+3RCu2mpnfx8=
github.com/avast/retry-go/v4 v4.6.0 h1:K9xNA+KeB8HHc2aWFuLb25Offp+0iVRXEvFx8IinRJA=
github.com/avast/retry-go/v4 v4.6.0/go.mod h1:gvWlPhBVsvBbLkVGDg/KwvBv0bEkCOLRRSHKIr2PyOE=
-github.com/aws/aws-sdk-go v1.22.1/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
-github.com/aws/aws-sdk-go v1.23.20/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.54.19 h1:tyWV+07jagrNiCcGRzRhdtVjQs7Vy41NwsuOcl0IbVI=
github.com/aws/aws-sdk-go v1.54.19/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59 h1:WWB576BN5zNSZc/M9d/10pqEx5VHNhaQ/yOVAkmj5Yo=
@@ -178,7 +166,6 @@ github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 h1:41iFGWnSlI2
github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE=
github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
-github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
github.com/blendle/zapdriver v1.3.1 h1:C3dydBOWYRiOk+B8X9IVZ5IOe+7cl+tGOexN4QqHfpE=
github.com/blendle/zapdriver v1.3.1/go.mod h1:mdXfREi6u5MArG4j9fewC+FGnXaBR+T4Ox4J2u4eHCc=
@@ -267,16 +254,13 @@ github.com/containerd/continuity v0.4.3 h1:6HVkalIp+2u1ZLH1J/pYX2oBVXlJZvh1X1A7b
github.com/containerd/continuity v0.4.3/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ=
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
-github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
-github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
-github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cosmos/btcutil v1.0.5 h1:t+ZFcX77LpKtDBhjucvnOH8C2l2ioGsBNEQ3jef8xFk=
github.com/cosmos/btcutil v1.0.5/go.mod h1:IyB7iuqZMJlthe2tkIFL33xPyzbFYP0XVdS8P5lUPis=
github.com/cosmos/cosmos-proto v1.0.0-beta.5 h1:eNcayDLpip+zVLRLYafhzLvQlSmyab+RC5W7ZfmxJLA=
@@ -314,7 +298,6 @@ github.com/creachadair/taskgroup v0.4.2 h1:jsBLdAJE42asreGss2xZGZ8fJra7WtwnHWeJF
github.com/creachadair/taskgroup v0.4.2/go.mod h1:qiXUOSrbwAY3u0JPGTzObbE3yf9hcXHDKBZ2ZjpCbgM=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
-github.com/daaku/go.zipexe v1.0.0/go.mod h1:z8IiR6TsVLEYKwXAoE/I+8ys/sDkgTzSL0CLnGVd57E=
github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0=
github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0=
github.com/danielkov/gin-helmet v0.0.0-20171108135313-1387e224435e h1:5jVSh2l/ho6ajWhSPNN84eHEdq3dp0T7+f6r3Tc6hsk=
@@ -328,23 +311,18 @@ github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80N
github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y=
github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
-github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs=
-github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
+github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg=
+github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I=
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE=
-github.com/dfuse-io/logging v0.0.0-20201110202154-26697de88c79/go.mod h1:V+ED4kT/t/lKtH99JQmKIb0v9WL3VaYkJ36CfHlVECI=
-github.com/dfuse-io/logging v0.0.0-20210109005628-b97a57253f70 h1:CuJS05R9jmNlUK8GOxrEELPbfXm0EuGh/30LjkjN5vo=
-github.com/dfuse-io/logging v0.0.0-20210109005628-b97a57253f70/go.mod h1:EoK/8RFbMEteaCaz89uessDTnCWjbbcr+DXcBh4el5o=
github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o=
github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk=
github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8=
github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA=
-github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y=
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
-github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
@@ -384,7 +362,6 @@ github.com/ethereum/go-ethereum v1.14.11/go.mod h1:+l/fr42Mma+xBnhefL/+z11/hcmJ2
github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9 h1:8NfxH2iXvJ60YRB8ChToFTUzl8awsc3cJ8CbLjGIl/A=
github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
-github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
@@ -403,12 +380,12 @@ github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
-github.com/gagliardetto/binary v0.7.7 h1:QZpT38+sgoPg+TIQjH94sLbl/vX+nlIRA37pEyOsjfY=
-github.com/gagliardetto/binary v0.7.7/go.mod h1:mUuay5LL8wFVnIlecHakSZMvcdqfs+CsotR5n77kyjM=
+github.com/gagliardetto/binary v0.8.0 h1:U9ahc45v9HW0d15LoN++vIXSJyqR/pWw8DDlhd7zvxg=
+github.com/gagliardetto/binary v0.8.0/go.mod h1:2tfj51g5o9dnvsc+fL3Jxr22MuWzYXwx9wEoN0XQ7/c=
github.com/gagliardetto/gofuzz v1.2.2 h1:XL/8qDMzcgvR4+CyRQW9UGdwPRPMHVJfqQ/uMvSUuQw=
github.com/gagliardetto/gofuzz v1.2.2/go.mod h1:bkH/3hYLZrMLbfYWA0pWzXmi5TTRZnu4pMGZBkqMKvY=
-github.com/gagliardetto/solana-go v1.8.4 h1:vmD/JmTlonyXGy39bAo0inMhmbdAwV7rXZtLDMZeodE=
-github.com/gagliardetto/solana-go v1.8.4/go.mod h1:i+7aAyNDTHG0jK8GZIBSI4OVvDqkt2Qx+LklYclRNG8=
+github.com/gagliardetto/solana-go v1.12.0 h1:rzsbilDPj6p+/DOPXBMLhwMZeBgeRuXjm5zQFCoXgsg=
+github.com/gagliardetto/solana-go v1.12.0/go.mod h1:l/qqqIN6qJJPtxW/G1PF4JtcE3Zg2vD2EliZrr9Gn5k=
github.com/gagliardetto/treeout v0.1.4 h1:ozeYerrLCmCubo1TcIjFiOWTTGteOOHND1twdFpgwaw=
github.com/gagliardetto/treeout v0.1.4/go.mod h1:loUefvXTrlRG5rYmJmExNryyBRh8f89VZhmMOyCyqok=
github.com/gagliardetto/utilz v0.1.1 h1:/etW4hl607emKg6R6Lj9jRJ9d6ue2AQOyjhuAwjzs1U=
@@ -526,7 +503,6 @@ github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVI
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v1.2.2 h1:1+mZ9upx1Dh6FmUTFR1naJ77miKiXgALjWOZ3NVFPmY=
github.com/golang/glog v1.2.2/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
-github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -562,7 +538,6 @@ github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
-github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk=
@@ -638,12 +613,10 @@ github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
-github.com/gorilla/rpc v1.2.0/go.mod h1:V4h9r+4sF5HnzqbwIez0fKSpANP0zlYd3qR7p36jkTQ=
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY=
github.com/gorilla/sessions v1.2.2/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ=
-github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
github.com/grafana/pyroscope-go v1.1.2 h1:7vCfdORYQMCxIzI3NlYAs3FcBP760+gWuYWOyiVyYx8=
@@ -654,15 +627,12 @@ github.com/graph-gophers/dataloader v5.0.0+incompatible h1:R+yjsbrNq1Mo3aPG+Z/EK
github.com/graph-gophers/dataloader v5.0.0+incompatible/go.mod h1:jk4jk0c5ZISbKaMe8WsVopGB5/15GvGHMdMdPtwlRp4=
github.com/graph-gophers/graphql-go v1.5.0 h1:fDqblo50TEpD0LY7RXk/LFVYEVqo3+tXMNMPSVXA1yc=
github.com/graph-gophers/graphql-go v1.5.0/go.mod h1:YtmJZDLbF1YYNrlNAuiO5zAStUWc3XZT07iGsVqe1Os=
-github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw=
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA=
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU=
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 h1:pRhl55Yx1eC7BZ1N+BBWwnKaMyD8uC+34TLdndZMAKk=
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0/go.mod h1:XKMd7iuf/RGPSMJ/U4HP0zS2Z9Fh8Ps9a+6X26m/tmI=
-github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
-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.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys=
@@ -717,7 +687,6 @@ github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
-github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/golang-lru v0.6.0 h1:uL2shRDx7RTrOrTCUZEGP/wJUFiUI8QT6E7z5o8jga4=
github.com/hashicorp/golang-lru v0.6.0/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
@@ -807,10 +776,8 @@ github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0f
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
-github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c=
github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo=
-github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
@@ -821,7 +788,6 @@ github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
-github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4=
github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
@@ -841,7 +807,6 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.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.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
@@ -883,7 +848,6 @@ github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczG
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
-github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
@@ -899,7 +863,6 @@ github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo=
github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
-github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
@@ -908,8 +871,6 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
-github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
-github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
@@ -964,7 +925,6 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
-github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE=
github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
@@ -983,7 +943,6 @@ github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJm
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
-github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229/go.mod h1:0aYXnNPJ8l7uZxf45rWW1a/uME32OF0rhiYGNQ2oF2E=
github.com/nsf/jsondiff v0.0.0-20230430225905-43f6cf3098c1 h1:dOYG7LS/WK00RWZc8XGgcUTlTxpp3mKhdR2Q9z9HbXM=
github.com/nsf/jsondiff v0.0.0-20230430225905-43f6cf3098c1/go.mod h1:mpRZBD8SJ55OIICQ3iWH0Yz3cjzA61JdqMLoWXeB2+8=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
@@ -991,7 +950,6 @@ github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
-github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
@@ -1055,7 +1013,6 @@ github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:Om
github.com/pressly/goose/v3 v3.21.1 h1:5SSAKKWej8LVVzNLuT6KIvP1eFDuPvxa+B6H0w78buQ=
github.com/pressly/goose/v3 v3.21.1/go.mod h1:sqthmzV8PitchEkjecFJII//l43dLOCzfWh8pHEe+vE=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
-github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
@@ -1066,21 +1023,17 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
-github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
-github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
github.com/prometheus/common v0.60.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc=
github.com/prometheus/common v0.60.1/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
-github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/prometheus/prometheus v0.54.1 h1:vKuwQNjnYN2/mDoWfHXDhAsz/68q/dQDb+YbcEqU7MQ=
github.com/prometheus/prometheus v0.54.1/go.mod h1:xlLByHhk2g3ycakQGrMaU8K7OySZx98BzeCR99991NY=
-github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rakyll/statik v0.1.7 h1:OF3QCZUuyPxuGEP7B4ypUa7sB/iHtqOTDYZXGM8KOdQ=
github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Unghqrcc=
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM=
@@ -1094,7 +1047,6 @@ github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
-github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
@@ -1155,12 +1107,14 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/smartcontractkit/ccip-owner-contracts v0.0.0-salt-fix h1:DPJD++yKLSx0EfT+U14P8vLVxjXFmoIETiCO9lVwQo8=
github.com/smartcontractkit/ccip-owner-contracts v0.0.0-salt-fix/go.mod h1:NnT6w4Kj42OFFXhSx99LvJZWPpMjmo4+CpDEWfw61xY=
-github.com/smartcontractkit/chain-selectors v1.0.34 h1:MJ17OGu8+jjl426pcKrJkCf3fePb3eCreuAnUA3RBj4=
-github.com/smartcontractkit/chain-selectors v1.0.34/go.mod h1:xsKM0aN3YGcQKTPRPDDtPx2l4mlTN1Djmg0VVXV40b8=
+github.com/smartcontractkit/chain-selectors v1.0.36 h1:KSpO8I+JOiuyN4FuXsV471sPorGF//PAqwq2Cm4gRK0=
+github.com/smartcontractkit/chain-selectors v1.0.36/go.mod h1:xsKM0aN3YGcQKTPRPDDtPx2l4mlTN1Djmg0VVXV40b8=
github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgBc2xpDKBco/Q4h4ydl6+UUU=
github.com/smartcontractkit/chainlink-automation v0.8.1/go.mod h1:Iij36PvWZ6blrdC5A/nrQUBuf3MH3JvsBB9sSyc9W08=
github.com/smartcontractkit/chainlink-ccip v0.0.0-20241218114855-f74219171000 h1:6Zzr/R1j6P7bbvcUlt5WUIbItvrrGdGzIsiAzQezcwo=
github.com/smartcontractkit/chainlink-ccip v0.0.0-20241218114855-f74219171000/go.mod h1:ncjd6mPZSRlelEqH/2KeLE1pU3UlqzBSn8RYkEoECzY=
+github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250103152858-8973fd0c912b h1:UBXi9Yj8YSMHDDaxQLu273x1fWjyEL9xP58nuJsqZfg=
+github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250103152858-8973fd0c912b/go.mod h1:Bmwq4lNb5tE47sydN0TKetcLEGbgl+VxHEWp4S0LI60=
github.com/smartcontractkit/chainlink-common v0.4.1-0.20241223143929-db7919d60550 h1:rRs74zjDJ7do5aYEXSU/sOnLnlbYCNqM8BrvEx/0NH8=
github.com/smartcontractkit/chainlink-common v0.4.1-0.20241223143929-db7919d60550/go.mod h1:yti7e1+G9hhkYhj+L5sVUULn9Bn3bBL5/AxaNqdJ5YQ=
github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e h1:PRoeby6ZlTuTkv2f+7tVU4+zboTfRzI+beECynF4JQ0=
@@ -1195,7 +1149,6 @@ github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60=
-github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
@@ -1210,7 +1163,6 @@ github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
-github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
@@ -1220,15 +1172,13 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
-github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
-github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA=
github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg=
-github.com/streamingfast/logging v0.0.0-20220405224725-2755dab2ce75 h1:ZqpS7rAhhKD7S7DnrpEdrnW1/gZcv82ytpMviovkli4=
-github.com/streamingfast/logging v0.0.0-20220405224725-2755dab2ce75/go.mod h1:VlduQ80JcGJSargkRU4Sg9Xo63wZD/l8A5NC/Uo1/uU=
+github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 h1:RN5mrigyirb8anBEtdjtHFIufXdacyTi6i4KBfeNXeo=
+github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091/go.mod h1:VlduQ80JcGJSargkRU4Sg9Xo63wZD/l8A5NC/Uo1/uU=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
@@ -1260,22 +1210,17 @@ github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDd
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48=
github.com/tendermint/go-amino v0.16.0 h1:GyhmgQKvqF82e2oZeuMSp9JTN0N09emoSZlb2lyGa2E=
github.com/tendermint/go-amino v0.16.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME=
-github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf/go.mod h1:M8agBzgqHIhgj7wEn9/0hJUZcrvt9VY+Ln+S1I5Mha0=
-github.com/teris-io/shortid v0.0.0-20201117134242-e59966efd125 h1:3SNcvBmEPE1YlB1JpVZouslJpI3GBNoiqW7+wb0Rz7w=
-github.com/teris-io/shortid v0.0.0-20201117134242-e59966efd125/go.mod h1:M8agBzgqHIhgj7wEn9/0hJUZcrvt9VY+Ln+S1I5Mha0=
github.com/test-go/testify v1.1.4 h1:Tf9lntrKUMHiXQ07qBScBTSA0dhYQlu83hswqelv1iE=
github.com/test-go/testify v1.1.4/go.mod h1:rH7cfJo/47vWGdi4GPj16x3/t1xGOj2YxzmNQzk2ghU=
github.com/theodesp/go-heaps v0.0.0-20190520121037-88e35354fe0a h1:YuO+afVc3eqrjiCUizNCxI53bl/BnPiVwXqLzqYTqgU=
github.com/theodesp/go-heaps v0.0.0-20190520121037-88e35354fe0a/go.mod h1:/sfW47zCZp9FrtGcWyo1VjbgDaodxX9ovZvgLb/MxaA=
github.com/tidwall/btree v1.6.0 h1:LDZfKfQIBHGHWSwckhXI0RPSXzlo+KYdjK7FWSqOzzg=
github.com/tidwall/btree v1.6.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY=
-github.com/tidwall/gjson v1.9.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM=
github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
-github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
@@ -1285,7 +1230,6 @@ github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFA
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
-github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
@@ -1311,24 +1255,17 @@ github.com/urfave/cli v1.22.14 h1:ebbhrRiGK2i4naQJr+1Xj92HXZCrK7MsyTS/ob3HnAk=
github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA=
github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w=
github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
-github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fastjson v1.4.1 h1:hrltpHpIpkaxll8QltMU8c3QZ5+qIiCL8yKqPFJI/yE=
github.com/valyala/fastjson v1.4.1/go.mod h1:nV6MsjxL2IMJQUoHDIrjEI7oLyeqK6aBD7EFWPsvP8o=
-github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
-github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
-github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=
-github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=
-github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
-github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@@ -1353,18 +1290,15 @@ go.dedis.ch/kyber/v3 v3.1.0/go.mod h1:kXy7p3STAurkADD+/aZcsznZGKVHEqbtmdIzvPfrs1
go.dedis.ch/protobuf v1.0.5/go.mod h1:eIV4wicvi6JK0q/QnfIEGeSFNG0ZeB24kzut5+HaRLo=
go.dedis.ch/protobuf v1.0.7/go.mod h1:pv5ysfkDX/EawiPqcW3ikOxsL5t+BqnV6xHSmE79KI4=
go.dedis.ch/protobuf v1.0.11/go.mod h1:97QR256dnkimeNdfmURz0wAMNVbd1VmLXhG1CrTYrJ4=
-go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI=
go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE=
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
-go.mongodb.org/mongo-driver v1.11.0/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8=
go.mongodb.org/mongo-driver v1.15.0 h1:rJCKC8eEliewXjZGf0ddURtl7tTVy1TK3bfl0gkUSLc=
go.mongodb.org/mongo-driver v1.15.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
-go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
@@ -1436,15 +1370,12 @@ go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKY
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
-go.uber.org/ratelimit v0.2.0/go.mod h1:YYBV4e4naJvhpitQrWJu1vCpgB7CboMe0qhltKt6mUg=
go.uber.org/ratelimit v0.3.1 h1:K4qVE+byfv/B3tC+4nYWP7v/6SimcO7HzHekoMNBma0=
go.uber.org/ratelimit v0.3.1/go.mod h1:6euWsTB6U/Nb3X++xEUXA8ciPJvr19Q/0h1+oDcJhRk=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
-go.uber.org/zap v1.14.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
-go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
@@ -1471,7 +1402,6 @@ golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-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.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
@@ -1485,8 +1415,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-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
-golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
+golang.org/x/exp v0.0.0-20241210194714-1829a127f884 h1:Y/Mj/94zIQQGHVSv1tTtQBDaQaJe62U9bkDZKKyhPCU=
+golang.org/x/exp v0.0.0-20241210194714-1829a127f884/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
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=
@@ -1515,15 +1445,14 @@ 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.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
-golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
-golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
+golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
+golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@@ -1536,7 +1465,6 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -1569,8 +1497,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
-golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
-golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
+golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI=
+golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs=
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=
@@ -1605,7 +1533,6 @@ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190124100055-b90733256f2e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1621,7 +1548,6 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1686,7 +1612,6 @@ golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.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=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
@@ -1731,7 +1656,6 @@ golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgw
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@@ -1741,7 +1665,6 @@ golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
@@ -1777,8 +1700,8 @@ 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.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
-golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
-golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
+golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8=
+golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw=
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=
@@ -1794,7 +1717,6 @@ google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEt
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
-google.golang.org/api v0.10.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
@@ -1819,7 +1741,6 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
-google.golang.org/appengine v1.6.2/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
@@ -1829,7 +1750,6 @@ google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRn
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190716160619-c506a9f90610/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
@@ -1878,9 +1798,7 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38/go.
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=
-google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
-google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
@@ -1934,16 +1852,13 @@ gopkg.in/guregu/null.v4 v4.0.0/go.mod h1:YoQhUrADuG3i9WqesrCmpNRwm1ypAgSHYqoOcTu
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
-gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
-gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
-gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
diff --git a/deployment/environment.go b/deployment/environment.go
index 6fc28fac764..a37622dc3ac 100644
--- a/deployment/environment.go
+++ b/deployment/environment.go
@@ -111,6 +111,7 @@ func NewEnvironment(
logger logger.Logger,
existingAddrs AddressBook,
chains map[uint64]Chain,
+ solChains map[uint64]SolChain,
nodeIDs []string,
offchain OffchainClient,
ctx func() context.Context,
@@ -121,6 +122,7 @@ func NewEnvironment(
Logger: logger,
ExistingAddresses: existingAddrs,
Chains: chains,
+ SolChains: solChains,
NodeIDs: nodeIDs,
Offchain: offchain,
GetContext: ctx,
@@ -159,6 +161,17 @@ func (e Environment) AllChainSelectorsExcluding(excluding []uint64) []uint64 {
return selectors
}
+func (e Environment) AllChainSelectorsSolana() []uint64 {
+ selectors := make([]uint64, 0, len(e.SolChains))
+ for sel := range e.SolChains {
+ selectors = append(selectors, sel)
+ }
+ sort.Slice(selectors, func(i, j int) bool {
+ return selectors[i] < selectors[j]
+ })
+ return selectors
+}
+
func (e Environment) AllDeployerKeys() []common.Address {
var deployerKeys []common.Address
for sel := range e.Chains {
diff --git a/deployment/environment/crib/types.go b/deployment/environment/crib/types.go
index 99baf8e8774..771880053f9 100644
--- a/deployment/environment/crib/types.go
+++ b/deployment/environment/crib/types.go
@@ -33,6 +33,7 @@ func NewDeployEnvironmentFromCribOutput(lggr logger.Logger, output DeployOutput)
lggr,
output.AddressBook,
chains,
+ nil, // nil for solana chains, can use memory solana chain example when required
output.NodeIDs,
nil, // todo: populate the offchain client using output.DON
func() context.Context { return context.Background() }, deployment.XXXGenerateTestOCRSecrets(),
diff --git a/deployment/environment/devenv/environment.go b/deployment/environment/devenv/environment.go
index 2fffe6adf2b..b6b5198f8fb 100644
--- a/deployment/environment/devenv/environment.go
+++ b/deployment/environment/devenv/environment.go
@@ -50,6 +50,7 @@ func NewEnvironment(ctx func() context.Context, lggr logger.Logger, config Envir
lggr,
deployment.NewMemoryAddressBook(),
chains,
+ nil, // sending nil for solana chains right now, we can build this when we need it
nodeIDs,
offChain,
ctx,
diff --git a/deployment/environment/memory/chain.go b/deployment/environment/memory/chain.go
index 77a8f397d39..cc22b40d844 100644
--- a/deployment/environment/memory/chain.go
+++ b/deployment/environment/memory/chain.go
@@ -9,12 +9,16 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient/simulated"
+ "github.com/gagliardetto/solana-go"
+ solRpc "github.com/gagliardetto/solana-go/rpc"
+
"github.com/stretchr/testify/require"
+ solTestUtil "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/testutils"
+
chainsel "github.com/smartcontractkit/chain-selectors"
"github.com/smartcontractkit/chainlink-common/pkg/utils/tests"
-
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets"
)
@@ -24,6 +28,11 @@ type EVMChain struct {
Users []*bind.TransactOpts
}
+type SolanaChain struct {
+ Client *solRpc.Client
+ DeployerKey *solana.PrivateKey
+}
+
func fundAddress(t *testing.T, from *bind.TransactOpts, to common.Address, amount *big.Int, backend *simulated.Backend) {
ctx := tests.Context(t)
nonce, err := backend.Client().PendingNonceAt(ctx, from.From)
@@ -53,6 +62,36 @@ func GenerateChains(t *testing.T, numChains int, numUsers int) map[uint64]EVMCha
return chains
}
+func getTestSolanaChainSelectors() []uint64 {
+ result := []uint64{}
+ for _, x := range chainsel.SolanaALL {
+ if x.Name == x.ChainID {
+ result = append(result, x.Selector)
+ }
+ }
+ return result
+}
+
+func GenerateChainsSol(t *testing.T, numChains int) map[uint64]SolanaChain {
+ chains := make(map[uint64]SolanaChain)
+ testSolanaChainSelectors := getTestSolanaChainSelectors()
+ if len(testSolanaChainSelectors) < numChains {
+ t.Fatalf("not enough test solana chain selectors available")
+ }
+
+ for i := 0; i < numChains; i++ {
+ chainID := testSolanaChainSelectors[i]
+ url, _ := solTestUtil.SetupLocalSolNodeWithFlags(t)
+ admin, gerr := solana.NewRandomPrivateKey()
+ require.NoError(t, gerr)
+ chains[chainID] = SolanaChain{
+ Client: solRpc.New(url),
+ DeployerKey: &admin,
+ }
+ }
+ return chains
+}
+
func GenerateChainsWithIds(t *testing.T, chainIDs []uint64, numUsers int) map[uint64]EVMChain {
chains := make(map[uint64]EVMChain)
for _, chainID := range chainIDs {
diff --git a/deployment/environment/memory/environment.go b/deployment/environment/memory/environment.go
index a74d23a847b..3c5fdc6e779 100644
--- a/deployment/environment/memory/environment.go
+++ b/deployment/environment/memory/environment.go
@@ -9,6 +9,7 @@ import (
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/core/types"
+ "github.com/gagliardetto/solana-go"
"github.com/hashicorp/consul/sdk/freeport"
"github.com/stretchr/testify/require"
"go.uber.org/zap/zapcore"
@@ -19,6 +20,9 @@ import (
"github.com/smartcontractkit/chainlink/deployment"
+ solRpc "github.com/gagliardetto/solana-go/rpc"
+
+ solCommomUtil "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/common"
"github.com/smartcontractkit/chainlink-common/pkg/logger"
)
@@ -28,6 +32,7 @@ const (
type MemoryEnvironmentConfig struct {
Chains int
+ SolChains int
NumOfUsersPerChain int
Nodes int
Bootstraps int
@@ -59,6 +64,11 @@ func NewMemoryChains(t *testing.T, numChains int, numUsers int) (map[uint64]depl
return generateMemoryChain(t, mchains), users
}
+func NewMemoryChainsSol(t *testing.T, numChains int) map[uint64]deployment.SolChain {
+ mchains := GenerateChainsSol(t, numChains)
+ return generateMemoryChainSol(t, mchains)
+}
+
func NewMemoryChainsWithChainIDs(t *testing.T, chainIDs []uint64, numUsers int) (map[uint64]deployment.Chain, map[uint64][]*bind.TransactOpts) {
mchains := GenerateChainsWithIds(t, chainIDs, numUsers)
users := make(map[uint64][]*bind.TransactOpts)
@@ -111,6 +121,28 @@ func generateMemoryChain(t *testing.T, inputs map[uint64]EVMChain) map[uint64]de
return chains
}
+func generateMemoryChainSol(t *testing.T, inputs map[uint64]SolanaChain) map[uint64]deployment.SolChain {
+ chains := make(map[uint64]deployment.SolChain)
+ for cid, chain := range inputs {
+ chain := chain
+ chains[cid] = deployment.SolChain{
+ Selector: cid,
+ Client: chain.Client,
+ DeployerKey: chain.DeployerKey,
+ Confirm: func(instructions []solana.Instruction, opts ...solCommomUtil.TxModifier) error {
+ _, err := solCommomUtil.SendAndConfirm(
+ context.Background(), chain.Client, instructions, *chain.DeployerKey, solRpc.CommitmentConfirmed, opts...,
+ )
+ if err != nil {
+ return err
+ }
+ return nil
+ },
+ }
+ }
+ return chains
+}
+
func NewNodes(t *testing.T, logLevel zapcore.Level, chains map[uint64]deployment.Chain, numNodes, numBootstraps int, registryConfig deployment.CapabilityRegistryConfig) map[string]Node {
nodesByPeerID := make(map[string]Node)
if numNodes+numBootstraps == 0 {
@@ -149,6 +181,7 @@ func NewMemoryEnvironmentFromChainsNodes(
lggr,
deployment.NewMemoryAddressBook(),
chains,
+ nil,
nodeIDs, // Note these have the p2p_ prefix.
NewMemoryJobClient(nodes),
ctx,
@@ -159,6 +192,7 @@ func NewMemoryEnvironmentFromChainsNodes(
// To be used by tests and any kind of deployment logic.
func NewMemoryEnvironment(t *testing.T, lggr logger.Logger, logLevel zapcore.Level, config MemoryEnvironmentConfig) deployment.Environment {
chains, _ := NewMemoryChains(t, config.Chains, config.NumOfUsersPerChain)
+ solChains := NewMemoryChainsSol(t, config.SolChains)
nodes := NewNodes(t, logLevel, chains, config.Nodes, config.Bootstraps, config.RegistryConfig)
var nodeIDs []string
for id := range nodes {
@@ -169,6 +203,7 @@ func NewMemoryEnvironment(t *testing.T, lggr logger.Logger, logLevel zapcore.Lev
lggr,
deployment.NewMemoryAddressBook(),
chains,
+ solChains,
nodeIDs,
NewMemoryJobClient(nodes),
func() context.Context { return tests.Context(t) },
diff --git a/deployment/go.mod b/deployment/go.mod
index 5261299679f..cbec5d95744 100644
--- a/deployment/go.mod
+++ b/deployment/go.mod
@@ -18,6 +18,7 @@ require (
github.com/aws/aws-sdk-go v1.54.19
github.com/deckarep/golang-set/v2 v2.6.0
github.com/ethereum/go-ethereum v1.14.11
+ github.com/gagliardetto/solana-go v1.12.0
github.com/go-resty/resty/v2 v2.15.3
github.com/google/uuid v1.6.0
github.com/hashicorp/consul/sdk v0.16.1
@@ -27,8 +28,9 @@ require (
github.com/rs/zerolog v1.33.0
github.com/sethvargo/go-retry v0.2.4
github.com/smartcontractkit/ccip-owner-contracts v0.0.0-salt-fix
- github.com/smartcontractkit/chain-selectors v1.0.34
+ github.com/smartcontractkit/chain-selectors v1.0.36
github.com/smartcontractkit/chainlink-ccip v0.0.0-20241218114855-f74219171000
+ github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250103152858-8973fd0c912b
github.com/smartcontractkit/chainlink-common v0.4.1-0.20241223143929-db7919d60550
github.com/smartcontractkit/chainlink-protos/job-distributor v0.6.0
github.com/smartcontractkit/chainlink-testing-framework/lib v1.50.13
@@ -38,7 +40,7 @@ require (
github.com/testcontainers/testcontainers-go v0.34.0
go.uber.org/multierr v1.11.0
go.uber.org/zap v1.27.0
- golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c
+ golang.org/x/exp v0.0.0-20241210194714-1829a127f884
golang.org/x/oauth2 v0.23.0
golang.org/x/sync v0.10.0
google.golang.org/grpc v1.67.1
@@ -48,7 +50,6 @@ require (
)
require (
- contrib.go.opencensus.io/exporter/stackdriver v0.13.5 // indirect
cosmossdk.io/api v0.3.1 // indirect
cosmossdk.io/core v0.5.1 // indirect
cosmossdk.io/depinject v1.0.0-alpha.4 // indirect
@@ -160,9 +161,8 @@ require (
github.com/crate-crypto/go-kzg-4844 v1.0.0 // indirect
github.com/danieljoos/wincred v1.1.2 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
- github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
+ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
github.com/dennwc/varint v1.0.0 // indirect
- github.com/dfuse-io/logging v0.0.0-20210109005628-b97a57253f70 // indirect
github.com/dgraph-io/badger/v2 v2.2007.4 // indirect
github.com/dgraph-io/ristretto v0.1.1 // indirect
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect
@@ -189,8 +189,7 @@ require (
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
- github.com/gagliardetto/binary v0.7.7 // indirect
- github.com/gagliardetto/solana-go v1.8.4 // indirect
+ github.com/gagliardetto/binary v0.8.0 // indirect
github.com/gagliardetto/treeout v0.1.4 // indirect
github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect
github.com/getsentry/sentry-go v0.27.0 // indirect
@@ -430,13 +429,12 @@ require (
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.19.0 // indirect
github.com/status-im/keycard-go v0.2.0 // indirect
- github.com/streamingfast/logging v0.0.0-20220405224725-2755dab2ce75 // indirect
+ github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/supranational/blst v0.3.13 // indirect
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect
github.com/tendermint/go-amino v0.16.0 // indirect
- github.com/teris-io/shortid v0.0.0-20201117134242-e59966efd125 // 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
@@ -465,7 +463,6 @@ require (
go.etcd.io/etcd/client/pkg/v3 v3.5.14 // indirect
go.etcd.io/etcd/client/v3 v3.5.14 // indirect
go.mongodb.org/mongo-driver v1.15.0 // indirect
- go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/collector/pdata v1.12.0 // indirect
go.opentelemetry.io/collector/semconv v0.105.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0 // indirect
@@ -495,13 +492,13 @@ require (
go4.org/netipx v0.0.0-20230125063823-8449b0a6169f // indirect
golang.org/x/arch v0.11.0 // indirect
golang.org/x/crypto v0.31.0 // indirect
- golang.org/x/mod v0.21.0 // indirect
- golang.org/x/net v0.30.0 // indirect
+ golang.org/x/mod v0.22.0 // indirect
+ golang.org/x/net v0.32.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/term v0.27.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/time v0.7.0 // indirect
- golang.org/x/tools v0.26.0 // indirect
+ golang.org/x/tools v0.28.0 // indirect
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
gonum.org/v1/gonum v0.15.1 // indirect
diff --git a/deployment/go.sum b/deployment/go.sum
index 5f3ef4e5776..e58794f2e97 100644
--- a/deployment/go.sum
+++ b/deployment/go.sum
@@ -3,7 +3,6 @@ cel.dev/expr v0.17.0/go.mod h1:HCwbrn+qQoHPXgfz6cl2J0hDybnX2N1sQQkl9EggXx8=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
-cloud.google.com/go v0.43.0/go.mod h1:BOSR3VbTLkk6FDC/TcffxP4NF/FFBGA5ku+jvKOP7pg=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
@@ -54,10 +53,6 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.45.0 h1:5av0QcIVj77t+44mV4gffFC/LscFRUhto6UBMB5SimM=
cloud.google.com/go/storage v1.45.0/go.mod h1:wpPblkIuMP5jCB/E48Pz9zIo2S/zD8g+ITmxKkPCITE=
-contrib.go.opencensus.io/exporter/stackdriver v0.12.6/go.mod h1:8x999/OcIPy5ivx/wDiV7Gx4D+VUPODf0mWRGRc5kSk=
-contrib.go.opencensus.io/exporter/stackdriver v0.13.4/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc=
-contrib.go.opencensus.io/exporter/stackdriver v0.13.5 h1:TNaexHK16gPUoc7uzELKOU7JULqccn1NDuqUxmxSqfo=
-contrib.go.opencensus.io/exporter/stackdriver v0.13.5/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc=
cosmossdk.io/api v0.3.1 h1:NNiOclKRR0AOlO4KIqeaG6PS6kswOMhHD0ir0SscNXE=
cosmossdk.io/api v0.3.1/go.mod h1:DfHfMkiNA2Uhy8fj0JJlOCYOBp4eWUUJ1te5zBGNyIw=
cosmossdk.io/core v0.5.1 h1:vQVtFrIYOQJDV3f7rw4pjjVqc1id4+mE0L9hHP66pyI=
@@ -75,7 +70,6 @@ cosmossdk.io/tools/rosetta v0.2.1/go.mod h1:Pqdc1FdvkNV3LcNIkYWt2RQY6IP1ge6YWZk8
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
-filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs=
@@ -118,8 +112,6 @@ github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8=
github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
github.com/Depado/ginprom v1.8.0 h1:zaaibRLNI1dMiiuj1MKzatm8qrcHzikMlCc1anqOdyo=
github.com/Depado/ginprom v1.8.0/go.mod h1:XBaKzeNBqPF4vxJpNLincSQZeMDnZp1tIbU0FU0UKgg=
-github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0=
-github.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtixis4Tib9if0=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.24.3 h1:cb3br57K508pQEFgBxn9GDhPS9HefpyMPK1RzmtMNzk=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.24.3/go.mod h1:itPGVDKf9cC/ov4MdvJ2QZ0khw4bfoo9jzwTJlaxy2k=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.3 h1:xir5X8TS8UBVPWg2jHL+cSTf0jZgqYQSA54TscSt1/0=
@@ -159,7 +151,6 @@ github.com/XSAM/otelsql v0.27.0 h1:i9xtxtdcqXV768a5C6SoT/RkG+ue3JTOgkYInzlTOqs=
github.com/XSAM/otelsql v0.27.0/go.mod h1:0mFB3TvLa7NCuhm/2nU7/b2wEtsczkj8Rey8ygO7V+A=
github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8=
github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo=
-github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
@@ -181,7 +172,6 @@ github.com/alicebob/miniredis/v2 v2.30.4/go.mod h1:b25qWj4fCEsBeAAR2mlb0ufImGC6u
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
github.com/allegro/bigcache v1.2.1 h1:hg1sY1raCwic3Vnsvje6TT7/pnZba83LeFck5NrFKSc=
github.com/allegro/bigcache v1.2.1/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
-github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129/go.mod h1:rFgpPQZYZ8vdbc+48xibu8ALc3yeyd64IhHS+PU6Yyg=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
@@ -210,8 +200,6 @@ github.com/avast/retry-go/v4 v4.6.0 h1:K9xNA+KeB8HHc2aWFuLb25Offp+0iVRXEvFx8IinR
github.com/avast/retry-go/v4 v4.6.0/go.mod h1:gvWlPhBVsvBbLkVGDg/KwvBv0bEkCOLRRSHKIr2PyOE=
github.com/awalterschulze/gographviz v2.0.3+incompatible h1:9sVEXJBJLwGX7EQVhLm2elIKCm7P2YHFC8v6096G09E=
github.com/awalterschulze/gographviz v2.0.3+incompatible/go.mod h1:GEV5wmg4YquNw7v1kkyoX9etIk8yVmXj+AkDHuuETHs=
-github.com/aws/aws-sdk-go v1.22.1/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
-github.com/aws/aws-sdk-go v1.23.20/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
github.com/aws/aws-sdk-go v1.54.19 h1:tyWV+07jagrNiCcGRzRhdtVjQs7Vy41NwsuOcl0IbVI=
github.com/aws/aws-sdk-go v1.54.19/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
@@ -269,7 +257,6 @@ github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 h1:41iFGWnSlI2
github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE=
github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
-github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
@@ -376,9 +363,7 @@ github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A=
github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw=
-github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
-github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
@@ -389,7 +374,6 @@ github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
-github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cosmos/btcutil v1.0.5 h1:t+ZFcX77LpKtDBhjucvnOH8C2l2ioGsBNEQ3jef8xFk=
github.com/cosmos/btcutil v1.0.5/go.mod h1:IyB7iuqZMJlthe2tkIFL33xPyzbFYP0XVdS8P5lUPis=
github.com/cosmos/cosmos-proto v1.0.0-beta.5 h1:eNcayDLpip+zVLRLYafhzLvQlSmyab+RC5W7ZfmxJLA=
@@ -430,7 +414,6 @@ github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7Do
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
-github.com/daaku/go.zipexe v1.0.0/go.mod h1:z8IiR6TsVLEYKwXAoE/I+8ys/sDkgTzSL0CLnGVd57E=
github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0=
github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0=
github.com/danielkov/gin-helmet v0.0.0-20171108135313-1387e224435e h1:5jVSh2l/ho6ajWhSPNN84eHEdq3dp0T7+f6r3Tc6hsk=
@@ -444,27 +427,22 @@ github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80N
github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y=
github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
-github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs=
-github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
+github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg=
+github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
github.com/dennwc/varint v1.0.0 h1:kGNFFSSw8ToIy3obO/kKr8U9GZYUAxQEVuix4zfDWzE=
github.com/dennwc/varint v1.0.0/go.mod h1:hnItb35rvZvJrbTALZtY/iQfDs48JKRG1RPpgziApxA=
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I=
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE=
-github.com/dfuse-io/logging v0.0.0-20201110202154-26697de88c79/go.mod h1:V+ED4kT/t/lKtH99JQmKIb0v9WL3VaYkJ36CfHlVECI=
-github.com/dfuse-io/logging v0.0.0-20210109005628-b97a57253f70 h1:CuJS05R9jmNlUK8GOxrEELPbfXm0EuGh/30LjkjN5vo=
-github.com/dfuse-io/logging v0.0.0-20210109005628-b97a57253f70/go.mod h1:EoK/8RFbMEteaCaz89uessDTnCWjbbcr+DXcBh4el5o=
github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o=
github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk=
github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8=
github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA=
-github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y=
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
-github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+UbP35JkH8yB7MYb4q/qhBarqZE6g=
github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
github.com/digitalocean/godo v1.118.0 h1:lkzGFQmACrVCp7UqH1sAi4JK/PWwlc5aaxubgorKmC4=
@@ -539,12 +517,12 @@ github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
-github.com/gagliardetto/binary v0.7.7 h1:QZpT38+sgoPg+TIQjH94sLbl/vX+nlIRA37pEyOsjfY=
-github.com/gagliardetto/binary v0.7.7/go.mod h1:mUuay5LL8wFVnIlecHakSZMvcdqfs+CsotR5n77kyjM=
+github.com/gagliardetto/binary v0.8.0 h1:U9ahc45v9HW0d15LoN++vIXSJyqR/pWw8DDlhd7zvxg=
+github.com/gagliardetto/binary v0.8.0/go.mod h1:2tfj51g5o9dnvsc+fL3Jxr22MuWzYXwx9wEoN0XQ7/c=
github.com/gagliardetto/gofuzz v1.2.2 h1:XL/8qDMzcgvR4+CyRQW9UGdwPRPMHVJfqQ/uMvSUuQw=
github.com/gagliardetto/gofuzz v1.2.2/go.mod h1:bkH/3hYLZrMLbfYWA0pWzXmi5TTRZnu4pMGZBkqMKvY=
-github.com/gagliardetto/solana-go v1.8.4 h1:vmD/JmTlonyXGy39bAo0inMhmbdAwV7rXZtLDMZeodE=
-github.com/gagliardetto/solana-go v1.8.4/go.mod h1:i+7aAyNDTHG0jK8GZIBSI4OVvDqkt2Qx+LklYclRNG8=
+github.com/gagliardetto/solana-go v1.12.0 h1:rzsbilDPj6p+/DOPXBMLhwMZeBgeRuXjm5zQFCoXgsg=
+github.com/gagliardetto/solana-go v1.12.0/go.mod h1:l/qqqIN6qJJPtxW/G1PF4JtcE3Zg2vD2EliZrr9Gn5k=
github.com/gagliardetto/treeout v0.1.4 h1:ozeYerrLCmCubo1TcIjFiOWTTGteOOHND1twdFpgwaw=
github.com/gagliardetto/treeout v0.1.4/go.mod h1:loUefvXTrlRG5rYmJmExNryyBRh8f89VZhmMOyCyqok=
github.com/gagliardetto/utilz v0.1.1 h1:/etW4hl607emKg6R6Lj9jRJ9d6ue2AQOyjhuAwjzs1U=
@@ -670,7 +648,6 @@ github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVI
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v1.2.2 h1:1+mZ9upx1Dh6FmUTFR1naJ77miKiXgALjWOZ3NVFPmY=
github.com/golang/glog v1.2.2/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
-github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -706,7 +683,6 @@ github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
-github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk=
@@ -790,12 +766,10 @@ github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
-github.com/gorilla/rpc v1.2.0/go.mod h1:V4h9r+4sF5HnzqbwIez0fKSpANP0zlYd3qR7p36jkTQ=
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY=
github.com/gorilla/sessions v1.2.2/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ=
-github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
github.com/grafana/dskit v0.0.0-20231120170505-765e343eda4f h1:gyojr97YeWZ70pKNakWv5/tKwBHuLy3icnIeCo9gQr4=
@@ -820,15 +794,12 @@ github.com/graph-gophers/graphql-go v1.5.0 h1:fDqblo50TEpD0LY7RXk/LFVYEVqo3+tXMN
github.com/graph-gophers/graphql-go v1.5.0/go.mod h1:YtmJZDLbF1YYNrlNAuiO5zAStUWc3XZT07iGsVqe1Os=
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA=
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
-github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw=
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA=
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU=
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 h1:pRhl55Yx1eC7BZ1N+BBWwnKaMyD8uC+34TLdndZMAKk=
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0/go.mod h1:XKMd7iuf/RGPSMJ/U4HP0zS2Z9Fh8Ps9a+6X26m/tmI=
-github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
-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.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys=
@@ -901,7 +872,6 @@ github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
-github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/golang-lru v0.6.0 h1:uL2shRDx7RTrOrTCUZEGP/wJUFiUI8QT6E7z5o8jga4=
github.com/hashicorp/golang-lru v0.6.0/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
@@ -1004,10 +974,8 @@ github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0f
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
-github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c=
github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo=
-github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
@@ -1018,7 +986,6 @@ github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
-github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4=
github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
@@ -1043,7 +1010,6 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.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.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
@@ -1093,7 +1059,6 @@ github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/z
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
-github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
@@ -1194,7 +1159,6 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0=
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4=
-github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE=
github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
@@ -1215,7 +1179,6 @@ github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
-github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229/go.mod h1:0aYXnNPJ8l7uZxf45rWW1a/uME32OF0rhiYGNQ2oF2E=
github.com/nsf/jsondiff v0.0.0-20230430225905-43f6cf3098c1 h1:dOYG7LS/WK00RWZc8XGgcUTlTxpp3mKhdR2Q9z9HbXM=
github.com/nsf/jsondiff v0.0.0-20230430225905-43f6cf3098c1/go.mod h1:mpRZBD8SJ55OIICQ3iWH0Yz3cjzA61JdqMLoWXeB2+8=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
@@ -1302,7 +1265,6 @@ github.com/pressly/goose/v3 v3.21.1/go.mod h1:sqthmzV8PitchEkjecFJII//l43dLOCzfW
github.com/prometheus/alertmanager v0.27.0 h1:V6nTa2J5V4s8TG4C4HtrBP/WNSebCCTYGGv4qecA/+I=
github.com/prometheus/alertmanager v0.27.0/go.mod h1:8Ia/R3urPmbzJ8OsdvmZvIprDwvwmYCmUbwBL+jlPOE=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
-github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
@@ -1315,8 +1277,6 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
-github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
-github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
@@ -1329,7 +1289,6 @@ github.com/prometheus/common/sigv4 v0.1.0/go.mod h1:2Jkxxk9yYvCkE5G1sQT7GuEXm57J
github.com/prometheus/exporter-toolkit v0.11.0 h1:yNTsuZ0aNCNFQ3aFTD2uhPOvr4iD7fdBvKPAEGkNf+g=
github.com/prometheus/exporter-toolkit v0.11.0/go.mod h1:BVnENhnNecpwoTLiABx7mrPB/OLRIgN74qlQbV+FK1Q=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
-github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
@@ -1338,7 +1297,6 @@ github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0leargg
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/prometheus/prometheus v0.54.1 h1:vKuwQNjnYN2/mDoWfHXDhAsz/68q/dQDb+YbcEqU7MQ=
github.com/prometheus/prometheus v0.54.1/go.mod h1:xlLByHhk2g3ycakQGrMaU8K7OySZx98BzeCR99991NY=
-github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rakyll/statik v0.1.7 h1:OF3QCZUuyPxuGEP7B4ypUa7sB/iHtqOTDYZXGM8KOdQ=
github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Unghqrcc=
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM=
@@ -1350,7 +1308,6 @@ github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
-github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
@@ -1420,12 +1377,14 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/smartcontractkit/ccip-owner-contracts v0.0.0-salt-fix h1:DPJD++yKLSx0EfT+U14P8vLVxjXFmoIETiCO9lVwQo8=
github.com/smartcontractkit/ccip-owner-contracts v0.0.0-salt-fix/go.mod h1:NnT6w4Kj42OFFXhSx99LvJZWPpMjmo4+CpDEWfw61xY=
-github.com/smartcontractkit/chain-selectors v1.0.34 h1:MJ17OGu8+jjl426pcKrJkCf3fePb3eCreuAnUA3RBj4=
-github.com/smartcontractkit/chain-selectors v1.0.34/go.mod h1:xsKM0aN3YGcQKTPRPDDtPx2l4mlTN1Djmg0VVXV40b8=
+github.com/smartcontractkit/chain-selectors v1.0.36 h1:KSpO8I+JOiuyN4FuXsV471sPorGF//PAqwq2Cm4gRK0=
+github.com/smartcontractkit/chain-selectors v1.0.36/go.mod h1:xsKM0aN3YGcQKTPRPDDtPx2l4mlTN1Djmg0VVXV40b8=
github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgBc2xpDKBco/Q4h4ydl6+UUU=
github.com/smartcontractkit/chainlink-automation v0.8.1/go.mod h1:Iij36PvWZ6blrdC5A/nrQUBuf3MH3JvsBB9sSyc9W08=
github.com/smartcontractkit/chainlink-ccip v0.0.0-20241218114855-f74219171000 h1:6Zzr/R1j6P7bbvcUlt5WUIbItvrrGdGzIsiAzQezcwo=
github.com/smartcontractkit/chainlink-ccip v0.0.0-20241218114855-f74219171000/go.mod h1:ncjd6mPZSRlelEqH/2KeLE1pU3UlqzBSn8RYkEoECzY=
+github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250103152858-8973fd0c912b h1:UBXi9Yj8YSMHDDaxQLu273x1fWjyEL9xP58nuJsqZfg=
+github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250103152858-8973fd0c912b/go.mod h1:Bmwq4lNb5tE47sydN0TKetcLEGbgl+VxHEWp4S0LI60=
github.com/smartcontractkit/chainlink-common v0.4.1-0.20241223143929-db7919d60550 h1:rRs74zjDJ7do5aYEXSU/sOnLnlbYCNqM8BrvEx/0NH8=
github.com/smartcontractkit/chainlink-common v0.4.1-0.20241223143929-db7919d60550/go.mod h1:yti7e1+G9hhkYhj+L5sVUULn9Bn3bBL5/AxaNqdJ5YQ=
github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e h1:PRoeby6ZlTuTkv2f+7tVU4+zboTfRzI+beECynF4JQ0=
@@ -1466,7 +1425,6 @@ github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60=
-github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js=
github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
github.com/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg=
@@ -1485,7 +1443,6 @@ github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
-github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
@@ -1495,15 +1452,13 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
-github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
-github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA=
github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg=
-github.com/streamingfast/logging v0.0.0-20220405224725-2755dab2ce75 h1:ZqpS7rAhhKD7S7DnrpEdrnW1/gZcv82ytpMviovkli4=
-github.com/streamingfast/logging v0.0.0-20220405224725-2755dab2ce75/go.mod h1:VlduQ80JcGJSargkRU4Sg9Xo63wZD/l8A5NC/Uo1/uU=
+github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 h1:RN5mrigyirb8anBEtdjtHFIufXdacyTi6i4KBfeNXeo=
+github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091/go.mod h1:VlduQ80JcGJSargkRU4Sg9Xo63wZD/l8A5NC/Uo1/uU=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
@@ -1535,9 +1490,6 @@ github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDd
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48=
github.com/tendermint/go-amino v0.16.0 h1:GyhmgQKvqF82e2oZeuMSp9JTN0N09emoSZlb2lyGa2E=
github.com/tendermint/go-amino v0.16.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME=
-github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf/go.mod h1:M8agBzgqHIhgj7wEn9/0hJUZcrvt9VY+Ln+S1I5Mha0=
-github.com/teris-io/shortid v0.0.0-20201117134242-e59966efd125 h1:3SNcvBmEPE1YlB1JpVZouslJpI3GBNoiqW7+wb0Rz7w=
-github.com/teris-io/shortid v0.0.0-20201117134242-e59966efd125/go.mod h1:M8agBzgqHIhgj7wEn9/0hJUZcrvt9VY+Ln+S1I5Mha0=
github.com/test-go/testify v1.1.4 h1:Tf9lntrKUMHiXQ07qBScBTSA0dhYQlu83hswqelv1iE=
github.com/test-go/testify v1.1.4/go.mod h1:rH7cfJo/47vWGdi4GPj16x3/t1xGOj2YxzmNQzk2ghU=
github.com/testcontainers/testcontainers-go v0.34.0 h1:5fbgF0vIN5u+nD3IWabQwRybuB4GY8G2HHgCkbMzMHo=
@@ -1546,12 +1498,10 @@ github.com/theodesp/go-heaps v0.0.0-20190520121037-88e35354fe0a h1:YuO+afVc3eqrj
github.com/theodesp/go-heaps v0.0.0-20190520121037-88e35354fe0a/go.mod h1:/sfW47zCZp9FrtGcWyo1VjbgDaodxX9ovZvgLb/MxaA=
github.com/tidwall/btree v1.6.0 h1:LDZfKfQIBHGHWSwckhXI0RPSXzlo+KYdjK7FWSqOzzg=
github.com/tidwall/btree v1.6.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY=
-github.com/tidwall/gjson v1.9.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM=
github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
-github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
@@ -1559,7 +1509,6 @@ github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFA
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
-github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
@@ -1586,10 +1535,8 @@ github.com/urfave/cli v1.22.14 h1:ebbhrRiGK2i4naQJr+1Xj92HXZCrK7MsyTS/ob3HnAk=
github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA=
github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w=
github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
-github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fastjson v1.4.1 h1:hrltpHpIpkaxll8QltMU8c3QZ5+qIiCL8yKqPFJI/yE=
github.com/valyala/fastjson v1.4.1/go.mod h1:nV6MsjxL2IMJQUoHDIrjEI7oLyeqK6aBD7EFWPsvP8o=
-github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
github.com/vektah/gqlparser/v2 v2.5.11 h1:JJxLtXIoN7+3x6MBdtIP59TP1RANnY7pXOaDnADQSf8=
github.com/vektah/gqlparser/v2 v2.5.11/go.mod h1:1rCcfwB2ekJofmluGWXMSEnPMZgbxzwj6FaZ/4OT8Cc=
github.com/vultr/govultr/v2 v2.17.2 h1:gej/rwr91Puc/tgh+j33p/BLR16UrIPnSr+AIwYWZQs=
@@ -1598,10 +1545,6 @@ github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
-github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
-github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=
-github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=
-github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ=
github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
@@ -1609,7 +1552,6 @@ github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGC
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
-github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@@ -1636,7 +1578,6 @@ go.dedis.ch/kyber/v3 v3.1.0/go.mod h1:kXy7p3STAurkADD+/aZcsznZGKVHEqbtmdIzvPfrs1
go.dedis.ch/protobuf v1.0.5/go.mod h1:eIV4wicvi6JK0q/QnfIEGeSFNG0ZeB24kzut5+HaRLo=
go.dedis.ch/protobuf v1.0.7/go.mod h1:pv5ysfkDX/EawiPqcW3ikOxsL5t+BqnV6xHSmE79KI4=
go.dedis.ch/protobuf v1.0.11/go.mod h1:97QR256dnkimeNdfmURz0wAMNVbd1VmLXhG1CrTYrJ4=
-go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI=
go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE=
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
@@ -1648,12 +1589,10 @@ go.etcd.io/etcd/client/pkg/v3 v3.5.14/go.mod h1:8uMgAokyG1czCtIdsq+AGyYQMvpIKnSv
go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
go.etcd.io/etcd/client/v3 v3.5.14 h1:CWfRs4FDaDoSz81giL7zPpZH2Z35tbOrAJkkjMqOupg=
go.etcd.io/etcd/client/v3 v3.5.14/go.mod h1:k3XfdV/VIHy/97rqWjoUzrj9tk7GgJGH9J8L4dNXmAk=
-go.mongodb.org/mongo-driver v1.11.0/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8=
go.mongodb.org/mongo-driver v1.15.0 h1:rJCKC8eEliewXjZGf0ddURtl7tTVy1TK3bfl0gkUSLc=
go.mongodb.org/mongo-driver v1.15.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
-go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
@@ -1727,15 +1666,12 @@ go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKY
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
-go.uber.org/ratelimit v0.2.0/go.mod h1:YYBV4e4naJvhpitQrWJu1vCpgB7CboMe0qhltKt6mUg=
go.uber.org/ratelimit v0.3.1 h1:K4qVE+byfv/B3tC+4nYWP7v/6SimcO7HzHekoMNBma0=
go.uber.org/ratelimit v0.3.1/go.mod h1:6euWsTB6U/Nb3X++xEUXA8ciPJvr19Q/0h1+oDcJhRk=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
-go.uber.org/zap v1.14.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
-go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
@@ -1765,7 +1701,6 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-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.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.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
@@ -1780,8 +1715,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-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
-golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
+golang.org/x/exp v0.0.0-20241210194714-1829a127f884 h1:Y/Mj/94zIQQGHVSv1tTtQBDaQaJe62U9bkDZKKyhPCU=
+golang.org/x/exp v0.0.0-20241210194714-1829a127f884/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
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=
@@ -1810,15 +1745,14 @@ 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.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
-golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
-golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
+golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
+golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@@ -1869,8 +1803,8 @@ golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
-golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
-golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
+golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI=
+golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs=
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=
@@ -1906,7 +1840,6 @@ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190124100055-b90733256f2e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1922,7 +1855,6 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1998,7 +1930,6 @@ golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.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=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
@@ -2047,7 +1978,6 @@ golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@@ -2057,7 +1987,6 @@ golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
@@ -2093,8 +2022,8 @@ 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.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
-golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
-golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
+golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8=
+golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw=
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=
@@ -2112,7 +2041,6 @@ google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEt
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
-google.golang.org/api v0.10.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
@@ -2137,7 +2065,6 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
-google.golang.org/appengine v1.6.2/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
@@ -2148,7 +2075,6 @@ google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRn
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190716160619-c506a9f90610/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
@@ -2198,7 +2124,6 @@ google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmE
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=
-google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
@@ -2251,16 +2176,13 @@ gopkg.in/guregu/null.v4 v4.0.0/go.mod h1:YoQhUrADuG3i9WqesrCmpNRwm1ypAgSHYqoOcTu
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
-gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
-gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
-gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
diff --git a/deployment/solana_chain.go b/deployment/solana_chain.go
index 338642e3e32..ba02e74f892 100644
--- a/deployment/solana_chain.go
+++ b/deployment/solana_chain.go
@@ -1,5 +1,43 @@
package deployment
+import (
+ "fmt"
+ "strconv"
+
+ "github.com/gagliardetto/solana-go"
+ solRpc "github.com/gagliardetto/solana-go/rpc"
+
+ solCommomUtil "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/common"
+)
+
// SolChain represents a Solana chain.
type SolChain struct {
+ // Selectors used as canonical chain identifier.
+ Selector uint64
+ // RPC cient
+ Client *solRpc.Client
+ // TODO: raw private key for now, need to replace with a more secure way
+ DeployerKey *solana.PrivateKey
+ Confirm func(instructions []solana.Instruction, opts ...solCommomUtil.TxModifier) error
+}
+
+func (c SolChain) String() string {
+ chainInfo, err := ChainInfo(c.Selector)
+ if err != nil {
+ // we should never get here, if the selector is invalid it should not be in the environment
+ panic(err)
+ }
+ return fmt.Sprintf("%s (%d)", chainInfo.ChainName, chainInfo.ChainSelector)
+}
+
+func (c SolChain) Name() string {
+ chainInfo, err := ChainInfo(c.Selector)
+ if err != nil {
+ // we should never get here, if the selector is invalid it should not be in the environment
+ panic(err)
+ }
+ if chainInfo.ChainName == "" {
+ return strconv.FormatUint(c.Selector, 10)
+ }
+ return chainInfo.ChainName
}
diff --git a/go.md b/go.md
index c2d3e050b1e..ed41edee2b0 100644
--- a/go.md
+++ b/go.md
@@ -108,6 +108,8 @@ flowchart LR
chainlink-ccip --> chain-selectors
chainlink-ccip --> chainlink-common
click chainlink-ccip href "https://github.com/smartcontractkit/chainlink-ccip"
+ chainlink-ccip/chains/solana --> chainlink-common
+ click chainlink-ccip/chains/solana href "https://github.com/smartcontractkit/chainlink-ccip"
chainlink-common --> grpc-proxy
chainlink-common --> libocr
click chainlink-common href "https://github.com/smartcontractkit/chainlink-common"
@@ -141,6 +143,7 @@ flowchart LR
chainlink/core/scripts --> chainlink/deployment
click chainlink/core/scripts href "https://github.com/smartcontractkit/chainlink"
chainlink/deployment --> ccip-owner-contracts
+ chainlink/deployment --> chainlink-ccip/chains/solana
chainlink/deployment --> chainlink-protos/job-distributor
chainlink/deployment --> chainlink-testing-framework/lib
chainlink/deployment --> chainlink/v2
@@ -184,6 +187,12 @@ flowchart LR
end
click chainlink-repo href "https://github.com/smartcontractkit/chainlink"
+ subgraph chainlink-ccip-repo[chainlink-ccip]
+ chainlink-ccip
+ chainlink-ccip/chains/solana
+ end
+ click chainlink-ccip-repo href "https://github.com/smartcontractkit/chainlink-ccip"
+
subgraph chainlink-protos-repo[chainlink-protos]
chainlink-protos/job-distributor
chainlink-protos/orchestrator
@@ -206,5 +215,5 @@ flowchart LR
click tdh2-repo href "https://github.com/smartcontractkit/tdh2"
classDef outline stroke-dasharray:6,fill:none;
- class chainlink-repo,chainlink-protos-repo,chainlink-testing-framework-repo,tdh2-repo outline
+ class chainlink-repo,chainlink-ccip-repo,chainlink-protos-repo,chainlink-testing-framework-repo,tdh2-repo outline
```
diff --git a/integration-tests/go.mod b/integration-tests/go.mod
index e19f4dbe60d..efd538007b1 100644
--- a/integration-tests/go.mod
+++ b/integration-tests/go.mod
@@ -44,7 +44,7 @@ require (
github.com/segmentio/ksuid v1.0.4
github.com/shopspring/decimal v1.4.0
github.com/slack-go/slack v0.15.0
- github.com/smartcontractkit/chain-selectors v1.0.34
+ github.com/smartcontractkit/chain-selectors v1.0.36
github.com/smartcontractkit/chainlink-automation v0.8.1
github.com/smartcontractkit/chainlink-ccip v0.0.0-20241218114855-f74219171000
github.com/smartcontractkit/chainlink-common v0.4.1-0.20241223143929-db7919d60550
@@ -65,7 +65,7 @@ require (
go.uber.org/multierr v1.11.0
go.uber.org/zap v1.27.0
golang.org/x/crypto v0.31.0
- golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c
+ golang.org/x/exp v0.0.0-20241210194714-1829a127f884
golang.org/x/sync v0.10.0
golang.org/x/text v0.21.0
google.golang.org/grpc v1.67.1
@@ -74,7 +74,6 @@ require (
)
require (
- contrib.go.opencensus.io/exporter/stackdriver v0.13.5 // indirect
cosmossdk.io/api v0.3.1 // indirect
cosmossdk.io/core v0.5.1 // indirect
cosmossdk.io/depinject v1.0.0-alpha.4 // indirect
@@ -183,9 +182,8 @@ require (
github.com/crate-crypto/go-kzg-4844 v1.0.0 // indirect
github.com/danieljoos/wincred v1.1.2 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
- github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
+ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
github.com/dennwc/varint v1.0.0 // indirect
- github.com/dfuse-io/logging v0.0.0-20210109005628-b97a57253f70 // indirect
github.com/dgraph-io/badger/v2 v2.2007.4 // indirect
github.com/dgraph-io/ristretto v0.1.1 // indirect
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect
@@ -211,8 +209,8 @@ require (
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
- github.com/gagliardetto/binary v0.7.7 // indirect
- github.com/gagliardetto/solana-go v1.8.4 // indirect
+ github.com/gagliardetto/binary v0.8.0 // indirect
+ github.com/gagliardetto/solana-go v1.12.0 // indirect
github.com/gagliardetto/treeout v0.1.4 // indirect
github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect
github.com/getsentry/sentry-go v0.27.0 // indirect
@@ -424,6 +422,7 @@ require (
github.com/shoenig/go-m1cpu v0.1.6 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/smartcontractkit/ccip-owner-contracts v0.0.0-salt-fix // indirect
+ github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250103152858-8973fd0c912b // indirect
github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e // indirect
github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241216163550-fa030d178ba3 // indirect
github.com/smartcontractkit/chainlink-feeds v0.1.1 // indirect
@@ -443,12 +442,11 @@ require (
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.19.0 // indirect
github.com/status-im/keycard-go v0.2.0 // indirect
- github.com/streamingfast/logging v0.0.0-20220405224725-2755dab2ce75 // indirect
+ github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/supranational/blst v0.3.13 // indirect
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect
github.com/tendermint/go-amino v0.16.0 // indirect
- github.com/teris-io/shortid v0.0.0-20201117134242-e59966efd125 // 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
@@ -479,7 +477,6 @@ require (
go.etcd.io/etcd/client/pkg/v3 v3.5.14 // indirect
go.etcd.io/etcd/client/v3 v3.5.14 // indirect
go.mongodb.org/mongo-driver v1.15.0 // indirect
- go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/collector/pdata v1.12.0 // indirect
go.opentelemetry.io/collector/semconv v0.105.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0 // indirect
@@ -507,13 +504,13 @@ require (
go.uber.org/ratelimit v0.3.1 // indirect
go4.org/netipx v0.0.0-20230125063823-8449b0a6169f // indirect
golang.org/x/arch v0.11.0 // indirect
- golang.org/x/mod v0.21.0 // indirect
- golang.org/x/net v0.30.0 // indirect
+ golang.org/x/mod v0.22.0 // indirect
+ golang.org/x/net v0.32.0 // indirect
golang.org/x/oauth2 v0.23.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/term v0.27.0 // indirect
golang.org/x/time v0.7.0 // indirect
- golang.org/x/tools v0.26.0 // indirect
+ golang.org/x/tools v0.28.0 // indirect
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
gonum.org/v1/gonum v0.15.1 // indirect
diff --git a/integration-tests/go.sum b/integration-tests/go.sum
index 6c71b224f8e..a2f198d4856 100644
--- a/integration-tests/go.sum
+++ b/integration-tests/go.sum
@@ -3,7 +3,6 @@ cel.dev/expr v0.17.0/go.mod h1:HCwbrn+qQoHPXgfz6cl2J0hDybnX2N1sQQkl9EggXx8=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
-cloud.google.com/go v0.43.0/go.mod h1:BOSR3VbTLkk6FDC/TcffxP4NF/FFBGA5ku+jvKOP7pg=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
@@ -54,10 +53,6 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.45.0 h1:5av0QcIVj77t+44mV4gffFC/LscFRUhto6UBMB5SimM=
cloud.google.com/go/storage v1.45.0/go.mod h1:wpPblkIuMP5jCB/E48Pz9zIo2S/zD8g+ITmxKkPCITE=
-contrib.go.opencensus.io/exporter/stackdriver v0.12.6/go.mod h1:8x999/OcIPy5ivx/wDiV7Gx4D+VUPODf0mWRGRc5kSk=
-contrib.go.opencensus.io/exporter/stackdriver v0.13.4/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc=
-contrib.go.opencensus.io/exporter/stackdriver v0.13.5 h1:TNaexHK16gPUoc7uzELKOU7JULqccn1NDuqUxmxSqfo=
-contrib.go.opencensus.io/exporter/stackdriver v0.13.5/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc=
cosmossdk.io/api v0.3.1 h1:NNiOclKRR0AOlO4KIqeaG6PS6kswOMhHD0ir0SscNXE=
cosmossdk.io/api v0.3.1/go.mod h1:DfHfMkiNA2Uhy8fj0JJlOCYOBp4eWUUJ1te5zBGNyIw=
cosmossdk.io/core v0.5.1 h1:vQVtFrIYOQJDV3f7rw4pjjVqc1id4+mE0L9hHP66pyI=
@@ -75,7 +70,6 @@ cosmossdk.io/tools/rosetta v0.2.1/go.mod h1:Pqdc1FdvkNV3LcNIkYWt2RQY6IP1ge6YWZk8
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
-filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs=
@@ -118,8 +112,6 @@ github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8=
github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
github.com/Depado/ginprom v1.8.0 h1:zaaibRLNI1dMiiuj1MKzatm8qrcHzikMlCc1anqOdyo=
github.com/Depado/ginprom v1.8.0/go.mod h1:XBaKzeNBqPF4vxJpNLincSQZeMDnZp1tIbU0FU0UKgg=
-github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0=
-github.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtixis4Tib9if0=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 h1:3c8yed4lgqTt+oTQ+JNMDo+F4xprBf+O/il4ZC0nRLw=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0/go.mod h1:obipzmGjfSjam60XLwGfqUkJsfiheAl+TUjG+4yzyPM=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.3 h1:xir5X8TS8UBVPWg2jHL+cSTf0jZgqYQSA54TscSt1/0=
@@ -159,7 +151,6 @@ github.com/Workiva/go-datastructures v1.1.0 h1:hu20UpgZneBhQ3ZvwiOGlqJSKIosin2Rd
github.com/Workiva/go-datastructures v1.1.0/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A=
github.com/XSAM/otelsql v0.27.0 h1:i9xtxtdcqXV768a5C6SoT/RkG+ue3JTOgkYInzlTOqs=
github.com/XSAM/otelsql v0.27.0/go.mod h1:0mFB3TvLa7NCuhm/2nU7/b2wEtsczkj8Rey8ygO7V+A=
-github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
@@ -177,7 +168,6 @@ github.com/alicebob/miniredis/v2 v2.30.4/go.mod h1:b25qWj4fCEsBeAAR2mlb0ufImGC6u
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
github.com/allegro/bigcache v1.2.1 h1:hg1sY1raCwic3Vnsvje6TT7/pnZba83LeFck5NrFKSc=
github.com/allegro/bigcache v1.2.1/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
-github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129/go.mod h1:rFgpPQZYZ8vdbc+48xibu8ALc3yeyd64IhHS+PU6Yyg=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
@@ -204,8 +194,6 @@ github.com/avast/retry-go/v4 v4.6.0 h1:K9xNA+KeB8HHc2aWFuLb25Offp+0iVRXEvFx8IinR
github.com/avast/retry-go/v4 v4.6.0/go.mod h1:gvWlPhBVsvBbLkVGDg/KwvBv0bEkCOLRRSHKIr2PyOE=
github.com/awalterschulze/gographviz v2.0.3+incompatible h1:9sVEXJBJLwGX7EQVhLm2elIKCm7P2YHFC8v6096G09E=
github.com/awalterschulze/gographviz v2.0.3+incompatible/go.mod h1:GEV5wmg4YquNw7v1kkyoX9etIk8yVmXj+AkDHuuETHs=
-github.com/aws/aws-sdk-go v1.22.1/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
-github.com/aws/aws-sdk-go v1.23.20/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
github.com/aws/aws-sdk-go v1.54.19 h1:tyWV+07jagrNiCcGRzRhdtVjQs7Vy41NwsuOcl0IbVI=
github.com/aws/aws-sdk-go v1.54.19/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
@@ -263,7 +251,6 @@ github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 h1:41iFGWnSlI2
github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE=
github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
-github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
@@ -380,9 +367,7 @@ github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A=
github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw=
-github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
-github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
@@ -393,7 +378,6 @@ github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
-github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cosmos/btcutil v1.0.5 h1:t+ZFcX77LpKtDBhjucvnOH8C2l2ioGsBNEQ3jef8xFk=
github.com/cosmos/btcutil v1.0.5/go.mod h1:IyB7iuqZMJlthe2tkIFL33xPyzbFYP0XVdS8P5lUPis=
github.com/cosmos/cosmos-proto v1.0.0-beta.5 h1:eNcayDLpip+zVLRLYafhzLvQlSmyab+RC5W7ZfmxJLA=
@@ -434,7 +418,6 @@ github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7Do
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
-github.com/daaku/go.zipexe v1.0.0/go.mod h1:z8IiR6TsVLEYKwXAoE/I+8ys/sDkgTzSL0CLnGVd57E=
github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0=
github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0=
github.com/danielkov/gin-helmet v0.0.0-20171108135313-1387e224435e h1:5jVSh2l/ho6ajWhSPNN84eHEdq3dp0T7+f6r3Tc6hsk=
@@ -448,27 +431,22 @@ github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80N
github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y=
github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
-github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs=
-github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
+github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg=
+github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
github.com/dennwc/varint v1.0.0 h1:kGNFFSSw8ToIy3obO/kKr8U9GZYUAxQEVuix4zfDWzE=
github.com/dennwc/varint v1.0.0/go.mod h1:hnItb35rvZvJrbTALZtY/iQfDs48JKRG1RPpgziApxA=
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I=
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE=
-github.com/dfuse-io/logging v0.0.0-20201110202154-26697de88c79/go.mod h1:V+ED4kT/t/lKtH99JQmKIb0v9WL3VaYkJ36CfHlVECI=
-github.com/dfuse-io/logging v0.0.0-20210109005628-b97a57253f70 h1:CuJS05R9jmNlUK8GOxrEELPbfXm0EuGh/30LjkjN5vo=
-github.com/dfuse-io/logging v0.0.0-20210109005628-b97a57253f70/go.mod h1:EoK/8RFbMEteaCaz89uessDTnCWjbbcr+DXcBh4el5o=
github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o=
github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk=
github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8=
github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA=
-github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y=
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
-github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/digitalocean/godo v1.118.0 h1:lkzGFQmACrVCp7UqH1sAi4JK/PWwlc5aaxubgorKmC4=
github.com/digitalocean/godo v1.118.0/go.mod h1:Vk0vpCot2HOAJwc5WE8wljZGtJ3ZtWIc8MQ8rF38sdo=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
@@ -541,12 +519,12 @@ github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
-github.com/gagliardetto/binary v0.7.7 h1:QZpT38+sgoPg+TIQjH94sLbl/vX+nlIRA37pEyOsjfY=
-github.com/gagliardetto/binary v0.7.7/go.mod h1:mUuay5LL8wFVnIlecHakSZMvcdqfs+CsotR5n77kyjM=
+github.com/gagliardetto/binary v0.8.0 h1:U9ahc45v9HW0d15LoN++vIXSJyqR/pWw8DDlhd7zvxg=
+github.com/gagliardetto/binary v0.8.0/go.mod h1:2tfj51g5o9dnvsc+fL3Jxr22MuWzYXwx9wEoN0XQ7/c=
github.com/gagliardetto/gofuzz v1.2.2 h1:XL/8qDMzcgvR4+CyRQW9UGdwPRPMHVJfqQ/uMvSUuQw=
github.com/gagliardetto/gofuzz v1.2.2/go.mod h1:bkH/3hYLZrMLbfYWA0pWzXmi5TTRZnu4pMGZBkqMKvY=
-github.com/gagliardetto/solana-go v1.8.4 h1:vmD/JmTlonyXGy39bAo0inMhmbdAwV7rXZtLDMZeodE=
-github.com/gagliardetto/solana-go v1.8.4/go.mod h1:i+7aAyNDTHG0jK8GZIBSI4OVvDqkt2Qx+LklYclRNG8=
+github.com/gagliardetto/solana-go v1.12.0 h1:rzsbilDPj6p+/DOPXBMLhwMZeBgeRuXjm5zQFCoXgsg=
+github.com/gagliardetto/solana-go v1.12.0/go.mod h1:l/qqqIN6qJJPtxW/G1PF4JtcE3Zg2vD2EliZrr9Gn5k=
github.com/gagliardetto/treeout v0.1.4 h1:ozeYerrLCmCubo1TcIjFiOWTTGteOOHND1twdFpgwaw=
github.com/gagliardetto/treeout v0.1.4/go.mod h1:loUefvXTrlRG5rYmJmExNryyBRh8f89VZhmMOyCyqok=
github.com/gagliardetto/utilz v0.1.1 h1:/etW4hl607emKg6R6Lj9jRJ9d6ue2AQOyjhuAwjzs1U=
@@ -674,7 +652,6 @@ github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVI
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v1.2.2 h1:1+mZ9upx1Dh6FmUTFR1naJ77miKiXgALjWOZ3NVFPmY=
github.com/golang/glog v1.2.2/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
-github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -710,7 +687,6 @@ github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
-github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk=
@@ -795,7 +771,6 @@ github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
-github.com/gorilla/rpc v1.2.0/go.mod h1:V4h9r+4sF5HnzqbwIez0fKSpANP0zlYd3qR7p36jkTQ=
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY=
@@ -825,15 +800,12 @@ github.com/graph-gophers/graphql-go v1.5.0 h1:fDqblo50TEpD0LY7RXk/LFVYEVqo3+tXMN
github.com/graph-gophers/graphql-go v1.5.0/go.mod h1:YtmJZDLbF1YYNrlNAuiO5zAStUWc3XZT07iGsVqe1Os=
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA=
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
-github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw=
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA=
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU=
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 h1:pRhl55Yx1eC7BZ1N+BBWwnKaMyD8uC+34TLdndZMAKk=
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0/go.mod h1:XKMd7iuf/RGPSMJ/U4HP0zS2Z9Fh8Ps9a+6X26m/tmI=
-github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
-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.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys=
@@ -906,7 +878,6 @@ github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
-github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/golang-lru v0.6.0 h1:uL2shRDx7RTrOrTCUZEGP/wJUFiUI8QT6E7z5o8jga4=
github.com/hashicorp/golang-lru v0.6.0/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
@@ -1011,10 +982,8 @@ github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0f
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
-github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c=
github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo=
-github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
@@ -1025,7 +994,6 @@ github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
-github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4=
github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
@@ -1050,7 +1018,6 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.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.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
@@ -1102,7 +1069,6 @@ github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
-github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
@@ -1203,7 +1169,6 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0=
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4=
-github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE=
github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
@@ -1228,7 +1193,6 @@ github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
-github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229/go.mod h1:0aYXnNPJ8l7uZxf45rWW1a/uME32OF0rhiYGNQ2oF2E=
github.com/nsf/jsondiff v0.0.0-20230430225905-43f6cf3098c1 h1:dOYG7LS/WK00RWZc8XGgcUTlTxpp3mKhdR2Q9z9HbXM=
github.com/nsf/jsondiff v0.0.0-20230430225905-43f6cf3098c1/go.mod h1:mpRZBD8SJ55OIICQ3iWH0Yz3cjzA61JdqMLoWXeB2+8=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
@@ -1319,7 +1283,6 @@ github.com/pressly/goose/v3 v3.21.1/go.mod h1:sqthmzV8PitchEkjecFJII//l43dLOCzfW
github.com/prometheus/alertmanager v0.27.0 h1:V6nTa2J5V4s8TG4C4HtrBP/WNSebCCTYGGv4qecA/+I=
github.com/prometheus/alertmanager v0.27.0/go.mod h1:8Ia/R3urPmbzJ8OsdvmZvIprDwvwmYCmUbwBL+jlPOE=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
-github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
@@ -1332,8 +1295,6 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
-github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
-github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
@@ -1346,7 +1307,6 @@ github.com/prometheus/common/sigv4 v0.1.0/go.mod h1:2Jkxxk9yYvCkE5G1sQT7GuEXm57J
github.com/prometheus/exporter-toolkit v0.11.0 h1:yNTsuZ0aNCNFQ3aFTD2uhPOvr4iD7fdBvKPAEGkNf+g=
github.com/prometheus/exporter-toolkit v0.11.0/go.mod h1:BVnENhnNecpwoTLiABx7mrPB/OLRIgN74qlQbV+FK1Q=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
-github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
@@ -1355,7 +1315,6 @@ github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0leargg
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/prometheus/prometheus v0.54.1 h1:vKuwQNjnYN2/mDoWfHXDhAsz/68q/dQDb+YbcEqU7MQ=
github.com/prometheus/prometheus v0.54.1/go.mod h1:xlLByHhk2g3ycakQGrMaU8K7OySZx98BzeCR99991NY=
-github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rakyll/statik v0.1.7 h1:OF3QCZUuyPxuGEP7B4ypUa7sB/iHtqOTDYZXGM8KOdQ=
github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Unghqrcc=
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM=
@@ -1367,7 +1326,6 @@ github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
-github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
@@ -1441,12 +1399,14 @@ github.com/slack-go/slack v0.15.0 h1:LE2lj2y9vqqiOf+qIIy0GvEoxgF1N5yLGZffmEZykt0
github.com/slack-go/slack v0.15.0/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw=
github.com/smartcontractkit/ccip-owner-contracts v0.0.0-salt-fix h1:DPJD++yKLSx0EfT+U14P8vLVxjXFmoIETiCO9lVwQo8=
github.com/smartcontractkit/ccip-owner-contracts v0.0.0-salt-fix/go.mod h1:NnT6w4Kj42OFFXhSx99LvJZWPpMjmo4+CpDEWfw61xY=
-github.com/smartcontractkit/chain-selectors v1.0.34 h1:MJ17OGu8+jjl426pcKrJkCf3fePb3eCreuAnUA3RBj4=
-github.com/smartcontractkit/chain-selectors v1.0.34/go.mod h1:xsKM0aN3YGcQKTPRPDDtPx2l4mlTN1Djmg0VVXV40b8=
+github.com/smartcontractkit/chain-selectors v1.0.36 h1:KSpO8I+JOiuyN4FuXsV471sPorGF//PAqwq2Cm4gRK0=
+github.com/smartcontractkit/chain-selectors v1.0.36/go.mod h1:xsKM0aN3YGcQKTPRPDDtPx2l4mlTN1Djmg0VVXV40b8=
github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgBc2xpDKBco/Q4h4ydl6+UUU=
github.com/smartcontractkit/chainlink-automation v0.8.1/go.mod h1:Iij36PvWZ6blrdC5A/nrQUBuf3MH3JvsBB9sSyc9W08=
github.com/smartcontractkit/chainlink-ccip v0.0.0-20241218114855-f74219171000 h1:6Zzr/R1j6P7bbvcUlt5WUIbItvrrGdGzIsiAzQezcwo=
github.com/smartcontractkit/chainlink-ccip v0.0.0-20241218114855-f74219171000/go.mod h1:ncjd6mPZSRlelEqH/2KeLE1pU3UlqzBSn8RYkEoECzY=
+github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250103152858-8973fd0c912b h1:UBXi9Yj8YSMHDDaxQLu273x1fWjyEL9xP58nuJsqZfg=
+github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250103152858-8973fd0c912b/go.mod h1:Bmwq4lNb5tE47sydN0TKetcLEGbgl+VxHEWp4S0LI60=
github.com/smartcontractkit/chainlink-common v0.4.1-0.20241223143929-db7919d60550 h1:rRs74zjDJ7do5aYEXSU/sOnLnlbYCNqM8BrvEx/0NH8=
github.com/smartcontractkit/chainlink-common v0.4.1-0.20241223143929-db7919d60550/go.mod h1:yti7e1+G9hhkYhj+L5sVUULn9Bn3bBL5/AxaNqdJ5YQ=
github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e h1:PRoeby6ZlTuTkv2f+7tVU4+zboTfRzI+beECynF4JQ0=
@@ -1489,7 +1449,6 @@ github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60=
-github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js=
github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
github.com/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg=
@@ -1508,7 +1467,6 @@ github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
-github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
@@ -1518,15 +1476,13 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
-github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
-github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA=
github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg=
-github.com/streamingfast/logging v0.0.0-20220405224725-2755dab2ce75 h1:ZqpS7rAhhKD7S7DnrpEdrnW1/gZcv82ytpMviovkli4=
-github.com/streamingfast/logging v0.0.0-20220405224725-2755dab2ce75/go.mod h1:VlduQ80JcGJSargkRU4Sg9Xo63wZD/l8A5NC/Uo1/uU=
+github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 h1:RN5mrigyirb8anBEtdjtHFIufXdacyTi6i4KBfeNXeo=
+github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091/go.mod h1:VlduQ80JcGJSargkRU4Sg9Xo63wZD/l8A5NC/Uo1/uU=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
@@ -1558,9 +1514,6 @@ github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDd
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48=
github.com/tendermint/go-amino v0.16.0 h1:GyhmgQKvqF82e2oZeuMSp9JTN0N09emoSZlb2lyGa2E=
github.com/tendermint/go-amino v0.16.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME=
-github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf/go.mod h1:M8agBzgqHIhgj7wEn9/0hJUZcrvt9VY+Ln+S1I5Mha0=
-github.com/teris-io/shortid v0.0.0-20201117134242-e59966efd125 h1:3SNcvBmEPE1YlB1JpVZouslJpI3GBNoiqW7+wb0Rz7w=
-github.com/teris-io/shortid v0.0.0-20201117134242-e59966efd125/go.mod h1:M8agBzgqHIhgj7wEn9/0hJUZcrvt9VY+Ln+S1I5Mha0=
github.com/test-go/testify v1.1.4 h1:Tf9lntrKUMHiXQ07qBScBTSA0dhYQlu83hswqelv1iE=
github.com/test-go/testify v1.1.4/go.mod h1:rH7cfJo/47vWGdi4GPj16x3/t1xGOj2YxzmNQzk2ghU=
github.com/testcontainers/testcontainers-go v0.34.0 h1:5fbgF0vIN5u+nD3IWabQwRybuB4GY8G2HHgCkbMzMHo=
@@ -1571,12 +1524,10 @@ github.com/thlib/go-timezone-local v0.0.0-20210907160436-ef149e42d28e h1:Buzhfgf
github.com/thlib/go-timezone-local v0.0.0-20210907160436-ef149e42d28e/go.mod h1:/Tnicc6m/lsJE0irFMA0LfIwTBo4QP7A8IfyIv4zZKI=
github.com/tidwall/btree v1.6.0 h1:LDZfKfQIBHGHWSwckhXI0RPSXzlo+KYdjK7FWSqOzzg=
github.com/tidwall/btree v1.6.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY=
-github.com/tidwall/gjson v1.9.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM=
github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
-github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
@@ -1584,7 +1535,6 @@ github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFA
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
-github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
@@ -1611,10 +1561,8 @@ github.com/urfave/cli v1.22.14 h1:ebbhrRiGK2i4naQJr+1Xj92HXZCrK7MsyTS/ob3HnAk=
github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA=
github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w=
github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
-github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fastjson v1.4.1 h1:hrltpHpIpkaxll8QltMU8c3QZ5+qIiCL8yKqPFJI/yE=
github.com/valyala/fastjson v1.4.1/go.mod h1:nV6MsjxL2IMJQUoHDIrjEI7oLyeqK6aBD7EFWPsvP8o=
-github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
github.com/vektah/gqlparser/v2 v2.5.11 h1:JJxLtXIoN7+3x6MBdtIP59TP1RANnY7pXOaDnADQSf8=
github.com/vektah/gqlparser/v2 v2.5.11/go.mod h1:1rCcfwB2ekJofmluGWXMSEnPMZgbxzwj6FaZ/4OT8Cc=
github.com/vultr/govultr/v2 v2.17.2 h1:gej/rwr91Puc/tgh+j33p/BLR16UrIPnSr+AIwYWZQs=
@@ -1623,10 +1571,6 @@ github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
-github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
-github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=
-github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=
-github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ=
github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
@@ -1634,7 +1578,6 @@ github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGC
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
-github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@@ -1661,7 +1604,6 @@ go.dedis.ch/kyber/v3 v3.1.0/go.mod h1:kXy7p3STAurkADD+/aZcsznZGKVHEqbtmdIzvPfrs1
go.dedis.ch/protobuf v1.0.5/go.mod h1:eIV4wicvi6JK0q/QnfIEGeSFNG0ZeB24kzut5+HaRLo=
go.dedis.ch/protobuf v1.0.7/go.mod h1:pv5ysfkDX/EawiPqcW3ikOxsL5t+BqnV6xHSmE79KI4=
go.dedis.ch/protobuf v1.0.11/go.mod h1:97QR256dnkimeNdfmURz0wAMNVbd1VmLXhG1CrTYrJ4=
-go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI=
go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE=
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
@@ -1673,12 +1615,10 @@ go.etcd.io/etcd/client/pkg/v3 v3.5.14/go.mod h1:8uMgAokyG1czCtIdsq+AGyYQMvpIKnSv
go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
go.etcd.io/etcd/client/v3 v3.5.14 h1:CWfRs4FDaDoSz81giL7zPpZH2Z35tbOrAJkkjMqOupg=
go.etcd.io/etcd/client/v3 v3.5.14/go.mod h1:k3XfdV/VIHy/97rqWjoUzrj9tk7GgJGH9J8L4dNXmAk=
-go.mongodb.org/mongo-driver v1.11.0/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8=
go.mongodb.org/mongo-driver v1.15.0 h1:rJCKC8eEliewXjZGf0ddURtl7tTVy1TK3bfl0gkUSLc=
go.mongodb.org/mongo-driver v1.15.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
-go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
@@ -1752,15 +1692,12 @@ go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKY
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
-go.uber.org/ratelimit v0.2.0/go.mod h1:YYBV4e4naJvhpitQrWJu1vCpgB7CboMe0qhltKt6mUg=
go.uber.org/ratelimit v0.3.1 h1:K4qVE+byfv/B3tC+4nYWP7v/6SimcO7HzHekoMNBma0=
go.uber.org/ratelimit v0.3.1/go.mod h1:6euWsTB6U/Nb3X++xEUXA8ciPJvr19Q/0h1+oDcJhRk=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
-go.uber.org/zap v1.14.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
-go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
@@ -1791,7 +1728,6 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-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.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.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
@@ -1806,8 +1742,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-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
-golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
+golang.org/x/exp v0.0.0-20241210194714-1829a127f884 h1:Y/Mj/94zIQQGHVSv1tTtQBDaQaJe62U9bkDZKKyhPCU=
+golang.org/x/exp v0.0.0-20241210194714-1829a127f884/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
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=
@@ -1836,15 +1772,14 @@ 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.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
-golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
-golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
+golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
+golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@@ -1895,8 +1830,8 @@ golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
-golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
-golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
+golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI=
+golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs=
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=
@@ -1932,7 +1867,6 @@ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1949,7 +1883,6 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -2026,7 +1959,6 @@ golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.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=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
@@ -2075,7 +2007,6 @@ golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@@ -2085,7 +2016,6 @@ golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
@@ -2121,8 +2051,8 @@ 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.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
-golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
-golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
+golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8=
+golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw=
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=
@@ -2140,7 +2070,6 @@ google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEt
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
-google.golang.org/api v0.10.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
@@ -2165,7 +2094,6 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
-google.golang.org/appengine v1.6.2/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
@@ -2176,7 +2104,6 @@ google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRn
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190716160619-c506a9f90610/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
@@ -2226,7 +2153,6 @@ google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmE
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=
-google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
@@ -2279,16 +2205,13 @@ gopkg.in/guregu/null.v4 v4.0.0/go.mod h1:YoQhUrADuG3i9WqesrCmpNRwm1ypAgSHYqoOcTu
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
-gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
-gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
-gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
diff --git a/integration-tests/load/go.mod b/integration-tests/load/go.mod
index 5c6be37328c..94d86cb5cd4 100644
--- a/integration-tests/load/go.mod
+++ b/integration-tests/load/go.mod
@@ -35,11 +35,10 @@ require (
github.com/stretchr/testify v1.10.0
github.com/wiremock/go-wiremock v1.9.0
go.uber.org/ratelimit v0.3.1
- golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c
+ golang.org/x/exp v0.0.0-20241210194714-1829a127f884
)
require (
- contrib.go.opencensus.io/exporter/stackdriver v0.13.5 // indirect
cosmossdk.io/api v0.3.1 // indirect
cosmossdk.io/core v0.5.1 // indirect
cosmossdk.io/depinject v1.0.0-alpha.4 // indirect
@@ -153,9 +152,8 @@ require (
github.com/danieljoos/wincred v1.1.2 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/deckarep/golang-set/v2 v2.6.0 // indirect
- github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
+ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
github.com/dennwc/varint v1.0.0 // indirect
- github.com/dfuse-io/logging v0.0.0-20210109005628-b97a57253f70 // indirect
github.com/dgraph-io/badger/v2 v2.2007.4 // indirect
github.com/dgraph-io/ristretto v0.1.1 // indirect
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect
@@ -182,8 +180,8 @@ require (
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
- github.com/gagliardetto/binary v0.7.7 // indirect
- github.com/gagliardetto/solana-go v1.8.4 // indirect
+ github.com/gagliardetto/binary v0.8.0 // indirect
+ github.com/gagliardetto/solana-go v1.12.0 // indirect
github.com/gagliardetto/treeout v0.1.4 // indirect
github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect
github.com/getsentry/sentry-go v0.27.0 // indirect
@@ -405,7 +403,7 @@ require (
github.com/shoenig/test v0.6.6 // indirect
github.com/shopspring/decimal v1.4.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
- github.com/smartcontractkit/chain-selectors v1.0.34 // indirect
+ github.com/smartcontractkit/chain-selectors v1.0.36 // indirect
github.com/smartcontractkit/chainlink-automation v0.8.1 // indirect
github.com/smartcontractkit/chainlink-ccip v0.0.0-20241218114855-f74219171000 // indirect
github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e // indirect
@@ -430,13 +428,12 @@ require (
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.19.0 // indirect
github.com/status-im/keycard-go v0.2.0 // indirect
- github.com/streamingfast/logging v0.0.0-20220405224725-2755dab2ce75 // indirect
+ github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/supranational/blst v0.3.13 // indirect
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect
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.34.0 // indirect
github.com/theodesp/go-heaps v0.0.0-20190520121037-88e35354fe0a // indirect
@@ -470,7 +467,6 @@ require (
go.etcd.io/etcd/client/pkg/v3 v3.5.14 // indirect
go.etcd.io/etcd/client/v3 v3.5.14 // indirect
go.mongodb.org/mongo-driver v1.15.0 // indirect
- go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/collector/pdata v1.12.0 // indirect
go.opentelemetry.io/collector/semconv v0.105.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0 // indirect
@@ -501,15 +497,15 @@ require (
go4.org/netipx v0.0.0-20230125063823-8449b0a6169f // indirect
golang.org/x/arch v0.11.0 // indirect
golang.org/x/crypto v0.31.0 // indirect
- golang.org/x/mod v0.21.0 // indirect
- golang.org/x/net v0.30.0 // indirect
+ golang.org/x/mod v0.22.0 // indirect
+ golang.org/x/net v0.32.0 // indirect
golang.org/x/oauth2 v0.23.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/term v0.27.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/time v0.7.0 // indirect
- golang.org/x/tools v0.26.0 // indirect
+ golang.org/x/tools v0.28.0 // indirect
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
gonum.org/v1/gonum v0.15.1 // indirect
diff --git a/integration-tests/load/go.sum b/integration-tests/load/go.sum
index 75a961d80a8..eb114fa0e28 100644
--- a/integration-tests/load/go.sum
+++ b/integration-tests/load/go.sum
@@ -3,7 +3,6 @@ cel.dev/expr v0.17.0/go.mod h1:HCwbrn+qQoHPXgfz6cl2J0hDybnX2N1sQQkl9EggXx8=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
-cloud.google.com/go v0.43.0/go.mod h1:BOSR3VbTLkk6FDC/TcffxP4NF/FFBGA5ku+jvKOP7pg=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
@@ -54,10 +53,6 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.45.0 h1:5av0QcIVj77t+44mV4gffFC/LscFRUhto6UBMB5SimM=
cloud.google.com/go/storage v1.45.0/go.mod h1:wpPblkIuMP5jCB/E48Pz9zIo2S/zD8g+ITmxKkPCITE=
-contrib.go.opencensus.io/exporter/stackdriver v0.12.6/go.mod h1:8x999/OcIPy5ivx/wDiV7Gx4D+VUPODf0mWRGRc5kSk=
-contrib.go.opencensus.io/exporter/stackdriver v0.13.4/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc=
-contrib.go.opencensus.io/exporter/stackdriver v0.13.5 h1:TNaexHK16gPUoc7uzELKOU7JULqccn1NDuqUxmxSqfo=
-contrib.go.opencensus.io/exporter/stackdriver v0.13.5/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc=
cosmossdk.io/api v0.3.1 h1:NNiOclKRR0AOlO4KIqeaG6PS6kswOMhHD0ir0SscNXE=
cosmossdk.io/api v0.3.1/go.mod h1:DfHfMkiNA2Uhy8fj0JJlOCYOBp4eWUUJ1te5zBGNyIw=
cosmossdk.io/core v0.5.1 h1:vQVtFrIYOQJDV3f7rw4pjjVqc1id4+mE0L9hHP66pyI=
@@ -75,7 +70,6 @@ cosmossdk.io/tools/rosetta v0.2.1/go.mod h1:Pqdc1FdvkNV3LcNIkYWt2RQY6IP1ge6YWZk8
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
-filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs=
@@ -118,8 +112,6 @@ github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8=
github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
github.com/Depado/ginprom v1.8.0 h1:zaaibRLNI1dMiiuj1MKzatm8qrcHzikMlCc1anqOdyo=
github.com/Depado/ginprom v1.8.0/go.mod h1:XBaKzeNBqPF4vxJpNLincSQZeMDnZp1tIbU0FU0UKgg=
-github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0=
-github.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtixis4Tib9if0=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 h1:3c8yed4lgqTt+oTQ+JNMDo+F4xprBf+O/il4ZC0nRLw=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0/go.mod h1:obipzmGjfSjam60XLwGfqUkJsfiheAl+TUjG+4yzyPM=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.3 h1:xir5X8TS8UBVPWg2jHL+cSTf0jZgqYQSA54TscSt1/0=
@@ -163,7 +155,6 @@ github.com/Workiva/go-datastructures v1.1.0 h1:hu20UpgZneBhQ3ZvwiOGlqJSKIosin2Rd
github.com/Workiva/go-datastructures v1.1.0/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A=
github.com/XSAM/otelsql v0.27.0 h1:i9xtxtdcqXV768a5C6SoT/RkG+ue3JTOgkYInzlTOqs=
github.com/XSAM/otelsql v0.27.0/go.mod h1:0mFB3TvLa7NCuhm/2nU7/b2wEtsczkj8Rey8ygO7V+A=
-github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
@@ -181,7 +172,6 @@ github.com/alicebob/miniredis/v2 v2.30.4/go.mod h1:b25qWj4fCEsBeAAR2mlb0ufImGC6u
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
github.com/allegro/bigcache v1.2.1 h1:hg1sY1raCwic3Vnsvje6TT7/pnZba83LeFck5NrFKSc=
github.com/allegro/bigcache v1.2.1/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
-github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129/go.mod h1:rFgpPQZYZ8vdbc+48xibu8ALc3yeyd64IhHS+PU6Yyg=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
@@ -208,8 +198,6 @@ github.com/avast/retry-go/v4 v4.6.0 h1:K9xNA+KeB8HHc2aWFuLb25Offp+0iVRXEvFx8IinR
github.com/avast/retry-go/v4 v4.6.0/go.mod h1:gvWlPhBVsvBbLkVGDg/KwvBv0bEkCOLRRSHKIr2PyOE=
github.com/awalterschulze/gographviz v2.0.3+incompatible h1:9sVEXJBJLwGX7EQVhLm2elIKCm7P2YHFC8v6096G09E=
github.com/awalterschulze/gographviz v2.0.3+incompatible/go.mod h1:GEV5wmg4YquNw7v1kkyoX9etIk8yVmXj+AkDHuuETHs=
-github.com/aws/aws-sdk-go v1.22.1/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
-github.com/aws/aws-sdk-go v1.23.20/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
github.com/aws/aws-sdk-go v1.54.19 h1:tyWV+07jagrNiCcGRzRhdtVjQs7Vy41NwsuOcl0IbVI=
github.com/aws/aws-sdk-go v1.54.19/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
@@ -267,7 +255,6 @@ github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 h1:41iFGWnSlI2
github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE=
github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
-github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
@@ -374,9 +361,7 @@ github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A=
github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw=
-github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
-github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
@@ -387,7 +372,6 @@ github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
-github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cosmos/btcutil v1.0.5 h1:t+ZFcX77LpKtDBhjucvnOH8C2l2ioGsBNEQ3jef8xFk=
github.com/cosmos/btcutil v1.0.5/go.mod h1:IyB7iuqZMJlthe2tkIFL33xPyzbFYP0XVdS8P5lUPis=
github.com/cosmos/cosmos-proto v1.0.0-beta.5 h1:eNcayDLpip+zVLRLYafhzLvQlSmyab+RC5W7ZfmxJLA=
@@ -428,7 +412,6 @@ github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7Do
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
-github.com/daaku/go.zipexe v1.0.0/go.mod h1:z8IiR6TsVLEYKwXAoE/I+8ys/sDkgTzSL0CLnGVd57E=
github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0=
github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0=
github.com/danielkov/gin-helmet v0.0.0-20171108135313-1387e224435e h1:5jVSh2l/ho6ajWhSPNN84eHEdq3dp0T7+f6r3Tc6hsk=
@@ -442,27 +425,22 @@ github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80N
github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y=
github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
-github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs=
-github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
+github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg=
+github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
github.com/dennwc/varint v1.0.0 h1:kGNFFSSw8ToIy3obO/kKr8U9GZYUAxQEVuix4zfDWzE=
github.com/dennwc/varint v1.0.0/go.mod h1:hnItb35rvZvJrbTALZtY/iQfDs48JKRG1RPpgziApxA=
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I=
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE=
-github.com/dfuse-io/logging v0.0.0-20201110202154-26697de88c79/go.mod h1:V+ED4kT/t/lKtH99JQmKIb0v9WL3VaYkJ36CfHlVECI=
-github.com/dfuse-io/logging v0.0.0-20210109005628-b97a57253f70 h1:CuJS05R9jmNlUK8GOxrEELPbfXm0EuGh/30LjkjN5vo=
-github.com/dfuse-io/logging v0.0.0-20210109005628-b97a57253f70/go.mod h1:EoK/8RFbMEteaCaz89uessDTnCWjbbcr+DXcBh4el5o=
github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o=
github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk=
github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8=
github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA=
-github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y=
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
-github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/digitalocean/godo v1.118.0 h1:lkzGFQmACrVCp7UqH1sAi4JK/PWwlc5aaxubgorKmC4=
github.com/digitalocean/godo v1.118.0/go.mod h1:Vk0vpCot2HOAJwc5WE8wljZGtJ3ZtWIc8MQ8rF38sdo=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
@@ -535,12 +513,12 @@ github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
-github.com/gagliardetto/binary v0.7.7 h1:QZpT38+sgoPg+TIQjH94sLbl/vX+nlIRA37pEyOsjfY=
-github.com/gagliardetto/binary v0.7.7/go.mod h1:mUuay5LL8wFVnIlecHakSZMvcdqfs+CsotR5n77kyjM=
+github.com/gagliardetto/binary v0.8.0 h1:U9ahc45v9HW0d15LoN++vIXSJyqR/pWw8DDlhd7zvxg=
+github.com/gagliardetto/binary v0.8.0/go.mod h1:2tfj51g5o9dnvsc+fL3Jxr22MuWzYXwx9wEoN0XQ7/c=
github.com/gagliardetto/gofuzz v1.2.2 h1:XL/8qDMzcgvR4+CyRQW9UGdwPRPMHVJfqQ/uMvSUuQw=
github.com/gagliardetto/gofuzz v1.2.2/go.mod h1:bkH/3hYLZrMLbfYWA0pWzXmi5TTRZnu4pMGZBkqMKvY=
-github.com/gagliardetto/solana-go v1.8.4 h1:vmD/JmTlonyXGy39bAo0inMhmbdAwV7rXZtLDMZeodE=
-github.com/gagliardetto/solana-go v1.8.4/go.mod h1:i+7aAyNDTHG0jK8GZIBSI4OVvDqkt2Qx+LklYclRNG8=
+github.com/gagliardetto/solana-go v1.12.0 h1:rzsbilDPj6p+/DOPXBMLhwMZeBgeRuXjm5zQFCoXgsg=
+github.com/gagliardetto/solana-go v1.12.0/go.mod h1:l/qqqIN6qJJPtxW/G1PF4JtcE3Zg2vD2EliZrr9Gn5k=
github.com/gagliardetto/treeout v0.1.4 h1:ozeYerrLCmCubo1TcIjFiOWTTGteOOHND1twdFpgwaw=
github.com/gagliardetto/treeout v0.1.4/go.mod h1:loUefvXTrlRG5rYmJmExNryyBRh8f89VZhmMOyCyqok=
github.com/gagliardetto/utilz v0.1.1 h1:/etW4hl607emKg6R6Lj9jRJ9d6ue2AQOyjhuAwjzs1U=
@@ -668,7 +646,6 @@ github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVI
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v1.2.2 h1:1+mZ9upx1Dh6FmUTFR1naJ77miKiXgALjWOZ3NVFPmY=
github.com/golang/glog v1.2.2/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
-github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -704,7 +681,6 @@ github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
-github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk=
@@ -789,7 +765,6 @@ github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
-github.com/gorilla/rpc v1.2.0/go.mod h1:V4h9r+4sF5HnzqbwIez0fKSpANP0zlYd3qR7p36jkTQ=
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY=
@@ -823,15 +798,12 @@ github.com/graph-gophers/graphql-go v1.5.0 h1:fDqblo50TEpD0LY7RXk/LFVYEVqo3+tXMN
github.com/graph-gophers/graphql-go v1.5.0/go.mod h1:YtmJZDLbF1YYNrlNAuiO5zAStUWc3XZT07iGsVqe1Os=
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA=
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
-github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw=
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA=
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU=
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 h1:pRhl55Yx1eC7BZ1N+BBWwnKaMyD8uC+34TLdndZMAKk=
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0/go.mod h1:XKMd7iuf/RGPSMJ/U4HP0zS2Z9Fh8Ps9a+6X26m/tmI=
-github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
-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.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys=
@@ -904,7 +876,6 @@ github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
-github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/golang-lru v0.6.0 h1:uL2shRDx7RTrOrTCUZEGP/wJUFiUI8QT6E7z5o8jga4=
github.com/hashicorp/golang-lru v0.6.0/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
@@ -1007,10 +978,8 @@ github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0f
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
-github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c=
github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo=
-github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
@@ -1021,7 +990,6 @@ github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
-github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4=
github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
@@ -1046,7 +1014,6 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.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.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
@@ -1096,7 +1063,6 @@ github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/z
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
-github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
@@ -1197,7 +1163,6 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0=
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4=
-github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE=
github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
@@ -1218,7 +1183,6 @@ github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
-github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229/go.mod h1:0aYXnNPJ8l7uZxf45rWW1a/uME32OF0rhiYGNQ2oF2E=
github.com/nsf/jsondiff v0.0.0-20230430225905-43f6cf3098c1 h1:dOYG7LS/WK00RWZc8XGgcUTlTxpp3mKhdR2Q9z9HbXM=
github.com/nsf/jsondiff v0.0.0-20230430225905-43f6cf3098c1/go.mod h1:mpRZBD8SJ55OIICQ3iWH0Yz3cjzA61JdqMLoWXeB2+8=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
@@ -1309,7 +1273,6 @@ github.com/pressly/goose/v3 v3.21.1/go.mod h1:sqthmzV8PitchEkjecFJII//l43dLOCzfW
github.com/prometheus/alertmanager v0.27.0 h1:V6nTa2J5V4s8TG4C4HtrBP/WNSebCCTYGGv4qecA/+I=
github.com/prometheus/alertmanager v0.27.0/go.mod h1:8Ia/R3urPmbzJ8OsdvmZvIprDwvwmYCmUbwBL+jlPOE=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
-github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
@@ -1322,8 +1285,6 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
-github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
-github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
@@ -1336,7 +1297,6 @@ github.com/prometheus/common/sigv4 v0.1.0/go.mod h1:2Jkxxk9yYvCkE5G1sQT7GuEXm57J
github.com/prometheus/exporter-toolkit v0.11.0 h1:yNTsuZ0aNCNFQ3aFTD2uhPOvr4iD7fdBvKPAEGkNf+g=
github.com/prometheus/exporter-toolkit v0.11.0/go.mod h1:BVnENhnNecpwoTLiABx7mrPB/OLRIgN74qlQbV+FK1Q=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
-github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
@@ -1345,7 +1305,6 @@ github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0leargg
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/prometheus/prometheus v0.54.1 h1:vKuwQNjnYN2/mDoWfHXDhAsz/68q/dQDb+YbcEqU7MQ=
github.com/prometheus/prometheus v0.54.1/go.mod h1:xlLByHhk2g3ycakQGrMaU8K7OySZx98BzeCR99991NY=
-github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rakyll/statik v0.1.7 h1:OF3QCZUuyPxuGEP7B4ypUa7sB/iHtqOTDYZXGM8KOdQ=
github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Unghqrcc=
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM=
@@ -1357,7 +1316,6 @@ github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
-github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
@@ -1432,12 +1390,14 @@ github.com/slack-go/slack v0.15.0 h1:LE2lj2y9vqqiOf+qIIy0GvEoxgF1N5yLGZffmEZykt0
github.com/slack-go/slack v0.15.0/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw=
github.com/smartcontractkit/ccip-owner-contracts v0.0.0-salt-fix h1:DPJD++yKLSx0EfT+U14P8vLVxjXFmoIETiCO9lVwQo8=
github.com/smartcontractkit/ccip-owner-contracts v0.0.0-salt-fix/go.mod h1:NnT6w4Kj42OFFXhSx99LvJZWPpMjmo4+CpDEWfw61xY=
-github.com/smartcontractkit/chain-selectors v1.0.34 h1:MJ17OGu8+jjl426pcKrJkCf3fePb3eCreuAnUA3RBj4=
-github.com/smartcontractkit/chain-selectors v1.0.34/go.mod h1:xsKM0aN3YGcQKTPRPDDtPx2l4mlTN1Djmg0VVXV40b8=
+github.com/smartcontractkit/chain-selectors v1.0.36 h1:KSpO8I+JOiuyN4FuXsV471sPorGF//PAqwq2Cm4gRK0=
+github.com/smartcontractkit/chain-selectors v1.0.36/go.mod h1:xsKM0aN3YGcQKTPRPDDtPx2l4mlTN1Djmg0VVXV40b8=
github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgBc2xpDKBco/Q4h4ydl6+UUU=
github.com/smartcontractkit/chainlink-automation v0.8.1/go.mod h1:Iij36PvWZ6blrdC5A/nrQUBuf3MH3JvsBB9sSyc9W08=
github.com/smartcontractkit/chainlink-ccip v0.0.0-20241218114855-f74219171000 h1:6Zzr/R1j6P7bbvcUlt5WUIbItvrrGdGzIsiAzQezcwo=
github.com/smartcontractkit/chainlink-ccip v0.0.0-20241218114855-f74219171000/go.mod h1:ncjd6mPZSRlelEqH/2KeLE1pU3UlqzBSn8RYkEoECzY=
+github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250103152858-8973fd0c912b h1:UBXi9Yj8YSMHDDaxQLu273x1fWjyEL9xP58nuJsqZfg=
+github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250103152858-8973fd0c912b/go.mod h1:Bmwq4lNb5tE47sydN0TKetcLEGbgl+VxHEWp4S0LI60=
github.com/smartcontractkit/chainlink-common v0.4.1-0.20241223143929-db7919d60550 h1:rRs74zjDJ7do5aYEXSU/sOnLnlbYCNqM8BrvEx/0NH8=
github.com/smartcontractkit/chainlink-common v0.4.1-0.20241223143929-db7919d60550/go.mod h1:yti7e1+G9hhkYhj+L5sVUULn9Bn3bBL5/AxaNqdJ5YQ=
github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e h1:PRoeby6ZlTuTkv2f+7tVU4+zboTfRzI+beECynF4JQ0=
@@ -1480,7 +1440,6 @@ github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60=
-github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js=
github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
github.com/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg=
@@ -1499,7 +1458,6 @@ github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
-github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
@@ -1509,15 +1467,13 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
-github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
-github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA=
github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg=
-github.com/streamingfast/logging v0.0.0-20220405224725-2755dab2ce75 h1:ZqpS7rAhhKD7S7DnrpEdrnW1/gZcv82ytpMviovkli4=
-github.com/streamingfast/logging v0.0.0-20220405224725-2755dab2ce75/go.mod h1:VlduQ80JcGJSargkRU4Sg9Xo63wZD/l8A5NC/Uo1/uU=
+github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 h1:RN5mrigyirb8anBEtdjtHFIufXdacyTi6i4KBfeNXeo=
+github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091/go.mod h1:VlduQ80JcGJSargkRU4Sg9Xo63wZD/l8A5NC/Uo1/uU=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
@@ -1549,9 +1505,6 @@ github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDd
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48=
github.com/tendermint/go-amino v0.16.0 h1:GyhmgQKvqF82e2oZeuMSp9JTN0N09emoSZlb2lyGa2E=
github.com/tendermint/go-amino v0.16.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME=
-github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf/go.mod h1:M8agBzgqHIhgj7wEn9/0hJUZcrvt9VY+Ln+S1I5Mha0=
-github.com/teris-io/shortid v0.0.0-20201117134242-e59966efd125 h1:3SNcvBmEPE1YlB1JpVZouslJpI3GBNoiqW7+wb0Rz7w=
-github.com/teris-io/shortid v0.0.0-20201117134242-e59966efd125/go.mod h1:M8agBzgqHIhgj7wEn9/0hJUZcrvt9VY+Ln+S1I5Mha0=
github.com/test-go/testify v1.1.4 h1:Tf9lntrKUMHiXQ07qBScBTSA0dhYQlu83hswqelv1iE=
github.com/test-go/testify v1.1.4/go.mod h1:rH7cfJo/47vWGdi4GPj16x3/t1xGOj2YxzmNQzk2ghU=
github.com/testcontainers/testcontainers-go v0.34.0 h1:5fbgF0vIN5u+nD3IWabQwRybuB4GY8G2HHgCkbMzMHo=
@@ -1560,12 +1513,10 @@ github.com/theodesp/go-heaps v0.0.0-20190520121037-88e35354fe0a h1:YuO+afVc3eqrj
github.com/theodesp/go-heaps v0.0.0-20190520121037-88e35354fe0a/go.mod h1:/sfW47zCZp9FrtGcWyo1VjbgDaodxX9ovZvgLb/MxaA=
github.com/tidwall/btree v1.6.0 h1:LDZfKfQIBHGHWSwckhXI0RPSXzlo+KYdjK7FWSqOzzg=
github.com/tidwall/btree v1.6.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY=
-github.com/tidwall/gjson v1.9.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM=
github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
-github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
@@ -1573,7 +1524,6 @@ github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFA
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
-github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
@@ -1600,10 +1550,8 @@ github.com/urfave/cli v1.22.14 h1:ebbhrRiGK2i4naQJr+1Xj92HXZCrK7MsyTS/ob3HnAk=
github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA=
github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w=
github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
-github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fastjson v1.4.1 h1:hrltpHpIpkaxll8QltMU8c3QZ5+qIiCL8yKqPFJI/yE=
github.com/valyala/fastjson v1.4.1/go.mod h1:nV6MsjxL2IMJQUoHDIrjEI7oLyeqK6aBD7EFWPsvP8o=
-github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
github.com/vektah/gqlparser/v2 v2.5.11 h1:JJxLtXIoN7+3x6MBdtIP59TP1RANnY7pXOaDnADQSf8=
github.com/vektah/gqlparser/v2 v2.5.11/go.mod h1:1rCcfwB2ekJofmluGWXMSEnPMZgbxzwj6FaZ/4OT8Cc=
github.com/vultr/govultr/v2 v2.17.2 h1:gej/rwr91Puc/tgh+j33p/BLR16UrIPnSr+AIwYWZQs=
@@ -1614,10 +1562,6 @@ github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
-github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
-github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=
-github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=
-github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ=
github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
@@ -1625,7 +1569,6 @@ github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGC
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
-github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@@ -1652,7 +1595,6 @@ go.dedis.ch/kyber/v3 v3.1.0/go.mod h1:kXy7p3STAurkADD+/aZcsznZGKVHEqbtmdIzvPfrs1
go.dedis.ch/protobuf v1.0.5/go.mod h1:eIV4wicvi6JK0q/QnfIEGeSFNG0ZeB24kzut5+HaRLo=
go.dedis.ch/protobuf v1.0.7/go.mod h1:pv5ysfkDX/EawiPqcW3ikOxsL5t+BqnV6xHSmE79KI4=
go.dedis.ch/protobuf v1.0.11/go.mod h1:97QR256dnkimeNdfmURz0wAMNVbd1VmLXhG1CrTYrJ4=
-go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI=
go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE=
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
@@ -1664,12 +1606,10 @@ go.etcd.io/etcd/client/pkg/v3 v3.5.14/go.mod h1:8uMgAokyG1czCtIdsq+AGyYQMvpIKnSv
go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
go.etcd.io/etcd/client/v3 v3.5.14 h1:CWfRs4FDaDoSz81giL7zPpZH2Z35tbOrAJkkjMqOupg=
go.etcd.io/etcd/client/v3 v3.5.14/go.mod h1:k3XfdV/VIHy/97rqWjoUzrj9tk7GgJGH9J8L4dNXmAk=
-go.mongodb.org/mongo-driver v1.11.0/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8=
go.mongodb.org/mongo-driver v1.15.0 h1:rJCKC8eEliewXjZGf0ddURtl7tTVy1TK3bfl0gkUSLc=
go.mongodb.org/mongo-driver v1.15.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
-go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
@@ -1743,15 +1683,12 @@ go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKY
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
-go.uber.org/ratelimit v0.2.0/go.mod h1:YYBV4e4naJvhpitQrWJu1vCpgB7CboMe0qhltKt6mUg=
go.uber.org/ratelimit v0.3.1 h1:K4qVE+byfv/B3tC+4nYWP7v/6SimcO7HzHekoMNBma0=
go.uber.org/ratelimit v0.3.1/go.mod h1:6euWsTB6U/Nb3X++xEUXA8ciPJvr19Q/0h1+oDcJhRk=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
-go.uber.org/zap v1.14.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
-go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
@@ -1782,7 +1719,6 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-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.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.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
@@ -1797,8 +1733,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-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
-golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
+golang.org/x/exp v0.0.0-20241210194714-1829a127f884 h1:Y/Mj/94zIQQGHVSv1tTtQBDaQaJe62U9bkDZKKyhPCU=
+golang.org/x/exp v0.0.0-20241210194714-1829a127f884/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
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=
@@ -1827,15 +1763,14 @@ 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.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
-golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
-golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
+golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
+golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@@ -1886,8 +1821,8 @@ golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
-golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
-golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
+golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI=
+golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs=
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=
@@ -1923,7 +1858,6 @@ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190124100055-b90733256f2e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1939,7 +1873,6 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -2015,7 +1948,6 @@ golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.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=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
@@ -2064,7 +1996,6 @@ golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@@ -2074,7 +2005,6 @@ golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
@@ -2110,8 +2040,8 @@ 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.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
-golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
-golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
+golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8=
+golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw=
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=
@@ -2129,7 +2059,6 @@ google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEt
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
-google.golang.org/api v0.10.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
@@ -2154,7 +2083,6 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
-google.golang.org/appengine v1.6.2/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
@@ -2165,7 +2093,6 @@ google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRn
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190716160619-c506a9f90610/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
@@ -2215,7 +2142,6 @@ google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmE
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=
-google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
@@ -2268,16 +2194,13 @@ gopkg.in/guregu/null.v4 v4.0.0/go.mod h1:YoQhUrADuG3i9WqesrCmpNRwm1ypAgSHYqoOcTu
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
-gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
-gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
-gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
From 0199523f9042d58c87ea2909e10e7468aa5b36c1 Mon Sep 17 00:00:00 2001
From: cfal
Date: Thu, 9 Jan 2025 12:50:22 +0000
Subject: [PATCH 19/91] add TRON integration (#14783)
* Add tron integration
* .changeset/wise-buttons-fry.md: add changeset
* .mockery.yaml: add tron keystore
* core/services: update mocks
* core: cleanup chain type
* add to graphql
* rename TronLoopKeystore -> TronLOOPKeystore
* clean up keystore addresses
* fix keystore chaintypes
* fix graphql errors
* fix linting
* fix linting
* chore: regenerate mock
---------
Co-authored-by: Calvin Wang
Co-authored-by: Calvin <78729586+calvwang9@users.noreply.github.com>
Co-authored-by: Graham Goh
---
.changeset/wise-buttons-fry.md | 5 +
.mockery.yaml | 3 +-
core/cmd/app.go | 1 +
core/cmd/shell.go | 7 +
core/cmd/shell_local.go | 6 +
core/cmd/tron_keys_commands.go | 57 ++
core/cmd/tron_keys_commands_test.go | 174 ++++++
core/config/app_config.go | 1 +
core/config/docs/chains-tron.toml | 13 +
core/config/env/env.go | 1 +
core/internal/cltest/cltest.go | 9 +
core/services/chainlink/config.go | 7 +
core/services/chainlink/config_general.go | 13 +
.../services/chainlink/config_general_test.go | 1 +
core/services/chainlink/config_test.go | 7 +-
.../chainlink/mocks/general_config.go | 92 +++
.../chainlink/relayer_chain_interoperators.go | 17 +
.../relayer_chain_interoperators_test.go | 2 +
core/services/chainlink/relayer_factory.go | 11 +
.../chainlink/testdata/config-invalid.toml | 9 +
core/services/chainlink/types.go | 1 +
core/services/job/job_orm_test.go | 12 +
core/services/job/orm.go | 5 +
core/services/keystore/chaintype/chaintype.go | 8 +-
core/services/keystore/keys/ocr2key/export.go | 2 +
.../keystore/keys/ocr2key/export_test.go | 1 +
.../keystore/keys/ocr2key/key_bundle.go | 6 +
.../services/keystore/keys/tronkey/account.go | 178 ++++++
.../keystore/keys/tronkey/account_test.go | 177 ++++++
core/services/keystore/keys/tronkey/export.go | 46 ++
.../keystore/keys/tronkey/export_test.go | 19 +
core/services/keystore/keys/tronkey/key.go | 109 ++++
.../keystore/keys/tronkey/key_test.go | 85 +++
core/services/keystore/keystoretest.go | 1 +
core/services/keystore/master.go | 10 +
core/services/keystore/mocks/master.go | 47 ++
core/services/keystore/mocks/tron.go | 534 ++++++++++++++++++
core/services/keystore/models.go | 18 +
core/services/keystore/models_test.go | 7 +
core/services/keystore/tron.go | 187 ++++++
core/services/keystore/tron_test.go | 240 ++++++++
core/services/relay/relay.go | 2 +
core/web/auth/auth_test.go | 4 +
core/web/presenters/node_test.go | 2 +-
core/web/presenters/tron_chain.go | 45 ++
core/web/presenters/tron_key.go | 34 ++
.../feeds_manager_chain_config_test.go | 75 +++
core/web/resolver/ocr2_keys.go | 6 +
core/web/resolver/ocr2_keys_test.go | 1 +
core/web/resolver/query.go | 13 +
core/web/resolver/resolver_test.go | 2 +
core/web/resolver/tron_key.go | 43 ++
core/web/resolver/tron_key_test.go | 74 +++
core/web/router.go | 1 +
core/web/schema/schema.graphql | 1 +
core/web/schema/type/ocr2_keys.graphql | 1 +
core/web/schema/type/tron_key.graphql | 7 +
core/web/tron_keys_controller.go | 12 +
core/web/tron_keys_controller_test.go | 105 ++++
.../environment/nodeclient/chainlink.go | 28 +
.../nodeclient/chainlink_models.go | 42 ++
testdata/scripts/chains/help.txtar | 1 +
testdata/scripts/chains/tron/help.txtar | 16 +
testdata/scripts/chains/tron/list/help.txtar | 9 +
testdata/scripts/help-all/help-all.txtar | 10 +
testdata/scripts/keys/help.txtar | 1 +
testdata/scripts/keys/tron/help.txtar | 20 +
.../node/validate/invalid-duplicates.txtar | 18 +-
testdata/scripts/nodes/help.txtar | 1 +
testdata/scripts/nodes/tron/help.txtar | 16 +
testdata/scripts/nodes/tron/list/help.txtar | 9 +
71 files changed, 2723 insertions(+), 5 deletions(-)
create mode 100644 .changeset/wise-buttons-fry.md
create mode 100644 core/cmd/tron_keys_commands.go
create mode 100644 core/cmd/tron_keys_commands_test.go
create mode 100644 core/config/docs/chains-tron.toml
create mode 100644 core/services/keystore/keys/tronkey/account.go
create mode 100644 core/services/keystore/keys/tronkey/account_test.go
create mode 100644 core/services/keystore/keys/tronkey/export.go
create mode 100644 core/services/keystore/keys/tronkey/export_test.go
create mode 100644 core/services/keystore/keys/tronkey/key.go
create mode 100644 core/services/keystore/keys/tronkey/key_test.go
create mode 100644 core/services/keystore/mocks/tron.go
create mode 100644 core/services/keystore/tron.go
create mode 100644 core/services/keystore/tron_test.go
create mode 100644 core/web/presenters/tron_chain.go
create mode 100644 core/web/presenters/tron_key.go
create mode 100644 core/web/resolver/tron_key.go
create mode 100644 core/web/resolver/tron_key_test.go
create mode 100644 core/web/schema/type/tron_key.graphql
create mode 100644 core/web/tron_keys_controller.go
create mode 100644 core/web/tron_keys_controller_test.go
create mode 100644 testdata/scripts/chains/tron/help.txtar
create mode 100644 testdata/scripts/chains/tron/list/help.txtar
create mode 100644 testdata/scripts/keys/tron/help.txtar
create mode 100644 testdata/scripts/nodes/tron/help.txtar
create mode 100644 testdata/scripts/nodes/tron/list/help.txtar
diff --git a/.changeset/wise-buttons-fry.md b/.changeset/wise-buttons-fry.md
new file mode 100644
index 00000000000..aa3cb1dab79
--- /dev/null
+++ b/.changeset/wise-buttons-fry.md
@@ -0,0 +1,5 @@
+---
+"chainlink": patch
+---
+
+Add TRON integration #added
diff --git a/.mockery.yaml b/.mockery.yaml
index 73f46deed57..7fd4ff242bb 100644
--- a/.mockery.yaml
+++ b/.mockery.yaml
@@ -221,6 +221,7 @@ packages:
StarkNet:
config:
filename: starknet.go
+ Tron:
VRF:
Workflow:
github.com/smartcontractkit/chainlink/v2/core/services/ocr:
@@ -579,4 +580,4 @@ packages:
dir: "{{ .InterfaceDir }}"
github.com/smartcontractkit/chainlink/v2/core/capabilities/targets:
interfaces:
- ContractValueGetter:
\ No newline at end of file
+ ContractValueGetter:
diff --git a/core/cmd/app.go b/core/cmd/app.go
index 8128d578238..f605b3973c7 100644
--- a/core/cmd/app.go
+++ b/core/cmd/app.go
@@ -201,6 +201,7 @@ func NewApp(s *Shell) *cli.App {
keysCommand("Solana", NewSolanaKeysClient(s)),
keysCommand("StarkNet", NewStarkNetKeysClient(s)),
keysCommand("Aptos", NewAptosKeysClient(s)),
+ keysCommand("Tron", NewTronKeysClient(s)),
initVRFKeysSubCmd(s),
},
diff --git a/core/cmd/shell.go b/core/cmd/shell.go
index 94664a3cf3d..09eacd7dc39 100644
--- a/core/cmd/shell.go
+++ b/core/cmd/shell.go
@@ -293,6 +293,13 @@ func (n ChainlinkAppFactory) NewApplication(ctx context.Context, cfg chainlink.G
}
initOps = append(initOps, chainlink.InitAptos(ctx, relayerFactory, aptosCfg))
}
+ if cfg.TronEnabled() {
+ tronCfg := chainlink.TronFactoryConfig{
+ Keystore: keyStore.Tron(),
+ TOMLConfigs: cfg.TronConfigs(),
+ }
+ initOps = append(initOps, chainlink.InitTron(ctx, relayerFactory, tronCfg))
+ }
relayChainInterops, err := chainlink.NewCoreRelayerChainInteroperators(initOps...)
if err != nil {
diff --git a/core/cmd/shell_local.go b/core/cmd/shell_local.go
index 1fdc1a46d34..ed71c5be369 100644
--- a/core/cmd/shell_local.go
+++ b/core/cmd/shell_local.go
@@ -469,6 +469,12 @@ func (s *Shell) runNode(c *cli.Context) error {
return errors.Wrap(err2, "failed to ensure aptos key")
}
}
+ if s.Config.TronEnabled() {
+ err2 := app.GetKeyStore().Tron().EnsureKey(rootCtx)
+ if err2 != nil {
+ return errors.Wrap(err2, "failed to ensure tron key")
+ }
+ }
err2 := app.GetKeyStore().Workflow().EnsureKey(rootCtx)
if err2 != nil {
diff --git a/core/cmd/tron_keys_commands.go b/core/cmd/tron_keys_commands.go
new file mode 100644
index 00000000000..67b3242e1f5
--- /dev/null
+++ b/core/cmd/tron_keys_commands.go
@@ -0,0 +1,57 @@
+package cmd
+
+import (
+ "github.com/smartcontractkit/chainlink-common/pkg/utils"
+ "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/tronkey"
+ "github.com/smartcontractkit/chainlink/v2/core/web/presenters"
+)
+
+type TronKeyPresenter struct {
+ JAID
+ presenters.TronKeyResource
+}
+
+// RenderTable implements TableRenderer
+func (p TronKeyPresenter) RenderTable(rt RendererTable) error {
+ headers := []string{"ID", "Public key"}
+ rows := [][]string{p.ToRow()}
+
+ if _, err := rt.Write([]byte("🔑 Tron Keys\n")); err != nil {
+ return err
+ }
+ renderList(headers, rows, rt.Writer)
+
+ return utils.JustError(rt.Write([]byte("\n")))
+}
+
+func (p *TronKeyPresenter) ToRow() []string {
+ row := []string{
+ p.ID,
+ p.PubKey,
+ }
+
+ return row
+}
+
+type TronKeyPresenters []TronKeyPresenter
+
+// RenderTable implements TableRenderer
+func (ps TronKeyPresenters) RenderTable(rt RendererTable) error {
+ headers := []string{"ID", "Public key"}
+ rows := [][]string{}
+
+ for _, p := range ps {
+ rows = append(rows, p.ToRow())
+ }
+
+ if _, err := rt.Write([]byte("🔑 Tron Keys\n")); err != nil {
+ return err
+ }
+ renderList(headers, rows, rt.Writer)
+
+ return utils.JustError(rt.Write([]byte("\n")))
+}
+
+func NewTronKeysClient(s *Shell) KeysClient {
+ return newKeysClient[tronkey.Key, TronKeyPresenter, TronKeyPresenters]("Tron", s)
+}
diff --git a/core/cmd/tron_keys_commands_test.go b/core/cmd/tron_keys_commands_test.go
new file mode 100644
index 00000000000..29480600d74
--- /dev/null
+++ b/core/cmd/tron_keys_commands_test.go
@@ -0,0 +1,174 @@
+package cmd_test
+
+import (
+ "bytes"
+ "context"
+ "flag"
+ "os"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ "github.com/urfave/cli"
+
+ "github.com/smartcontractkit/chainlink-common/pkg/utils"
+ "github.com/smartcontractkit/chainlink/v2/core/cmd"
+ "github.com/smartcontractkit/chainlink/v2/core/internal/cltest"
+ "github.com/smartcontractkit/chainlink/v2/core/internal/testutils"
+ "github.com/smartcontractkit/chainlink/v2/core/services/chainlink"
+ "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/tronkey"
+ "github.com/smartcontractkit/chainlink/v2/core/web/presenters"
+)
+
+func TestTronKeyPresenter_RenderTable(t *testing.T) {
+ t.Parallel()
+
+ var (
+ id = "1"
+ pubKey = "somepubkey"
+ buffer = bytes.NewBufferString("")
+ r = cmd.RendererTable{Writer: buffer}
+ )
+
+ p := cmd.TronKeyPresenter{
+ JAID: cmd.JAID{ID: id},
+ TronKeyResource: presenters.TronKeyResource{
+ JAID: presenters.NewJAID(id),
+ PubKey: pubKey,
+ },
+ }
+
+ // Render a single resource
+ require.NoError(t, p.RenderTable(r))
+
+ output := buffer.String()
+ assert.Contains(t, output, id)
+ assert.Contains(t, output, pubKey)
+
+ // Render many resources
+ buffer.Reset()
+ ps := cmd.TronKeyPresenters{p}
+ require.NoError(t, ps.RenderTable(r))
+
+ output = buffer.String()
+ assert.Contains(t, output, id)
+ assert.Contains(t, output, pubKey)
+}
+
+func TestShell_TronKeys(t *testing.T) {
+ app := startNewApplicationV2(t, nil)
+ ks := app.GetKeyStore().Tron()
+ cleanup := func() {
+ ctx := context.Background()
+ keys, err := ks.GetAll()
+ require.NoError(t, err)
+ for _, key := range keys {
+ require.NoError(t, utils.JustError(ks.Delete(ctx, key.ID())))
+ }
+ requireTronKeyCount(t, app, 0)
+ }
+
+ t.Run("ListTronKeys", func(tt *testing.T) {
+ defer cleanup()
+ ctx := testutils.Context(t)
+ client, r := app.NewShellAndRenderer()
+ key, err := app.GetKeyStore().Tron().Create(ctx)
+ require.NoError(t, err)
+ requireTronKeyCount(t, app, 1)
+ require.NoError(t, cmd.NewTronKeysClient(client).ListKeys(cltest.EmptyCLIContext()))
+ require.Len(t, r.Renders, 1)
+ keys := *r.Renders[0].(*cmd.TronKeyPresenters)
+ assert.Equal(t, key.PublicKeyStr(), keys[0].PubKey)
+ })
+
+ t.Run("CreateTronKey", func(tt *testing.T) {
+ defer cleanup()
+ client, _ := app.NewShellAndRenderer()
+ require.NoError(t, cmd.NewTronKeysClient(client).CreateKey(nilContext))
+ keys, err := app.GetKeyStore().Tron().GetAll()
+ require.NoError(t, err)
+ require.Len(t, keys, 1)
+ })
+
+ t.Run("DeleteTronKey", func(tt *testing.T) {
+ defer cleanup()
+ ctx := testutils.Context(t)
+ client, _ := app.NewShellAndRenderer()
+ key, err := app.GetKeyStore().Tron().Create(ctx)
+ require.NoError(t, err)
+ requireTronKeyCount(t, app, 1)
+ set := flag.NewFlagSet("test", 0)
+ flagSetApplyFromAction(cmd.NewTronKeysClient(client).DeleteKey, set, "tron")
+
+ require.NoError(tt, set.Set("yes", "true"))
+
+ strID := key.ID()
+ err = set.Parse([]string{strID})
+ require.NoError(t, err)
+ c := cli.NewContext(nil, set, nil)
+ err = cmd.NewTronKeysClient(client).DeleteKey(c)
+ require.NoError(t, err)
+ requireTronKeyCount(t, app, 0)
+ })
+
+ t.Run("ImportExportTronKey", func(tt *testing.T) {
+ defer cleanup()
+ defer deleteKeyExportFile(t)
+ ctx := testutils.Context(t)
+ client, _ := app.NewShellAndRenderer()
+
+ _, err := app.GetKeyStore().Tron().Create(ctx)
+ require.NoError(t, err)
+
+ keys := requireTronKeyCount(t, app, 1)
+ key := keys[0]
+ keyName := keyNameForTest(t)
+
+ // Export test invalid id
+ set := flag.NewFlagSet("test Tron export", 0)
+ flagSetApplyFromAction(cmd.NewTronKeysClient(client).ExportKey, set, "tron")
+
+ require.NoError(tt, set.Parse([]string{"0"}))
+ require.NoError(tt, set.Set("new-password", "../internal/fixtures/incorrect_password.txt"))
+ require.NoError(tt, set.Set("output", keyName))
+
+ c := cli.NewContext(nil, set, nil)
+ err = cmd.NewTronKeysClient(client).ExportKey(c)
+ require.Error(t, err, "Error exporting")
+ require.Error(t, utils.JustError(os.Stat(keyName)))
+
+ // Export test
+ set = flag.NewFlagSet("test Tron export", 0)
+ flagSetApplyFromAction(cmd.NewTronKeysClient(client).ExportKey, set, "tron")
+
+ require.NoError(tt, set.Parse([]string{key.ID()}))
+ require.NoError(tt, set.Set("new-password", "../internal/fixtures/incorrect_password.txt"))
+ require.NoError(tt, set.Set("output", keyName))
+
+ c = cli.NewContext(nil, set, nil)
+
+ require.NoError(t, cmd.NewTronKeysClient(client).ExportKey(c))
+ require.NoError(t, utils.JustError(os.Stat(keyName)))
+
+ require.NoError(t, utils.JustError(app.GetKeyStore().Tron().Delete(ctx, key.ID())))
+ requireTronKeyCount(t, app, 0)
+
+ set = flag.NewFlagSet("test Tron import", 0)
+ flagSetApplyFromAction(cmd.NewTronKeysClient(client).ImportKey, set, "tron")
+
+ require.NoError(tt, set.Parse([]string{keyName}))
+ require.NoError(tt, set.Set("old-password", "../internal/fixtures/incorrect_password.txt"))
+ c = cli.NewContext(nil, set, nil)
+ require.NoError(t, cmd.NewTronKeysClient(client).ImportKey(c))
+
+ requireTronKeyCount(t, app, 1)
+ })
+}
+
+func requireTronKeyCount(t *testing.T, app chainlink.Application, length int) []tronkey.Key {
+ t.Helper()
+ keys, err := app.GetKeyStore().Tron().GetAll()
+ require.NoError(t, err)
+ require.Len(t, keys, length)
+ return keys
+}
diff --git a/core/config/app_config.go b/core/config/app_config.go
index 3f2a5472b24..4ce8873bb96 100644
--- a/core/config/app_config.go
+++ b/core/config/app_config.go
@@ -25,6 +25,7 @@ type AppConfig interface {
SolanaEnabled() bool
StarkNetEnabled() bool
AptosEnabled() bool
+ TronEnabled() bool
Validate() error
ValidateDB() error
diff --git a/core/config/docs/chains-tron.toml b/core/config/docs/chains-tron.toml
new file mode 100644
index 00000000000..55a44bacd7a
--- /dev/null
+++ b/core/config/docs/chains-tron.toml
@@ -0,0 +1,13 @@
+[[Tron]]
+# ChainID is the Tron chain ID.
+ChainID = 'foobar' # Example
+# Enabled enables this chain.
+Enabled = true # Default
+
+[[Tron.Nodes]]
+# Name is a unique (per-chain) identifier for this node.
+Name = 'primary' # Example
+# URL is the full node HTTP endpoint for this node.
+URL = 'https://api.trongrid.io/wallet' # Example
+# SolidityURL is the solidity node HTTP endpoint for this node.
+SolidityURL = 'http://api.trongrid.io/wallet' # Example
diff --git a/core/config/env/env.go b/core/config/env/env.go
index c34cd7f4f5e..68b79c7575c 100644
--- a/core/config/env/env.go
+++ b/core/config/env/env.go
@@ -29,6 +29,7 @@ var (
MercuryPlugin = NewPlugin("mercury")
SolanaPlugin = NewPlugin("solana")
StarknetPlugin = NewPlugin("starknet")
+ TronPlugin = NewPlugin("tron")
// PrometheusDiscoveryHostName is the externally accessible hostname
// published by the node in the `/discovery` endpoint. Generally, it is expected to match
// the public hostname of node.
diff --git a/core/internal/cltest/cltest.go b/core/internal/cltest/cltest.go
index 85faad8e5fe..5780410ab60 100644
--- a/core/internal/cltest/cltest.go
+++ b/core/internal/cltest/cltest.go
@@ -82,6 +82,7 @@ import (
"github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey"
"github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/solkey"
"github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/starkkey"
+ "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/tronkey"
"github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/vrfkey"
"github.com/smartcontractkit/chainlink/v2/core/services/pg"
"github.com/smartcontractkit/chainlink/v2/core/services/pipeline"
@@ -133,6 +134,7 @@ var (
DefaultSolanaKey = solkey.MustNewInsecure(keystest.NewRandReaderFromSeed(KeyBigIntSeed))
DefaultStarkNetKey = starkkey.MustNewInsecure(keystest.NewRandReaderFromSeed(KeyBigIntSeed))
DefaultAptosKey = aptoskey.MustNewInsecure(keystest.NewRandReaderFromSeed(KeyBigIntSeed))
+ DefaultTronKey = tronkey.MustNewInsecure(keystest.NewRandReaderFromSeed(KeyBigIntSeed))
DefaultVRFKey = vrfkey.MustNewV2XXXTestingOnly(big.NewInt(KeyBigIntSeed))
)
@@ -471,6 +473,13 @@ func NewApplicationWithConfig(t testing.TB, cfg chainlink.GeneralConfig, flagsAn
}
initOps = append(initOps, chainlink.InitAptos(ctx, relayerFactory, aptosCfg))
}
+ if cfg.TronEnabled() {
+ tronCfg := chainlink.TronFactoryConfig{
+ Keystore: keyStore.Tron(),
+ TOMLConfigs: cfg.TronConfigs(),
+ }
+ initOps = append(initOps, chainlink.InitTron(ctx, relayerFactory, tronCfg))
+ }
relayChainInterops, err := chainlink.NewCoreRelayerChainInteroperators(initOps...)
if err != nil {
diff --git a/core/services/chainlink/config.go b/core/services/chainlink/config.go
index 9f083ef89af..dd54856cf0a 100644
--- a/core/services/chainlink/config.go
+++ b/core/services/chainlink/config.go
@@ -46,6 +46,8 @@ type Config struct {
Starknet stkcfg.TOMLConfigs `toml:",omitempty"`
Aptos RawConfigs `toml:",omitempty"`
+
+ Tron RawConfigs `toml:",omitempty"`
}
// RawConfigs is a list of RawConfig.
@@ -260,6 +262,7 @@ func (c *Config) TOMLString() (string, error) {
// warnings aggregates warnings from valueWarnings and deprecationWarnings
func (c *Config) warnings() (err error) {
deprecationErr := c.deprecationWarnings()
+
warningErr := c.valueWarnings()
err = multierr.Append(deprecationErr, warningErr)
_, list := commonconfig.MultiErrorList(err)
@@ -352,6 +355,10 @@ func (c *Config) SetFrom(f *Config) (err error) {
err = multierr.Append(err, commonconfig.NamedMultiErrorList(err5, "Aptos"))
}
+ if err6 := c.Tron.SetFrom(f.Tron); err6 != nil {
+ err = multierr.Append(err, commonconfig.NamedMultiErrorList(err6, "Tron"))
+ }
+
_, err = commonconfig.MultiErrorList(err)
return err
diff --git a/core/services/chainlink/config_general.go b/core/services/chainlink/config_general.go
index dd0dc87b59a..e67d92fefc9 100644
--- a/core/services/chainlink/config_general.go
+++ b/core/services/chainlink/config_general.go
@@ -213,6 +213,10 @@ func (g *generalConfig) AptosConfigs() RawConfigs {
return g.c.Aptos
}
+func (g *generalConfig) TronConfigs() RawConfigs {
+ return g.c.Tron
+}
+
func (g *generalConfig) Validate() error {
return g.validate(g.secrets.Validate)
}
@@ -358,6 +362,15 @@ func (g *generalConfig) AptosEnabled() bool {
return false
}
+func (g *generalConfig) TronEnabled() bool {
+ for _, c := range g.c.Tron {
+ if c.IsEnabled() {
+ return true
+ }
+ }
+ return false
+}
+
func (g *generalConfig) WebServer() config.WebServer {
return &webServerConfig{c: g.c.WebServer, s: g.secrets.WebServer, rootDir: g.RootDir}
}
diff --git a/core/services/chainlink/config_general_test.go b/core/services/chainlink/config_general_test.go
index 29393ee0fdd..3f02b880baf 100644
--- a/core/services/chainlink/config_general_test.go
+++ b/core/services/chainlink/config_general_test.go
@@ -28,6 +28,7 @@ func TestTOMLGeneralConfig_Defaults(t *testing.T) {
assert.False(t, config.CosmosEnabled())
assert.False(t, config.SolanaEnabled())
assert.False(t, config.StarkNetEnabled())
+ assert.False(t, config.TronEnabled())
assert.Equal(t, false, config.JobPipeline().ExternalInitiatorsEnabled())
assert.Equal(t, 15*time.Minute, config.WebServer().SessionTimeout().Duration())
}
diff --git a/core/services/chainlink/config_test.go b/core/services/chainlink/config_test.go
index 7d5b8628651..eb467835b6f 100644
--- a/core/services/chainlink/config_test.go
+++ b/core/services/chainlink/config_test.go
@@ -1440,7 +1440,7 @@ func TestConfig_Validate(t *testing.T) {
toml string
exp string
}{
- {name: "invalid", toml: invalidTOML, exp: `invalid configuration: 8 errors:
+ {name: "invalid", toml: invalidTOML, exp: `invalid configuration: 9 errors:
- P2P.V2.Enabled: invalid value (false): P2P required for OCR or OCR2. Please enable P2P or disable OCR/OCR2.
- Database.Lock.LeaseRefreshInterval: invalid value (6s): must be less than or equal to half of LeaseDuration (10s)
- WebServer: 8 errors:
@@ -1537,6 +1537,11 @@ func TestConfig_Validate(t *testing.T) {
- Nodes: missing: must have at least one node
- Aptos: 2 errors:
- 0.Nodes.1.Name: invalid value (primary): duplicate - must be unique
+ - 0: 2 errors:
+ - Enabled: invalid value (1): expected bool
+ - ChainID: missing: required for all chains
+ - Tron: 2 errors:
+ - 0.Nodes.1.Name: invalid value (tron-test): duplicate - must be unique
- 0: 2 errors:
- Enabled: invalid value (1): expected bool
- ChainID: missing: required for all chains`},
diff --git a/core/services/chainlink/mocks/general_config.go b/core/services/chainlink/mocks/general_config.go
index 738909538f3..9df0ab85985 100644
--- a/core/services/chainlink/mocks/general_config.go
+++ b/core/services/chainlink/mocks/general_config.go
@@ -1959,6 +1959,98 @@ func (_c *GeneralConfig_Tracing_Call) RunAndReturn(run func() config.Tracing) *G
return _c
}
+// TronConfigs provides a mock function with no fields
+func (_m *GeneralConfig) TronConfigs() chainlink.RawConfigs {
+ ret := _m.Called()
+
+ if len(ret) == 0 {
+ panic("no return value specified for TronConfigs")
+ }
+
+ 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_TronConfigs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'TronConfigs'
+type GeneralConfig_TronConfigs_Call struct {
+ *mock.Call
+}
+
+// TronConfigs is a helper method to define mock.On call
+func (_e *GeneralConfig_Expecter) TronConfigs() *GeneralConfig_TronConfigs_Call {
+ return &GeneralConfig_TronConfigs_Call{Call: _e.mock.On("TronConfigs")}
+}
+
+func (_c *GeneralConfig_TronConfigs_Call) Run(run func()) *GeneralConfig_TronConfigs_Call {
+ _c.Call.Run(func(args mock.Arguments) {
+ run()
+ })
+ return _c
+}
+
+func (_c *GeneralConfig_TronConfigs_Call) Return(_a0 chainlink.RawConfigs) *GeneralConfig_TronConfigs_Call {
+ _c.Call.Return(_a0)
+ return _c
+}
+
+func (_c *GeneralConfig_TronConfigs_Call) RunAndReturn(run func() chainlink.RawConfigs) *GeneralConfig_TronConfigs_Call {
+ _c.Call.Return(run)
+ return _c
+}
+
+// TronEnabled provides a mock function with no fields
+func (_m *GeneralConfig) TronEnabled() bool {
+ ret := _m.Called()
+
+ if len(ret) == 0 {
+ panic("no return value specified for TronEnabled")
+ }
+
+ var r0 bool
+ if rf, ok := ret.Get(0).(func() bool); ok {
+ r0 = rf()
+ } else {
+ r0 = ret.Get(0).(bool)
+ }
+
+ return r0
+}
+
+// GeneralConfig_TronEnabled_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'TronEnabled'
+type GeneralConfig_TronEnabled_Call struct {
+ *mock.Call
+}
+
+// TronEnabled is a helper method to define mock.On call
+func (_e *GeneralConfig_Expecter) TronEnabled() *GeneralConfig_TronEnabled_Call {
+ return &GeneralConfig_TronEnabled_Call{Call: _e.mock.On("TronEnabled")}
+}
+
+func (_c *GeneralConfig_TronEnabled_Call) Run(run func()) *GeneralConfig_TronEnabled_Call {
+ _c.Call.Run(func(args mock.Arguments) {
+ run()
+ })
+ return _c
+}
+
+func (_c *GeneralConfig_TronEnabled_Call) Return(_a0 bool) *GeneralConfig_TronEnabled_Call {
+ _c.Call.Return(_a0)
+ return _c
+}
+
+func (_c *GeneralConfig_TronEnabled_Call) RunAndReturn(run func() bool) *GeneralConfig_TronEnabled_Call {
+ _c.Call.Return(run)
+ return _c
+}
+
// Validate provides a mock function with no fields
func (_m *GeneralConfig) Validate() error {
ret := _m.Called()
diff --git a/core/services/chainlink/relayer_chain_interoperators.go b/core/services/chainlink/relayer_chain_interoperators.go
index 2fc671bfe6e..6242af51935 100644
--- a/core/services/chainlink/relayer_chain_interoperators.go
+++ b/core/services/chainlink/relayer_chain_interoperators.go
@@ -204,6 +204,23 @@ func InitAptos(ctx context.Context, factory RelayerFactory, config AptosFactoryC
}
}
+// InitTron is a option for instantiating Tron relayers
+func InitTron(ctx context.Context, factory RelayerFactory, config TronFactoryConfig) CoreRelayerChainInitFunc {
+ return func(op *CoreRelayerChainInteroperators) error {
+ tronRelayers, err := factory.NewTron(config.Keystore, config.TOMLConfigs)
+ if err != nil {
+ return fmt.Errorf("failed to setup Tron relayer: %w", err)
+ }
+
+ for id, relayer := range tronRelayers {
+ 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_chain_interoperators_test.go b/core/services/chainlink/relayer_chain_interoperators_test.go
index a4bd8c168ba..f03e172542c 100644
--- a/core/services/chainlink/relayer_chain_interoperators_test.go
+++ b/core/services/chainlink/relayer_chain_interoperators_test.go
@@ -378,6 +378,8 @@ func TestCoreRelayerChainInteroperators(t *testing.T) {
expectedChainCnt, expectedNodeCnt = tt.expectedDummyChainCnt, tt.expectedDummyNodeCnt
case relay.NetworkAptos:
t.Skip("aptos doesn't need a CoreRelayerChainInteroperator")
+ case relay.NetworkTron:
+ t.Skip("tron doesn't need a CoreRelayerChainInteroperator")
default:
require.Fail(t, "untested relay network", relayNetwork)
diff --git a/core/services/chainlink/relayer_factory.go b/core/services/chainlink/relayer_factory.go
index a1571663d5a..c173c7fecb7 100644
--- a/core/services/chainlink/relayer_factory.go
+++ b/core/services/chainlink/relayer_factory.go
@@ -371,3 +371,14 @@ func (r *RelayerFactory) NewLOOPRelayer(name string, network string, plugin env.
}
return relayers, nil
}
+
+type TronFactoryConfig struct {
+ Keystore keystore.Tron
+ TOMLConfigs RawConfigs
+}
+
+func (r *RelayerFactory) NewTron(ks keystore.Tron, chainCfgs RawConfigs) (map[types.RelayID]loop.Relayer, error) {
+ plugin := env.NewPlugin("tron")
+ loopKs := &keystore.TronLOOPKeystore{Tron: ks}
+ return r.NewLOOPRelayer("Tron", relay.NetworkTron, plugin, loopKs, chainCfgs)
+}
diff --git a/core/services/chainlink/testdata/config-invalid.toml b/core/services/chainlink/testdata/config-invalid.toml
index 967ef76de8e..347530cec53 100644
--- a/core/services/chainlink/testdata/config-invalid.toml
+++ b/core/services/chainlink/testdata/config-invalid.toml
@@ -187,6 +187,15 @@ Name = 'primary'
[[Aptos.Nodes]]
Name = 'primary'
+[[Tron]]
+Enabled = 1
+
+[[Tron.Nodes]]
+Name = 'tron-test'
+
+[[Tron.Nodes]]
+Name = 'tron-test'
+
[OCR2]
Enabled = true
diff --git a/core/services/chainlink/types.go b/core/services/chainlink/types.go
index 74ffc5dc66d..53e7d2a9366 100644
--- a/core/services/chainlink/types.go
+++ b/core/services/chainlink/types.go
@@ -16,6 +16,7 @@ type GeneralConfig interface {
SolanaConfigs() solcfg.TOMLConfigs
StarknetConfigs() stkcfg.TOMLConfigs
AptosConfigs() RawConfigs
+ TronConfigs() RawConfigs
// ConfigTOML returns both the user provided and effective configuration as TOML.
ConfigTOML() (user, effective string)
}
diff --git a/core/services/job/job_orm_test.go b/core/services/job/job_orm_test.go
index 27223e0d706..7a310d6f791 100644
--- a/core/services/job/job_orm_test.go
+++ b/core/services/job/job_orm_test.go
@@ -1020,6 +1020,18 @@ func TestORM_ValidateKeyStoreMatch(t *testing.T) {
require.NoError(t, err)
})
+ t.Run(("test Tron key validation"), func(t *testing.T) {
+ ctx := testutils.Context(t)
+ jb.OCR2OracleSpec.Relay = relay.NetworkTron
+ err := job.ValidateKeyStoreMatch(ctx, jb.OCR2OracleSpec, keyStore, "bad key")
+ require.EqualError(t, err, "no Tron key matching: \"bad key\"")
+
+ tronKey, err := keyStore.Tron().Create(ctx)
+ require.NoError(t, err)
+ err = job.ValidateKeyStoreMatch(ctx, jb.OCR2OracleSpec, keyStore, tronKey.ID())
+ require.NoError(t, err)
+ })
+
t.Run("test Mercury ETH key validation", func(t *testing.T) {
ctx := testutils.Context(t)
jb.OCR2OracleSpec.PluginType = types.Mercury
diff --git a/core/services/job/orm.go b/core/services/job/orm.go
index cfd8060d60c..fa64404ec3a 100644
--- a/core/services/job/orm.go
+++ b/core/services/job/orm.go
@@ -658,6 +658,11 @@ func validateKeyStoreMatchForRelay(ctx context.Context, network string, keyStore
if err != nil {
return errors.Errorf("no Aptos key matching: %q", key)
}
+ case relay.NetworkTron:
+ _, err := keyStore.Tron().Get(key)
+ if err != nil {
+ return errors.Errorf("no Tron key matching: %q", key)
+ }
}
return nil
}
diff --git a/core/services/keystore/chaintype/chaintype.go b/core/services/keystore/chaintype/chaintype.go
index 419dfa2d073..8aca72d4f83 100644
--- a/core/services/keystore/chaintype/chaintype.go
+++ b/core/services/keystore/chaintype/chaintype.go
@@ -21,6 +21,8 @@ const (
StarkNet ChainType = "starknet"
// Aptos for the Aptos chain
Aptos ChainType = "aptos"
+ // Tron for the Tron chain
+ Tron ChainType = "tron"
)
type ChainTypes []ChainType
@@ -48,6 +50,8 @@ func NewChainType(typ uint8) (ChainType, error) {
return StarkNet, nil
case 5:
return Aptos, nil
+ case 6:
+ return Tron, nil
default:
return "", fmt.Errorf("unexpected chaintype.ChainType: %#v", typ)
}
@@ -65,13 +69,15 @@ func (c ChainType) Type() (uint8, error) {
return 4, nil
case Aptos:
return 5, nil
+ case Tron:
+ return 6, nil
default:
return 0, fmt.Errorf("unexpected chaintype.ChainType: %#v", c)
}
}
// SupportedChainTypes contain all chains that are supported
-var SupportedChainTypes = ChainTypes{EVM, Cosmos, Solana, StarkNet, Aptos}
+var SupportedChainTypes = ChainTypes{EVM, Cosmos, Solana, StarkNet, Aptos, Tron}
// ErrInvalidChainType is an error to indicate an unsupported chain type
var ErrInvalidChainType error
diff --git a/core/services/keystore/keys/ocr2key/export.go b/core/services/keystore/keys/ocr2key/export.go
index 8fa5ffedfed..eb7fe5f5eb9 100644
--- a/core/services/keystore/keys/ocr2key/export.go
+++ b/core/services/keystore/keys/ocr2key/export.go
@@ -48,6 +48,8 @@ func FromEncryptedJSON(keyJSON []byte, password string) (KeyBundle, error) {
kb = newKeyBundle(new(starkkey.OCR2Key))
case chaintype.Aptos:
kb = newKeyBundle(new(aptosKeyring))
+ case chaintype.Tron:
+ kb = newKeyBundle(new(evmKeyring))
default:
return nil, chaintype.NewErrInvalidChainType(export.ChainType)
}
diff --git a/core/services/keystore/keys/ocr2key/export_test.go b/core/services/keystore/keys/ocr2key/export_test.go
index b0ffa2db009..fd1e867dfa9 100644
--- a/core/services/keystore/keys/ocr2key/export_test.go
+++ b/core/services/keystore/keys/ocr2key/export_test.go
@@ -19,6 +19,7 @@ func TestExport(t *testing.T) {
{chain: chaintype.Solana},
{chain: chaintype.StarkNet},
{chain: chaintype.Aptos},
+ {chain: chaintype.Tron},
}
for _, tc := range tt {
tc := tc
diff --git a/core/services/keystore/keys/ocr2key/key_bundle.go b/core/services/keystore/keys/ocr2key/key_bundle.go
index a08bd84ac30..07ac352a17d 100644
--- a/core/services/keystore/keys/ocr2key/key_bundle.go
+++ b/core/services/keystore/keys/ocr2key/key_bundle.go
@@ -59,6 +59,8 @@ func New(chainType chaintype.ChainType) (KeyBundle, error) {
return newKeyBundleRand(chaintype.StarkNet, starkkey.NewOCR2Key)
case chaintype.Aptos:
return newKeyBundleRand(chaintype.Aptos, newAptosKeyring)
+ case chaintype.Tron:
+ return newKeyBundleRand(chaintype.Tron, newEVMKeyring)
}
return nil, chaintype.NewErrInvalidChainType(chainType)
}
@@ -76,6 +78,8 @@ func MustNewInsecure(reader io.Reader, chainType chaintype.ChainType) KeyBundle
return mustNewKeyBundleInsecure(chaintype.StarkNet, starkkey.NewOCR2Key, reader)
case chaintype.Aptos:
return mustNewKeyBundleInsecure(chaintype.Aptos, newAptosKeyring, reader)
+ case chaintype.Tron:
+ return mustNewKeyBundleInsecure(chaintype.Tron, newEVMKeyring, reader)
}
panic(chaintype.NewErrInvalidChainType(chainType))
}
@@ -126,6 +130,8 @@ func (raw Raw) Key() (kb KeyBundle) {
kb = newKeyBundle(new(starkkey.OCR2Key))
case chaintype.Aptos:
kb = newKeyBundle(new(aptosKeyring))
+ case chaintype.Tron:
+ kb = newKeyBundle(new(evmKeyring))
default:
return nil
}
diff --git a/core/services/keystore/keys/tronkey/account.go b/core/services/keystore/keys/tronkey/account.go
new file mode 100644
index 00000000000..9c90422d2a7
--- /dev/null
+++ b/core/services/keystore/keys/tronkey/account.go
@@ -0,0 +1,178 @@
+package tronkey
+
+import (
+ "crypto/ecdsa"
+ "crypto/sha256"
+ "encoding/hex"
+ "errors"
+ "fmt"
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/mr-tron/base58"
+)
+
+// Extracted from go-tron sdk: https://github.com/fbsobreira/gotron-sdk
+
+const (
+ // HashLength is the expected length of the hash
+ HashLength = 32
+ // AddressLengthBase58 is the expected length of the address in base58format
+ AddressLengthBase58 = 34
+ // Tron Address Prefix
+ prefixMainnet = 0x41
+ // TronBytePrefix is the hex prefix to address
+ TronBytePrefix = byte(prefixMainnet)
+ // Tron address should have 21 bytes (20 bytes + 1 byte prefix)
+ AddressLength = 21
+)
+
+// Address represents the 21 byte address of an Tron account.
+type Address [AddressLength]byte
+
+// Bytes get bytes from address
+func (a Address) Bytes() []byte {
+ return a[:]
+}
+
+// Hex get bytes from address in string
+func (a Address) Hex() string {
+ return BytesToHexString(a[:])
+}
+
+// HexToAddress returns Address with byte values of s.
+func HexToAddress(s string) (Address, error) {
+ addr, err := FromHex(s)
+ if err != nil {
+ return Address{}, err
+ }
+ // Check if the address starts with '41' and is 21 characters long
+ if len(addr) != AddressLength || addr[0] != prefixMainnet {
+ return Address{}, errors.New("invalid Tron address")
+ }
+ return Address(addr), nil
+}
+
+// Base58ToAddress returns Address with byte values of s.
+func Base58ToAddress(s string) (Address, error) {
+ addr, err := DecodeCheck(s)
+ if err != nil {
+ return Address{}, err
+ }
+ return Address(addr), nil
+}
+
+// String implements fmt.Stringer.
+// Returns the address as a base58 encoded string.
+func (a Address) String() string {
+ if len(a) == 0 {
+ return ""
+ }
+
+ if a[0] == 0 {
+ return new(big.Int).SetBytes(a.Bytes()).String()
+ }
+ return EncodeCheck(a.Bytes())
+}
+
+// PubkeyToAddress returns address from ecdsa public key
+func PubkeyToAddress(p ecdsa.PublicKey) Address {
+ address := crypto.PubkeyToAddress(p)
+
+ addressTron := make([]byte, 0)
+ addressTron = append(addressTron, TronBytePrefix)
+ addressTron = append(addressTron, address.Bytes()...)
+ return Address(addressTron)
+}
+
+// BytesToHexString encodes bytes as a hex string.
+func BytesToHexString(bytes []byte) string {
+ encode := make([]byte, len(bytes)*2)
+ hex.Encode(encode, bytes)
+ return "0x" + string(encode)
+}
+
+// FromHex returns the bytes represented by the hexadecimal string s.
+// s may be prefixed with "0x".
+func FromHex(s string) ([]byte, error) {
+ if Has0xPrefix(s) {
+ s = s[2:]
+ }
+ if len(s)%2 == 1 {
+ s = "0" + s
+ }
+ return HexToBytes(s)
+}
+
+// Has0xPrefix validates str begins with '0x' or '0X'.
+func Has0xPrefix(str string) bool {
+ return len(str) >= 2 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X')
+}
+
+// HexToBytes returns the bytes represented by the hexadecimal string str.
+func HexToBytes(str string) ([]byte, error) {
+ return hex.DecodeString(str)
+}
+
+func Encode(input []byte) string {
+ return base58.Encode(input)
+}
+
+func EncodeCheck(input []byte) string {
+ h256h0 := sha256.New()
+ h256h0.Write(input)
+ h0 := h256h0.Sum(nil)
+
+ h256h1 := sha256.New()
+ h256h1.Write(h0)
+ h1 := h256h1.Sum(nil)
+
+ inputCheck := input
+ inputCheck = append(inputCheck, h1[:4]...)
+
+ return Encode(inputCheck)
+}
+
+func DecodeCheck(input string) ([]byte, error) {
+ decodeCheck, err := Decode(input)
+ if err != nil {
+ return nil, err
+ }
+
+ if len(decodeCheck) < 4 {
+ return nil, errors.New("base58 check error")
+ }
+
+ // tron address should should have 21 bytes (including prefix) + 4 checksum
+ if len(decodeCheck) != AddressLength+4 {
+ return nil, fmt.Errorf("invalid address length: %d", len(decodeCheck))
+ }
+
+ // check prefix
+ if decodeCheck[0] != prefixMainnet {
+ return nil, errors.New("invalid prefix")
+ }
+
+ decodeData := decodeCheck[:len(decodeCheck)-4]
+
+ h256h0 := sha256.New()
+ h256h0.Write(decodeData)
+ h0 := h256h0.Sum(nil)
+
+ h256h1 := sha256.New()
+ h256h1.Write(h0)
+ h1 := h256h1.Sum(nil)
+
+ if h1[0] == decodeCheck[len(decodeData)] &&
+ h1[1] == decodeCheck[len(decodeData)+1] &&
+ h1[2] == decodeCheck[len(decodeData)+2] &&
+ h1[3] == decodeCheck[len(decodeData)+3] {
+ return decodeData, nil
+ }
+
+ return nil, errors.New("base58 check error")
+}
+
+func Decode(input string) ([]byte, error) {
+ return base58.Decode(input)
+}
diff --git a/core/services/keystore/keys/tronkey/account_test.go b/core/services/keystore/keys/tronkey/account_test.go
new file mode 100644
index 00000000000..9a92801bb13
--- /dev/null
+++ b/core/services/keystore/keys/tronkey/account_test.go
@@ -0,0 +1,177 @@
+package tronkey
+
+import (
+ "bytes"
+ "regexp"
+ "strings"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func Test_DecodeBase58(t *testing.T) {
+ invalidAddresses := []string{
+ "TronEnergyioE1Z3ukeRv38sYkv5Jn55bL",
+ "TronEnergyioNijNo8g3LF2ABKUAae6D2Z",
+ "TronEnergyio3ZMcXA5hSjrTxaioKGgqyr",
+ }
+
+ validAddresses := []string{
+ "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
+ "TVj7RNVHy6thbM7BWdSe9G6gXwKhjhdNZS",
+ "THPvaUhoh2Qn2y9THCZML3H815hhFhn5YC",
+ }
+
+ for _, addr := range invalidAddresses {
+ _, err := DecodeCheck(addr)
+ require.Error(t, err)
+ }
+
+ for _, addr := range validAddresses {
+ _, err := DecodeCheck(addr)
+ require.NoError(t, err)
+ }
+}
+
+func TestAddress(t *testing.T) {
+ t.Run("Valid Addresses", func(t *testing.T) {
+ validAddresses := []string{
+ "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
+ "TVj7RNVHy6thbM7BWdSe9G6gXwKhjhdNZS",
+ "THPvaUhoh2Qn2y9THCZML3H815hhFhn5YC",
+ }
+
+ for _, addrStr := range validAddresses {
+ t.Run(addrStr, func(t *testing.T) {
+ addr, err := Base58ToAddress(addrStr)
+ require.NoError(t, err)
+ require.Equal(t, addrStr, addr.String())
+
+ decoded, err := DecodeCheck(addrStr)
+ require.NoError(t, err)
+ require.True(t, bytes.Equal(decoded, addr.Bytes()))
+ })
+ }
+ })
+
+ t.Run("Invalid Addresses", func(t *testing.T) {
+ invalidAddresses := []string{
+ "TronEnergyioE1Z3ukeRv38sYkv5Jn55bL",
+ "TronEnergyioNijNo8g3LF2ABKUAae6D2Z",
+ "TronEnergyio3ZMcXA5hSjrTxaioKGgqyr",
+ }
+
+ for _, addrStr := range invalidAddresses {
+ t.Run(addrStr, func(t *testing.T) {
+ _, err := Base58ToAddress(addrStr)
+ require.Error(t, err)
+
+ _, err = DecodeCheck(addrStr)
+ require.Error(t, err)
+ })
+ }
+ })
+
+ t.Run("Address Conversion", func(t *testing.T) {
+ addrStr := "TSvT6Bg3siokv3dbdtt9o4oM1CTXmymGn1"
+ addr, err := Base58ToAddress(addrStr)
+ require.NoError(t, err)
+
+ t.Run("To Bytes", func(t *testing.T) {
+ bytes := addr.Bytes()
+ require.Len(t, bytes, 21)
+ })
+
+ t.Run("To Hex", func(t *testing.T) {
+ hex := addr.Hex()
+ require.Equal(t, "0x", hex[:2])
+ require.Len(t, hex, 44)
+ })
+ })
+
+ t.Run("Address Validity", func(t *testing.T) {
+ t.Run("Valid Address", func(t *testing.T) {
+ addr, err := Base58ToAddress("TSvT6Bg3siokv3dbdtt9o4oM1CTXmymGn1")
+ require.NoError(t, err)
+ require.True(t, isValid(addr))
+ })
+
+ t.Run("Zero Address", func(t *testing.T) {
+ addr := Address{}
+ require.False(t, isValid(addr))
+ })
+ })
+}
+
+func TestHexToAddress(t *testing.T) {
+ t.Run("Valid Hex Addresses", func(t *testing.T) {
+ validHexAddresses := []string{
+ "41a614f803b6fd780986a42c78ec9c7f77e6ded13c",
+ "41b2a2e1b2e1b2e1b2e1b2e1b2e1b2e1b2e1b2e1b2",
+ "41c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3",
+ }
+
+ for _, hexStr := range validHexAddresses {
+ t.Run(hexStr, func(t *testing.T) {
+ addr, err := HexToAddress(hexStr)
+ require.NoError(t, err)
+ require.Equal(t, "0x"+hexStr, addr.Hex())
+ })
+ }
+ })
+
+ t.Run("Invalid Hex Addresses", func(t *testing.T) {
+ invalidHexAddresses := []string{
+ "41a614f803b6fd780986a42c78ec9c7f77e6ded13", // Too short
+ "41b2a2e1b2e1b2e1b2e1b2e1b2e1b2e1b2e1b2e1b2e1b2", // Too long
+ "41g3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3", // Invalid character 'g'
+ "c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3", // Missing prefix '41'
+ }
+
+ for _, hexStr := range invalidHexAddresses {
+ t.Run(hexStr, func(t *testing.T) {
+ _, err := HexToAddress(hexStr)
+ require.Error(t, err)
+ })
+ }
+ })
+}
+
+// Helper Functions for testing
+
+// isValid checks if the address is a valid TRON address
+func isValid(a Address) bool {
+ // Check if it's a valid Base58 address
+ base58Str := a.String()
+ if isValidBase58Address(base58Str) {
+ return true
+ }
+
+ // Check if it's a valid hex address
+ hexStr := a.Hex()
+ return isValidHexAddress(strings.TrimPrefix(hexStr, "0x"))
+}
+
+// isValidBase58Address check if a string is a valid Base58 TRON address
+func isValidBase58Address(address string) bool {
+ // Check if the address starts with 'T' and is 34 characters long
+ if len(address) != 34 || address[0] != 'T' {
+ return false
+ }
+
+ // Check if the address contains only valid Base58 characters
+ validChars := regexp.MustCompile("^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+$")
+ return validChars.MatchString(address)
+}
+
+// isValidHexAddressto check if a string is a valid hex TRON address
+func isValidHexAddress(address string) bool {
+ // Check if the address starts with '41' and is 42 characters long
+ if len(address) != 42 || address[:2] != "41" {
+ return false
+ }
+
+ // Check if the address contains only valid hexadecimal characters
+ validChars := regexp.MustCompile("^[0-9A-Fa-f]+$")
+ return validChars.MatchString(address[2:]) // Check the part after '41'
+}
diff --git a/core/services/keystore/keys/tronkey/export.go b/core/services/keystore/keys/tronkey/export.go
new file mode 100644
index 00000000000..7688650c58d
--- /dev/null
+++ b/core/services/keystore/keys/tronkey/export.go
@@ -0,0 +1,46 @@
+package tronkey
+
+import (
+ "github.com/ethereum/go-ethereum/accounts/keystore"
+
+ "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys"
+ "github.com/smartcontractkit/chainlink/v2/core/utils"
+)
+
+const keyTypeIdentifier = "Tron"
+
+// FromEncryptedJSON gets key from json and password
+func FromEncryptedJSON(keyJSON []byte, password string) (Key, error) {
+ return keys.FromEncryptedJSON(
+ keyTypeIdentifier,
+ keyJSON,
+ password,
+ adulteratedPassword,
+ func(_ keys.EncryptedKeyExport, rawPrivKey []byte) (Key, error) {
+ return Raw(rawPrivKey).Key(), nil
+ },
+ )
+}
+
+// ToEncryptedJSON returns encrypted JSON representing key
+func (key Key) ToEncryptedJSON(password string, scryptParams utils.ScryptParams) (export []byte, err error) {
+ return keys.ToEncryptedJSON(
+ keyTypeIdentifier,
+ key.Raw(),
+ key,
+ password,
+ scryptParams,
+ adulteratedPassword,
+ func(id string, key Key, cryptoJSON keystore.CryptoJSON) keys.EncryptedKeyExport {
+ return keys.EncryptedKeyExport{
+ KeyType: id,
+ PublicKey: key.PublicKeyStr(),
+ Crypto: cryptoJSON,
+ }
+ },
+ )
+}
+
+func adulteratedPassword(password string) string {
+ return "tronkey" + password
+}
diff --git a/core/services/keystore/keys/tronkey/export_test.go b/core/services/keystore/keys/tronkey/export_test.go
new file mode 100644
index 00000000000..5e3e605ed34
--- /dev/null
+++ b/core/services/keystore/keys/tronkey/export_test.go
@@ -0,0 +1,19 @@
+package tronkey
+
+import (
+ "testing"
+
+ "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys"
+)
+
+func TestTronKeys_ExportImport(t *testing.T) {
+ keys.RunKeyExportImportTestcase(t, createKey, decryptKey)
+}
+
+func createKey() (keys.KeyType, error) {
+ return New()
+}
+
+func decryptKey(keyJSON []byte, password string) (keys.KeyType, error) {
+ return FromEncryptedJSON(keyJSON, password)
+}
diff --git a/core/services/keystore/keys/tronkey/key.go b/core/services/keystore/keys/tronkey/key.go
new file mode 100644
index 00000000000..5f5b36b8c14
--- /dev/null
+++ b/core/services/keystore/keys/tronkey/key.go
@@ -0,0 +1,109 @@
+package tronkey
+
+import (
+ "crypto/ecdsa"
+ "crypto/rand"
+ "encoding/hex"
+ "fmt"
+ "io"
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/crypto"
+)
+
+// Tron uses the same elliptic curve cryptography as Ethereum (ECDSA with secp256k1)
+var curve = crypto.S256()
+
+// Raw represents the Tron private key
+type Raw []byte
+
+// Key generates a public-private key pair from the raw private key
+func (raw Raw) Key() Key {
+ var privKey ecdsa.PrivateKey
+ d := big.NewInt(0).SetBytes(raw)
+ privKey.PublicKey.Curve = curve
+ privKey.D = d
+ privKey.PublicKey.X, privKey.PublicKey.Y = curve.ScalarBaseMult(d.Bytes())
+ return Key{
+ pubKey: &privKey.PublicKey,
+ privKey: &privKey,
+ }
+}
+
+func (raw Raw) String() string {
+ return ""
+}
+
+func (raw Raw) GoString() string {
+ return raw.String()
+}
+
+var _ fmt.GoStringer = &Key{}
+
+type Key struct {
+ privKey *ecdsa.PrivateKey
+ pubKey *ecdsa.PublicKey
+}
+
+func New() (Key, error) {
+ return newFrom(rand.Reader)
+}
+
+// MustNewInsecure return Key if no error
+// This insecure function is used for testing purposes only
+func MustNewInsecure(reader io.Reader) Key {
+ key, err := newFrom(reader)
+ if err != nil {
+ panic(err)
+ }
+ return key
+}
+
+func newFrom(reader io.Reader) (Key, error) {
+ privKeyECDSA, err := ecdsa.GenerateKey(curve, reader)
+ if err != nil {
+ return Key{}, err
+ }
+ return Key{
+ privKey: privKeyECDSA,
+ pubKey: &privKeyECDSA.PublicKey,
+ }, nil
+}
+
+func (key Key) ID() string {
+ return key.Base58Address()
+}
+
+func (key Key) Raw() Raw {
+ return key.privKey.D.Bytes()
+}
+
+func (key Key) ToEcdsaPrivKey() *ecdsa.PrivateKey {
+ return key.privKey
+}
+
+func (key Key) String() string {
+ return fmt.Sprintf("TronKey{PrivateKey: , Address: %s}", key.Base58Address())
+}
+
+// GoString wraps String()
+func (key Key) GoString() string {
+ return key.String()
+}
+
+// Sign is used to sign a message
+func (key Key) Sign(msg []byte) ([]byte, error) {
+ return crypto.Sign(msg, key.privKey)
+}
+
+// PublicKeyStr returns the public key as a hexadecimal string
+func (key Key) PublicKeyStr() string {
+ pubKeyBytes := crypto.FromECDSAPub(key.pubKey)
+ return hex.EncodeToString(pubKeyBytes)
+}
+
+// Base58Address returns the Tron address in Base58 format with checksum
+func (key Key) Base58Address() string {
+ address := PubkeyToAddress(*key.pubKey)
+ return address.String()
+}
diff --git a/core/services/keystore/keys/tronkey/key_test.go b/core/services/keystore/keys/tronkey/key_test.go
new file mode 100644
index 00000000000..d3714228483
--- /dev/null
+++ b/core/services/keystore/keys/tronkey/key_test.go
@@ -0,0 +1,85 @@
+package tronkey
+
+import (
+ "crypto/ecdsa"
+ "crypto/rand"
+ "encoding/hex"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestTronKeyRawPrivateKey(t *testing.T) {
+ t.Run("Create from raw bytes and check string representation", func(t *testing.T) {
+ // Generate a private key
+ privateKeyECDSA, err := ecdsa.GenerateKey(crypto.S256(), rand.Reader)
+ require.NoError(t, err, "Failed to generate ECDSA key")
+
+ // Create TronKey from raw bytes
+ tronKey := Raw(privateKeyECDSA.D.Bytes())
+
+ // Check string representation
+ expectedStr := ""
+ assert.Equal(t, expectedStr, tronKey.String(), "Unexpected string representation")
+ assert.Equal(t, expectedStr, tronKey.GoString(), "String() and GoString() should return the same value")
+ })
+}
+
+func TestTronKeyNewKeyGeneration(t *testing.T) {
+ t.Run("Generate new key and verify its components", func(t *testing.T) {
+ // Generate a new key
+ key, err := New()
+ require.NoError(t, err, "Failed to generate new TronKey")
+
+ // Verify key components
+ assert.NotNil(t, key.pubKey, "Public key should not be nil")
+ assert.NotNil(t, key.privKey, "Private key should not be nil")
+ })
+
+ t.Run("Multiple key generations produce unique keys", func(t *testing.T) {
+ key1, err := New()
+ require.NoError(t, err, "Failed to generate first key")
+
+ key2, err := New()
+ require.NoError(t, err, "Failed to generate second key")
+
+ assert.NotEqual(t, key1.privKey, key2.privKey, "Generated private keys should be unique")
+ assert.NotEqual(t, key1.pubKey, key2.pubKey, "Generated public keys should be unique")
+ })
+}
+
+func TestKeyAddress(t *testing.T) {
+ t.Run("Known private key and expected address", func(t *testing.T) {
+ // Tests cases from https://developers.tron.network/docs/account
+ privateKeyHex := "b406adb115b43e103c7b1dc8b5931f63279a5b6b2cf7328638814c43171a2908"
+ expectedAddress := "TDdcf5iMDkB61oGM27TNak55eVX214thBG"
+
+ privateKeyBytes, err := hex.DecodeString(privateKeyHex)
+ require.NoError(t, err, "Failed to decode private key hex")
+
+ privateKey, err := crypto.ToECDSA(privateKeyBytes)
+ require.NoError(t, err, "Failed to convert private key to ECDSA")
+
+ key := Key{
+ privKey: privateKey,
+ pubKey: &privateKey.PublicKey,
+ }
+ require.NotNil(t, key.privKey, "Private key is nil")
+
+ address := key.Base58Address()
+ require.Equal(t, expectedAddress, address, "Generated address does not match expected address")
+ })
+
+ t.Run("Generate new key and check address format", func(t *testing.T) {
+ newKey, err := New()
+ if err != nil {
+ t.Fatalf("Failed to generate new key: %v", err)
+ }
+
+ newAddress := newKey.Base58Address()
+ isValid := isValidBase58Address(newAddress)
+ require.True(t, isValid, "Generated address is not valid")
+ })
+}
diff --git a/core/services/keystore/keystoretest.go b/core/services/keystore/keystoretest.go
index 626cc4bab99..9814801b2a5 100644
--- a/core/services/keystore/keystoretest.go
+++ b/core/services/keystore/keystoretest.go
@@ -74,6 +74,7 @@ func NewInMemory(ds sqlutil.DataSource, scryptParams utils.ScryptParams, lggr lo
solana: newSolanaKeyStore(km),
starknet: newStarkNetKeyStore(km),
aptos: newAptosKeyStore(km),
+ tron: newTronKeyStore(km),
vrf: newVRFKeyStore(km),
workflow: newWorkflowKeyStore(km),
}
diff --git a/core/services/keystore/master.go b/core/services/keystore/master.go
index 72677a166a3..50ca6d0c34d 100644
--- a/core/services/keystore/master.go
+++ b/core/services/keystore/master.go
@@ -20,6 +20,7 @@ import (
"github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey"
"github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/solkey"
"github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/starkkey"
+ "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/tronkey"
"github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/vrfkey"
"github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/workflowkey"
"github.com/smartcontractkit/chainlink/v2/core/utils"
@@ -45,6 +46,7 @@ type Master interface {
Cosmos() Cosmos
StarkNet() StarkNet
Aptos() Aptos
+ Tron() Tron
VRF() VRF
Workflow() Workflow
Unlock(ctx context.Context, password string) error
@@ -62,6 +64,7 @@ type master struct {
solana *solana
starknet *starknet
aptos *aptos
+ tron *tron
vrf *vrf
workflow *workflow
}
@@ -91,6 +94,7 @@ func newMaster(ds sqlutil.DataSource, scryptParams utils.ScryptParams, lggr logg
solana: newSolanaKeyStore(km),
starknet: newStarkNetKeyStore(km),
aptos: newAptosKeyStore(km),
+ tron: newTronKeyStore(km),
vrf: newVRFKeyStore(km),
workflow: newWorkflowKeyStore(km),
}
@@ -132,6 +136,10 @@ func (ks *master) Aptos() Aptos {
return ks.aptos
}
+func (ks *master) Tron() Tron {
+ return ks.tron
+}
+
func (ks *master) VRF() VRF {
return ks.vrf
}
@@ -273,6 +281,8 @@ func GetFieldNameForKey(unknownKey Key) (string, error) {
return "StarkNet", nil
case aptoskey.Key:
return "Aptos", nil
+ case tronkey.Key:
+ return "Tron", nil
case vrfkey.KeyV2:
return "VRF", nil
case workflowkey.Key:
diff --git a/core/services/keystore/mocks/master.go b/core/services/keystore/mocks/master.go
index 01b85a542d8..6ea57639196 100644
--- a/core/services/keystore/mocks/master.go
+++ b/core/services/keystore/mocks/master.go
@@ -501,6 +501,53 @@ func (_c *Master_StarkNet_Call) RunAndReturn(run func() keystore.StarkNet) *Mast
return _c
}
+// Tron provides a mock function with no fields
+func (_m *Master) Tron() keystore.Tron {
+ ret := _m.Called()
+
+ if len(ret) == 0 {
+ panic("no return value specified for Tron")
+ }
+
+ var r0 keystore.Tron
+ if rf, ok := ret.Get(0).(func() keystore.Tron); ok {
+ r0 = rf()
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(keystore.Tron)
+ }
+ }
+
+ return r0
+}
+
+// Master_Tron_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Tron'
+type Master_Tron_Call struct {
+ *mock.Call
+}
+
+// Tron is a helper method to define mock.On call
+func (_e *Master_Expecter) Tron() *Master_Tron_Call {
+ return &Master_Tron_Call{Call: _e.mock.On("Tron")}
+}
+
+func (_c *Master_Tron_Call) Run(run func()) *Master_Tron_Call {
+ _c.Call.Run(func(args mock.Arguments) {
+ run()
+ })
+ return _c
+}
+
+func (_c *Master_Tron_Call) Return(_a0 keystore.Tron) *Master_Tron_Call {
+ _c.Call.Return(_a0)
+ return _c
+}
+
+func (_c *Master_Tron_Call) RunAndReturn(run func() keystore.Tron) *Master_Tron_Call {
+ _c.Call.Return(run)
+ return _c
+}
+
// Unlock provides a mock function with given fields: ctx, password
func (_m *Master) Unlock(ctx context.Context, password string) error {
ret := _m.Called(ctx, password)
diff --git a/core/services/keystore/mocks/tron.go b/core/services/keystore/mocks/tron.go
new file mode 100644
index 00000000000..0636f4e54e9
--- /dev/null
+++ b/core/services/keystore/mocks/tron.go
@@ -0,0 +1,534 @@
+// Code generated by mockery v2.50.0. DO NOT EDIT.
+
+package mocks
+
+import (
+ context "context"
+
+ mock "github.com/stretchr/testify/mock"
+
+ tronkey "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/tronkey"
+)
+
+// Tron is an autogenerated mock type for the Tron type
+type Tron struct {
+ mock.Mock
+}
+
+type Tron_Expecter struct {
+ mock *mock.Mock
+}
+
+func (_m *Tron) EXPECT() *Tron_Expecter {
+ return &Tron_Expecter{mock: &_m.Mock}
+}
+
+// Add provides a mock function with given fields: ctx, key
+func (_m *Tron) Add(ctx context.Context, key tronkey.Key) error {
+ ret := _m.Called(ctx, key)
+
+ if len(ret) == 0 {
+ panic("no return value specified for Add")
+ }
+
+ var r0 error
+ if rf, ok := ret.Get(0).(func(context.Context, tronkey.Key) error); ok {
+ r0 = rf(ctx, key)
+ } else {
+ r0 = ret.Error(0)
+ }
+
+ return r0
+}
+
+// Tron_Add_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Add'
+type Tron_Add_Call struct {
+ *mock.Call
+}
+
+// Add is a helper method to define mock.On call
+// - ctx context.Context
+// - key tronkey.Key
+func (_e *Tron_Expecter) Add(ctx interface{}, key interface{}) *Tron_Add_Call {
+ return &Tron_Add_Call{Call: _e.mock.On("Add", ctx, key)}
+}
+
+func (_c *Tron_Add_Call) Run(run func(ctx context.Context, key tronkey.Key)) *Tron_Add_Call {
+ _c.Call.Run(func(args mock.Arguments) {
+ run(args[0].(context.Context), args[1].(tronkey.Key))
+ })
+ return _c
+}
+
+func (_c *Tron_Add_Call) Return(_a0 error) *Tron_Add_Call {
+ _c.Call.Return(_a0)
+ return _c
+}
+
+func (_c *Tron_Add_Call) RunAndReturn(run func(context.Context, tronkey.Key) error) *Tron_Add_Call {
+ _c.Call.Return(run)
+ return _c
+}
+
+// Create provides a mock function with given fields: ctx
+func (_m *Tron) Create(ctx context.Context) (tronkey.Key, error) {
+ ret := _m.Called(ctx)
+
+ if len(ret) == 0 {
+ panic("no return value specified for Create")
+ }
+
+ var r0 tronkey.Key
+ var r1 error
+ if rf, ok := ret.Get(0).(func(context.Context) (tronkey.Key, error)); ok {
+ return rf(ctx)
+ }
+ if rf, ok := ret.Get(0).(func(context.Context) tronkey.Key); ok {
+ r0 = rf(ctx)
+ } else {
+ r0 = ret.Get(0).(tronkey.Key)
+ }
+
+ if rf, ok := ret.Get(1).(func(context.Context) error); ok {
+ r1 = rf(ctx)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// Tron_Create_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Create'
+type Tron_Create_Call struct {
+ *mock.Call
+}
+
+// Create is a helper method to define mock.On call
+// - ctx context.Context
+func (_e *Tron_Expecter) Create(ctx interface{}) *Tron_Create_Call {
+ return &Tron_Create_Call{Call: _e.mock.On("Create", ctx)}
+}
+
+func (_c *Tron_Create_Call) Run(run func(ctx context.Context)) *Tron_Create_Call {
+ _c.Call.Run(func(args mock.Arguments) {
+ run(args[0].(context.Context))
+ })
+ return _c
+}
+
+func (_c *Tron_Create_Call) Return(_a0 tronkey.Key, _a1 error) *Tron_Create_Call {
+ _c.Call.Return(_a0, _a1)
+ return _c
+}
+
+func (_c *Tron_Create_Call) RunAndReturn(run func(context.Context) (tronkey.Key, error)) *Tron_Create_Call {
+ _c.Call.Return(run)
+ return _c
+}
+
+// Delete provides a mock function with given fields: ctx, id
+func (_m *Tron) Delete(ctx context.Context, id string) (tronkey.Key, error) {
+ ret := _m.Called(ctx, id)
+
+ if len(ret) == 0 {
+ panic("no return value specified for Delete")
+ }
+
+ var r0 tronkey.Key
+ var r1 error
+ if rf, ok := ret.Get(0).(func(context.Context, string) (tronkey.Key, error)); ok {
+ return rf(ctx, id)
+ }
+ if rf, ok := ret.Get(0).(func(context.Context, string) tronkey.Key); ok {
+ r0 = rf(ctx, id)
+ } else {
+ r0 = ret.Get(0).(tronkey.Key)
+ }
+
+ if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
+ r1 = rf(ctx, id)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// Tron_Delete_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Delete'
+type Tron_Delete_Call struct {
+ *mock.Call
+}
+
+// Delete is a helper method to define mock.On call
+// - ctx context.Context
+// - id string
+func (_e *Tron_Expecter) Delete(ctx interface{}, id interface{}) *Tron_Delete_Call {
+ return &Tron_Delete_Call{Call: _e.mock.On("Delete", ctx, id)}
+}
+
+func (_c *Tron_Delete_Call) Run(run func(ctx context.Context, id string)) *Tron_Delete_Call {
+ _c.Call.Run(func(args mock.Arguments) {
+ run(args[0].(context.Context), args[1].(string))
+ })
+ return _c
+}
+
+func (_c *Tron_Delete_Call) Return(_a0 tronkey.Key, _a1 error) *Tron_Delete_Call {
+ _c.Call.Return(_a0, _a1)
+ return _c
+}
+
+func (_c *Tron_Delete_Call) RunAndReturn(run func(context.Context, string) (tronkey.Key, error)) *Tron_Delete_Call {
+ _c.Call.Return(run)
+ return _c
+}
+
+// EnsureKey provides a mock function with given fields: ctx
+func (_m *Tron) EnsureKey(ctx context.Context) error {
+ ret := _m.Called(ctx)
+
+ if len(ret) == 0 {
+ panic("no return value specified for EnsureKey")
+ }
+
+ var r0 error
+ if rf, ok := ret.Get(0).(func(context.Context) error); ok {
+ r0 = rf(ctx)
+ } else {
+ r0 = ret.Error(0)
+ }
+
+ return r0
+}
+
+// Tron_EnsureKey_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'EnsureKey'
+type Tron_EnsureKey_Call struct {
+ *mock.Call
+}
+
+// EnsureKey is a helper method to define mock.On call
+// - ctx context.Context
+func (_e *Tron_Expecter) EnsureKey(ctx interface{}) *Tron_EnsureKey_Call {
+ return &Tron_EnsureKey_Call{Call: _e.mock.On("EnsureKey", ctx)}
+}
+
+func (_c *Tron_EnsureKey_Call) Run(run func(ctx context.Context)) *Tron_EnsureKey_Call {
+ _c.Call.Run(func(args mock.Arguments) {
+ run(args[0].(context.Context))
+ })
+ return _c
+}
+
+func (_c *Tron_EnsureKey_Call) Return(_a0 error) *Tron_EnsureKey_Call {
+ _c.Call.Return(_a0)
+ return _c
+}
+
+func (_c *Tron_EnsureKey_Call) RunAndReturn(run func(context.Context) error) *Tron_EnsureKey_Call {
+ _c.Call.Return(run)
+ return _c
+}
+
+// Export provides a mock function with given fields: id, password
+func (_m *Tron) Export(id string, password string) ([]byte, error) {
+ ret := _m.Called(id, password)
+
+ if len(ret) == 0 {
+ panic("no return value specified for Export")
+ }
+
+ var r0 []byte
+ var r1 error
+ if rf, ok := ret.Get(0).(func(string, string) ([]byte, error)); ok {
+ return rf(id, password)
+ }
+ if rf, ok := ret.Get(0).(func(string, string) []byte); ok {
+ r0 = rf(id, password)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).([]byte)
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func(string, string) error); ok {
+ r1 = rf(id, password)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// Tron_Export_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Export'
+type Tron_Export_Call struct {
+ *mock.Call
+}
+
+// Export is a helper method to define mock.On call
+// - id string
+// - password string
+func (_e *Tron_Expecter) Export(id interface{}, password interface{}) *Tron_Export_Call {
+ return &Tron_Export_Call{Call: _e.mock.On("Export", id, password)}
+}
+
+func (_c *Tron_Export_Call) Run(run func(id string, password string)) *Tron_Export_Call {
+ _c.Call.Run(func(args mock.Arguments) {
+ run(args[0].(string), args[1].(string))
+ })
+ return _c
+}
+
+func (_c *Tron_Export_Call) Return(_a0 []byte, _a1 error) *Tron_Export_Call {
+ _c.Call.Return(_a0, _a1)
+ return _c
+}
+
+func (_c *Tron_Export_Call) RunAndReturn(run func(string, string) ([]byte, error)) *Tron_Export_Call {
+ _c.Call.Return(run)
+ return _c
+}
+
+// Get provides a mock function with given fields: id
+func (_m *Tron) Get(id string) (tronkey.Key, error) {
+ ret := _m.Called(id)
+
+ if len(ret) == 0 {
+ panic("no return value specified for Get")
+ }
+
+ var r0 tronkey.Key
+ var r1 error
+ if rf, ok := ret.Get(0).(func(string) (tronkey.Key, error)); ok {
+ return rf(id)
+ }
+ if rf, ok := ret.Get(0).(func(string) tronkey.Key); ok {
+ r0 = rf(id)
+ } else {
+ r0 = ret.Get(0).(tronkey.Key)
+ }
+
+ if rf, ok := ret.Get(1).(func(string) error); ok {
+ r1 = rf(id)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// Tron_Get_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Get'
+type Tron_Get_Call struct {
+ *mock.Call
+}
+
+// Get is a helper method to define mock.On call
+// - id string
+func (_e *Tron_Expecter) Get(id interface{}) *Tron_Get_Call {
+ return &Tron_Get_Call{Call: _e.mock.On("Get", id)}
+}
+
+func (_c *Tron_Get_Call) Run(run func(id string)) *Tron_Get_Call {
+ _c.Call.Run(func(args mock.Arguments) {
+ run(args[0].(string))
+ })
+ return _c
+}
+
+func (_c *Tron_Get_Call) Return(_a0 tronkey.Key, _a1 error) *Tron_Get_Call {
+ _c.Call.Return(_a0, _a1)
+ return _c
+}
+
+func (_c *Tron_Get_Call) RunAndReturn(run func(string) (tronkey.Key, error)) *Tron_Get_Call {
+ _c.Call.Return(run)
+ return _c
+}
+
+// GetAll provides a mock function with no fields
+func (_m *Tron) GetAll() ([]tronkey.Key, error) {
+ ret := _m.Called()
+
+ if len(ret) == 0 {
+ panic("no return value specified for GetAll")
+ }
+
+ var r0 []tronkey.Key
+ var r1 error
+ if rf, ok := ret.Get(0).(func() ([]tronkey.Key, error)); ok {
+ return rf()
+ }
+ if rf, ok := ret.Get(0).(func() []tronkey.Key); ok {
+ r0 = rf()
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).([]tronkey.Key)
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func() error); ok {
+ r1 = rf()
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// Tron_GetAll_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetAll'
+type Tron_GetAll_Call struct {
+ *mock.Call
+}
+
+// GetAll is a helper method to define mock.On call
+func (_e *Tron_Expecter) GetAll() *Tron_GetAll_Call {
+ return &Tron_GetAll_Call{Call: _e.mock.On("GetAll")}
+}
+
+func (_c *Tron_GetAll_Call) Run(run func()) *Tron_GetAll_Call {
+ _c.Call.Run(func(args mock.Arguments) {
+ run()
+ })
+ return _c
+}
+
+func (_c *Tron_GetAll_Call) Return(_a0 []tronkey.Key, _a1 error) *Tron_GetAll_Call {
+ _c.Call.Return(_a0, _a1)
+ return _c
+}
+
+func (_c *Tron_GetAll_Call) RunAndReturn(run func() ([]tronkey.Key, error)) *Tron_GetAll_Call {
+ _c.Call.Return(run)
+ return _c
+}
+
+// Import provides a mock function with given fields: ctx, keyJSON, password
+func (_m *Tron) Import(ctx context.Context, keyJSON []byte, password string) (tronkey.Key, error) {
+ ret := _m.Called(ctx, keyJSON, password)
+
+ if len(ret) == 0 {
+ panic("no return value specified for Import")
+ }
+
+ var r0 tronkey.Key
+ var r1 error
+ if rf, ok := ret.Get(0).(func(context.Context, []byte, string) (tronkey.Key, error)); ok {
+ return rf(ctx, keyJSON, password)
+ }
+ if rf, ok := ret.Get(0).(func(context.Context, []byte, string) tronkey.Key); ok {
+ r0 = rf(ctx, keyJSON, password)
+ } else {
+ r0 = ret.Get(0).(tronkey.Key)
+ }
+
+ if rf, ok := ret.Get(1).(func(context.Context, []byte, string) error); ok {
+ r1 = rf(ctx, keyJSON, password)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// Tron_Import_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Import'
+type Tron_Import_Call struct {
+ *mock.Call
+}
+
+// Import is a helper method to define mock.On call
+// - ctx context.Context
+// - keyJSON []byte
+// - password string
+func (_e *Tron_Expecter) Import(ctx interface{}, keyJSON interface{}, password interface{}) *Tron_Import_Call {
+ return &Tron_Import_Call{Call: _e.mock.On("Import", ctx, keyJSON, password)}
+}
+
+func (_c *Tron_Import_Call) Run(run func(ctx context.Context, keyJSON []byte, password string)) *Tron_Import_Call {
+ _c.Call.Run(func(args mock.Arguments) {
+ run(args[0].(context.Context), args[1].([]byte), args[2].(string))
+ })
+ return _c
+}
+
+func (_c *Tron_Import_Call) Return(_a0 tronkey.Key, _a1 error) *Tron_Import_Call {
+ _c.Call.Return(_a0, _a1)
+ return _c
+}
+
+func (_c *Tron_Import_Call) RunAndReturn(run func(context.Context, []byte, string) (tronkey.Key, error)) *Tron_Import_Call {
+ _c.Call.Return(run)
+ return _c
+}
+
+// Sign provides a mock function with given fields: ctx, id, msg
+func (_m *Tron) Sign(ctx context.Context, id string, msg []byte) ([]byte, error) {
+ ret := _m.Called(ctx, id, msg)
+
+ if len(ret) == 0 {
+ panic("no return value specified for Sign")
+ }
+
+ var r0 []byte
+ var r1 error
+ if rf, ok := ret.Get(0).(func(context.Context, string, []byte) ([]byte, error)); ok {
+ return rf(ctx, id, msg)
+ }
+ if rf, ok := ret.Get(0).(func(context.Context, string, []byte) []byte); ok {
+ r0 = rf(ctx, id, msg)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).([]byte)
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func(context.Context, string, []byte) error); ok {
+ r1 = rf(ctx, id, msg)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// Tron_Sign_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Sign'
+type Tron_Sign_Call struct {
+ *mock.Call
+}
+
+// Sign is a helper method to define mock.On call
+// - ctx context.Context
+// - id string
+// - msg []byte
+func (_e *Tron_Expecter) Sign(ctx interface{}, id interface{}, msg interface{}) *Tron_Sign_Call {
+ return &Tron_Sign_Call{Call: _e.mock.On("Sign", ctx, id, msg)}
+}
+
+func (_c *Tron_Sign_Call) Run(run func(ctx context.Context, id string, msg []byte)) *Tron_Sign_Call {
+ _c.Call.Run(func(args mock.Arguments) {
+ run(args[0].(context.Context), args[1].(string), args[2].([]byte))
+ })
+ return _c
+}
+
+func (_c *Tron_Sign_Call) Return(signature []byte, err error) *Tron_Sign_Call {
+ _c.Call.Return(signature, err)
+ return _c
+}
+
+func (_c *Tron_Sign_Call) RunAndReturn(run func(context.Context, string, []byte) ([]byte, error)) *Tron_Sign_Call {
+ _c.Call.Return(run)
+ return _c
+}
+
+// NewTron creates a new instance of Tron. 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 NewTron(t interface {
+ mock.TestingT
+ Cleanup(func())
+}) *Tron {
+ mock := &Tron{}
+ mock.Mock.Test(t)
+
+ t.Cleanup(func() { mock.AssertExpectations(t) })
+
+ return mock
+}
diff --git a/core/services/keystore/models.go b/core/services/keystore/models.go
index 151934827c3..1ebc7480997 100644
--- a/core/services/keystore/models.go
+++ b/core/services/keystore/models.go
@@ -20,6 +20,7 @@ import (
"github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey"
"github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/solkey"
"github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/starkkey"
+ "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/tronkey"
"github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/vrfkey"
"github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/workflowkey"
"github.com/smartcontractkit/chainlink/v2/core/utils"
@@ -158,6 +159,7 @@ type keyRing struct {
Solana map[string]solkey.Key
StarkNet map[string]starkkey.Key
Aptos map[string]aptoskey.Key
+ Tron map[string]tronkey.Key
VRF map[string]vrfkey.KeyV2
Workflow map[string]workflowkey.Key
LegacyKeys LegacyKeyStorage
@@ -174,6 +176,7 @@ func newKeyRing() *keyRing {
Solana: make(map[string]solkey.Key),
StarkNet: make(map[string]starkkey.Key),
Aptos: make(map[string]aptoskey.Key),
+ Tron: make(map[string]tronkey.Key),
VRF: make(map[string]vrfkey.KeyV2),
Workflow: make(map[string]workflowkey.Key),
}
@@ -236,6 +239,9 @@ func (kr *keyRing) raw() (rawKeys rawKeyRing) {
for _, aptoskey := range kr.Aptos {
rawKeys.Aptos = append(rawKeys.Aptos, aptoskey.Raw())
}
+ for _, tronkey := range kr.Tron {
+ rawKeys.Tron = append(rawKeys.Tron, tronkey.Raw())
+ }
for _, vrfKey := range kr.VRF {
rawKeys.VRF = append(rawKeys.VRF, vrfKey.Raw())
}
@@ -283,6 +289,10 @@ func (kr *keyRing) logPubKeys(lggr logger.Logger) {
for _, aptosKey := range kr.Aptos {
aptosIDs = append(aptosIDs, aptosKey.ID())
}
+ tronIDs := []string{}
+ for _, tronKey := range kr.Tron {
+ tronIDs = append(tronIDs, tronKey.ID())
+ }
var vrfIDs []string
for _, VRFKey := range kr.VRF {
vrfIDs = append(vrfIDs, VRFKey.ID())
@@ -320,6 +330,9 @@ func (kr *keyRing) logPubKeys(lggr logger.Logger) {
if len(aptosIDs) > 0 {
lggr.Infow(fmt.Sprintf("Unlocked %d Aptos keys", len(aptosIDs)), "keys", aptosIDs)
}
+ if len(tronIDs) > 0 {
+ lggr.Infow(fmt.Sprintf("Unlocked %d Tron keys", len(tronIDs)), "keys", tronIDs)
+ }
if len(vrfIDs) > 0 {
lggr.Infow(fmt.Sprintf("Unlocked %d VRF keys", len(vrfIDs)), "keys", vrfIDs)
}
@@ -344,6 +357,7 @@ type rawKeyRing struct {
Solana []solkey.Raw
StarkNet []starkkey.Raw
Aptos []aptoskey.Raw
+ Tron []tronkey.Raw
VRF []vrfkey.Raw
Workflow []workflowkey.Raw
LegacyKeys LegacyKeyStorage `json:"-"`
@@ -388,6 +402,10 @@ func (rawKeys rawKeyRing) keys() (*keyRing, error) {
aptosKey := rawAptosKey.Key()
keyRing.Aptos[aptosKey.ID()] = aptosKey
}
+ for _, rawTronKey := range rawKeys.Tron {
+ tronKey := rawTronKey.Key()
+ keyRing.Tron[tronKey.ID()] = tronKey
+ }
for _, rawVRFKey := range rawKeys.VRF {
vrfKey := rawVRFKey.Key()
keyRing.VRF[vrfKey.ID()] = vrfKey
diff --git a/core/services/keystore/models_test.go b/core/services/keystore/models_test.go
index a828fbbf4f6..a66e29865d1 100644
--- a/core/services/keystore/models_test.go
+++ b/core/services/keystore/models_test.go
@@ -16,6 +16,7 @@ import (
"github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ocrkey"
"github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey"
"github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/solkey"
+ "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/tronkey"
"github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/vrfkey"
"github.com/smartcontractkit/chainlink/v2/core/utils"
)
@@ -40,6 +41,7 @@ func TestKeyRing_Encrypt_Decrypt(t *testing.T) {
sol1, sol2 := solkey.MustNewInsecure(rand.Reader), solkey.MustNewInsecure(rand.Reader)
vrf1, vrf2 := vrfkey.MustNewV2XXXTestingOnly(big.NewInt(1)), vrfkey.MustNewV2XXXTestingOnly(big.NewInt(2))
tk1, tk2 := cosmoskey.MustNewInsecure(rand.Reader), cosmoskey.MustNewInsecure(rand.Reader)
+ uk1, uk2 := tronkey.MustNewInsecure(rand.Reader), tronkey.MustNewInsecure(rand.Reader)
originalKeyRingRaw := rawKeyRing{
CSA: []csakey.Raw{csa1.Raw(), csa2.Raw()},
Eth: []ethkey.Raw{eth1.Raw(), eth2.Raw()},
@@ -49,6 +51,7 @@ func TestKeyRing_Encrypt_Decrypt(t *testing.T) {
Solana: []solkey.Raw{sol1.Raw(), sol2.Raw()},
VRF: []vrfkey.Raw{vrf1.Raw(), vrf2.Raw()},
Cosmos: []cosmoskey.Raw{tk1.Raw(), tk2.Raw()},
+ Tron: []tronkey.Raw{uk1.Raw(), uk2.Raw()},
}
originalKeyRing, kerr := originalKeyRingRaw.keys()
require.NoError(t, kerr)
@@ -62,6 +65,10 @@ func TestKeyRing_Encrypt_Decrypt(t *testing.T) {
require.Equal(t, 2, len(decryptedKeyRing.Cosmos))
require.Equal(t, originalKeyRing.Cosmos[tk1.ID()].PublicKey(), decryptedKeyRing.Cosmos[tk1.ID()].PublicKey())
require.Equal(t, originalKeyRing.Cosmos[tk2.ID()].PublicKey(), decryptedKeyRing.Cosmos[tk2.ID()].PublicKey())
+ // compare tron keys
+ require.Len(t, decryptedKeyRing.Tron, 2)
+ require.Equal(t, originalKeyRing.Tron[uk1.ID()].Base58Address(), decryptedKeyRing.Tron[uk1.ID()].Base58Address())
+ require.Equal(t, originalKeyRing.Tron[uk2.ID()].Base58Address(), decryptedKeyRing.Tron[uk2.ID()].Base58Address())
// compare csa keys
require.Equal(t, 2, len(decryptedKeyRing.CSA))
require.Equal(t, originalKeyRing.CSA[csa1.ID()].PublicKey, decryptedKeyRing.CSA[csa1.ID()].PublicKey)
diff --git a/core/services/keystore/tron.go b/core/services/keystore/tron.go
new file mode 100644
index 00000000000..d5302d572b0
--- /dev/null
+++ b/core/services/keystore/tron.go
@@ -0,0 +1,187 @@
+package keystore
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/pkg/errors"
+
+ "github.com/smartcontractkit/chainlink-common/pkg/loop"
+ "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/tronkey"
+)
+
+type Tron interface {
+ Get(id string) (tronkey.Key, error)
+ GetAll() ([]tronkey.Key, error)
+ Create(ctx context.Context) (tronkey.Key, error)
+ Add(ctx context.Context, key tronkey.Key) error
+ Delete(ctx context.Context, id string) (tronkey.Key, error)
+ Import(ctx context.Context, keyJSON []byte, password string) (tronkey.Key, error)
+ Export(id string, password string) ([]byte, error)
+ EnsureKey(ctx context.Context) error
+ Sign(ctx context.Context, id string, msg []byte) (signature []byte, err error)
+}
+
+type tron struct {
+ *keyManager
+}
+
+var _ Tron = &tron{}
+
+func newTronKeyStore(km *keyManager) *tron {
+ return &tron{
+ km,
+ }
+}
+
+func (ks *tron) Get(id string) (tronkey.Key, error) {
+ ks.lock.RLock()
+ defer ks.lock.RUnlock()
+ if ks.isLocked() {
+ return tronkey.Key{}, ErrLocked
+ }
+ return ks.getByID(id)
+}
+
+func (ks *tron) GetAll() (keys []tronkey.Key, _ error) {
+ ks.lock.RLock()
+ defer ks.lock.RUnlock()
+ if ks.isLocked() {
+ return nil, ErrLocked
+ }
+ for _, key := range ks.keyRing.Tron {
+ keys = append(keys, key)
+ }
+ return keys, nil
+}
+
+func (ks *tron) Create(ctx context.Context) (tronkey.Key, error) {
+ ks.lock.Lock()
+ defer ks.lock.Unlock()
+ if ks.isLocked() {
+ return tronkey.Key{}, ErrLocked
+ }
+ key, err := tronkey.New()
+ if err != nil {
+ return tronkey.Key{}, err
+ }
+ return key, ks.safeAddKey(ctx, key)
+}
+
+func (ks *tron) Add(ctx context.Context, key tronkey.Key) error {
+ ks.lock.Lock()
+ defer ks.lock.Unlock()
+ if ks.isLocked() {
+ return ErrLocked
+ }
+ if _, found := ks.keyRing.Tron[key.ID()]; found {
+ return fmt.Errorf("key with ID %s already exists", key.ID())
+ }
+ return ks.safeAddKey(ctx, key)
+}
+
+func (ks *tron) Delete(ctx context.Context, id string) (tronkey.Key, error) {
+ ks.lock.Lock()
+ defer ks.lock.Unlock()
+ if ks.isLocked() {
+ return tronkey.Key{}, ErrLocked
+ }
+ key, err := ks.getByID(id)
+ if err != nil {
+ return tronkey.Key{}, err
+ }
+ err = ks.safeRemoveKey(ctx, key)
+ return key, err
+}
+
+func (ks *tron) Import(ctx context.Context, keyJSON []byte, password string) (tronkey.Key, error) {
+ ks.lock.Lock()
+ defer ks.lock.Unlock()
+ if ks.isLocked() {
+ return tronkey.Key{}, ErrLocked
+ }
+ key, err := tronkey.FromEncryptedJSON(keyJSON, password)
+ if err != nil {
+ return tronkey.Key{}, errors.Wrap(err, "TronKeyStore#ImportKey failed to decrypt key")
+ }
+ if _, found := ks.keyRing.Tron[key.ID()]; found {
+ return tronkey.Key{}, fmt.Errorf("key with ID %s already exists", key.ID())
+ }
+ return key, ks.keyManager.safeAddKey(ctx, key)
+}
+
+func (ks *tron) Export(id string, password string) ([]byte, error) {
+ ks.lock.RLock()
+ defer ks.lock.RUnlock()
+ if ks.isLocked() {
+ return nil, ErrLocked
+ }
+ key, err := ks.getByID(id)
+ if err != nil {
+ return nil, err
+ }
+ return key.ToEncryptedJSON(password, ks.scryptParams)
+}
+
+func (ks *tron) EnsureKey(ctx context.Context) error {
+ ks.lock.Lock()
+ defer ks.lock.Unlock()
+ if ks.isLocked() {
+ return ErrLocked
+ }
+
+ if len(ks.keyRing.Tron) > 0 {
+ return nil
+ }
+
+ key, err := tronkey.New()
+ if err != nil {
+ return err
+ }
+
+ ks.logger.Infof("Created Tron key with ID %s", key.ID())
+
+ return ks.safeAddKey(ctx, key)
+}
+
+func (ks *tron) getByID(id string) (tronkey.Key, error) {
+ key, found := ks.keyRing.Tron[id]
+ if !found {
+ return tronkey.Key{}, KeyNotFoundError{ID: id, KeyType: "Tron"}
+ }
+ return key, nil
+}
+
+func (ks *tron) Sign(_ context.Context, id string, msg []byte) (signature []byte, err error) {
+ k, err := ks.Get(id)
+ if err != nil {
+ return nil, err
+ }
+ // loopp spec requires passing nil hash to check existence of id
+ if msg == nil {
+ return nil, nil
+ }
+ return k.Sign(msg)
+}
+
+// TronLOOPKeystore implements the [github.com/smartcontractkit/chainlink-common/pkg/loop.Keystore] interface and
+// handles signing for Tron messages.
+type TronLOOPKeystore struct {
+ Tron
+}
+
+var _ loop.Keystore = &TronLOOPKeystore{}
+
+func (lk *TronLOOPKeystore) Accounts(ctx context.Context) ([]string, error) {
+ keys, err := lk.GetAll()
+ if err != nil {
+ return nil, err
+ }
+
+ accounts := []string{}
+ for _, k := range keys {
+ accounts = append(accounts, k.PublicKeyStr())
+ }
+
+ return accounts, nil
+}
diff --git a/core/services/keystore/tron_test.go b/core/services/keystore/tron_test.go
new file mode 100644
index 00000000000..2450e573898
--- /dev/null
+++ b/core/services/keystore/tron_test.go
@@ -0,0 +1,240 @@
+package keystore_test
+
+import (
+ "context"
+ "crypto/sha256"
+ "encoding/json"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/require"
+
+ "github.com/smartcontractkit/chainlink-common/pkg/utils"
+ "github.com/smartcontractkit/chainlink/v2/core/internal/cltest"
+ "github.com/smartcontractkit/chainlink/v2/core/internal/testutils"
+ "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest"
+ "github.com/smartcontractkit/chainlink/v2/core/services/keystore"
+ "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/tronkey"
+)
+
+func Test_TronKeyStore_E2E(t *testing.T) {
+ db := pgtest.NewSqlxDB(t)
+
+ keyStore := keystore.ExposedNewMaster(t, db)
+ require.NoError(t, keyStore.Unlock(testutils.Context(t), cltest.Password))
+ ks := keyStore.Tron()
+ reset := func() {
+ ctx := context.Background() // Executed on cleanup
+ require.NoError(t, utils.JustError(db.Exec("DELETE FROM encrypted_key_rings")))
+ keyStore.ResetXXXTestOnly()
+ require.NoError(t, keyStore.Unlock(ctx, cltest.Password))
+ }
+
+ t.Run("initializes with an empty state", func(t *testing.T) {
+ defer reset()
+ keys, err := ks.GetAll()
+ require.NoError(t, err)
+ require.Empty(t, keys)
+ })
+
+ t.Run("errors when getting non-existent ID", func(t *testing.T) {
+ defer reset()
+ _, err := ks.Get("non-existent-id")
+ require.Error(t, err)
+ })
+
+ t.Run("creates a key", func(t *testing.T) {
+ defer reset()
+ ctx := testutils.Context(t)
+ key, err := ks.Create(ctx)
+ require.NoError(t, err)
+ retrievedKey, err := ks.Get(key.ID())
+ require.NoError(t, err)
+ require.Equal(t, key, retrievedKey)
+ })
+
+ t.Run("imports and exports a key", func(t *testing.T) {
+ defer reset()
+ ctx := testutils.Context(t)
+ key, err := ks.Create(ctx)
+ require.NoError(t, err)
+ exportJSON, err := ks.Export(key.ID(), cltest.Password)
+ require.NoError(t, err)
+ _, err = ks.Export("non-existent", cltest.Password)
+ require.Error(t, err)
+ _, err = ks.Delete(ctx, key.ID())
+ require.NoError(t, err)
+ _, err = ks.Get(key.ID())
+ require.Error(t, err)
+ importedKey, err := ks.Import(ctx, exportJSON, cltest.Password)
+ require.NoError(t, err)
+ _, err = ks.Import(ctx, exportJSON, cltest.Password)
+ require.Error(t, err)
+ _, err = ks.Import(ctx, []byte(""), cltest.Password)
+ require.Error(t, err)
+ require.Equal(t, key.ID(), importedKey.ID())
+ retrievedKey, err := ks.Get(key.ID())
+ require.NoError(t, err)
+ require.Equal(t, importedKey, retrievedKey)
+ })
+
+ t.Run("adds an externally created key / deletes a key", func(t *testing.T) {
+ defer reset()
+ ctx := testutils.Context(t)
+ newKey, err := tronkey.New()
+ require.NoError(t, err)
+ err = ks.Add(ctx, newKey)
+ require.NoError(t, err)
+ err = ks.Add(ctx, newKey)
+ require.Error(t, err)
+ keys, err := ks.GetAll()
+ require.NoError(t, err)
+ require.Len(t, keys, 1)
+ _, err = ks.Delete(ctx, newKey.ID())
+ require.NoError(t, err)
+ _, err = ks.Delete(ctx, newKey.ID())
+ require.Error(t, err)
+ keys, err = ks.GetAll()
+ require.NoError(t, err)
+ require.Empty(t, keys)
+ _, err = ks.Get(newKey.ID())
+ require.Error(t, err)
+ })
+
+ t.Run("ensures key", func(t *testing.T) {
+ defer reset()
+ ctx := testutils.Context(t)
+ err := ks.EnsureKey(ctx)
+ require.NoError(t, err)
+
+ err = ks.EnsureKey(ctx)
+ require.NoError(t, err)
+
+ keys, err := ks.GetAll()
+ require.NoError(t, err)
+ require.Len(t, keys, 1)
+ })
+
+ t.Run("sign tx", func(t *testing.T) {
+ defer reset()
+ ctx := testutils.Context(t)
+ newKey, err := tronkey.New()
+ require.NoError(t, err)
+ require.NoError(t, ks.Add(ctx, newKey))
+
+ // sign unknown ID
+ _, err = ks.Sign(testutils.Context(t), "not-real", nil)
+ require.Error(t, err)
+
+ // sign known key
+
+ // Create a mock transaction
+ mockTx := createMockTronTransaction(newKey.PublicKeyStr(), "TJRabPrwbZy45sbavfcjinPJC18kjpRTv8", 1000000)
+ serializedTx, err := serializeMockTransaction(mockTx)
+ require.NoError(t, err)
+
+ hash := sha256.Sum256(serializedTx)
+ txHash := hash[:]
+ sig, err := ks.Sign(testutils.Context(t), newKey.ID(), txHash)
+ require.NoError(t, err)
+
+ directSig, err := newKey.Sign(txHash)
+ require.NoError(t, err)
+
+ // signatures should match using keystore sign or key sign
+ require.Equal(t, directSig, sig)
+ })
+}
+
+// MockTronTransaction represents a mock TRON transaction
+// This is based on https://developers.tron.network/docs/tron-protocol-transaction
+type MockTronTransaction struct {
+ RawData struct {
+ Contract []struct {
+ Parameter struct {
+ Value struct {
+ Amount int64 `json:"amount"`
+ OwnerAddress string `json:"owner_address"`
+ ToAddress string `json:"to_address"`
+ } `json:"value"`
+ TypeURL string `json:"type_url"`
+ } `json:"parameter"`
+ Type string `json:"type"`
+ } `json:"contract"`
+ RefBlockBytes string `json:"ref_block_bytes"`
+ RefBlockHash string `json:"ref_block_hash"`
+ Expiration int64 `json:"expiration"`
+ Timestamp int64 `json:"timestamp"`
+ FeeLimit int64 `json:"fee_limit"`
+ } `json:"raw_data"`
+ Signature []string `json:"signature"`
+ TxID string `json:"txID"`
+}
+
+// CreateMockTronTransaction generates a mock TRON transaction for testing
+func createMockTronTransaction(ownerAddress, toAddress string, amount int64) MockTronTransaction {
+ return MockTronTransaction{
+ RawData: struct {
+ Contract []struct {
+ Parameter struct {
+ Value struct {
+ Amount int64 `json:"amount"`
+ OwnerAddress string `json:"owner_address"`
+ ToAddress string `json:"to_address"`
+ } `json:"value"`
+ TypeURL string `json:"type_url"`
+ } `json:"parameter"`
+ Type string `json:"type"`
+ } `json:"contract"`
+ RefBlockBytes string `json:"ref_block_bytes"`
+ RefBlockHash string `json:"ref_block_hash"`
+ Expiration int64 `json:"expiration"`
+ Timestamp int64 `json:"timestamp"`
+ FeeLimit int64 `json:"fee_limit"`
+ }{
+ Contract: []struct {
+ Parameter struct {
+ Value struct {
+ Amount int64 `json:"amount"`
+ OwnerAddress string `json:"owner_address"`
+ ToAddress string `json:"to_address"`
+ } `json:"value"`
+ TypeURL string `json:"type_url"`
+ } `json:"parameter"`
+ Type string `json:"type"`
+ }{
+ {
+ Parameter: struct {
+ Value struct {
+ Amount int64 `json:"amount"`
+ OwnerAddress string `json:"owner_address"`
+ ToAddress string `json:"to_address"`
+ } `json:"value"`
+ TypeURL string `json:"type_url"`
+ }{
+ Value: struct {
+ Amount int64 `json:"amount"`
+ OwnerAddress string `json:"owner_address"`
+ ToAddress string `json:"to_address"`
+ }{
+ Amount: amount,
+ OwnerAddress: ownerAddress,
+ ToAddress: toAddress,
+ },
+ TypeURL: "type.googleapis.com/protocol.TransferContract",
+ },
+ Type: "TransferContract",
+ },
+ },
+ RefBlockBytes: "1234",
+ RefBlockHash: "abcdef0123456789",
+ Expiration: time.Now().Unix() + 60*60,
+ Timestamp: time.Now().Unix(),
+ FeeLimit: 10000000,
+ },
+ }
+}
+
+func serializeMockTransaction(tx MockTronTransaction) ([]byte, error) {
+ return json.Marshal(tx)
+}
diff --git a/core/services/relay/relay.go b/core/services/relay/relay.go
index 0b1293c8d79..8a6a12e30e3 100644
--- a/core/services/relay/relay.go
+++ b/core/services/relay/relay.go
@@ -14,6 +14,7 @@ const (
NetworkSolana = "solana"
NetworkStarkNet = "starknet"
NetworkAptos = "aptos"
+ NetworkTron = "tron"
NetworkDummy = "dummy"
)
@@ -24,6 +25,7 @@ var SupportedNetworks = map[string]struct{}{
NetworkSolana: {},
NetworkStarkNet: {},
NetworkAptos: {},
+ NetworkTron: {},
NetworkDummy: {},
}
diff --git a/core/web/auth/auth_test.go b/core/web/auth/auth_test.go
index 25479409545..df869a8b1a3 100644
--- a/core/web/auth/auth_test.go
+++ b/core/web/auth/auth_test.go
@@ -276,14 +276,17 @@ var routesRolesMap = [...]routeRules{
{"GET", "/v2/keys/cosmos", true, true, true},
{"GET", "/v2/keys/starknet", true, true, true},
{"GET", "/v2/keys/aptos", true, true, true},
+ {"GET", "/v2/keys/tron", true, true, true},
{"POST", "/v2/keys/solana", false, false, true},
{"POST", "/v2/keys/cosmos", false, false, true},
{"POST", "/v2/keys/starknet", false, false, true},
{"POST", "/v2/keys/aptos", false, false, true},
+ {"POST", "/v2/keys/tron", false, false, true},
{"DELETE", "/v2/keys/solana/MOCK", false, false, false},
{"DELETE", "/v2/keys/cosmos/MOCK", false, false, false},
{"DELETE", "/v2/keys/starknet/MOCK", false, false, false},
{"DELETE", "/v2/keys/aptos/MOCK", false, false, false},
+ {"DELETE", "/v2/keys/tron/MOCK", false, false, false},
{"POST", "/v2/keys/solana/import", false, false, false},
{"POST", "/v2/keys/cosmos/import", false, false, false},
{"POST", "/v2/keys/starknet/import", false, false, false},
@@ -292,6 +295,7 @@ var routesRolesMap = [...]routeRules{
{"POST", "/v2/keys/cosmos/export/MOCK", false, false, false},
{"POST", "/v2/keys/starknet/export/MOCK", false, false, false},
{"POST", "/v2/keys/aptos/export/MOCK", false, false, false},
+ {"POST", "/v2/keys/tron/export/MOCK", false, false, false},
{"GET", "/v2/keys/vrf", true, true, true},
{"POST", "/v2/keys/vrf", false, false, true},
{"DELETE", "/v2/keys/vrf/MOCK", false, false, false},
diff --git a/core/web/presenters/node_test.go b/core/web/presenters/node_test.go
index d2db83009d9..9f980e22484 100644
--- a/core/web/presenters/node_test.go
+++ b/core/web/presenters/node_test.go
@@ -15,7 +15,7 @@ func TestNodeResource(t *testing.T) {
var nodeResource NodeResource
state := "test"
cfg := "cfg"
- testCases := []string{"solana", "cosmos", "starknet"}
+ testCases := []string{"solana", "cosmos", "starknet", "tron"}
for _, tc := range testCases {
chainID := fmt.Sprintf("%s chain ID", tc)
nodeName := fmt.Sprintf("%s_node", tc)
diff --git a/core/web/presenters/tron_chain.go b/core/web/presenters/tron_chain.go
new file mode 100644
index 00000000000..7ab6109bd39
--- /dev/null
+++ b/core/web/presenters/tron_chain.go
@@ -0,0 +1,45 @@
+package presenters
+
+import (
+ "github.com/smartcontractkit/chainlink-common/pkg/types"
+)
+
+// TronChainResource is an Tron chain JSONAPI resource.
+type TronChainResource struct {
+ ChainResource
+}
+
+// GetName implements the api2go EntityNamer interface
+func (r TronChainResource) GetName() string {
+ return "tron_chain"
+}
+
+// NewTronChainResource returns a new TronChainResource for chain.
+func NewTronChainResource(chain types.ChainStatus) TronChainResource {
+ return TronChainResource{ChainResource{
+ JAID: NewJAID(chain.ID),
+ Config: chain.Config,
+ Enabled: chain.Enabled,
+ }}
+}
+
+// TronNodeResource is a Tron node JSONAPI resource.
+type TronNodeResource struct {
+ NodeResource
+}
+
+// GetName implements the api2go EntityNamer interface
+func (r TronNodeResource) GetName() string {
+ return "tron_node"
+}
+
+// NewTronNodeResource returns a new TronNodeResource for node.
+func NewTronNodeResource(node types.NodeStatus) TronNodeResource {
+ return TronNodeResource{NodeResource{
+ JAID: NewPrefixedJAID(node.Name, node.ChainID),
+ ChainID: node.ChainID,
+ Name: node.Name,
+ State: node.State,
+ Config: node.Config,
+ }}
+}
diff --git a/core/web/presenters/tron_key.go b/core/web/presenters/tron_key.go
new file mode 100644
index 00000000000..abe74ed7f41
--- /dev/null
+++ b/core/web/presenters/tron_key.go
@@ -0,0 +1,34 @@
+package presenters
+
+import (
+ "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/tronkey"
+)
+
+// TronKeyResource represents a Tron key JSONAPI resource.
+type TronKeyResource struct {
+ JAID
+ PubKey string `json:"publicKey"`
+}
+
+// GetName implements the api2go EntityNamer interface
+func (TronKeyResource) GetName() string {
+ return "encryptedTronKeys"
+}
+
+func NewTronKeyResource(key tronkey.Key) *TronKeyResource {
+ r := &TronKeyResource{
+ JAID: JAID{ID: key.ID()},
+ PubKey: key.PublicKeyStr(),
+ }
+
+ return r
+}
+
+func NewTronKeyResources(keys []tronkey.Key) []TronKeyResource {
+ rs := []TronKeyResource{}
+ for _, key := range keys {
+ rs = append(rs, *NewTronKeyResource(key))
+ }
+
+ return rs
+}
diff --git a/core/web/resolver/feeds_manager_chain_config_test.go b/core/web/resolver/feeds_manager_chain_config_test.go
index 957583dbb7d..b4118cfa873 100644
--- a/core/web/resolver/feeds_manager_chain_config_test.go
+++ b/core/web/resolver/feeds_manager_chain_config_test.go
@@ -375,6 +375,81 @@ func Test_CreateFeedsManagerChainConfig(t *testing.T) {
}
}`,
},
+ {
+ name: "success Tron",
+ authenticated: true,
+ before: func(ctx context.Context, f *gqlTestFramework) {
+ f.App.On("GetFeedsService").Return(f.Mocks.feedsSvc)
+ f.Mocks.feedsSvc.On("CreateChainConfig", mock.Anything, feeds.ChainConfig{
+ FeedsManagerID: mgrID,
+ ChainType: feeds.ChainTypeTron,
+ ChainID: chainID,
+ AccountAddress: accountAddr,
+ AccountAddressPublicKey: null.StringFrom(acctAddrPubKey),
+ AdminAddress: adminAddr,
+ FluxMonitorConfig: feeds.FluxMonitorConfig{
+ Enabled: false,
+ },
+ OCR1Config: feeds.OCR1Config{
+ Enabled: true,
+ P2PPeerID: peerID,
+ KeyBundleID: keyBundleID,
+ },
+ OCR2Config: feeds.OCR2ConfigModel{
+ Enabled: true,
+ P2PPeerID: peerID,
+ KeyBundleID: keyBundleID,
+ ForwarderAddress: null.StringFrom(forwarderAddr),
+ Plugins: feeds.Plugins{
+ Commit: true,
+ Execute: true,
+ Median: false,
+ Mercury: true,
+ Rebalancer: true,
+ },
+ },
+ }).Return(cfgID, nil)
+ f.Mocks.feedsSvc.On("GetChainConfig", mock.Anything, cfgID).Return(&feeds.ChainConfig{
+ ID: cfgID,
+ ChainType: feeds.ChainTypeTron,
+ ChainID: chainID,
+ AccountAddress: accountAddr,
+ AccountAddressPublicKey: null.StringFrom(acctAddrPubKey),
+ AdminAddress: adminAddr,
+ FluxMonitorConfig: feeds.FluxMonitorConfig{
+ Enabled: false,
+ },
+ OCR1Config: feeds.OCR1Config{
+ Enabled: true,
+ P2PPeerID: peerID,
+ KeyBundleID: keyBundleID,
+ },
+ OCR2Config: feeds.OCR2ConfigModel{
+ Enabled: true,
+ P2PPeerID: peerID,
+ KeyBundleID: keyBundleID,
+ ForwarderAddress: null.StringFrom(forwarderAddr),
+ Plugins: feeds.Plugins{
+ Commit: true,
+ Execute: true,
+ Median: false,
+ Mercury: true,
+ Rebalancer: true,
+ },
+ },
+ }, nil)
+ },
+ query: mutation,
+ variables: withVariables("TRON"),
+ result: `
+ {
+ "createFeedsManagerChainConfig": {
+ "chainConfig": {
+ "id": "1"
+ }
+ }
+ }`,
+ },
{
name: "create call not found",
authenticated: true,
diff --git a/core/web/resolver/ocr2_keys.go b/core/web/resolver/ocr2_keys.go
index d04ebbd0e2f..345dd07d984 100644
--- a/core/web/resolver/ocr2_keys.go
+++ b/core/web/resolver/ocr2_keys.go
@@ -27,6 +27,8 @@ const (
OCR2ChainTypeStarkNet = "STARKNET"
// OCRChainTypeAptos defines OCR Aptos Chain Type
OCRChainTypeAptos = "APTOS"
+ // OCRChainTypeTron defines OCR2 Tron Chain Type
+ OCRChainTypeTron = "TRON"
)
// ToOCR2ChainType turns a valid string into a OCR2ChainType
@@ -42,6 +44,8 @@ func ToOCR2ChainType(s string) (OCR2ChainType, error) {
return OCR2ChainTypeStarkNet, nil
case string(chaintype.Aptos):
return OCRChainTypeAptos, nil
+ case string(chaintype.Tron):
+ return OCRChainTypeTron, nil
default:
return "", errors.New("unknown ocr2 chain type")
}
@@ -60,6 +64,8 @@ func FromOCR2ChainType(ct OCR2ChainType) string {
return string(chaintype.StarkNet)
case OCRChainTypeAptos:
return string(chaintype.Aptos)
+ case OCRChainTypeTron:
+ return string(chaintype.Tron)
default:
return strings.ToLower(string(ct))
}
diff --git a/core/web/resolver/ocr2_keys_test.go b/core/web/resolver/ocr2_keys_test.go
index 033d22799b1..e131aa0b5f5 100644
--- a/core/web/resolver/ocr2_keys_test.go
+++ b/core/web/resolver/ocr2_keys_test.go
@@ -42,6 +42,7 @@ func TestResolver_GetOCR2KeyBundles(t *testing.T) {
ocr2key.MustNewInsecure(keystest.NewRandReaderFromSeed(1), "solana"),
ocr2key.MustNewInsecure(keystest.NewRandReaderFromSeed(1), "starknet"),
ocr2key.MustNewInsecure(keystest.NewRandReaderFromSeed(1), "aptos"),
+ ocr2key.MustNewInsecure(keystest.NewRandReaderFromSeed(1), "tron"),
}
expectedBundles := []map[string]interface{}{}
for _, k := range fakeKeys {
diff --git a/core/web/resolver/query.go b/core/web/resolver/query.go
index ae33e5688bb..6348b805b3a 100644
--- a/core/web/resolver/query.go
+++ b/core/web/resolver/query.go
@@ -625,6 +625,19 @@ func (r *Resolver) StarkNetKeys(ctx context.Context) (*StarkNetKeysPayloadResolv
return NewStarkNetKeysPayload(keys), nil
}
+func (r *Resolver) TronKeys(ctx context.Context) (*TronKeysPayloadResolver, error) {
+ if err := authenticateUser(ctx); err != nil {
+ return nil, err
+ }
+
+ keys, err := r.App.GetKeyStore().Tron().GetAll()
+ if err != nil {
+ return nil, err
+ }
+
+ return NewTronKeysPayload(keys), nil
+}
+
func (r *Resolver) SQLLogging(ctx context.Context) (*GetSQLLoggingPayloadResolver, error) {
if err := authenticateUser(ctx); err != nil {
return nil, err
diff --git a/core/web/resolver/resolver_test.go b/core/web/resolver/resolver_test.go
index 0d365b0891e..6ff9d954b37 100644
--- a/core/web/resolver/resolver_test.go
+++ b/core/web/resolver/resolver_test.go
@@ -54,6 +54,7 @@ type mocks struct {
aptos *keystoreMocks.Aptos
cosmos *keystoreMocks.Cosmos
starknet *keystoreMocks.StarkNet
+ tron *keystoreMocks.Tron
chain *legacyEvmORMMocks.Chain
legacyEVMChains *legacyEvmORMMocks.LegacyChainContainer
relayerChainInterops *chainlinkMocks.FakeRelayerChainInteroperators
@@ -112,6 +113,7 @@ func setupFramework(t *testing.T) *gqlTestFramework {
aptos: keystoreMocks.NewAptos(t),
cosmos: keystoreMocks.NewCosmos(t),
starknet: keystoreMocks.NewStarkNet(t),
+ tron: keystoreMocks.NewTron(t),
chain: legacyEvmORMMocks.NewChain(t),
legacyEVMChains: legacyEvmORMMocks.NewLegacyChainContainer(t),
relayerChainInterops: &chainlinkMocks.FakeRelayerChainInteroperators{},
diff --git a/core/web/resolver/tron_key.go b/core/web/resolver/tron_key.go
new file mode 100644
index 00000000000..d42ed2071c4
--- /dev/null
+++ b/core/web/resolver/tron_key.go
@@ -0,0 +1,43 @@
+package resolver
+
+import (
+ "github.com/graph-gophers/graphql-go"
+
+ "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/tronkey"
+)
+
+type TronKeyResolver struct {
+ key tronkey.Key
+}
+
+func NewTronKey(key tronkey.Key) *TronKeyResolver {
+ return &TronKeyResolver{key: key}
+}
+
+func NewTronKeys(keys []tronkey.Key) []*TronKeyResolver {
+ resolvers := make([]*TronKeyResolver, 0, len(keys))
+
+ for _, k := range keys {
+ resolvers = append(resolvers, NewTronKey(k))
+ }
+
+ return resolvers
+}
+
+func (r *TronKeyResolver) ID() graphql.ID {
+ return graphql.ID(r.key.ID())
+}
+
+// -- GetTronKeys Query --
+
+type TronKeysPayloadResolver struct {
+ keys []tronkey.Key
+}
+
+func NewTronKeysPayload(keys []tronkey.Key) *TronKeysPayloadResolver {
+ return &TronKeysPayloadResolver{keys: keys}
+}
+
+func (r *TronKeysPayloadResolver) Results() []*TronKeyResolver {
+ return NewTronKeys(r.keys)
+}
diff --git a/core/web/resolver/tron_key_test.go b/core/web/resolver/tron_key_test.go
new file mode 100644
index 00000000000..6ccbeb1072d
--- /dev/null
+++ b/core/web/resolver/tron_key_test.go
@@ -0,0 +1,74 @@
+package resolver
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "testing"
+
+ gqlerrors "github.com/graph-gophers/graphql-go/errors"
+
+ "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/keystest"
+ "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/tronkey"
+)
+
+func TestResolver_TronKeys(t *testing.T) {
+ t.Parallel()
+
+ query := `
+ query GetTronKeys {
+ tronKeys {
+ results {
+ id
+ }
+ }
+ }`
+ k := tronkey.MustNewInsecure(keystest.NewRandReaderFromSeed(1))
+ result := fmt.Sprintf(`
+ {
+ "tronKeys": {
+ "results": [
+ {
+ "id": "%s"
+ }
+ ]
+ }
+ }`, k.ID())
+ gError := errors.New("error")
+
+ testCases := []GQLTestCase{
+ unauthorizedTestCase(GQLTestCase{query: query}, "tronKeys"),
+ {
+ name: "success",
+ authenticated: true,
+ before: func(ctx context.Context, f *gqlTestFramework) {
+ f.Mocks.tron.On("GetAll").Return([]tronkey.Key{k}, nil)
+ f.Mocks.keystore.On("Tron").Return(f.Mocks.tron)
+ f.App.On("GetKeyStore").Return(f.Mocks.keystore)
+ },
+ query: query,
+ result: result,
+ },
+ {
+ name: "no keys returned by GetAll",
+ authenticated: true,
+ before: func(ctx context.Context, f *gqlTestFramework) {
+ f.Mocks.tron.On("GetAll").Return([]tronkey.Key{}, gError)
+ f.Mocks.keystore.On("Tron").Return(f.Mocks.tron)
+ f.App.On("GetKeyStore").Return(f.Mocks.keystore)
+ },
+ query: query,
+ result: `null`,
+ errors: []*gqlerrors.QueryError{
+ {
+ Extensions: nil,
+ ResolverError: gError,
+ Path: []interface{}{"tronKeys"},
+ Message: gError.Error(),
+ },
+ },
+ },
+ }
+
+ RunGQLTests(t, testCases)
+}
diff --git a/core/web/router.go b/core/web/router.go
index c57bf3c8095..f56d3e69651 100644
--- a/core/web/router.go
+++ b/core/web/router.go
@@ -351,6 +351,7 @@ func v2Routes(app chainlink.Application, r *gin.RouterGroup) {
{"cosmos", NewCosmosKeysController(app)},
{"starknet", NewStarkNetKeysController(app)},
{"aptos", NewAptosKeysController(app)},
+ {"tron", NewTronKeysController(app)},
} {
authv2.GET("/keys/"+keys.path, keys.kc.Index)
authv2.POST("/keys/"+keys.path, auth.RequiresEditRole(keys.kc.Create))
diff --git a/core/web/schema/schema.graphql b/core/web/schema/schema.graphql
index 568716f7b76..6f9e51b79eb 100644
--- a/core/web/schema/schema.graphql
+++ b/core/web/schema/schema.graphql
@@ -36,6 +36,7 @@ type Query {
aptosKeys: AptosKeysPayload!
cosmosKeys: CosmosKeysPayload!
starknetKeys: StarkNetKeysPayload!
+ tronKeys: TronKeysPayload!
sqlLogging: GetSQLLoggingPayload!
vrfKey(id: ID!): VRFKeyPayload!
vrfKeys: VRFKeysPayload!
diff --git a/core/web/schema/type/ocr2_keys.graphql b/core/web/schema/type/ocr2_keys.graphql
index c25148c686a..89125d86b54 100644
--- a/core/web/schema/type/ocr2_keys.graphql
+++ b/core/web/schema/type/ocr2_keys.graphql
@@ -4,6 +4,7 @@ enum OCR2ChainType {
SOLANA
STARKNET
APTOS
+ TRON
}
type OCR2KeyBundle {
diff --git a/core/web/schema/type/tron_key.graphql b/core/web/schema/type/tron_key.graphql
new file mode 100644
index 00000000000..e7319f08d6b
--- /dev/null
+++ b/core/web/schema/type/tron_key.graphql
@@ -0,0 +1,7 @@
+type TronKey {
+ id: ID!
+}
+
+type TronKeysPayload {
+ results: [TronKey!]!
+}
diff --git a/core/web/tron_keys_controller.go b/core/web/tron_keys_controller.go
new file mode 100644
index 00000000000..e9ac2e0252e
--- /dev/null
+++ b/core/web/tron_keys_controller.go
@@ -0,0 +1,12 @@
+package web
+
+import (
+ "github.com/smartcontractkit/chainlink/v2/core/services/chainlink"
+ "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/tronkey"
+ "github.com/smartcontractkit/chainlink/v2/core/web/presenters"
+)
+
+func NewTronKeysController(app chainlink.Application) KeysController {
+ return NewKeysController[tronkey.Key, presenters.TronKeyResource](app.GetKeyStore().Tron(), app.GetLogger(), app.GetAuditLogger(),
+ "tronKey", presenters.NewTronKeyResource, presenters.NewTronKeyResources)
+}
diff --git a/core/web/tron_keys_controller_test.go b/core/web/tron_keys_controller_test.go
new file mode 100644
index 00000000000..5628d7ac2dc
--- /dev/null
+++ b/core/web/tron_keys_controller_test.go
@@ -0,0 +1,105 @@
+package web_test
+
+import (
+ "net/http"
+ "testing"
+
+ "github.com/smartcontractkit/chainlink-common/pkg/utils"
+ "github.com/smartcontractkit/chainlink/v2/core/internal/cltest"
+ "github.com/smartcontractkit/chainlink/v2/core/internal/testutils"
+ "github.com/smartcontractkit/chainlink/v2/core/services/keystore"
+ "github.com/smartcontractkit/chainlink/v2/core/web"
+ "github.com/smartcontractkit/chainlink/v2/core/web/presenters"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestTronKeysController_Index_HappyPath(t *testing.T) {
+ t.Parallel()
+
+ client, keyStore := setupTronKeysControllerTests(t)
+ keys, _ := keyStore.Tron().GetAll()
+
+ response, cleanup := client.Get("/v2/keys/tron")
+ t.Cleanup(cleanup)
+ cltest.AssertServerResponse(t, response, http.StatusOK)
+
+ resources := []presenters.TronKeyResource{}
+ err := web.ParseJSONAPIResponse(cltest.ParseResponseBody(t, response), &resources)
+ require.NoError(t, err)
+
+ require.Len(t, resources, len(keys))
+
+ require.Equal(t, keys[0].ID(), resources[0].ID)
+ require.Equal(t, keys[0].PublicKeyStr(), resources[0].PubKey)
+}
+
+func TestTronKeysController_Create_HappyPath(t *testing.T) {
+ t.Parallel()
+
+ app := cltest.NewApplicationEVMDisabled(t)
+ require.NoError(t, app.Start(testutils.Context(t)))
+ client := app.NewHTTPClient(nil)
+ keyStore := app.GetKeyStore()
+
+ response, cleanup := client.Post("/v2/keys/tron", nil)
+ t.Cleanup(cleanup)
+ cltest.AssertServerResponse(t, response, http.StatusOK)
+
+ keys, _ := keyStore.Tron().GetAll()
+ require.Len(t, keys, 1)
+
+ resource := presenters.TronKeyResource{}
+ err := web.ParseJSONAPIResponse(cltest.ParseResponseBody(t, response), &resource)
+ require.NoError(t, err)
+
+ require.Equal(t, keys[0].ID(), resource.ID)
+ require.Equal(t, keys[0].PublicKeyStr(), resource.PubKey)
+
+ _, err = keyStore.Tron().Get(resource.ID)
+ require.NoError(t, err)
+}
+
+func TestTronKeysController_Delete_NonExistentTronKeyID(t *testing.T) {
+ t.Parallel()
+
+ client, _ := setupTronKeysControllerTests(t)
+
+ nonExistentTronKeyID := "foobar"
+ response, cleanup := client.Delete("/v2/keys/tron/" + nonExistentTronKeyID)
+ t.Cleanup(cleanup)
+ require.Equal(t, http.StatusNotFound, response.StatusCode)
+}
+
+func TestTronKeysController_Delete_HappyPath(t *testing.T) {
+ t.Parallel()
+ ctx := testutils.Context(t)
+
+ client, keyStore := setupTronKeysControllerTests(t)
+
+ keys, _ := keyStore.Tron().GetAll()
+ initialLength := len(keys)
+ key, _ := keyStore.Tron().Create(ctx)
+
+ response, cleanup := client.Delete("/v2/keys/tron/" + key.ID())
+ t.Cleanup(cleanup)
+ require.Equal(t, http.StatusOK, response.StatusCode)
+ require.Error(t, utils.JustError(keyStore.Tron().Get(key.ID())))
+
+ keys, _ = keyStore.Tron().GetAll()
+ require.Len(t, keys, initialLength)
+}
+
+func setupTronKeysControllerTests(t *testing.T) (cltest.HTTPClientCleaner, keystore.Master) {
+ t.Helper()
+ ctx := testutils.Context(t)
+
+ app := cltest.NewApplication(t)
+ require.NoError(t, app.Start(ctx))
+ require.NoError(t, app.KeyStore.OCR().Add(ctx, cltest.DefaultOCRKey))
+ require.NoError(t, app.KeyStore.Tron().Add(ctx, cltest.DefaultTronKey))
+
+ client := app.NewHTTPClient(nil)
+
+ return client, app.GetKeyStore()
+}
diff --git a/deployment/environment/nodeclient/chainlink.go b/deployment/environment/nodeclient/chainlink.go
index 9aed808d5be..52aee76cd98 100644
--- a/deployment/environment/nodeclient/chainlink.go
+++ b/deployment/environment/nodeclient/chainlink.go
@@ -1006,6 +1006,34 @@ func (c *ChainlinkClient) CreateStarkNetNode(node *StarkNetNodeAttributes) (*Sta
return &response, resp.RawResponse, err
}
+// CreateTronChain creates a tron chain
+func (c *ChainlinkClient) CreateTronChain(chain *TronChainAttributes) (*TronChainCreate, *http.Response, error) {
+ response := TronChainCreate{}
+ c.l.Info().Str(NodeURL, c.Config.URL).Str("Chain ID", chain.ChainID).Msg("Creating Tron Chain")
+ resp, err := c.APIClient.R().
+ SetBody(chain).
+ SetResult(&response).
+ Post("/v2/chains/tron")
+ if err != nil {
+ return nil, nil, err
+ }
+ return &response, resp.RawResponse, err
+}
+
+// CreateTronNode creates a tron node
+func (c *ChainlinkClient) CreateTronNode(node *TronNodeAttributes) (*TronNodeCreate, *http.Response, error) {
+ response := TronNodeCreate{}
+ c.l.Info().Str(NodeURL, c.Config.URL).Str("Name", node.Name).Msg("Creating Tron Node")
+ resp, err := c.APIClient.R().
+ SetBody(node).
+ SetResult(&response).
+ Post("/v2/nodes/tron")
+ if err != nil {
+ return nil, nil, err
+ }
+ return &response, resp.RawResponse, err
+}
+
// InternalIP retrieves the inter-cluster IP of the Chainlink node, for use with inter-node communications
func (c *ChainlinkClient) InternalIP() string {
return c.Config.InternalIP
diff --git a/deployment/environment/nodeclient/chainlink_models.go b/deployment/environment/nodeclient/chainlink_models.go
index 84bea9cec31..1f4bbe4ebd0 100644
--- a/deployment/environment/nodeclient/chainlink_models.go
+++ b/deployment/environment/nodeclient/chainlink_models.go
@@ -520,6 +520,48 @@ type StarkNetNodeCreate struct {
Data StarkNetNode `json:"data"`
}
+type TronChainConfig struct {
+ OCR2CachePollPeriod null.String
+ OCR2CacheTTL null.String
+ RequestTimeout null.String
+ TxTimeout null.Bool
+ TxSendFrequency null.String
+ TxMaxBatchSize null.String
+}
+
+// TronChainAttributes is the model that represents the tron chain
+type TronChainAttributes struct {
+ ChainID string `json:"chainID"`
+ Config TronChainConfig `json:"config"`
+}
+
+// TronChain is the model that represents the tron chain when read
+type TronChain struct {
+ Attributes TronChainAttributes `json:"attributes"`
+}
+
+// TronChainCreate is the model that represents the tron chain when created
+type TronChainCreate struct {
+ Data TronChain `json:"data"`
+}
+
+// TronNodeAttributes is the model that represents the tron node
+type TronNodeAttributes struct {
+ Name string `json:"name"`
+ ChainID string `json:"chainId"`
+ URL string `json:"url"`
+}
+
+// TronNode is the model that represents the tron node when read
+type TronNode struct {
+ Attributes TronNodeAttributes `json:"attributes"`
+}
+
+// TronNodeCreate is the model that represents the tron node when created
+type TronNodeCreate struct {
+ Data TronNode `json:"data"`
+}
+
// SpecForm is the form used when creating a v2 job spec, containing the TOML of the v2 job
type SpecForm struct {
TOML string `json:"toml"`
diff --git a/testdata/scripts/chains/help.txtar b/testdata/scripts/chains/help.txtar
index ccfb54d2928..5d9a8945ad9 100644
--- a/testdata/scripts/chains/help.txtar
+++ b/testdata/scripts/chains/help.txtar
@@ -14,6 +14,7 @@ COMMANDS:
evm Commands for handling evm chains
solana Commands for handling solana chains
starknet Commands for handling starknet chains
+ tron Commands for handling tron chains
OPTIONS:
--help, -h show help
diff --git a/testdata/scripts/chains/tron/help.txtar b/testdata/scripts/chains/tron/help.txtar
new file mode 100644
index 00000000000..b0af73f1a25
--- /dev/null
+++ b/testdata/scripts/chains/tron/help.txtar
@@ -0,0 +1,16 @@
+exec chainlink chains tron --help
+cmp stdout out.txt
+
+-- out.txt --
+NAME:
+ chainlink chains tron - Commands for handling tron chains
+
+USAGE:
+ chainlink chains tron command [command options] [arguments...]
+
+COMMANDS:
+ list List all existing tron chains
+
+OPTIONS:
+ --help, -h show help
+
diff --git a/testdata/scripts/chains/tron/list/help.txtar b/testdata/scripts/chains/tron/list/help.txtar
new file mode 100644
index 00000000000..ce18cf3de6d
--- /dev/null
+++ b/testdata/scripts/chains/tron/list/help.txtar
@@ -0,0 +1,9 @@
+exec chainlink chains tron list --help
+cmp stdout out.txt
+
+-- out.txt --
+NAME:
+ chainlink chains tron list - List all existing tron chains
+
+USAGE:
+ chainlink chains tron list [arguments...]
diff --git a/testdata/scripts/help-all/help-all.txtar b/testdata/scripts/help-all/help-all.txtar
index 078853ef6a5..87d715edcba 100644
--- a/testdata/scripts/help-all/help-all.txtar
+++ b/testdata/scripts/help-all/help-all.txtar
@@ -34,6 +34,8 @@ chains solana # Commands for handling solana chains
chains solana list # List all existing solana chains
chains starknet # Commands for handling starknet chains
chains starknet list # List all existing starknet chains
+chains tron # Commands for handling tron chains
+chains tron list # List all existing tron chains
config # Commands for the node's configuration
config loglevel # Set log level
config logsql # Enable/disable SQL statement logging
@@ -111,6 +113,12 @@ keys starknet delete # Delete StarkNet key if present
keys starknet export # Export StarkNet key to keyfile
keys starknet import # Import StarkNet key from keyfile
keys starknet list # List the StarkNet keys
+keys tron # Remote commands for administering the node's Tron keys
+keys tron create # Create a Tron key
+keys tron delete # Delete Tron key if present
+keys tron export # Export Tron key to keyfile
+keys tron import # Import Tron key from keyfile
+keys tron list # List the Tron keys
keys vrf # Remote commands for administering the node's vrf keys
keys vrf create # Create a VRF key
keys vrf delete # Archive or delete VRF key from memory and the database, if present. Note that jobs referencing the removed key will also be removed.
@@ -144,6 +152,8 @@ nodes solana # Commands for handling solana node configuration
nodes solana list # List all existing solana nodes
nodes starknet # Commands for handling starknet node configuration
nodes starknet list # List all existing starknet nodes
+nodes tron # Commands for handling tron node configuration
+nodes tron list # List all existing tron nodes
txs # Commands for handling transactions
txs cosmos # Commands for handling Cosmos transactions
txs cosmos create # Send of from node Cosmos account to destination .
diff --git a/testdata/scripts/keys/help.txtar b/testdata/scripts/keys/help.txtar
index 83253d6906d..e930b928f54 100644
--- a/testdata/scripts/keys/help.txtar
+++ b/testdata/scripts/keys/help.txtar
@@ -18,6 +18,7 @@ COMMANDS:
solana Remote commands for administering the node's Solana keys
starknet Remote commands for administering the node's StarkNet keys
aptos Remote commands for administering the node's Aptos keys
+ tron Remote commands for administering the node's Tron keys
vrf Remote commands for administering the node's vrf keys
OPTIONS:
diff --git a/testdata/scripts/keys/tron/help.txtar b/testdata/scripts/keys/tron/help.txtar
new file mode 100644
index 00000000000..6e0b8bf31a2
--- /dev/null
+++ b/testdata/scripts/keys/tron/help.txtar
@@ -0,0 +1,20 @@
+exec chainlink keys tron --help
+cmp stdout out.txt
+
+-- out.txt --
+NAME:
+ chainlink keys tron - Remote commands for administering the node's Tron keys
+
+USAGE:
+ chainlink keys tron command [command options] [arguments...]
+
+COMMANDS:
+ create Create a Tron key
+ import Import Tron key from keyfile
+ export Export Tron key to keyfile
+ delete Delete Tron key if present
+ list List the Tron keys
+
+OPTIONS:
+ --help, -h show help
+
diff --git a/testdata/scripts/node/validate/invalid-duplicates.txtar b/testdata/scripts/node/validate/invalid-duplicates.txtar
index 84e6c23aa71..d13fff5f620 100644
--- a/testdata/scripts/node/validate/invalid-duplicates.txtar
+++ b/testdata/scripts/node/validate/invalid-duplicates.txtar
@@ -63,6 +63,19 @@ URL = 'http://stark.node'
Name = 'primary'
URL = 'http://stark.node'
+[[Tron]]
+ChainID = '1'
+
+[[Tron]]
+ChainID = '1'
+
+[[Tron.Nodes]]
+Name = 'fake'
+URL = 'https://foo.bar'
+
+[[Tron.Nodes]]
+Name = 'fake'
+URL = 'https://foo.bar'
-- secrets.toml --
[Database]
@@ -74,7 +87,7 @@ Keystore = ''
-- out.txt --
-- err.txt --
-Error running app: invalid configuration: 4 errors:
+Error running app: invalid configuration: 5 errors:
- EVM: 4 errors:
- 1.ChainID: invalid value (1): duplicate - must be unique
- 1.Nodes.1.Name: invalid value (fake): duplicate - must be unique
@@ -92,3 +105,6 @@ Error running app: invalid configuration: 4 errors:
- 1.ChainID: invalid value (foobar): duplicate - must be unique
- 1.Nodes.1.Name: invalid value (primary): duplicate - must be unique
- 1.Nodes.1.URL: invalid value (http://stark.node): duplicate - must be unique
+ - Tron: 2 errors:
+ - 1.ChainID: invalid value (1): duplicate - must be unique
+ - 1.Nodes.1.Name: invalid value (fake): duplicate - must be unique
diff --git a/testdata/scripts/nodes/help.txtar b/testdata/scripts/nodes/help.txtar
index f9132045d29..c8409d62691 100644
--- a/testdata/scripts/nodes/help.txtar
+++ b/testdata/scripts/nodes/help.txtar
@@ -14,6 +14,7 @@ COMMANDS:
evm Commands for handling evm node configuration
solana Commands for handling solana node configuration
starknet Commands for handling starknet node configuration
+ tron Commands for handling tron node configuration
OPTIONS:
--help, -h show help
diff --git a/testdata/scripts/nodes/tron/help.txtar b/testdata/scripts/nodes/tron/help.txtar
new file mode 100644
index 00000000000..e35e174e6d8
--- /dev/null
+++ b/testdata/scripts/nodes/tron/help.txtar
@@ -0,0 +1,16 @@
+exec chainlink nodes tron --help
+cmp stdout out.txt
+
+-- out.txt --
+NAME:
+ chainlink nodes tron - Commands for handling tron node configuration
+
+USAGE:
+ chainlink nodes tron command [command options] [arguments...]
+
+COMMANDS:
+ list List all existing tron nodes
+
+OPTIONS:
+ --help, -h show help
+
diff --git a/testdata/scripts/nodes/tron/list/help.txtar b/testdata/scripts/nodes/tron/list/help.txtar
new file mode 100644
index 00000000000..08c9d07d56b
--- /dev/null
+++ b/testdata/scripts/nodes/tron/list/help.txtar
@@ -0,0 +1,9 @@
+exec chainlink nodes tron list --help
+cmp stdout out.txt
+
+-- out.txt --
+NAME:
+ chainlink nodes tron list - List all existing tron nodes
+
+USAGE:
+ chainlink nodes tron list [arguments...]
From ba343f35df697b7a8a5a9262376969b9bbc6d834 Mon Sep 17 00:00:00 2001
From: Erik Burton
Date: Thu, 9 Jan 2025 06:06:50 -0800
Subject: [PATCH 20/91] fix: goreleaser builds cache/dist path (#15875)
---
.github/workflows/build-publish-develop-pr.yml | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/.github/workflows/build-publish-develop-pr.yml b/.github/workflows/build-publish-develop-pr.yml
index 4aed811daa6..58f5ee560a7 100644
--- a/.github/workflows/build-publish-develop-pr.yml
+++ b/.github/workflows/build-publish-develop-pr.yml
@@ -83,11 +83,11 @@ jobs:
include:
- runner: ubuntu-latest
goarch: amd64
- dist_name: linux_amd64_v1
+ dist_name: linux_amd64
- runner: ubuntu-24.04-4cores-16GB-ARM
goarch: arm64
- dist_name: linux_arm64_v8.0
+ dist_name: linux_arm64
steps:
- name: Checkout chainlink repository
uses: actions/checkout@v4.2.1
@@ -122,7 +122,7 @@ jobs:
- id: cache
uses: actions/cache@v4
with:
- path: dist/${{ matrix.dist_name }}
+ path: ./dist/${{ matrix.dist_name }}
key: chainlink-${{ matrix.goarch }}-${{ github.sha }}
- name: Build images for ${{ matrix.goarch }}
@@ -159,13 +159,13 @@ jobs:
- uses: actions/cache/restore@v4
with:
- path: dist/linux_amd64_v1
+ path: ./dist/linux_amd64
key: chainlink-amd64-${{ github.sha }}
fail-on-cache-miss: true
- uses: actions/cache/restore@v4
with:
- path: dist/linux_arm64_v8.0
+ path: ./dist/linux_arm64
key: chainlink-arm64-${{ github.sha }}
fail-on-cache-miss: true
From bfe91d405dd7f9d016dc604d93da97c42d40acea Mon Sep 17 00:00:00 2001
From: Gabriel Paradiso
Date: Thu, 9 Jan 2025 15:26:59 +0100
Subject: [PATCH 21/91] [CAPPL-320] use decoded workflowName when logged via
beholder (#15815)
* fix: hex decode workflowName when logged via beholder
* feat: decode workflow name on the engine
* chore: bump chainlink-common
* chore: add workflow name to model to avoid decoding in multiple places
---
core/capabilities/compute/compute.go | 2 +-
core/capabilities/targets/write_target.go | 3 ++-
core/scripts/go.mod | 2 +-
core/scripts/go.sum | 4 ++--
core/services/workflows/delegate.go | 2 +-
core/services/workflows/engine.go | 8 ++++++--
core/services/workflows/engine_test.go | 4 ++--
core/services/workflows/models.go | 1 +
core/services/workflows/syncer/handler.go | 7 ++++---
core/services/workflows/syncer/handler_test.go | 13 ++++++++-----
deployment/go.mod | 2 +-
deployment/go.sum | 4 ++--
go.mod | 2 +-
go.sum | 4 ++--
integration-tests/go.mod | 2 +-
integration-tests/go.sum | 4 ++--
integration-tests/load/go.mod | 2 +-
integration-tests/load/go.sum | 4 ++--
18 files changed, 40 insertions(+), 30 deletions(-)
diff --git a/core/capabilities/compute/compute.go b/core/capabilities/compute/compute.go
index 2ba5daefaa6..156c5154c99 100644
--- a/core/capabilities/compute/compute.go
+++ b/core/capabilities/compute/compute.go
@@ -300,7 +300,7 @@ func (c *Compute) createFetcher() func(ctx context.Context, req *wasmpb.FetchReq
cma := c.emitter.With(
platform.KeyWorkflowID, req.Metadata.WorkflowId,
- platform.KeyWorkflowName, req.Metadata.WorkflowName,
+ platform.KeyWorkflowName, req.Metadata.DecodedWorkflowName,
platform.KeyWorkflowOwner, req.Metadata.WorkflowOwner,
platform.KeyWorkflowExecutionID, req.Metadata.WorkflowExecutionId,
timestampKey, time.Now().UTC().Format(time.RFC3339Nano),
diff --git a/core/capabilities/targets/write_target.go b/core/capabilities/targets/write_target.go
index 09a0bbd2b36..3fc51270c86 100644
--- a/core/capabilities/targets/write_target.go
+++ b/core/capabilities/targets/write_target.go
@@ -345,10 +345,11 @@ func (cap *WriteTarget) Execute(ctx context.Context, rawRequest capabilities.Cap
return capabilities.CapabilityResponse{}, nil
case commontypes.Failed, commontypes.Fatal:
cap.lggr.Error("Transaction failed", "request", request, "transaction", txID)
+
msg := "failed to submit transaction with ID: " + txID.String()
err = cap.emitter.With(
platform.KeyWorkflowID, request.Metadata.WorkflowID,
- platform.KeyWorkflowName, request.Metadata.WorkflowName,
+ platform.KeyWorkflowName, request.Metadata.DecodedWorkflowName,
platform.KeyWorkflowOwner, request.Metadata.WorkflowOwner,
platform.KeyWorkflowExecutionID, request.Metadata.WorkflowExecutionID,
).Emit(ctx, msg)
diff --git a/core/scripts/go.mod b/core/scripts/go.mod
index e016559d6cf..bccad12b396 100644
--- a/core/scripts/go.mod
+++ b/core/scripts/go.mod
@@ -33,7 +33,7 @@ require (
github.com/prometheus/client_golang v1.20.5
github.com/shopspring/decimal v1.4.0
github.com/smartcontractkit/chainlink-automation v0.8.1
- github.com/smartcontractkit/chainlink-common v0.4.1-0.20241223143929-db7919d60550
+ github.com/smartcontractkit/chainlink-common v0.4.1-0.20250108194320-2ebd63bbb16e
github.com/smartcontractkit/libocr v0.0.0-20241223215956-e5b78d8e3919
github.com/spf13/cobra v1.8.1
github.com/spf13/viper v1.19.0
diff --git a/core/scripts/go.sum b/core/scripts/go.sum
index b716caa9ec3..d0c9ebe9e1f 100644
--- a/core/scripts/go.sum
+++ b/core/scripts/go.sum
@@ -1115,8 +1115,8 @@ github.com/smartcontractkit/chainlink-ccip v0.0.0-20241218114855-f74219171000 h1
github.com/smartcontractkit/chainlink-ccip v0.0.0-20241218114855-f74219171000/go.mod h1:ncjd6mPZSRlelEqH/2KeLE1pU3UlqzBSn8RYkEoECzY=
github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250103152858-8973fd0c912b h1:UBXi9Yj8YSMHDDaxQLu273x1fWjyEL9xP58nuJsqZfg=
github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250103152858-8973fd0c912b/go.mod h1:Bmwq4lNb5tE47sydN0TKetcLEGbgl+VxHEWp4S0LI60=
-github.com/smartcontractkit/chainlink-common v0.4.1-0.20241223143929-db7919d60550 h1:rRs74zjDJ7do5aYEXSU/sOnLnlbYCNqM8BrvEx/0NH8=
-github.com/smartcontractkit/chainlink-common v0.4.1-0.20241223143929-db7919d60550/go.mod h1:yti7e1+G9hhkYhj+L5sVUULn9Bn3bBL5/AxaNqdJ5YQ=
+github.com/smartcontractkit/chainlink-common v0.4.1-0.20250108194320-2ebd63bbb16e h1:8BStiP1F4W8AvjBRga0TYtjvAtkwN8oHYnHJztAlSF4=
+github.com/smartcontractkit/chainlink-common v0.4.1-0.20250108194320-2ebd63bbb16e/go.mod h1:yti7e1+G9hhkYhj+L5sVUULn9Bn3bBL5/AxaNqdJ5YQ=
github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e h1:PRoeby6ZlTuTkv2f+7tVU4+zboTfRzI+beECynF4JQ0=
github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e/go.mod h1:mUh5/woemsVaHgTorA080hrYmO3syBCmPdnWc/5dOqk=
github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241216163550-fa030d178ba3 h1:aeiBdBHGY8QNftps+VqrIk6OnfeeOD5z4jrAabW4ZSc=
diff --git a/core/services/workflows/delegate.go b/core/services/workflows/delegate.go
index fc8fe3fe840..9e50f5ec092 100644
--- a/core/services/workflows/delegate.go
+++ b/core/services/workflows/delegate.go
@@ -81,7 +81,7 @@ func (d *Delegate) ServicesForSpec(ctx context.Context, spec job.Job) ([]job.Ser
type noopSecretsFetcher struct{}
-func (n *noopSecretsFetcher) SecretsFor(ctx context.Context, workflowOwner, workflowName, workflowID string) (map[string]string, error) {
+func (n *noopSecretsFetcher) SecretsFor(ctx context.Context, workflowOwner, hexWorkflowName, decodedWorkflowName, workflowID string) (map[string]string, error) {
return map[string]string{}, nil
}
diff --git a/core/services/workflows/engine.go b/core/services/workflows/engine.go
index d153e53bc07..0b35bf4b06f 100644
--- a/core/services/workflows/engine.go
+++ b/core/services/workflows/engine.go
@@ -96,7 +96,7 @@ func (sucm *stepUpdateManager) len() int64 {
}
type secretsFetcher interface {
- SecretsFor(ctx context.Context, workflowOwner, workflowName, workflowID string) (map[string]string, error)
+ SecretsFor(ctx context.Context, workflowOwner, hexWorkflowName, decodedWorkflowName, workflowID string) (map[string]string, error)
}
// Engine handles the lifecycle of a single workflow and its executions.
@@ -440,6 +440,7 @@ func (e *Engine) registerTrigger(ctx context.Context, t *triggerCapability, trig
WorkflowDonID: e.localNode.WorkflowDON.ID,
WorkflowDonConfigVersion: e.localNode.WorkflowDON.ConfigVersion,
ReferenceID: t.Ref,
+ DecodedWorkflowName: e.workflow.name,
},
Config: t.config.Load(),
TriggerID: triggerID,
@@ -868,7 +869,7 @@ func (e *Engine) interpolateEnvVars(config map[string]any, env exec.Env) (*value
// registry (for capability-level configuration). It doesn't perform any caching of the config values, since
// the two registries perform their own caching.
func (e *Engine) configForStep(ctx context.Context, lggr logger.Logger, step *step) (*values.Map, error) {
- secrets, err := e.secretsFetcher.SecretsFor(ctx, e.workflow.owner, e.workflow.hexName, e.workflow.id)
+ secrets, err := e.secretsFetcher.SecretsFor(ctx, e.workflow.owner, e.workflow.hexName, e.workflow.name, e.workflow.id)
if err != nil {
return nil, fmt.Errorf("failed to fetch secrets: %w", err)
}
@@ -964,6 +965,7 @@ func (e *Engine) executeStep(ctx context.Context, lggr logger.Logger, msg stepRe
WorkflowDonID: e.localNode.WorkflowDON.ID,
WorkflowDonConfigVersion: e.localNode.WorkflowDON.ConfigVersion,
ReferenceID: msg.stepRef,
+ DecodedWorkflowName: e.workflow.name,
},
}
@@ -989,6 +991,7 @@ func (e *Engine) deregisterTrigger(ctx context.Context, t *triggerCapability, tr
WorkflowName: e.workflow.hexName,
WorkflowOwner: e.workflow.owner,
ReferenceID: t.Ref,
+ DecodedWorkflowName: e.workflow.name,
},
TriggerID: generateTriggerId(e.workflow.id, triggerIdx),
Config: t.config.Load(),
@@ -1295,6 +1298,7 @@ func NewEngine(ctx context.Context, cfg Config) (engine *Engine, err error) {
workflow.id = cfg.WorkflowID
workflow.owner = cfg.WorkflowOwner
workflow.hexName = hex.EncodeToString([]byte(cfg.WorkflowName))
+ workflow.name = cfg.WorkflowName
engine = &Engine{
cma: cma,
diff --git a/core/services/workflows/engine_test.go b/core/services/workflows/engine_test.go
index 95ac74f0c76..839e3680f72 100644
--- a/core/services/workflows/engine_test.go
+++ b/core/services/workflows/engine_test.go
@@ -153,7 +153,7 @@ func newTestEngineWithYAMLSpec(t *testing.T, reg *coreCap.Registry, spec string,
type mockSecretsFetcher struct{}
-func (s mockSecretsFetcher) SecretsFor(ctx context.Context, workflowOwner, workflowName, workflowID string) (map[string]string, error) {
+func (s mockSecretsFetcher) SecretsFor(ctx context.Context, workflowOwner, hexWorkflowName, decodedWorkflowName, workflowID string) (map[string]string, error) {
return map[string]string{}, nil
}
@@ -1606,7 +1606,7 @@ type mockFetcher struct {
retval map[string]string
}
-func (m *mockFetcher) SecretsFor(ctx context.Context, workflowOwner, workflowName, workflowID string) (map[string]string, error) {
+func (m *mockFetcher) SecretsFor(ctx context.Context, workflowOwner, hexWorkflowName, decodedWorkflowName, workflowID string) (map[string]string, error) {
return m.retval, nil
}
diff --git a/core/services/workflows/models.go b/core/services/workflows/models.go
index e5d26a474f6..ec149bf6371 100644
--- a/core/services/workflows/models.go
+++ b/core/services/workflows/models.go
@@ -23,6 +23,7 @@ type workflow struct {
id string
owner string
hexName string
+ name string
graph.Graph[string, *step]
triggers []*triggerCapability
diff --git a/core/services/workflows/syncer/handler.go b/core/services/workflows/syncer/handler.go
index cb4f013d502..bae311c846b 100644
--- a/core/services/workflows/syncer/handler.go
+++ b/core/services/workflows/syncer/handler.go
@@ -224,7 +224,7 @@ func (h *eventHandler) refreshSecrets(ctx context.Context, workflowOwner, workfl
return updatedSecrets, nil
}
-func (h *eventHandler) SecretsFor(ctx context.Context, workflowOwner, workflowName, workflowID string) (map[string]string, error) {
+func (h *eventHandler) SecretsFor(ctx context.Context, workflowOwner, hexWorkflowName, decodedWorkflowName, workflowID string) (map[string]string, error) {
secretsURLHash, secretsPayload, err := h.orm.GetContentsByWorkflowID(ctx, workflowID)
if err != nil {
// The workflow record was found, but secrets_id was empty.
@@ -238,15 +238,16 @@ func (h *eventHandler) SecretsFor(ctx context.Context, workflowOwner, workflowNa
lastFetchedAt, ok := h.lastFetchedAtMap.Get(secretsURLHash)
if !ok || h.clock.Now().Sub(lastFetchedAt) > h.secretsFreshnessDuration {
- updatedSecrets, innerErr := h.refreshSecrets(ctx, workflowOwner, workflowName, workflowID, secretsURLHash)
+ updatedSecrets, innerErr := h.refreshSecrets(ctx, workflowOwner, hexWorkflowName, workflowID, secretsURLHash)
if innerErr != nil {
msg := fmt.Sprintf("could not refresh secrets: proceeding with stale secrets for workflowID %s: %s", workflowID, innerErr)
h.lggr.Error(msg)
+
logCustMsg(
ctx,
h.emitter.With(
platform.KeyWorkflowID, workflowID,
- platform.KeyWorkflowName, workflowName,
+ platform.KeyWorkflowName, decodedWorkflowName,
platform.KeyWorkflowOwner, workflowOwner,
),
msg,
diff --git a/core/services/workflows/syncer/handler_test.go b/core/services/workflows/syncer/handler_test.go
index 994b820b5ce..3b3231a3d3d 100644
--- a/core/services/workflows/syncer/handler_test.go
+++ b/core/services/workflows/syncer/handler_test.go
@@ -780,6 +780,7 @@ func Test_Handler_SecretsFor(t *testing.T) {
workflowOwner := hex.EncodeToString([]byte("anOwner"))
workflowName := "aName"
workflowID := "anID"
+ decodedWorkflowName := "decodedName"
encryptionKey, err := workflowkey.New()
require.NoError(t, err)
@@ -820,7 +821,7 @@ func Test_Handler_SecretsFor(t *testing.T) {
encryptionKey,
)
- gotSecrets, err := h.SecretsFor(testutils.Context(t), workflowOwner, workflowName, workflowID)
+ gotSecrets, err := h.SecretsFor(testutils.Context(t), workflowOwner, workflowName, decodedWorkflowName, workflowID)
require.NoError(t, err)
expectedSecrets := map[string]string{
@@ -837,6 +838,7 @@ func Test_Handler_SecretsFor_RefreshesSecrets(t *testing.T) {
workflowOwner := hex.EncodeToString([]byte("anOwner"))
workflowName := "aName"
workflowID := "anID"
+ decodedWorkflowName := "decodedName"
encryptionKey, err := workflowkey.New()
require.NoError(t, err)
@@ -881,7 +883,7 @@ func Test_Handler_SecretsFor_RefreshesSecrets(t *testing.T) {
encryptionKey,
)
- gotSecrets, err := h.SecretsFor(testutils.Context(t), workflowOwner, workflowName, workflowID)
+ gotSecrets, err := h.SecretsFor(testutils.Context(t), workflowOwner, workflowName, decodedWorkflowName, workflowID)
require.NoError(t, err)
expectedSecrets := map[string]string{
@@ -898,6 +900,7 @@ func Test_Handler_SecretsFor_RefreshLogic(t *testing.T) {
workflowOwner := hex.EncodeToString([]byte("anOwner"))
workflowName := "aName"
workflowID := "anID"
+ decodedWorkflowName := "decodedName"
encryptionKey, err := workflowkey.New()
require.NoError(t, err)
@@ -943,7 +946,7 @@ func Test_Handler_SecretsFor_RefreshLogic(t *testing.T) {
encryptionKey,
)
- gotSecrets, err := h.SecretsFor(testutils.Context(t), workflowOwner, workflowName, workflowID)
+ gotSecrets, err := h.SecretsFor(testutils.Context(t), workflowOwner, workflowName, decodedWorkflowName, workflowID)
require.NoError(t, err)
expectedSecrets := map[string]string{
@@ -955,7 +958,7 @@ func Test_Handler_SecretsFor_RefreshLogic(t *testing.T) {
// SecretsFor should still succeed.
fetcher.responseMap[url] = mockFetchResp{}
- gotSecrets, err = h.SecretsFor(testutils.Context(t), workflowOwner, workflowName, workflowID)
+ gotSecrets, err = h.SecretsFor(testutils.Context(t), workflowOwner, workflowName, decodedWorkflowName, workflowID)
require.NoError(t, err)
assert.Equal(t, expectedSecrets, gotSecrets)
@@ -963,7 +966,7 @@ func Test_Handler_SecretsFor_RefreshLogic(t *testing.T) {
// Now advance so that we hit the freshness limit
clock.Advance(48 * time.Hour)
- _, err = h.SecretsFor(testutils.Context(t), workflowOwner, workflowName, workflowID)
+ _, err = h.SecretsFor(testutils.Context(t), workflowOwner, workflowName, decodedWorkflowName, workflowID)
assert.ErrorContains(t, err, "unexpected end of JSON input")
}
diff --git a/deployment/go.mod b/deployment/go.mod
index cbec5d95744..7ca3e8a4398 100644
--- a/deployment/go.mod
+++ b/deployment/go.mod
@@ -31,7 +31,7 @@ require (
github.com/smartcontractkit/chain-selectors v1.0.36
github.com/smartcontractkit/chainlink-ccip v0.0.0-20241218114855-f74219171000
github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250103152858-8973fd0c912b
- github.com/smartcontractkit/chainlink-common v0.4.1-0.20241223143929-db7919d60550
+ github.com/smartcontractkit/chainlink-common v0.4.1-0.20250108194320-2ebd63bbb16e
github.com/smartcontractkit/chainlink-protos/job-distributor v0.6.0
github.com/smartcontractkit/chainlink-testing-framework/lib v1.50.13
github.com/smartcontractkit/libocr v0.0.0-20241223215956-e5b78d8e3919
diff --git a/deployment/go.sum b/deployment/go.sum
index e58794f2e97..49d9771239e 100644
--- a/deployment/go.sum
+++ b/deployment/go.sum
@@ -1385,8 +1385,8 @@ github.com/smartcontractkit/chainlink-ccip v0.0.0-20241218114855-f74219171000 h1
github.com/smartcontractkit/chainlink-ccip v0.0.0-20241218114855-f74219171000/go.mod h1:ncjd6mPZSRlelEqH/2KeLE1pU3UlqzBSn8RYkEoECzY=
github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250103152858-8973fd0c912b h1:UBXi9Yj8YSMHDDaxQLu273x1fWjyEL9xP58nuJsqZfg=
github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250103152858-8973fd0c912b/go.mod h1:Bmwq4lNb5tE47sydN0TKetcLEGbgl+VxHEWp4S0LI60=
-github.com/smartcontractkit/chainlink-common v0.4.1-0.20241223143929-db7919d60550 h1:rRs74zjDJ7do5aYEXSU/sOnLnlbYCNqM8BrvEx/0NH8=
-github.com/smartcontractkit/chainlink-common v0.4.1-0.20241223143929-db7919d60550/go.mod h1:yti7e1+G9hhkYhj+L5sVUULn9Bn3bBL5/AxaNqdJ5YQ=
+github.com/smartcontractkit/chainlink-common v0.4.1-0.20250108194320-2ebd63bbb16e h1:8BStiP1F4W8AvjBRga0TYtjvAtkwN8oHYnHJztAlSF4=
+github.com/smartcontractkit/chainlink-common v0.4.1-0.20250108194320-2ebd63bbb16e/go.mod h1:yti7e1+G9hhkYhj+L5sVUULn9Bn3bBL5/AxaNqdJ5YQ=
github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e h1:PRoeby6ZlTuTkv2f+7tVU4+zboTfRzI+beECynF4JQ0=
github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e/go.mod h1:mUh5/woemsVaHgTorA080hrYmO3syBCmPdnWc/5dOqk=
github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241216163550-fa030d178ba3 h1:aeiBdBHGY8QNftps+VqrIk6OnfeeOD5z4jrAabW4ZSc=
diff --git a/go.mod b/go.mod
index c12790b12c7..85baf8f3812 100644
--- a/go.mod
+++ b/go.mod
@@ -79,7 +79,7 @@ require (
github.com/smartcontractkit/chain-selectors v1.0.34
github.com/smartcontractkit/chainlink-automation v0.8.1
github.com/smartcontractkit/chainlink-ccip v0.0.0-20241218114855-f74219171000
- github.com/smartcontractkit/chainlink-common v0.4.1-0.20241223143929-db7919d60550
+ github.com/smartcontractkit/chainlink-common v0.4.1-0.20250108194320-2ebd63bbb16e
github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e
github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241216163550-fa030d178ba3
github.com/smartcontractkit/chainlink-feeds v0.1.1
diff --git a/go.sum b/go.sum
index c8778ab3532..1a2b9a010c6 100644
--- a/go.sum
+++ b/go.sum
@@ -1150,8 +1150,8 @@ github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgB
github.com/smartcontractkit/chainlink-automation v0.8.1/go.mod h1:Iij36PvWZ6blrdC5A/nrQUBuf3MH3JvsBB9sSyc9W08=
github.com/smartcontractkit/chainlink-ccip v0.0.0-20241218114855-f74219171000 h1:6Zzr/R1j6P7bbvcUlt5WUIbItvrrGdGzIsiAzQezcwo=
github.com/smartcontractkit/chainlink-ccip v0.0.0-20241218114855-f74219171000/go.mod h1:ncjd6mPZSRlelEqH/2KeLE1pU3UlqzBSn8RYkEoECzY=
-github.com/smartcontractkit/chainlink-common v0.4.1-0.20241223143929-db7919d60550 h1:rRs74zjDJ7do5aYEXSU/sOnLnlbYCNqM8BrvEx/0NH8=
-github.com/smartcontractkit/chainlink-common v0.4.1-0.20241223143929-db7919d60550/go.mod h1:yti7e1+G9hhkYhj+L5sVUULn9Bn3bBL5/AxaNqdJ5YQ=
+github.com/smartcontractkit/chainlink-common v0.4.1-0.20250108194320-2ebd63bbb16e h1:8BStiP1F4W8AvjBRga0TYtjvAtkwN8oHYnHJztAlSF4=
+github.com/smartcontractkit/chainlink-common v0.4.1-0.20250108194320-2ebd63bbb16e/go.mod h1:yti7e1+G9hhkYhj+L5sVUULn9Bn3bBL5/AxaNqdJ5YQ=
github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e h1:PRoeby6ZlTuTkv2f+7tVU4+zboTfRzI+beECynF4JQ0=
github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e/go.mod h1:mUh5/woemsVaHgTorA080hrYmO3syBCmPdnWc/5dOqk=
github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241216163550-fa030d178ba3 h1:aeiBdBHGY8QNftps+VqrIk6OnfeeOD5z4jrAabW4ZSc=
diff --git a/integration-tests/go.mod b/integration-tests/go.mod
index efd538007b1..299723a8c9a 100644
--- a/integration-tests/go.mod
+++ b/integration-tests/go.mod
@@ -47,7 +47,7 @@ require (
github.com/smartcontractkit/chain-selectors v1.0.36
github.com/smartcontractkit/chainlink-automation v0.8.1
github.com/smartcontractkit/chainlink-ccip v0.0.0-20241218114855-f74219171000
- github.com/smartcontractkit/chainlink-common v0.4.1-0.20241223143929-db7919d60550
+ github.com/smartcontractkit/chainlink-common v0.4.1-0.20250108194320-2ebd63bbb16e
github.com/smartcontractkit/chainlink-protos/job-distributor v0.6.0
github.com/smartcontractkit/chainlink-testing-framework/havoc v1.50.2
github.com/smartcontractkit/chainlink-testing-framework/lib v1.50.19
diff --git a/integration-tests/go.sum b/integration-tests/go.sum
index a2f198d4856..f94166bdeae 100644
--- a/integration-tests/go.sum
+++ b/integration-tests/go.sum
@@ -1407,8 +1407,8 @@ github.com/smartcontractkit/chainlink-ccip v0.0.0-20241218114855-f74219171000 h1
github.com/smartcontractkit/chainlink-ccip v0.0.0-20241218114855-f74219171000/go.mod h1:ncjd6mPZSRlelEqH/2KeLE1pU3UlqzBSn8RYkEoECzY=
github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250103152858-8973fd0c912b h1:UBXi9Yj8YSMHDDaxQLu273x1fWjyEL9xP58nuJsqZfg=
github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250103152858-8973fd0c912b/go.mod h1:Bmwq4lNb5tE47sydN0TKetcLEGbgl+VxHEWp4S0LI60=
-github.com/smartcontractkit/chainlink-common v0.4.1-0.20241223143929-db7919d60550 h1:rRs74zjDJ7do5aYEXSU/sOnLnlbYCNqM8BrvEx/0NH8=
-github.com/smartcontractkit/chainlink-common v0.4.1-0.20241223143929-db7919d60550/go.mod h1:yti7e1+G9hhkYhj+L5sVUULn9Bn3bBL5/AxaNqdJ5YQ=
+github.com/smartcontractkit/chainlink-common v0.4.1-0.20250108194320-2ebd63bbb16e h1:8BStiP1F4W8AvjBRga0TYtjvAtkwN8oHYnHJztAlSF4=
+github.com/smartcontractkit/chainlink-common v0.4.1-0.20250108194320-2ebd63bbb16e/go.mod h1:yti7e1+G9hhkYhj+L5sVUULn9Bn3bBL5/AxaNqdJ5YQ=
github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e h1:PRoeby6ZlTuTkv2f+7tVU4+zboTfRzI+beECynF4JQ0=
github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e/go.mod h1:mUh5/woemsVaHgTorA080hrYmO3syBCmPdnWc/5dOqk=
github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241216163550-fa030d178ba3 h1:aeiBdBHGY8QNftps+VqrIk6OnfeeOD5z4jrAabW4ZSc=
diff --git a/integration-tests/load/go.mod b/integration-tests/load/go.mod
index 94d86cb5cd4..8d7d2e18488 100644
--- a/integration-tests/load/go.mod
+++ b/integration-tests/load/go.mod
@@ -27,7 +27,7 @@ require (
github.com/pkg/errors v0.9.1
github.com/rs/zerolog v1.33.0
github.com/slack-go/slack v0.15.0
- github.com/smartcontractkit/chainlink-common v0.4.1-0.20241223143929-db7919d60550
+ github.com/smartcontractkit/chainlink-common v0.4.1-0.20250108194320-2ebd63bbb16e
github.com/smartcontractkit/chainlink-testing-framework/lib v1.50.19
github.com/smartcontractkit/chainlink-testing-framework/seth v1.50.9
github.com/smartcontractkit/chainlink-testing-framework/wasp v1.50.2
diff --git a/integration-tests/load/go.sum b/integration-tests/load/go.sum
index eb114fa0e28..033b52e5d4f 100644
--- a/integration-tests/load/go.sum
+++ b/integration-tests/load/go.sum
@@ -1398,8 +1398,8 @@ github.com/smartcontractkit/chainlink-ccip v0.0.0-20241218114855-f74219171000 h1
github.com/smartcontractkit/chainlink-ccip v0.0.0-20241218114855-f74219171000/go.mod h1:ncjd6mPZSRlelEqH/2KeLE1pU3UlqzBSn8RYkEoECzY=
github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250103152858-8973fd0c912b h1:UBXi9Yj8YSMHDDaxQLu273x1fWjyEL9xP58nuJsqZfg=
github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250103152858-8973fd0c912b/go.mod h1:Bmwq4lNb5tE47sydN0TKetcLEGbgl+VxHEWp4S0LI60=
-github.com/smartcontractkit/chainlink-common v0.4.1-0.20241223143929-db7919d60550 h1:rRs74zjDJ7do5aYEXSU/sOnLnlbYCNqM8BrvEx/0NH8=
-github.com/smartcontractkit/chainlink-common v0.4.1-0.20241223143929-db7919d60550/go.mod h1:yti7e1+G9hhkYhj+L5sVUULn9Bn3bBL5/AxaNqdJ5YQ=
+github.com/smartcontractkit/chainlink-common v0.4.1-0.20250108194320-2ebd63bbb16e h1:8BStiP1F4W8AvjBRga0TYtjvAtkwN8oHYnHJztAlSF4=
+github.com/smartcontractkit/chainlink-common v0.4.1-0.20250108194320-2ebd63bbb16e/go.mod h1:yti7e1+G9hhkYhj+L5sVUULn9Bn3bBL5/AxaNqdJ5YQ=
github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e h1:PRoeby6ZlTuTkv2f+7tVU4+zboTfRzI+beECynF4JQ0=
github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e/go.mod h1:mUh5/woemsVaHgTorA080hrYmO3syBCmPdnWc/5dOqk=
github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241216163550-fa030d178ba3 h1:aeiBdBHGY8QNftps+VqrIk6OnfeeOD5z4jrAabW4ZSc=
From c7759e15e2b15bdeacf00ddbd433a4b420072cf3 Mon Sep 17 00:00:00 2001
From: Sam
Date: Thu, 9 Jan 2025 09:35:52 -0500
Subject: [PATCH 22/91] Multistream specs (#15603)
* Multistream specs
* .
* w
* Fix lint
* Lint
* Add concurrency stress tests
* Fix lint
* Lint
* Add benchmark
* Benchmark on datasource.Observe
* Fix lint
---
.changeset/rotten-books-cross.md | 5 +
core/internal/testutils/httptest/httptest.go | 3 +-
core/services/llo/data_source.go | 95 +----
core/services/llo/data_source_test.go | 165 +++++++-
core/services/llo/observation_context.go | 193 ++++++++++
core/services/llo/observation_context_test.go | 358 ++++++++++++++++++
core/services/pipeline/common.go | 1 +
core/services/pipeline/runner.go | 4 +-
core/services/pipeline/task.base.go | 9 +
.../relay/evm/mercury/mocks/pipeline.go | 1 +
core/services/streams/delegate.go | 37 +-
core/services/streams/delegate_test.go | 11 +-
core/services/streams/pipeline.go | 131 +++++++
core/services/streams/stream.go | 94 -----
core/services/streams/stream_registry.go | 59 ++-
core/services/streams/stream_registry_test.go | 198 +++++++---
core/services/streams/stream_test.go | 25 +-
go.mod | 1 +
18 files changed, 1103 insertions(+), 287 deletions(-)
create mode 100644 .changeset/rotten-books-cross.md
create mode 100644 core/services/llo/observation_context.go
create mode 100644 core/services/llo/observation_context_test.go
create mode 100644 core/services/streams/pipeline.go
delete mode 100644 core/services/streams/stream.go
diff --git a/.changeset/rotten-books-cross.md b/.changeset/rotten-books-cross.md
new file mode 100644
index 00000000000..95231ec47f2
--- /dev/null
+++ b/.changeset/rotten-books-cross.md
@@ -0,0 +1,5 @@
+---
+"chainlink": patch
+---
+
+Support multiple streamIDs in stream specs #added
diff --git a/core/internal/testutils/httptest/httptest.go b/core/internal/testutils/httptest/httptest.go
index a1bd941fb02..69549ae3e45 100644
--- a/core/internal/testutils/httptest/httptest.go
+++ b/core/internal/testutils/httptest/httptest.go
@@ -12,7 +12,8 @@ import (
// NewTestHTTPClient returns a real HTTP client that may only make requests to
// localhost
func NewTestLocalOnlyHTTPClient() *http.Client {
- tr := http.DefaultTransport.(*http.Transport).Clone()
+ // Don't use the default transport, we want zero limits and zero timeouts
+ tr := &http.Transport{}
tr.DialContext = testDialContext
tr.DisableCompression = true
return &http.Client{Transport: tr}
diff --git a/core/services/llo/data_source.go b/core/services/llo/data_source.go
index 2afe9e090a3..855ac7d9940 100644
--- a/core/services/llo/data_source.go
+++ b/core/services/llo/data_source.go
@@ -2,15 +2,16 @@ package llo
import (
"context"
+ "errors"
"fmt"
"slices"
"sort"
+ "strconv"
"sync"
"time"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
- "github.com/shopspring/decimal"
"golang.org/x/exp/maps"
"github.com/smartcontractkit/chainlink-common/pkg/logger"
@@ -19,7 +20,6 @@ import (
"github.com/smartcontractkit/chainlink/v2/core/services/pipeline"
"github.com/smartcontractkit/chainlink/v2/core/services/streams"
- "github.com/smartcontractkit/chainlink/v2/core/utils"
)
var (
@@ -42,7 +42,7 @@ var (
)
type Registry interface {
- Get(streamID streams.StreamID) (strm streams.Stream, exists bool)
+ Get(streamID streams.StreamID) (p streams.Pipeline, exists bool)
}
type ErrObservationFailed struct {
@@ -109,43 +109,25 @@ func (d *dataSource) Observe(ctx context.Context, streamValues llo.StreamValues,
successfulStreamIDs := make([]streams.StreamID, 0, len(streamValues))
var errs []ErrObservationFailed
+ // oc only lives for the duration of this Observe call
+ oc := NewObservationContext(d.registry, d.t)
+
for _, streamID := range maps.Keys(streamValues) {
go func(streamID llotypes.StreamID) {
defer wg.Done()
-
- var val llo.StreamValue
-
- stream, exists := d.registry.Get(streamID)
- if !exists {
- mu.Lock()
- errs = append(errs, ErrObservationFailed{streamID: streamID, reason: fmt.Sprintf("missing stream: %d", streamID)})
- mu.Unlock()
- promMissingStreamCount.WithLabelValues(fmt.Sprintf("%d", streamID)).Inc()
- return
- }
- run, trrs, err := stream.Run(ctx)
- if err != nil {
- mu.Lock()
- errs = append(errs, ErrObservationFailed{inner: err, run: run, streamID: streamID, reason: "pipeline run failed"})
- mu.Unlock()
- promObservationErrorCount.WithLabelValues(fmt.Sprintf("%d", streamID)).Inc()
- // TODO: Consolidate/reduce telemetry. We should send all observation results in a single packet
- // https://smartcontract-it.atlassian.net/browse/MERC-6290
- d.t.EnqueueV3PremiumLegacy(run, trrs, streamID, opts, nil, err)
- return
- }
- // TODO: Consolidate/reduce telemetry. We should send all observation results in a single packet
- // https://smartcontract-it.atlassian.net/browse/MERC-6290
- val, err = ExtractStreamValue(trrs)
+ val, err := oc.Observe(ctx, streamID, opts)
if err != nil {
+ strmIDStr := strconv.FormatUint(uint64(streamID), 10)
+ if errors.As(err, &MissingStreamError{}) {
+ promMissingStreamCount.WithLabelValues(strmIDStr).Inc()
+ }
+ promObservationErrorCount.WithLabelValues(strmIDStr).Inc()
mu.Lock()
- errs = append(errs, ErrObservationFailed{inner: err, run: run, streamID: streamID, reason: "failed to extract big.Int"})
+ errs = append(errs, ErrObservationFailed{inner: err, streamID: streamID, reason: "failed to observe stream"})
mu.Unlock()
return
}
- d.t.EnqueueV3PremiumLegacy(run, trrs, streamID, opts, val, nil)
-
mu.Lock()
defer mu.Unlock()
@@ -186,54 +168,3 @@ 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()
-
- // 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 932c4c0c73a..349ec70007d 100644
--- a/core/services/llo/data_source_test.go
+++ b/core/services/llo/data_source_test.go
@@ -3,6 +3,8 @@ package llo
import (
"context"
"errors"
+ "fmt"
+ "math"
"math/big"
"sync"
"testing"
@@ -10,38 +12,54 @@ import (
"github.com/shopspring/decimal"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "gopkg.in/guregu/null.v4"
"github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types"
ocr2types "github.com/smartcontractkit/libocr/offchainreporting2plus/types"
+ llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo"
+
"github.com/smartcontractkit/chainlink-data-streams/llo"
+ "github.com/smartcontractkit/chainlink/v2/core/bridges"
"github.com/smartcontractkit/chainlink/v2/core/internal/testutils"
+ clhttptest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/httptest"
+ "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest"
"github.com/smartcontractkit/chainlink/v2/core/logger"
+ "github.com/smartcontractkit/chainlink/v2/core/services/job"
"github.com/smartcontractkit/chainlink/v2/core/services/pipeline"
"github.com/smartcontractkit/chainlink/v2/core/services/streams"
)
-type mockStream struct {
+type mockPipeline struct {
run *pipeline.Run
trrs pipeline.TaskRunResults
err error
+
+ streamIDs []streams.StreamID
+
+ runCount int
}
-func (m *mockStream) Run(ctx context.Context) (*pipeline.Run, pipeline.TaskRunResults, error) {
+func (m *mockPipeline) Run(ctx context.Context) (*pipeline.Run, pipeline.TaskRunResults, error) {
+ m.runCount++
return m.run, m.trrs, m.err
}
+func (m *mockPipeline) StreamIDs() []streams.StreamID {
+ return m.streamIDs
+}
+
type mockRegistry struct {
- streams map[streams.StreamID]*mockStream
+ pipelines map[streams.StreamID]*mockPipeline
}
-func (m *mockRegistry) Get(streamID streams.StreamID) (strm streams.Stream, exists bool) {
- strm, exists = m.streams[streamID]
+func (m *mockRegistry) Get(streamID streams.StreamID) (p streams.Pipeline, exists bool) {
+ p, exists = m.pipelines[streamID]
return
}
-func makeStreamWithSingleResult[T any](runID int64, res T, err error) *mockStream {
- return &mockStream{
+func makePipelineWithSingleResult[T any](runID int64, res T, err error) *mockPipeline {
+ return &mockPipeline{
run: &pipeline.Run{ID: runID},
trrs: []pipeline.TaskRunResult{pipeline.TaskRunResult{Task: &pipeline.MemoTask{}, Result: pipeline.Result{Value: res}}},
err: err,
@@ -56,9 +74,11 @@ func makeStreamValues() llo.StreamValues {
}
}
-type mockOpts struct{}
+type mockOpts struct {
+ verboseLogging bool
+}
-func (m *mockOpts) VerboseLogging() bool { return true }
+func (m *mockOpts) VerboseLogging() bool { return m.verboseLogging }
func (m *mockOpts) SeqNr() uint64 { return 1042 }
func (m *mockOpts) OutCtx() ocr3types.OutcomeContext {
return ocr3types.OutcomeContext{SeqNr: 1042, PreviousOutcome: ocr3types.Outcome([]byte("foo"))}
@@ -91,7 +111,7 @@ func (m *mockTelemeter) EnqueueV3PremiumLegacy(run *pipeline.Run, trrs pipeline.
func Test_DataSource(t *testing.T) {
lggr := logger.TestLogger(t)
- reg := &mockRegistry{make(map[streams.StreamID]*mockStream)}
+ reg := &mockRegistry{make(map[streams.StreamID]*mockPipeline)}
ds := newDataSource(lggr, reg, NullTelemeter)
ctx := testutils.Context(t)
opts := &mockOpts{}
@@ -105,9 +125,9 @@ func Test_DataSource(t *testing.T) {
assert.Equal(t, makeStreamValues(), vals)
})
t.Run("observes each stream with success and returns values matching map argument", func(t *testing.T) {
- reg.streams[1] = makeStreamWithSingleResult[*big.Int](1, big.NewInt(2181), nil)
- reg.streams[2] = makeStreamWithSingleResult[*big.Int](2, big.NewInt(40602), nil)
- reg.streams[3] = makeStreamWithSingleResult[*big.Int](3, big.NewInt(15), nil)
+ reg.pipelines[1] = makePipelineWithSingleResult[*big.Int](1, big.NewInt(2181), nil)
+ reg.pipelines[2] = makePipelineWithSingleResult[*big.Int](2, big.NewInt(40602), nil)
+ reg.pipelines[3] = makePipelineWithSingleResult[*big.Int](3, big.NewInt(15), nil)
vals := makeStreamValues()
err := ds.Observe(ctx, vals, opts)
@@ -120,9 +140,9 @@ func Test_DataSource(t *testing.T) {
}, vals)
})
t.Run("observes each stream and returns success/errors", func(t *testing.T) {
- reg.streams[1] = makeStreamWithSingleResult[*big.Int](1, big.NewInt(2181), errors.New("something exploded"))
- reg.streams[2] = makeStreamWithSingleResult[*big.Int](2, big.NewInt(40602), nil)
- reg.streams[3] = makeStreamWithSingleResult[*big.Int](3, nil, errors.New("something exploded 2"))
+ reg.pipelines[1] = makePipelineWithSingleResult[*big.Int](1, big.NewInt(2181), errors.New("something exploded"))
+ reg.pipelines[2] = makePipelineWithSingleResult[*big.Int](2, big.NewInt(40602), nil)
+ reg.pipelines[3] = makePipelineWithSingleResult[*big.Int](3, nil, errors.New("something exploded 2"))
vals := makeStreamValues()
err := ds.Observe(ctx, vals, opts)
@@ -139,9 +159,9 @@ func Test_DataSource(t *testing.T) {
tm := &mockTelemeter{}
ds.t = tm
- reg.streams[1] = makeStreamWithSingleResult[*big.Int](100, big.NewInt(2181), nil)
- reg.streams[2] = makeStreamWithSingleResult[*big.Int](101, big.NewInt(40602), nil)
- reg.streams[3] = makeStreamWithSingleResult[*big.Int](102, big.NewInt(15), nil)
+ reg.pipelines[1] = makePipelineWithSingleResult[*big.Int](100, big.NewInt(2181), nil)
+ reg.pipelines[2] = makePipelineWithSingleResult[*big.Int](101, big.NewInt(40602), nil)
+ reg.pipelines[3] = makePipelineWithSingleResult[*big.Int](102, big.NewInt(15), nil)
vals := makeStreamValues()
err := ds.Observe(ctx, vals, opts)
@@ -166,5 +186,112 @@ func Test_DataSource(t *testing.T) {
assert.Equal(t, "2181", pkt.val.(*llo.Decimal).String())
assert.Nil(t, pkt.err)
})
+
+ t.Run("records telemetry for errors", func(t *testing.T) {
+ tm := &mockTelemeter{}
+ ds.t = tm
+
+ reg.pipelines[1] = makePipelineWithSingleResult[*big.Int](100, big.NewInt(2181), errors.New("something exploded"))
+ reg.pipelines[2] = makePipelineWithSingleResult[*big.Int](101, big.NewInt(40602), nil)
+ reg.pipelines[3] = makePipelineWithSingleResult[*big.Int](102, nil, errors.New("something exploded 2"))
+
+ vals := makeStreamValues()
+ err := ds.Observe(ctx, vals, opts)
+ require.NoError(t, err)
+
+ assert.Equal(t, llo.StreamValues{
+ 2: llo.ToDecimal(decimal.NewFromInt(40602)),
+ 1: nil,
+ 3: nil,
+ }, vals)
+
+ require.Len(t, tm.v3PremiumLegacyPackets, 3)
+ m := make(map[int]v3PremiumLegacyPacket)
+ for _, pkt := range tm.v3PremiumLegacyPackets {
+ m[int(pkt.run.ID)] = pkt
+ }
+ pkt := m[100]
+ assert.Equal(t, 100, int(pkt.run.ID))
+ assert.Len(t, pkt.trrs, 1)
+ assert.Equal(t, 1, int(pkt.streamID))
+ assert.Equal(t, opts, pkt.opts)
+ assert.Nil(t, pkt.val)
+ assert.Error(t, pkt.err)
+ })
})
}
+
+func BenchmarkObserve(b *testing.B) {
+ lggr := logger.TestLogger(b)
+ ctx := testutils.Context(b)
+ // can enable/disable verbose logging to test performance here
+ opts := &mockOpts{verboseLogging: true}
+
+ db := pgtest.NewSqlxDB(b)
+ bridgesORM := bridges.NewORM(db)
+
+ if b.N > math.MaxInt32 {
+ b.Fatalf("N is too large: %d", b.N)
+ }
+
+ n := uint32(b.N) //nolint:gosec // G115 // overflow impossible
+
+ createBridge(b, "foo-bridge", `123.456`, bridgesORM, 0)
+ createBridge(b, "bar-bridge", `"124.456"`, bridgesORM, 0)
+
+ c := clhttptest.NewTestLocalOnlyHTTPClient()
+ runner := pipeline.NewRunner(
+ nil,
+ bridgesORM,
+ &mockPipelineConfig{},
+ &mockBridgeConfig{},
+ nil,
+ nil,
+ nil,
+ lggr,
+ c,
+ c,
+ )
+
+ r := streams.NewRegistry(lggr, runner)
+ for i := uint32(0); i < n; i++ {
+ i := i
+ jb := job.Job{
+ ID: int32(i), //nolint:gosec // G115 // overflow impossible
+ Name: null.StringFrom(fmt.Sprintf("job-%d", i)),
+ Type: job.Stream,
+ StreamID: &i,
+ PipelineSpec: &pipeline.Spec{
+ ID: int32(i * 100), //nolint:gosec // G115 // overflow impossible
+ DotDagSource: fmt.Sprintf(`
+// Benchmark Price
+result1 [type=memo value="900.0022"];
+multiply2 [type=multiply times=1 streamID=%d index=0]; // force conversion to decimal
+
+result2 [type=bridge name="foo-bridge" requestData="{\"data\":{\"data\":\"foo\"}}"];
+result2_parse [type=jsonparse path="result" streamID=%d index=1];
+
+result3 [type=bridge name="bar-bridge" requestData="{\"data\":{\"data\":\"bar\"}}"];
+result3_parse [type=jsonparse path="result"];
+multiply3 [type=multiply times=1 streamID=%d index=2]; // force conversion to decimal
+
+result1 -> multiply2;
+result2 -> result2_parse;
+result3 -> result3_parse -> multiply3;
+`, i+n, i+2*n, i+3*n),
+ },
+ }
+ err := r.Register(jb, nil)
+ require.NoError(b, err)
+ }
+
+ ds := newDataSource(lggr, r, NullTelemeter)
+ vals := make(map[llotypes.StreamID]llo.StreamValue)
+ for i := uint32(0); i < 4*n; i++ {
+ vals[i] = nil
+ }
+
+ b.ResetTimer()
+ err := ds.Observe(ctx, vals, opts)
+ require.NoError(b, err)
+}
diff --git a/core/services/llo/observation_context.go b/core/services/llo/observation_context.go
new file mode 100644
index 00000000000..5bf82fa5a79
--- /dev/null
+++ b/core/services/llo/observation_context.go
@@ -0,0 +1,193 @@
+package llo
+
+import (
+ "context"
+ "fmt"
+ "sync"
+
+ "github.com/shopspring/decimal"
+
+ "github.com/smartcontractkit/chainlink-data-streams/llo"
+ "github.com/smartcontractkit/chainlink/v2/core/services/pipeline"
+ "github.com/smartcontractkit/chainlink/v2/core/services/streams"
+ "github.com/smartcontractkit/chainlink/v2/core/utils"
+)
+
+// ObservationContext ensures that each pipeline is only executed once. It is
+// intended to be instantiated and used then discarded as part of one
+// Observation cycle. Subsequent calls to Observe will return the same cached
+// values.
+
+var _ ObservationContext = (*observationContext)(nil)
+
+type ObservationContext interface {
+ Observe(ctx context.Context, streamID streams.StreamID, opts llo.DSOpts) (val llo.StreamValue, err error)
+}
+
+type execution struct {
+ done <-chan struct{}
+
+ run *pipeline.Run
+ trrs pipeline.TaskRunResults
+ err error
+}
+
+type observationContext struct {
+ r Registry
+ t Telemeter
+
+ executionsMu sync.Mutex
+ // only execute each pipeline once
+ executions map[streams.Pipeline]*execution
+}
+
+func NewObservationContext(r Registry, t Telemeter) ObservationContext {
+ return newObservationContext(r, t)
+}
+
+func newObservationContext(r Registry, t Telemeter) *observationContext {
+ return &observationContext{r, t, sync.Mutex{}, make(map[streams.Pipeline]*execution)}
+}
+
+func (oc *observationContext) Observe(ctx context.Context, streamID streams.StreamID, opts llo.DSOpts) (val llo.StreamValue, err error) {
+ run, trrs, err := oc.run(ctx, streamID)
+ if err != nil {
+ // FIXME: This is a hack specific for V3 telemetry, future schemas should
+ // use a generic stream value telemetry instead
+ // https://smartcontract-it.atlassian.net/browse/MERC-6290
+ oc.t.EnqueueV3PremiumLegacy(run, trrs, streamID, opts, val, err)
+ return nil, err
+ }
+ // Extract stream value based on streamID attribute
+ for _, trr := range trrs {
+ if trr.Task.TaskStreamID() != nil && *trr.Task.TaskStreamID() == streamID {
+ val, err = resultToStreamValue(trr.Result.Value)
+ if err != nil {
+ return nil, fmt.Errorf("failed to convert result to StreamValue for streamID %d: %w", streamID, err)
+ }
+ return val, nil
+ }
+ }
+ // If no streamID attribute is found in the task results, then assume the
+ // final output is the stream ID and return that. This is safe to do since
+ // the registry will never return a spec that doesn't match either by tag
+ // or by spec streamID.
+
+ val, err = extractFinalResultAsStreamValue(trrs)
+ // FIXME: This is a hack specific for V3 telemetry, future schemas should
+ // use a generic stream value telemetry instead
+ // https://smartcontract-it.atlassian.net/browse/MERC-6290
+ oc.t.EnqueueV3PremiumLegacy(run, trrs, streamID, opts, val, err)
+ return
+}
+
+func resultToStreamValue(val interface{}) (llo.StreamValue, error) {
+ switch v := val.(type) {
+ case decimal.Decimal:
+ return llo.ToDecimal(v), nil
+ case float64:
+ return llo.ToDecimal(decimal.NewFromFloat(v)), nil
+ case pipeline.ObjectParam:
+ switch v.Type {
+ case pipeline.DecimalType:
+ return llo.ToDecimal(decimal.Decimal(v.DecimalValue)), nil
+ default:
+ return nil, fmt.Errorf("don't know how to convert pipeline.ObjectParam with type %d to llo.StreamValue", v.Type)
+ }
+ default:
+ return nil, fmt.Errorf("don't know how to convert pipeline output result of type %T to llo.StreamValue (got: %v)", val, val)
+ }
+}
+
+// extractFinalResultAsStreamValue extracts a final StreamValue from a TaskRunResults
+func extractFinalResultAsStreamValue(trrs pipeline.TaskRunResults) (llo.StreamValue, error) {
+ // pipeline.TaskRunResults comes ordered asc by index, this is guaranteed
+ // by the pipeline executor
+ finaltrrs := trrs.Terminals()
+
+ // HACK: Right now we rely on the number of outputs to determine whether
+ // its a Decimal or a Quote.
+ // This is a hack to support the legacy "Quote" case.
+ // Future stream specs should use streamID tags instead.
+ 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)
+}
+
+type MissingStreamError struct {
+ StreamID streams.StreamID
+}
+
+func (e MissingStreamError) Error() string {
+ return fmt.Sprintf("no pipeline for stream: %d", e.StreamID)
+}
+
+func (oc *observationContext) run(ctx context.Context, streamID streams.StreamID) (*pipeline.Run, pipeline.TaskRunResults, error) {
+ p, exists := oc.r.Get(streamID)
+ if !exists {
+ return nil, nil, MissingStreamError{StreamID: streamID}
+ }
+
+ // In case of multiple streamIDs per pipeline then the
+ // first call executes and the others wait for result
+ oc.executionsMu.Lock()
+ ex, isExecuting := oc.executions[p]
+ if isExecuting {
+ oc.executionsMu.Unlock()
+ // wait for it to finish
+ select {
+ case <-ex.done:
+ return ex.run, ex.trrs, ex.err
+ case <-ctx.Done():
+ return nil, nil, ctx.Err()
+ }
+ }
+
+ // execute here
+ ch := make(chan struct{})
+ ex = &execution{done: ch}
+ oc.executions[p] = ex
+ oc.executionsMu.Unlock()
+
+ run, trrs, err := p.Run(ctx)
+ ex.run = run
+ ex.trrs = trrs
+ ex.err = err
+ close(ch)
+
+ return run, trrs, err
+}
diff --git a/core/services/llo/observation_context_test.go b/core/services/llo/observation_context_test.go
new file mode 100644
index 00000000000..fe626815603
--- /dev/null
+++ b/core/services/llo/observation_context_test.go
@@ -0,0 +1,358 @@
+package llo
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "math"
+ "math/rand/v2"
+ "net/http"
+ "net/http/httptest"
+ "net/url"
+ "testing"
+ "time"
+
+ "github.com/shopspring/decimal"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ "go.uber.org/atomic"
+ "golang.org/x/sync/errgroup"
+
+ "gopkg.in/guregu/null.v4"
+
+ commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config"
+ "github.com/smartcontractkit/chainlink-common/pkg/utils/tests"
+ "github.com/smartcontractkit/chainlink-data-streams/llo"
+ "github.com/smartcontractkit/chainlink/v2/core/bridges"
+ clhttptest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/httptest"
+ "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest"
+ "github.com/smartcontractkit/chainlink/v2/core/logger"
+ clnull "github.com/smartcontractkit/chainlink/v2/core/null"
+ "github.com/smartcontractkit/chainlink/v2/core/services/job"
+ "github.com/smartcontractkit/chainlink/v2/core/services/pipeline"
+ "github.com/smartcontractkit/chainlink/v2/core/services/streams"
+ "github.com/smartcontractkit/chainlink/v2/core/store/models"
+)
+
+func makeErroringPipeline() *mockPipeline {
+ return &mockPipeline{
+ err: errors.New("pipeline error"),
+ }
+}
+
+func makePipelineWithMultipleStreamResults(streamIDs []streams.StreamID, results []interface{}) *mockPipeline {
+ if len(streamIDs) != len(results) {
+ panic("streamIDs and results must have the same length")
+ }
+ trrs := make([]pipeline.TaskRunResult, len(streamIDs))
+ for i, res := range results {
+ trrs[i] = pipeline.TaskRunResult{Task: &pipeline.MemoTask{BaseTask: pipeline.BaseTask{StreamID: clnull.Uint32From(streamIDs[i])}}, Result: pipeline.Result{Value: res}}
+ }
+ return &mockPipeline{
+ run: &pipeline.Run{},
+ trrs: trrs,
+ err: nil,
+ streamIDs: streamIDs,
+ }
+}
+
+func TestObservationContext_Observe(t *testing.T) {
+ ctx := tests.Context(t)
+ r := &mockRegistry{}
+ telem := &mockTelemeter{}
+ oc := newObservationContext(r, telem)
+ opts := llo.DSOpts(nil)
+
+ missingStreamID := streams.StreamID(0)
+ streamID1 := streams.StreamID(1)
+ streamID2 := streams.StreamID(2)
+ streamID3 := streams.StreamID(3)
+ streamID4 := streams.StreamID(4)
+ streamID5 := streams.StreamID(5)
+ streamID6 := streams.StreamID(6)
+
+ multiPipelineDecimal := makePipelineWithMultipleStreamResults([]streams.StreamID{streamID4, streamID5, streamID6}, []interface{}{decimal.NewFromFloat(12.34), decimal.NewFromFloat(56.78), decimal.NewFromFloat(90.12)})
+
+ r.pipelines = map[streams.StreamID]*mockPipeline{
+ streamID1: &mockPipeline{},
+ streamID2: makePipelineWithSingleResult[decimal.Decimal](rand.Int64(), decimal.NewFromFloat(12.34), nil),
+ streamID3: makeErroringPipeline(),
+ streamID4: multiPipelineDecimal,
+ streamID5: multiPipelineDecimal,
+ streamID6: multiPipelineDecimal,
+ }
+
+ t.Run("returns error in case of missing pipeline", func(t *testing.T) {
+ _, err := oc.Observe(ctx, missingStreamID, opts)
+ require.EqualError(t, err, "no pipeline for stream: 0")
+ })
+ t.Run("returns error in case of zero results", func(t *testing.T) {
+ _, err := oc.Observe(ctx, streamID1, opts)
+ require.EqualError(t, err, "invalid number of results, expected: 1 or 3, got: 0")
+ })
+ t.Run("returns composite value from legacy job with single top-level streamID", func(t *testing.T) {
+ val, err := oc.Observe(ctx, streamID2, opts)
+ require.NoError(t, err)
+
+ assert.Equal(t, "12.34", val.(*llo.Decimal).String())
+ })
+ t.Run("returns error in case of erroring pipeline", func(t *testing.T) {
+ _, err := oc.Observe(ctx, streamID3, opts)
+ require.EqualError(t, err, "pipeline error")
+ })
+ t.Run("returns values for multiple stream IDs within the same job based on streamID tag with a single pipeline execution", func(t *testing.T) {
+ val, err := oc.Observe(ctx, streamID4, opts)
+ require.NoError(t, err)
+ assert.Equal(t, "12.34", val.(*llo.Decimal).String())
+
+ val, err = oc.Observe(ctx, streamID5, opts)
+ require.NoError(t, err)
+ assert.Equal(t, "56.78", val.(*llo.Decimal).String())
+
+ val, err = oc.Observe(ctx, streamID6, opts)
+ require.NoError(t, err)
+ assert.Equal(t, "90.12", val.(*llo.Decimal).String())
+
+ assert.Equal(t, 1, multiPipelineDecimal.runCount)
+
+ // returns cached values on subsequent calls
+ val, err = oc.Observe(ctx, streamID6, opts)
+ require.NoError(t, err)
+ assert.Equal(t, "90.12", val.(*llo.Decimal).String())
+
+ assert.Equal(t, 1, multiPipelineDecimal.runCount)
+ })
+}
+
+func TestObservationContext_Observe_concurrencyStressTest(t *testing.T) {
+ ctx := tests.Context(t)
+ r := &mockRegistry{}
+ telem := &mockTelemeter{}
+ oc := newObservationContext(r, telem)
+ opts := llo.DSOpts(nil)
+
+ streamID := streams.StreamID(1)
+ val := decimal.NewFromFloat(123.456)
+
+ // observes the same pipeline 1000 times to try and detect races etc
+ r.pipelines = make(map[streams.StreamID]*mockPipeline)
+ r.pipelines[streamID] = makePipelineWithSingleResult[decimal.Decimal](0, val, nil)
+ g, ctx := errgroup.WithContext(ctx)
+ for i := 0; i < 1000; i++ {
+ g.Go(func() error {
+ _, err := oc.Observe(ctx, streamID, opts)
+ return err
+ })
+ }
+ if err := g.Wait(); err != nil {
+ t.Fatalf("Observation failed: %v", err)
+ }
+}
+
+type mockPipelineConfig struct{}
+
+func (m *mockPipelineConfig) DefaultHTTPLimit() int64 { return 10000 }
+func (m *mockPipelineConfig) DefaultHTTPTimeout() commonconfig.Duration {
+ return *commonconfig.MustNewDuration(1 * time.Hour)
+}
+func (m *mockPipelineConfig) MaxRunDuration() time.Duration { return 1 * time.Hour }
+func (m *mockPipelineConfig) ReaperInterval() time.Duration { return 0 }
+func (m *mockPipelineConfig) ReaperThreshold() time.Duration { return 0 }
+
+// func (m *mockPipelineConfig) VerboseLogging() bool { return true }
+func (m *mockPipelineConfig) VerboseLogging() bool { return false }
+
+type mockBridgeConfig struct{}
+
+func (m *mockBridgeConfig) BridgeResponseURL() *url.URL {
+ return nil
+}
+func (m *mockBridgeConfig) BridgeCacheTTL() time.Duration {
+ return 0
+}
+
+func createBridge(t testing.TB, name string, val string, borm bridges.ORM, maxCalls int64) {
+ callcount := atomic.NewInt64(0)
+ bridge := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
+ n := callcount.Inc()
+ if maxCalls > 0 && n > maxCalls {
+ panic("too many calls to bridge" + name)
+ }
+ _, herr := io.ReadAll(req.Body)
+ if herr != nil {
+ panic(herr)
+ }
+
+ res.WriteHeader(http.StatusOK)
+ resp := fmt.Sprintf(`{"result": %s}`, val)
+ _, herr = res.Write([]byte(resp))
+ if herr != nil {
+ panic(herr)
+ }
+ }))
+ t.Cleanup(bridge.Close)
+ u, _ := url.Parse(bridge.URL)
+ require.NoError(t, borm.CreateBridgeType(tests.Context(t), &bridges.BridgeType{
+ Name: bridges.BridgeName(name),
+ URL: models.WebURL(*u),
+ }))
+}
+
+func TestObservationContext_Observe_integrationRealPipeline(t *testing.T) {
+ ctx := tests.Context(t)
+ lggr := logger.TestLogger(t)
+ db := pgtest.NewSqlxDB(t)
+ bridgesORM := bridges.NewORM(db)
+
+ createBridge(t, "foo-bridge", `123.456`, bridgesORM, 1)
+ createBridge(t, "bar-bridge", `"124.456"`, bridgesORM, 1)
+
+ c := clhttptest.NewTestLocalOnlyHTTPClient()
+ runner := pipeline.NewRunner(
+ nil,
+ bridgesORM,
+ &mockPipelineConfig{},
+ &mockBridgeConfig{},
+ nil,
+ nil,
+ nil,
+ lggr,
+ c,
+ c,
+ )
+
+ r := streams.NewRegistry(lggr, runner)
+
+ jobStreamID := streams.StreamID(5)
+
+ t.Run("using only streamID attributes", func(t *testing.T) {
+ jb := job.Job{
+ Type: job.Stream,
+ StreamID: &jobStreamID,
+ PipelineSpec: &pipeline.Spec{
+ DotDagSource: `
+// Benchmark Price
+result1 [type=memo value="900.0022"];
+multiply2 [type=multiply times=1 streamID=1 index=0]; // force conversion to decimal
+
+result2 [type=bridge name="foo-bridge" requestData="{\"data\":{\"data\":\"foo\"}}"];
+result2_parse [type=jsonparse path="result" streamID=2 index=1];
+
+result3 [type=bridge name="bar-bridge" requestData="{\"data\":{\"data\":\"bar\"}}"];
+result3_parse [type=jsonparse path="result"];
+multiply3 [type=multiply times=1 streamID=3 index=2]; // force conversion to decimal
+
+result1 -> multiply2;
+result2 -> result2_parse;
+result3 -> result3_parse -> multiply3;
+`,
+ },
+ }
+ err := r.Register(jb, nil)
+ require.NoError(t, err)
+
+ telem := &mockTelemeter{}
+ oc := newObservationContext(r, telem)
+ opts := llo.DSOpts(nil)
+
+ val, err := oc.Observe(ctx, streams.StreamID(1), opts)
+ require.NoError(t, err)
+ assert.Equal(t, "900.0022", val.(*llo.Decimal).String())
+ val, err = oc.Observe(ctx, streams.StreamID(2), opts)
+ require.NoError(t, err)
+ assert.Equal(t, "123.456", val.(*llo.Decimal).String())
+ val, err = oc.Observe(ctx, streams.StreamID(3), opts)
+ require.NoError(t, err)
+ assert.Equal(t, "124.456", val.(*llo.Decimal).String())
+
+ val, err = oc.Observe(ctx, jobStreamID, opts)
+ require.NoError(t, err)
+ assert.Equal(t, &llo.Quote{
+ Bid: decimal.NewFromFloat32(123.456),
+ Benchmark: decimal.NewFromFloat32(900.0022),
+ Ask: decimal.NewFromFloat32(124.456),
+ }, val.(*llo.Quote))
+ })
+}
+
+func BenchmarkObservationContext_Observe_integrationRealPipeline_concurrencyStressTest_manyStreams(b *testing.B) {
+ ctx := tests.Context(b)
+ lggr := logger.TestLogger(b)
+ db := pgtest.NewSqlxDB(b)
+ bridgesORM := bridges.NewORM(db)
+
+ if b.N > math.MaxInt32 {
+ b.Fatalf("N is too large: %d", b.N)
+ }
+ n := uint32(b.N) //nolint:gosec // G115 // overflow impossible
+
+ createBridge(b, "foo-bridge", `123.456`, bridgesORM, 0)
+ createBridge(b, "bar-bridge", `"124.456"`, bridgesORM, 0)
+
+ c := clhttptest.NewTestLocalOnlyHTTPClient()
+ runner := pipeline.NewRunner(
+ nil,
+ bridgesORM,
+ &mockPipelineConfig{},
+ &mockBridgeConfig{},
+ nil,
+ nil,
+ nil,
+ lggr,
+ c,
+ c,
+ )
+
+ r := streams.NewRegistry(lggr, runner)
+
+ for i := uint32(0); i < n; i++ {
+ i := i
+ jb := job.Job{
+ ID: int32(i), //nolint:gosec // G115 // overflow impossible
+ Name: null.StringFrom(fmt.Sprintf("job-%d", i)),
+ Type: job.Stream,
+ StreamID: &i,
+ PipelineSpec: &pipeline.Spec{
+ ID: int32(i * 100), //nolint:gosec // G115 // overflow impossible
+ DotDagSource: fmt.Sprintf(`
+// Benchmark Price
+result1 [type=memo value="900.0022"];
+multiply2 [type=multiply times=1 streamID=%d index=0]; // force conversion to decimal
+
+result2 [type=bridge name="foo-bridge" requestData="{\"data\":{\"data\":\"foo\"}}"];
+result2_parse [type=jsonparse path="result" streamID=%d index=1];
+
+result3 [type=bridge name="bar-bridge" requestData="{\"data\":{\"data\":\"bar\"}}"];
+result3_parse [type=jsonparse path="result"];
+multiply3 [type=multiply times=1 streamID=%d index=2]; // force conversion to decimal
+
+result1 -> multiply2;
+result2 -> result2_parse;
+result3 -> result3_parse -> multiply3;
+`, i+n, i+2*n, i+3*n),
+ },
+ }
+ err := r.Register(jb, nil)
+ require.NoError(b, err)
+ }
+
+ telem := &mockTelemeter{}
+ oc := newObservationContext(r, telem)
+ opts := llo.DSOpts(nil)
+
+ // concurrency stress test
+ b.ResetTimer()
+ g, ctx := errgroup.WithContext(ctx)
+ for i := uint32(0); i < n; i++ {
+ for _, strmID := range []uint32{i, i + n, i + 2*n, i + 3*n} {
+ g.Go(func() error {
+ // ignore errors, only care about races
+ oc.Observe(ctx, strmID, opts) //nolint:errcheck // ignore error
+ return nil
+ })
+ }
+ }
+ if err := g.Wait(); err != nil {
+ b.Fatalf("Observation failed: %v", err)
+ }
+}
diff --git a/core/services/pipeline/common.go b/core/services/pipeline/common.go
index 50611ee32a4..56af199078a 100644
--- a/core/services/pipeline/common.go
+++ b/core/services/pipeline/common.go
@@ -61,6 +61,7 @@ type (
TaskMinBackoff() time.Duration
TaskMaxBackoff() time.Duration
TaskTags() string
+ TaskStreamID() *uint32
GetDescendantTasks() []Task
}
diff --git a/core/services/pipeline/runner.go b/core/services/pipeline/runner.go
index 2194cb8be46..30c7842914d 100644
--- a/core/services/pipeline/runner.go
+++ b/core/services/pipeline/runner.go
@@ -377,7 +377,9 @@ func (r *runner) InitializePipeline(spec Spec) (pipeline *Pipeline, err error) {
func (r *runner) run(ctx context.Context, pipeline *Pipeline, run *Run, vars Vars) TaskRunResults {
l := r.lggr.With("run.ID", run.ID, "executionID", uuid.New(), "specID", run.PipelineSpecID, "jobID", run.PipelineSpec.JobID, "jobName", run.PipelineSpec.JobName)
- l.Debug("Initiating tasks for pipeline run of spec")
+ if r.config.VerboseLogging() {
+ l.Debug("Initiating tasks for pipeline run of spec")
+ }
scheduler := newScheduler(pipeline, run, vars, l)
go scheduler.Run()
diff --git a/core/services/pipeline/task.base.go b/core/services/pipeline/task.base.go
index 3e1db5fcdb5..fdedb69193e 100644
--- a/core/services/pipeline/task.base.go
+++ b/core/services/pipeline/task.base.go
@@ -24,6 +24,8 @@ type BaseTask struct {
Tags string `mapstructure:"tags" json:"-"`
+ StreamID null.Uint32 `mapstructure:"streamID"`
+
uuid uuid.UUID
}
@@ -84,6 +86,13 @@ func (t BaseTask) TaskTags() string {
return t.Tags
}
+func (t BaseTask) TaskStreamID() *uint32 {
+ if t.StreamID.Valid {
+ return &t.StreamID.Uint32
+ }
+ return nil
+}
+
// GetDescendantTasks retrieves all descendant tasks of a given task
func (t BaseTask) GetDescendantTasks() []Task {
if len(t.outputs) == 0 {
diff --git a/core/services/relay/evm/mercury/mocks/pipeline.go b/core/services/relay/evm/mercury/mocks/pipeline.go
index a7183c9a037..429eba66674 100644
--- a/core/services/relay/evm/mercury/mocks/pipeline.go
+++ b/core/services/relay/evm/mercury/mocks/pipeline.go
@@ -41,3 +41,4 @@ func (m *MockTask) TaskTimeout() (time.Duration, bool) { return 0, false }
func (m *MockTask) TaskRetries() uint32 { return 0 }
func (m *MockTask) TaskMinBackoff() time.Duration { return 0 }
func (m *MockTask) TaskMaxBackoff() time.Duration { return 0 }
+func (m *MockTask) TaskStreamID() *uint32 { return nil }
diff --git a/core/services/streams/delegate.go b/core/services/streams/delegate.go
index bf492d4bd15..2f62a7bf1f4 100644
--- a/core/services/streams/delegate.go
+++ b/core/services/streams/delegate.go
@@ -52,8 +52,7 @@ func (d *Delegate) ServicesForSpec(ctx context.Context, jb job.Job) (services []
rrs := ocrcommon.NewResultRunSaver(d.runner, lggr, d.cfg.MaxSuccessfulRuns(), d.cfg.ResultWriteQueueDepth())
services = append(services, rrs, &StreamService{
d.registry,
- id,
- jb.PipelineSpec,
+ jb,
lggr,
rrs,
})
@@ -66,23 +65,22 @@ type ResultRunSaver interface {
type StreamService struct {
registry Registry
- id StreamID
- spec *pipeline.Spec
+ jb job.Job
lggr logger.Logger
rrs ResultRunSaver
}
func (s *StreamService) Start(_ context.Context) error {
- if s.spec == nil {
- return fmt.Errorf("pipeline spec unexpectedly missing for stream %q", s.id)
+ if s.jb.PipelineSpec == nil {
+ return errors.New("pipeline spec unexpectedly missing for stream")
}
- s.lggr.Debugf("Starting stream %d", s.id)
- return s.registry.Register(s.id, *s.spec, s.rrs)
+ s.lggr.Debugw("Registering stream", "jobID", s.jb.ID)
+ return s.registry.Register(s.jb, s.rrs)
}
func (s *StreamService) Close() error {
- s.lggr.Debugf("Stopping stream %d", s.id)
- s.registry.Unregister(s.id)
+ s.lggr.Debugw("Unregistering stream", "jobID", s.jb.ID)
+ s.registry.Unregister(s.jb.ID)
return nil
}
@@ -101,8 +99,23 @@ func ValidatedStreamSpec(tomlString string) (job.Job, error) {
return jb, errors.Errorf("unsupported type: %q", jb.Type)
}
- if jb.StreamID == nil {
- return jb, errors.New("jobs of type 'stream' require streamID to be specified")
+ // The spec stream ID is optional, but if provided represents the final output of the pipeline run.
+ // nodes in the DAG may also contain streamID tags.
+ // Every spec must have at least one streamID.
+ var streamIDs []StreamID
+
+ if jb.StreamID != nil {
+ streamIDs = append(streamIDs, *jb.StreamID)
+ }
+
+ for _, t := range jb.Pipeline.Tasks {
+ if streamID := t.TaskStreamID(); streamID != nil {
+ streamIDs = append(streamIDs, *streamID)
+ }
+ }
+
+ if len(streamIDs) == 0 {
+ return jb, errors.New("no streamID found in spec (must be either specified as top-level key 'streamID' or at least one streamID tag must be provided in the pipeline)")
}
return jb, nil
diff --git a/core/services/streams/delegate_test.go b/core/services/streams/delegate_test.go
index d177c977e1b..dfd3da8ca07 100644
--- a/core/services/streams/delegate_test.go
+++ b/core/services/streams/delegate_test.go
@@ -15,11 +15,11 @@ import (
type mockRegistry struct{}
-func (m *mockRegistry) Get(streamID StreamID) (strm Stream, exists bool) { return }
-func (m *mockRegistry) Register(streamID StreamID, spec pipeline.Spec, rrs ResultRunSaver) error {
+func (m *mockRegistry) Get(streamID StreamID) (p Pipeline, exists bool) { return }
+func (m *mockRegistry) Register(jb job.Job, rrs ResultRunSaver) error {
return nil
}
-func (m *mockRegistry) Unregister(streamID StreamID) {}
+func (m *mockRegistry) Unregister(int32) {}
type mockDelegateConfig struct{}
@@ -49,8 +49,7 @@ func Test_Delegate(t *testing.T) {
strmSrv := srvs[1].(*StreamService)
assert.Equal(t, registry, strmSrv.registry)
- assert.Equal(t, StreamID(42), strmSrv.id)
- assert.Equal(t, jb.PipelineSpec, strmSrv.spec)
+ assert.Equal(t, jb, strmSrv.jb)
assert.NotNil(t, strmSrv.lggr)
assert.Equal(t, srvs[0], strmSrv.rrs)
})
@@ -168,7 +167,7 @@ answer1 [type=median index=0];
"""
`,
assertion: func(t *testing.T, jb job.Job, err error) {
- assert.EqualError(t, err, "jobs of type 'stream' require streamID to be specified")
+ assert.EqualError(t, err, "no streamID found in spec (must be either specified as top-level key 'streamID' or at least one streamID tag must be provided in the pipeline)")
},
},
}
diff --git a/core/services/streams/pipeline.go b/core/services/streams/pipeline.go
new file mode 100644
index 00000000000..de52fed26e5
--- /dev/null
+++ b/core/services/streams/pipeline.go
@@ -0,0 +1,131 @@
+package streams
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/pkg/errors"
+
+ "github.com/smartcontractkit/chainlink/v2/core/logger"
+ "github.com/smartcontractkit/chainlink/v2/core/services/job"
+ "github.com/smartcontractkit/chainlink/v2/core/services/pipeline"
+)
+
+type Runner interface {
+ ExecuteRun(ctx context.Context, spec pipeline.Spec, vars pipeline.Vars) (run *pipeline.Run, trrs pipeline.TaskRunResults, err error)
+ InitializePipeline(spec pipeline.Spec) (*pipeline.Pipeline, error)
+}
+
+type RunResultSaver interface {
+ Save(run *pipeline.Run)
+}
+
+type Pipeline interface {
+ Run(ctx context.Context) (*pipeline.Run, pipeline.TaskRunResults, error)
+ StreamIDs() []StreamID
+}
+
+type multiStreamPipeline struct {
+ lggr logger.Logger
+ spec pipeline.Spec
+ runner Runner
+ rrs RunResultSaver
+ streamIDs []StreamID
+ vars pipeline.Vars
+}
+
+func NewMultiStreamPipeline(lggr logger.Logger, jb job.Job, runner Runner, rrs RunResultSaver) (Pipeline, error) {
+ return newMultiStreamPipeline(lggr, jb, runner, rrs)
+}
+
+func newMultiStreamPipeline(lggr logger.Logger, jb job.Job, runner Runner, rrs RunResultSaver) (*multiStreamPipeline, error) {
+ if jb.PipelineSpec == nil {
+ // should never happen
+ return nil, errors.New("job has no pipeline spec")
+ }
+ spec := *jb.PipelineSpec
+ spec.JobID = jb.ID
+ spec.JobName = jb.Name.ValueOrZero()
+ spec.JobType = string(jb.Type)
+ if spec.Pipeline == nil {
+ pipeline, err := spec.ParsePipeline()
+ if err != nil {
+ return nil, fmt.Errorf("unparseable pipeline: %w", err)
+ }
+
+ spec.Pipeline = pipeline
+ // initialize it for the given runner
+ if _, err := runner.InitializePipeline(spec); err != nil {
+ return nil, fmt.Errorf("error while initializing pipeline: %w", err)
+ }
+ }
+ var streamIDs []StreamID
+ for _, t := range spec.Pipeline.Tasks {
+ if t.TaskStreamID() != nil {
+ streamIDs = append(streamIDs, *t.TaskStreamID())
+ }
+ }
+ if jb.StreamID != nil {
+ streamIDs = append(streamIDs, *jb.StreamID)
+ }
+ if err := validateStreamIDs(streamIDs); err != nil {
+ return nil, fmt.Errorf("invalid stream IDs: %w", err)
+ }
+ vars := pipeline.NewVarsFrom(map[string]interface{}{
+ "pipelineSpec": map[string]interface{}{
+ "id": jb.PipelineSpecID,
+ },
+ "jb": map[string]interface{}{
+ "databaseID": jb.ID,
+ "externalJobID": jb.ExternalJobID,
+ "name": jb.Name.ValueOrZero(),
+ },
+ })
+
+ return &multiStreamPipeline{
+ lggr.Named("MultiStreamPipeline").With("spec.ID", spec.ID, "jobID", spec.JobID, "jobName", spec.JobName, "jobType", spec.JobType),
+ spec,
+ runner,
+ rrs,
+ streamIDs,
+ vars}, nil
+}
+
+func validateStreamIDs(streamIDs []StreamID) error {
+ seen := make(map[StreamID]struct{})
+ for _, id := range streamIDs {
+ if _, ok := seen[id]; ok {
+ return fmt.Errorf("duplicate stream ID: %v", id)
+ }
+ seen[id] = struct{}{}
+ }
+ return nil
+}
+
+func (s *multiStreamPipeline) Run(ctx context.Context) (run *pipeline.Run, trrs pipeline.TaskRunResults, err error) {
+ run, trrs, err = s.executeRun(ctx)
+
+ if err != nil {
+ return nil, nil, fmt.Errorf("Run failed: %w", err)
+ }
+ if s.rrs != nil {
+ s.rrs.Save(run)
+ }
+
+ return
+}
+
+func (s *multiStreamPipeline) StreamIDs() []StreamID {
+ return s.streamIDs
+}
+
+// The context passed in here has a timeout of (ObservationTimeout + ObservationGracePeriod).
+// Upon context cancellation, its expected that we return any usable values within ObservationGracePeriod.
+func (s *multiStreamPipeline) executeRun(ctx context.Context) (*pipeline.Run, pipeline.TaskRunResults, error) {
+ run, trrs, err := s.runner.ExecuteRun(ctx, s.spec, s.vars)
+ if err != nil {
+ return nil, nil, fmt.Errorf("error executing run for spec ID %v: %w", s.spec.ID, err)
+ }
+
+ return run, trrs, err
+}
diff --git a/core/services/streams/stream.go b/core/services/streams/stream.go
deleted file mode 100644
index b65c6dc12f6..00000000000
--- a/core/services/streams/stream.go
+++ /dev/null
@@ -1,94 +0,0 @@
-package streams
-
-import (
- "context"
- "fmt"
- "sync"
-
- "github.com/smartcontractkit/chainlink/v2/core/logger"
- "github.com/smartcontractkit/chainlink/v2/core/services/pipeline"
-)
-
-type Runner interface {
- ExecuteRun(ctx context.Context, spec pipeline.Spec, vars pipeline.Vars) (run *pipeline.Run, trrs pipeline.TaskRunResults, err error)
- InitializePipeline(spec pipeline.Spec) (*pipeline.Pipeline, error)
-}
-
-type RunResultSaver interface {
- Save(run *pipeline.Run)
-}
-
-type Stream interface {
- Run(ctx context.Context) (*pipeline.Run, pipeline.TaskRunResults, error)
-}
-
-type stream struct {
- sync.RWMutex
- id StreamID
- lggr logger.Logger
- spec *pipeline.Spec
- runner Runner
- rrs RunResultSaver
-}
-
-func NewStream(lggr logger.Logger, id StreamID, spec pipeline.Spec, runner Runner, rrs RunResultSaver) Stream {
- return newStream(lggr, id, spec, runner, rrs)
-}
-
-func newStream(lggr logger.Logger, id StreamID, spec pipeline.Spec, runner Runner, rrs RunResultSaver) *stream {
- return &stream{sync.RWMutex{}, id, lggr.Named("Stream").With("streamID", id), &spec, runner, rrs}
-}
-
-func (s *stream) Run(ctx context.Context) (run *pipeline.Run, trrs pipeline.TaskRunResults, err error) {
- run, trrs, err = s.executeRun(ctx)
-
- if err != nil {
- return nil, nil, fmt.Errorf("Run failed: %w", err)
- }
- if s.rrs != nil {
- s.rrs.Save(run)
- }
-
- return
-}
-
-// The context passed in here has a timeout of (ObservationTimeout + ObservationGracePeriod).
-// Upon context cancellation, its expected that we return any usable values within ObservationGracePeriod.
-func (s *stream) executeRun(ctx context.Context) (*pipeline.Run, pipeline.TaskRunResults, error) {
- // the hot path here is to avoid parsing and use the pre-parsed, cached, pipeline
- s.RLock()
- initialize := s.spec.Pipeline == nil
- s.RUnlock()
- if initialize {
- pipeline, err := s.spec.ParsePipeline()
- if err != nil {
- return nil, nil, fmt.Errorf("Run failed due to unparseable pipeline: %w", err)
- }
-
- s.Lock()
- if s.spec.Pipeline == nil {
- s.spec.Pipeline = pipeline
- // initialize it for the given runner
- if _, err := s.runner.InitializePipeline(*s.spec); err != nil {
- return nil, nil, fmt.Errorf("Run failed due to error while initializing pipeline: %w", err)
- }
- }
- s.Unlock()
- }
-
- vars := pipeline.NewVarsFrom(map[string]interface{}{
- "pipelineSpec": map[string]interface{}{
- "id": s.spec.ID,
- },
- "stream": map[string]interface{}{
- "id": s.id,
- },
- })
-
- run, trrs, err := s.runner.ExecuteRun(ctx, *s.spec, vars)
- if err != nil {
- return nil, nil, fmt.Errorf("error executing run for spec ID %v: %w", s.spec.ID, err)
- }
-
- return run, trrs, err
-}
diff --git a/core/services/streams/stream_registry.go b/core/services/streams/stream_registry.go
index 9ab2df11d33..cf5a59944b7 100644
--- a/core/services/streams/stream_registry.go
+++ b/core/services/streams/stream_registry.go
@@ -7,7 +7,7 @@ import (
"github.com/smartcontractkit/chainlink-common/pkg/types/llo"
"github.com/smartcontractkit/chainlink/v2/core/logger"
- "github.com/smartcontractkit/chainlink/v2/core/services/pipeline"
+ "github.com/smartcontractkit/chainlink/v2/core/services/job"
)
// alias for easier refactoring
@@ -15,19 +15,22 @@ type StreamID = llo.StreamID
type Registry interface {
Getter
- Register(streamID StreamID, spec pipeline.Spec, rrs ResultRunSaver) error
- Unregister(streamID StreamID)
+ Register(jb job.Job, rrs ResultRunSaver) error
+ Unregister(jobID int32)
}
type Getter interface {
- Get(streamID StreamID) (strm Stream, exists bool)
+ Get(streamID StreamID) (p Pipeline, exists bool)
}
type streamRegistry struct {
sync.RWMutex
- lggr logger.Logger
- runner Runner
- streams map[StreamID]Stream
+ lggr logger.Logger
+ runner Runner
+ // keyed by stream ID
+ pipelines map[StreamID]Pipeline
+ // keyed by job ID
+ pipelinesByJobID map[int32]Pipeline
}
func NewRegistry(lggr logger.Logger, runner Runner) Registry {
@@ -39,29 +42,53 @@ func newRegistry(lggr logger.Logger, runner Runner) *streamRegistry {
sync.RWMutex{},
lggr.Named("Registry"),
runner,
- make(map[StreamID]Stream),
+ make(map[StreamID]Pipeline),
+ make(map[int32]Pipeline),
}
}
-func (s *streamRegistry) Get(streamID StreamID) (strm Stream, exists bool) {
+func (s *streamRegistry) Get(streamID StreamID) (p Pipeline, exists bool) {
s.RLock()
defer s.RUnlock()
- strm, exists = s.streams[streamID]
+ p, exists = s.pipelines[streamID]
return
}
-func (s *streamRegistry) Register(streamID StreamID, spec pipeline.Spec, rrs ResultRunSaver) error {
+func (s *streamRegistry) Register(jb job.Job, rrs ResultRunSaver) error {
+ if jb.Type != job.Stream {
+ return fmt.Errorf("cannot register job type %s; only Stream jobs are supported", jb.Type)
+ }
+ p, err := NewMultiStreamPipeline(s.lggr, jb, s.runner, rrs)
+ if err != nil {
+ return fmt.Errorf("cannot register job with ID: %d; %w", jb.ID, err)
+ }
s.Lock()
defer s.Unlock()
- if _, exists := s.streams[streamID]; exists {
- return fmt.Errorf("stream already registered for id: %d", streamID)
+ if _, exists := s.pipelinesByJobID[jb.ID]; exists {
+ return fmt.Errorf("cannot register job with ID: %d; it is already registered", jb.ID)
+ }
+ for _, strmID := range p.StreamIDs() {
+ if _, exists := s.pipelines[strmID]; exists {
+ return fmt.Errorf("cannot register job with ID: %d; stream id %d is already registered", jb.ID, strmID)
+ }
+ }
+ s.pipelinesByJobID[jb.ID] = p
+ streamIDs := p.StreamIDs()
+ for _, strmID := range streamIDs {
+ s.pipelines[strmID] = p
}
- s.streams[streamID] = NewStream(s.lggr, streamID, spec, s.runner, rrs)
return nil
}
-func (s *streamRegistry) Unregister(streamID StreamID) {
+func (s *streamRegistry) Unregister(jobID int32) {
s.Lock()
defer s.Unlock()
- delete(s.streams, streamID)
+ p, exists := s.pipelinesByJobID[jobID]
+ if !exists {
+ return
+ }
+ streamIDs := p.StreamIDs()
+ for _, id := range streamIDs {
+ delete(s.pipelines, id)
+ }
}
diff --git a/core/services/streams/stream_registry_test.go b/core/services/streams/stream_registry_test.go
index 738b68f5d4d..e20b29e9262 100644
--- a/core/services/streams/stream_registry_test.go
+++ b/core/services/streams/stream_registry_test.go
@@ -5,22 +5,31 @@ import (
"testing"
"github.com/smartcontractkit/chainlink/v2/core/logger"
+ "github.com/smartcontractkit/chainlink/v2/core/services/job"
"github.com/smartcontractkit/chainlink/v2/core/services/pipeline"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
-type mockStream struct {
+var _ Pipeline = &mockPipeline{}
+
+type mockPipeline struct {
run *pipeline.Run
trrs pipeline.TaskRunResults
err error
+
+ streamIDs []StreamID
}
-func (m *mockStream) Run(ctx context.Context) (*pipeline.Run, pipeline.TaskRunResults, error) {
+func (m *mockPipeline) Run(ctx context.Context) (*pipeline.Run, pipeline.TaskRunResults, error) {
return m.run, m.trrs, m.err
}
+func (m *mockPipeline) StreamIDs() []StreamID {
+ return m.streamIDs
+}
+
func Test_Registry(t *testing.T) {
lggr := logger.TestLogger(t)
runner := &mockRunner{}
@@ -28,21 +37,21 @@ func Test_Registry(t *testing.T) {
t.Run("Get", func(t *testing.T) {
sr := newRegistry(lggr, runner)
- sr.streams[1] = &mockStream{run: &pipeline.Run{ID: 1}}
- sr.streams[2] = &mockStream{run: &pipeline.Run{ID: 2}}
- sr.streams[3] = &mockStream{run: &pipeline.Run{ID: 3}}
+ sr.pipelines[1] = &mockPipeline{run: &pipeline.Run{ID: 1}}
+ sr.pipelines[2] = &mockPipeline{run: &pipeline.Run{ID: 2}}
+ sr.pipelines[3] = &mockPipeline{run: &pipeline.Run{ID: 3}}
v, exists := sr.Get(1)
assert.True(t, exists)
- assert.Equal(t, sr.streams[1], v)
+ assert.Equal(t, sr.pipelines[1], v)
v, exists = sr.Get(2)
assert.True(t, exists)
- assert.Equal(t, sr.streams[2], v)
+ assert.Equal(t, sr.pipelines[2], v)
v, exists = sr.Get(3)
assert.True(t, exists)
- assert.Equal(t, sr.streams[3], v)
+ assert.Equal(t, sr.pipelines[3], v)
v, exists = sr.Get(4)
assert.Nil(t, v)
@@ -51,56 +60,159 @@ func Test_Registry(t *testing.T) {
t.Run("Register", func(t *testing.T) {
sr := newRegistry(lggr, runner)
- t.Run("registers new stream", func(t *testing.T) {
- assert.Len(t, sr.streams, 0)
- err := sr.Register(1, pipeline.Spec{ID: 32, DotDagSource: "source"}, nil)
- require.NoError(t, err)
- assert.Len(t, sr.streams, 1)
-
- v, exists := sr.Get(1)
- require.True(t, exists)
- strm := v.(*stream)
- assert.Equal(t, StreamID(1), strm.id)
- assert.Equal(t, int32(32), strm.spec.ID)
- })
+ // registers new pipeline with multiple stream IDs
+ assert.Empty(t, sr.pipelines)
+ // err := sr.Register(job.Job{PipelineSpec: &pipeline.Spec{ID: 32, DotDagSource: "source"}}, nil)
+ // TODO: what if the dag is unparseable?
+ // err := sr.Register(1, pipeline.Spec{ID: 32, DotDagSource: "source"}, nil)
+ err := sr.Register(job.Job{ID: 100, Type: job.Stream, PipelineSpec: &pipeline.Spec{ID: 32, DotDagSource: `
+result1 [type=memo value="900.0022"];
+multiply2 [type=multiply times=1 streamID=1 index=0]; // force conversion to decimal
+result2 [type=bridge name="foo-bridge" requestData="{\"data\":{\"data\":\"foo\"}}"];
+result2_parse [type=jsonparse path="result" streamID=2 index=1];
+result3 [type=bridge name="bar-bridge" requestData="{\"data\":{\"data\":\"bar\"}}"];
+result3_parse [type=jsonparse path="result"];
+multiply3 [type=multiply times=1 streamID=3 index=2]; // force conversion to decimal
+result1 -> multiply2;
+result2 -> result2_parse;
+result3 -> result3_parse -> multiply3;
+`}}, nil)
+ require.NoError(t, err)
+ assert.Len(t, sr.pipelines, 3) // three streams, one pipeline
+ assert.Contains(t, sr.pipelines, StreamID(1))
+ assert.Contains(t, sr.pipelines, StreamID(2))
+ assert.Contains(t, sr.pipelines, StreamID(3))
+ p := sr.pipelines[1]
+ assert.Equal(t, p, sr.pipelines[2])
+ assert.Equal(t, p, sr.pipelines[3])
- t.Run("errors when attempt to re-register a stream with an existing ID", func(t *testing.T) {
- assert.Len(t, sr.streams, 1)
- err := sr.Register(1, pipeline.Spec{ID: 33, DotDagSource: "source"}, nil)
- require.Error(t, err)
- assert.Len(t, sr.streams, 1)
- assert.EqualError(t, err, "stream already registered for id: 1")
-
- v, exists := sr.Get(1)
- require.True(t, exists)
- strm := v.(*stream)
- assert.Equal(t, StreamID(1), strm.id)
- assert.Equal(t, int32(32), strm.spec.ID)
- })
+ v, exists := sr.Get(1)
+ require.True(t, exists)
+ msp := v.(*multiStreamPipeline)
+ assert.Equal(t, []StreamID{1, 2, 3}, msp.StreamIDs())
+ assert.Equal(t, int32(32), msp.spec.ID)
+
+ // errors when attempt to re-register a stream with an existing job ID
+ err = sr.Register(job.Job{ID: 100, Type: job.Stream, PipelineSpec: &pipeline.Spec{ID: 33, DotDagSource: `
+result1 [type=memo value="900.0022"];
+ `}}, nil)
+ require.EqualError(t, err, "cannot register job with ID: 100; it is already registered")
+
+ // errors when attempt to register a new job with duplicates stream IDs within ig
+ err = sr.Register(job.Job{ID: 101, StreamID: ptr(StreamID(100)), Type: job.Stream, PipelineSpec: &pipeline.Spec{ID: 33, DotDagSource: `
+result1 [type=memo value="900.0022" streamID=100];
+ `}}, nil)
+ require.EqualError(t, err, "cannot register job with ID: 101; invalid stream IDs: duplicate stream ID: 100")
+
+ // errors with unparseable pipeline
+ err = sr.Register(job.Job{ID: 101, Type: job.Stream, PipelineSpec: &pipeline.Spec{ID: 33, DotDagSource: "source"}}, nil)
+ require.Error(t, err)
+ require.EqualError(t, err, "cannot register job with ID: 101; unparseable pipeline: UnmarshalTaskFromMap: unknown task type: \"\"")
+
+ // errors when attempt to re-register a stream with an existing streamID at top-level
+ err = sr.Register(job.Job{ID: 101, StreamID: ptr(StreamID(3)), Type: job.Stream, PipelineSpec: &pipeline.Spec{ID: 33, DotDagSource: `
+result1 [type=memo value="900.0022"];
+multiply2 [type=multiply times=1 streamID=4 index=0]; // force conversion to decimal
+result2 [type=bridge name="foo-bridge" requestData="{\"data\":{\"data\":\"foo\"}}"];
+result2_parse [type=jsonparse path="result" streamID=5 index=1];
+result3 [type=bridge name="bar-bridge" requestData="{\"data\":{\"data\":\"bar\"}}"];
+result3_parse [type=jsonparse path="result"];
+multiply3 [type=multiply times=1 streamID=6 index=2]; // force conversion to decimal
+result1 -> multiply2;
+result2 -> result2_parse;
+result3 -> result3_parse -> multiply3;
+`}}, nil)
+ require.Error(t, err)
+ require.EqualError(t, err, "cannot register job with ID: 101; stream id 3 is already registered")
+
+ // errors when attempt to re-register a stream with an existing streamID in DAG
+ err = sr.Register(job.Job{ID: 101, StreamID: ptr(StreamID(4)), Type: job.Stream, PipelineSpec: &pipeline.Spec{ID: 33, DotDagSource: `
+result1 [type=memo value="900.0022"];
+multiply2 [type=multiply times=1 streamID=1 index=0]; // force conversion to decimal
+result2 [type=bridge name="foo-bridge" requestData="{\"data\":{\"data\":\"foo\"}}"];
+result2_parse [type=jsonparse path="result" streamID=5 index=1];
+result3 [type=bridge name="bar-bridge" requestData="{\"data\":{\"data\":\"bar\"}}"];
+result3_parse [type=jsonparse path="result"];
+multiply3 [type=multiply times=1 streamID=6 index=2]; // force conversion to decimal
+result1 -> multiply2;
+result2 -> result2_parse;
+result3 -> result3_parse -> multiply3;
+`}}, nil)
+ require.Error(t, err)
+ require.EqualError(t, err, "cannot register job with ID: 101; stream id 1 is already registered")
+
+ // registers new job with all new stream IDs
+ err = sr.Register(job.Job{ID: 101, StreamID: ptr(StreamID(4)), Type: job.Stream, PipelineSpec: &pipeline.Spec{ID: 33, DotDagSource: `
+result1 [type=memo value="900.0022"];
+multiply2 [type=multiply times=1 streamID=5 index=0]; // force conversion to decimal
+result2 [type=bridge name="foo-bridge" requestData="{\"data\":{\"data\":\"foo\"}}"];
+result2_parse [type=jsonparse path="result" streamID=6 index=1];
+result3 [type=bridge name="bar-bridge" requestData="{\"data\":{\"data\":\"bar\"}}"];
+result3_parse [type=jsonparse path="result"];
+multiply3 [type=multiply times=1 streamID=7 index=2]; // force conversion to decimal
+result1 -> multiply2;
+result2 -> result2_parse;
+result3 -> result3_parse -> multiply3;
+`}}, nil)
+ require.NoError(t, err)
+
+ // did not overwrite existing stream
+ assert.Len(t, sr.pipelines, 7)
+ assert.Equal(t, p, sr.pipelines[1])
+ assert.Equal(t, p, sr.pipelines[2])
+ assert.Equal(t, p, sr.pipelines[3])
+ p2 := sr.pipelines[4]
+ assert.NotEqual(t, p, p2)
+ assert.Equal(t, p2, sr.pipelines[5])
+ assert.Equal(t, p2, sr.pipelines[6])
+ assert.Equal(t, p2, sr.pipelines[7])
+
+ v, exists = sr.Get(1)
+ require.True(t, exists)
+ msp = v.(*multiStreamPipeline)
+ assert.ElementsMatch(t, []StreamID{1, 2, 3}, msp.StreamIDs())
+ assert.Equal(t, int32(32), msp.spec.ID)
+
+ v, exists = sr.Get(4)
+ require.True(t, exists)
+ msp = v.(*multiStreamPipeline)
+ assert.ElementsMatch(t, []StreamID{4, 5, 6, 7}, msp.StreamIDs())
+ assert.Equal(t, int32(33), msp.spec.ID)
})
t.Run("Unregister", func(t *testing.T) {
sr := newRegistry(lggr, runner)
- sr.streams[1] = &mockStream{run: &pipeline.Run{ID: 1}}
- sr.streams[2] = &mockStream{run: &pipeline.Run{ID: 2}}
- sr.streams[3] = &mockStream{run: &pipeline.Run{ID: 3}}
+ err := sr.Register(job.Job{ID: 100, StreamID: ptr(StreamID(1)), Type: job.Stream, PipelineSpec: &pipeline.Spec{ID: 33, DotDagSource: `
+result1 [type=memo value="900.0022" streamID=2];
+ `}}, nil)
+ require.NoError(t, err)
+ err = sr.Register(job.Job{ID: 101, Type: job.Stream, PipelineSpec: &pipeline.Spec{ID: 33, DotDagSource: `
+result1 [type=memo value="900.0022" streamID=3];
+ `}}, nil)
+ require.NoError(t, err)
+ err = sr.Register(job.Job{ID: 102, Type: job.Stream, PipelineSpec: &pipeline.Spec{ID: 33, DotDagSource: `
+result1 [type=memo value="900.0022" streamID=4];
+ `}}, nil)
+ require.NoError(t, err)
t.Run("unregisters a stream", func(t *testing.T) {
- assert.Len(t, sr.streams, 3)
+ assert.Len(t, sr.pipelines, 4)
- sr.Unregister(1)
+ sr.Unregister(100)
- assert.Len(t, sr.streams, 2)
- _, exists := sr.streams[1]
+ assert.Len(t, sr.pipelines, 2)
+ _, exists := sr.pipelines[1]
+ assert.False(t, exists)
+ _, exists = sr.pipelines[2]
assert.False(t, exists)
})
t.Run("no effect when unregistering a non-existent stream", func(t *testing.T) {
- assert.Len(t, sr.streams, 2)
+ assert.Len(t, sr.pipelines, 2)
sr.Unregister(1)
- assert.Len(t, sr.streams, 2)
- _, exists := sr.streams[1]
+ assert.Len(t, sr.pipelines, 2)
+ _, exists := sr.pipelines[1]
assert.False(t, exists)
})
})
diff --git a/core/services/streams/stream_test.go b/core/services/streams/stream_test.go
index 78174138121..905d421bddf 100644
--- a/core/services/streams/stream_test.go
+++ b/core/services/streams/stream_test.go
@@ -13,6 +13,7 @@ import (
"github.com/smartcontractkit/chainlink-common/pkg/sqlutil"
"github.com/smartcontractkit/chainlink/v2/core/internal/testutils"
"github.com/smartcontractkit/chainlink/v2/core/logger"
+ "github.com/smartcontractkit/chainlink/v2/core/services/job"
"github.com/smartcontractkit/chainlink/v2/core/services/pipeline"
)
@@ -57,24 +58,22 @@ func (m *MockTask) TaskMaxBackoff() time.Duration { return 0 }
func Test_Stream(t *testing.T) {
lggr := logger.TestLogger(t)
runner := &mockRunner{}
- spec := pipeline.Spec{}
- id := StreamID(123)
ctx := testutils.Context(t)
- t.Run("Run", func(t *testing.T) {
- strm := newStream(lggr, id, spec, runner, nil)
-
- t.Run("errors with empty pipeline", func(t *testing.T) {
- _, _, err := strm.Run(ctx)
- assert.EqualError(t, err, "Run failed: Run failed due to unparseable pipeline: empty pipeline")
- })
+ t.Run("errors with empty pipeline", func(t *testing.T) {
+ jbInvalid := job.Job{StreamID: ptr(StreamID(123)), PipelineSpec: &pipeline.Spec{DotDagSource: ``}}
+ _, err := newMultiStreamPipeline(lggr, jbInvalid, runner, nil)
+ require.EqualError(t, err, "unparseable pipeline: empty pipeline")
+ })
- spec.DotDagSource = `
-succeed [type=memo value=42]
+ jb := job.Job{StreamID: ptr(StreamID(123)), PipelineSpec: &pipeline.Spec{DotDagSource: `
+succeed [type=memo value=42 streamID=124];
succeed;
-`
+ `}}
- strm = newStream(lggr, id, spec, runner, nil)
+ t.Run("Run", func(t *testing.T) {
+ strm, err := newMultiStreamPipeline(lggr, jb, runner, nil)
+ require.NoError(t, err)
t.Run("executes the pipeline (success)", func(t *testing.T) {
runner.run = &pipeline.Run{ID: 42}
diff --git a/go.mod b/go.mod
index 85baf8f3812..0fb1fc579e9 100644
--- a/go.mod
+++ b/go.mod
@@ -109,6 +109,7 @@ require (
go.opentelemetry.io/otel/metric v1.31.0
go.opentelemetry.io/otel/sdk/metric v1.31.0
go.opentelemetry.io/otel/trace v1.31.0
+ go.uber.org/atomic v1.11.0
go.uber.org/multierr v1.11.0
go.uber.org/zap v1.27.0
golang.org/x/crypto v0.31.0
From 401842030a451589cdab61062dda1e2530b0530b Mon Sep 17 00:00:00 2001
From: Austin <107539019+0xAustinWang@users.noreply.github.com>
Date: Thu, 9 Jan 2025 23:51:41 +0800
Subject: [PATCH 23/91] add chains to ccip in crib setup (#15862)
* support the chains in crib setup
* use fchain of 1
* actually add the changesets, apply them
* apply changeset within the whole system
* go imports
---
deployment/environment/crib/ccip_deployer.go | 44 ++++++++++++++++----
1 file changed, 35 insertions(+), 9 deletions(-)
diff --git a/deployment/environment/crib/ccip_deployer.go b/deployment/environment/crib/ccip_deployer.go
index bb3acec8aa4..639e42b4024 100644
--- a/deployment/environment/crib/ccip_deployer.go
+++ b/deployment/environment/crib/ccip_deployer.go
@@ -7,22 +7,23 @@ import (
"math/big"
"github.com/ethereum/go-ethereum/common"
+
"github.com/smartcontractkit/ccip-owner-contracts/pkg/config"
+ "github.com/smartcontractkit/chainlink-ccip/chainconfig"
+ cciptypes "github.com/smartcontractkit/chainlink-ccip/pkg/types/ccipocr3"
+ "github.com/smartcontractkit/chainlink/deployment"
+ "github.com/smartcontractkit/chainlink/deployment/ccip/changeset"
commonchangeset "github.com/smartcontractkit/chainlink/deployment/common/changeset"
commontypes "github.com/smartcontractkit/chainlink/deployment/common/types"
"github.com/smartcontractkit/chainlink/deployment/environment/devenv"
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/fee_quoter"
- "github.com/smartcontractkit/chainlink/v2/core/services/relay"
-
- "github.com/smartcontractkit/chainlink/deployment"
- "github.com/smartcontractkit/chainlink/deployment/ccip/changeset"
"github.com/smartcontractkit/chainlink/v2/core/logger"
+ "github.com/smartcontractkit/chainlink/v2/core/services/relay"
)
-// DeployHomeChainContracts deploys the home chain contracts so that the chainlink nodes can be started with the CR address in Capabilities.ExternalRegistry
-// DeployHomeChainContracts is to 1. Set up crib with chains and chainlink nodes ( cap reg is not known yet so not setting the config with capreg address)
-// Call DeployHomeChain changeset with nodeinfo ( the peer id and all)
+// DeployHomeChainContracts deploys the home chain contracts so that the chainlink nodes can use the CR address in Capabilities.ExternalRegistry
+// Afterwards, we call DeployHomeChain changeset with nodeinfo ( the peer id and all)
func DeployHomeChainContracts(ctx context.Context, lggr logger.Logger, envConfig devenv.EnvironmentConfig, homeChainSel uint64, feedChainSel uint64) (deployment.CapabilityRegistryConfig, deployment.AddressBook, error) {
e, _, err := devenv.NewEnvironment(func() context.Context { return ctx }, lggr, envConfig)
if err != nil {
@@ -68,6 +69,7 @@ func DeployHomeChainContracts(ctx context.Context, lggr logger.Logger, envConfig
return capRegConfig, e.ExistingAddresses, nil
}
+// DeployCCIPAndAddLanes is the actual ccip setup once the nodes are initialized.
func DeployCCIPAndAddLanes(ctx context.Context, lggr logger.Logger, envConfig devenv.EnvironmentConfig, homeChainSel, feedChainSel uint64, ab deployment.AddressBook) (DeployCCIPOutput, error) {
e, _, err := devenv.NewEnvironment(func() context.Context { return ctx }, lggr, envConfig)
if err != nil {
@@ -93,9 +95,33 @@ func DeployCCIPAndAddLanes(ctx context.Context, lggr logger.Logger, envConfig de
})
}
- // This will not apply any proposals because we pass nil to testing.
- // However, setup is ok because we only need to deploy the contracts and distribute job specs
+ // set up chains
+ chainConfigs := make(map[uint64]changeset.ChainConfig)
+ nodeInfo, err := deployment.NodeInfo(e.NodeIDs, e.Offchain)
+ if err != nil {
+ return DeployCCIPOutput{}, fmt.Errorf("failed to get node info from env: %w", err)
+ }
+ for _, chain := range chainSelectors {
+ chainConfigs[chain] = changeset.ChainConfig{
+ Readers: nodeInfo.NonBootstraps().PeerIDs(),
+ FChain: 1,
+ EncodableChainConfig: chainconfig.ChainConfig{
+ GasPriceDeviationPPB: cciptypes.BigInt{Int: big.NewInt(1000)},
+ DAGasPriceDeviationPPB: cciptypes.BigInt{Int: big.NewInt(1_000_000)},
+ OptimisticConfirmations: 1,
+ },
+ }
+ }
+
+ // Setup because we only need to deploy the contracts and distribute job specs
*e, err = commonchangeset.ApplyChangesets(nil, *e, nil, []commonchangeset.ChangesetApplication{
+ {
+ Changeset: commonchangeset.WrapChangeSet(changeset.UpdateChainConfig),
+ Config: changeset.UpdateChainConfigConfig{
+ HomeChainSelector: homeChainSel,
+ RemoteChainAdds: chainConfigs,
+ },
+ },
{
Changeset: commonchangeset.WrapChangeSet(commonchangeset.DeployLinkToken),
Config: chainSelectors,
From f2da1e158c1aee3180f906cdd937ef50d0e7aaa6 Mon Sep 17 00:00:00 2001
From: Yashvardhan Nevatia
Date: Thu, 9 Jan 2025 16:02:50 +0000
Subject: [PATCH 24/91] Solana link deploy (B) (#15845)
* Adding solchains in NewEnv
* Revert "Adding solchains in NewEnv"
This reverts commit aaab52e01412ea4f1de4f748cadf0c127682418c.
* adding sol chains to newenv
* newEnv needs to send nil
* adding test env setup
* adding link token deployment and test
* adding nil for crib sol chains
* using switch case
* Adding decimal const
* adding chain selectors commit
* go mod tidy
* linting
* chain sel update
* update core/scripts go files
* again
* add changeset
* go imports
* go mod
* go mod
* go mod tidy
* linting
---------
Co-authored-by: Terry Tata
---
.../common/changeset/deploy_link_token.go | 76 +++++++++++++++++--
.../changeset/deploy_link_token_test.go | 12 ++-
deployment/common/changeset/test_helpers.go | 1 +
deployment/environment/memory/chain.go | 4 +-
4 files changed, 81 insertions(+), 12 deletions(-)
diff --git a/deployment/common/changeset/deploy_link_token.go b/deployment/common/changeset/deploy_link_token.go
index 0c648939c9f..607c33fbeaa 100644
--- a/deployment/common/changeset/deploy_link_token.go
+++ b/deployment/common/changeset/deploy_link_token.go
@@ -1,10 +1,17 @@
package changeset
import (
- "errors"
+ "context"
+ "fmt"
"github.com/smartcontractkit/chainlink-common/pkg/logger"
+ "github.com/gagliardetto/solana-go"
+ solRpc "github.com/gagliardetto/solana-go/rpc"
+ chainsel "github.com/smartcontractkit/chain-selectors"
+
+ solCommomUtil "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/common"
+ solTokenUtil "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/tokens"
"github.com/smartcontractkit/chainlink/deployment"
"github.com/smartcontractkit/chainlink/deployment/common/types"
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/link_token"
@@ -12,27 +19,48 @@ import (
var _ deployment.ChangeSet[[]uint64] = DeployLinkToken
+const (
+ TokenDecimalsSolana = 9
+)
+
// DeployLinkToken deploys a link token contract to the chain identified by the ChainSelector.
func DeployLinkToken(e deployment.Environment, chains []uint64) (deployment.ChangesetOutput, error) {
for _, chain := range chains {
- _, ok := e.Chains[chain]
- if !ok {
- return deployment.ChangesetOutput{}, errors.New("chain not found in environment")
+ _, evmOk := e.Chains[chain]
+ _, solOk := e.SolChains[chain]
+ if !evmOk && !solOk {
+ return deployment.ChangesetOutput{}, fmt.Errorf("chain %d not found in environment", chain)
}
}
newAddresses := deployment.NewMemoryAddressBook()
for _, chain := range chains {
- _, err := deployLinkTokenContract(
- e.Logger, e.Chains[chain], newAddresses,
- )
+ family, err := chainsel.GetSelectorFamily(chain)
if err != nil {
return deployment.ChangesetOutput{AddressBook: newAddresses}, err
}
+ switch family {
+ case chainsel.FamilyEVM:
+ // Deploy EVM LINK token
+ _, err := deployLinkTokenContractEVM(
+ e.Logger, e.Chains[chain], newAddresses,
+ )
+ if err != nil {
+ return deployment.ChangesetOutput{AddressBook: newAddresses}, err
+ }
+ case chainsel.FamilySolana:
+ // Deploy Solana LINK token
+ err := deployLinkTokenContractSolana(
+ e.Logger, e.SolChains[chain], newAddresses,
+ )
+ if err != nil {
+ return deployment.ChangesetOutput{AddressBook: newAddresses}, err
+ }
+ }
}
return deployment.ChangesetOutput{AddressBook: newAddresses}, nil
}
-func deployLinkTokenContract(
+func deployLinkTokenContractEVM(
lggr logger.Logger,
chain deployment.Chain,
ab deployment.AddressBook,
@@ -57,3 +85,35 @@ func deployLinkTokenContract(
}
return linkToken, nil
}
+
+func deployLinkTokenContractSolana(
+ lggr logger.Logger,
+ chain deployment.SolChain,
+ ab deployment.AddressBook,
+) error {
+ adminPublicKey := chain.DeployerKey.PublicKey()
+ mint, _ := solana.NewRandomPrivateKey()
+ // this is the token address
+ mintPublicKey := mint.PublicKey()
+ instructions, err := solTokenUtil.CreateToken(
+ context.Background(), solana.Token2022ProgramID, mintPublicKey, adminPublicKey, TokenDecimalsSolana, chain.Client, solRpc.CommitmentConfirmed,
+ )
+ if err != nil {
+ lggr.Errorw("Failed to generate instructions for link token deployment", "chain", chain.String(), "err", err)
+ return err
+ }
+ err = chain.Confirm(instructions, solCommomUtil.AddSigners(mint))
+ if err != nil {
+ lggr.Errorw("Failed to confirm instructions for link token deployment", "chain", chain.String(), "err", err)
+ return err
+ }
+ tv := deployment.NewTypeAndVersion(types.LinkToken, deployment.Version1_0_0)
+ lggr.Infow("Deployed contract", "Contract", tv.String(), "addr", mintPublicKey.String(), "chain", chain.String())
+ err = ab.Save(chain.Selector, mintPublicKey.String(), tv)
+ if err != nil {
+ lggr.Errorw("Failed to save link token", "chain", chain.String(), "err", err)
+ return err
+ }
+
+ return nil
+}
diff --git a/deployment/common/changeset/deploy_link_token_test.go b/deployment/common/changeset/deploy_link_token_test.go
index bc472d2a247..ddaca52c2d5 100644
--- a/deployment/common/changeset/deploy_link_token_test.go
+++ b/deployment/common/changeset/deploy_link_token_test.go
@@ -15,13 +15,15 @@ func TestDeployLinkToken(t *testing.T) {
t.Parallel()
lggr := logger.TestLogger(t)
e := memory.NewMemoryEnvironment(t, lggr, zapcore.InfoLevel, memory.MemoryEnvironmentConfig{
- Chains: 1,
+ Chains: 1,
+ SolChains: 1,
})
chain1 := e.AllChainSelectors()[0]
+ solChain1 := e.AllChainSelectorsSolana()[0]
e, err := changeset.ApplyChangesets(t, e, nil, []changeset.ChangesetApplication{
{
Changeset: changeset.WrapChangeSet(changeset.DeployLinkToken),
- Config: []uint64{chain1},
+ Config: []uint64{chain1, solChain1},
},
})
require.NoError(t, err)
@@ -32,4 +34,10 @@ func TestDeployLinkToken(t *testing.T) {
// View itself already unit tested
_, err = state.GenerateLinkView()
require.NoError(t, err)
+
+ // solana test
+ addrs, err = e.ExistingAddresses.AddressesForChain(solChain1)
+ require.NoError(t, err)
+ require.NotEmpty(t, addrs)
+
}
diff --git a/deployment/common/changeset/test_helpers.go b/deployment/common/changeset/test_helpers.go
index 5d524e542ad..a8105b26561 100644
--- a/deployment/common/changeset/test_helpers.go
+++ b/deployment/common/changeset/test_helpers.go
@@ -90,6 +90,7 @@ func ApplyChangesets(t *testing.T, e deployment.Environment, timelockContractsPe
Logger: e.Logger,
ExistingAddresses: addresses,
Chains: e.Chains,
+ SolChains: e.SolChains,
NodeIDs: e.NodeIDs,
Offchain: e.Offchain,
OCRSecrets: e.OCRSecrets,
diff --git a/deployment/environment/memory/chain.go b/deployment/environment/memory/chain.go
index cc22b40d844..193def7ba08 100644
--- a/deployment/environment/memory/chain.go
+++ b/deployment/environment/memory/chain.go
@@ -73,16 +73,16 @@ func getTestSolanaChainSelectors() []uint64 {
}
func GenerateChainsSol(t *testing.T, numChains int) map[uint64]SolanaChain {
- chains := make(map[uint64]SolanaChain)
testSolanaChainSelectors := getTestSolanaChainSelectors()
if len(testSolanaChainSelectors) < numChains {
t.Fatalf("not enough test solana chain selectors available")
}
-
+ chains := make(map[uint64]SolanaChain)
for i := 0; i < numChains; i++ {
chainID := testSolanaChainSelectors[i]
url, _ := solTestUtil.SetupLocalSolNodeWithFlags(t)
admin, gerr := solana.NewRandomPrivateKey()
+ solTestUtil.FundTestAccounts(t, []solana.PublicKey{admin.PublicKey()}, url)
require.NoError(t, gerr)
chains[chainID] = SolanaChain{
Client: solRpc.New(url),
From 8c65527c82a20c74b2a4707221ef496802b21804 Mon Sep 17 00:00:00 2001
From: Josh Weintraub <26035072+jhweintraub@users.noreply.github.com>
Date: Thu, 9 Jan 2025 11:10:05 -0500
Subject: [PATCH 25/91] CCIP-4058 replace f with fObserve and fSign in RMHome
and RMNRemote (#15605)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* replace f with fObserve in RMHome and RMNRemote
* Update gethwrappers
* rename fObserve to fSign
* fix off-chain components of renaming RMN parameters
* core changeset
* update missing file for new parameter name
* fix wrappers
* fix merge conflict broken text
* Update gethwrappers
* attempt fix json export tag
* Update gethwrappers
* upgrade cl-ccip and fix test
* upgrade cl-ccip@main
* upgrade cl-ccip@main
* fix test
* upgrade cl-ccip@main
* Update gethwrappers
* upgrade chainlink ccip
* DEVSVCS-1087: remove unused automation hardhat tests (#15847)
* remove unused automation hardhat tests
* freeze contracts
* remove more tests
* update
* CCIP Config backported from CCIP repo (#15856)
* Moving configs directly from CCIP repo
* Moving configs directly from CCIP repo
* Remove panic recovery for wsrpc (#15865)
- Due to some other bug in the library, the redialled connection never
becomes ready.
* Added is_backfiled filed to solana's filter table (#15796)
* Extract MultiNode to chainlink-framework (#15791)
* Extract MultiNode
* tidy
* tidy
* Fix generate
* Add mock Subscription
* lint
* Fix sendonly allocation
* lint
* Add QueryTimeout to client
* Use multinode
* Update rpc_client_test.go
* improve peer group dialer sync function logs (#15867)
* Solana devnet spin up in memory env (A) (#15831)
* Adding solchains in NewEnv
* Revert "Adding solchains in NewEnv"
This reverts commit aaab52e01412ea4f1de4f748cadf0c127682418c.
* adding sol chains to newenv
* newEnv needs to send nil
* adding test env setup
* adding nil for crib sol chains
* adding chain selectors commit
* go mod tidy
* linting
* chain sel update
* update core/scripts go files
* again
* add changeset
* go imports
* go mod tidy
* Update modgraph
---------
Co-authored-by: Terry Tata
Co-authored-by: Blaž Hrastnik
* upgrade chainlink ccip fixing router binding issues in migration
* upgrade chainlink ccip fixing router binding issues in migration
* fix comment
* gomodtidy and formatting fix
* update go mod file for newest cl-ccip
* update comment
---------
Co-authored-by: app-token-issuer-infra-releng[bot] <120227048+app-token-issuer-infra-releng[bot]@users.noreply.github.com>
Co-authored-by: Simon B.Robert
Co-authored-by: dimkouv
Co-authored-by: asoliman
Co-authored-by: FelixFan1992
Co-authored-by: Mateusz Sekara
Co-authored-by: Sam
Co-authored-by: Dmytro Haidashenko <34754799+dhaidashenko@users.noreply.github.com>
Co-authored-by: Dylan Tinianov
Co-authored-by: Yashvardhan Nevatia
Co-authored-by: Terry Tata
Co-authored-by: Blaž Hrastnik
---
.changeset/fluffy-lizards-laugh.md | 5 +++++
contracts/.changeset/stale-dots-destroy.md | 5 +++++
contracts/src/v0.8/ccip/rmn/RMNHome.sol | 4 ++--
contracts/src/v0.8/ccip/rmn/RMNRemote.sol | 6 +++---
.../test/rmn/RMNHome/RMNHome.revokeCandidate.t.sol | 2 +-
.../test/rmn/RMNHome/RMNHome.setCandidate.t.sol | 2 +-
.../rmn/RMNHome/RMNHome.setDynamicConfig.t.sol | 8 +++++---
.../RMNHome.validateStaticAndDynamicConfig.t.sol | 2 +-
.../ccip/test/rmn/RMNHome/RMNHomeTestSetup.t.sol | 6 ++++--
.../test/rmn/RMNRemote/RMNRemote.setConfig.t.sol | 14 +++++++-------
.../RMNRemote/RMNRemote.verifywithConfigSet.t.sol | 4 ++--
.../integrationhelpers/integration_helpers.go | 2 +-
.../usdcreader/usdcreader_test.go | 7 ++++---
.../ccip/generated/rmn_home/rmn_home.go | 4 ++--
.../ccip/generated/rmn_remote/rmn_remote.go | 4 ++--
...ted-wrapper-dependency-versions-do-not-edit.txt | 4 ++--
core/scripts/go.mod | 2 +-
core/scripts/go.sum | 4 ++--
deployment/ccip/changeset/cs_deploy_chain.go | 2 +-
deployment/ccip/changeset/cs_update_rmn_config.go | 2 +-
deployment/ccip/view/v1_6/rmnhome.go | 4 ++--
deployment/ccip/view/v1_6/rmnremote.go | 4 ++--
deployment/go.mod | 2 +-
deployment/go.sum | 4 ++--
go.mod | 2 +-
go.sum | 4 ++--
integration-tests/contracts/ccipreader_test.go | 6 +++---
integration-tests/go.mod | 2 +-
integration-tests/go.sum | 4 ++--
integration-tests/load/go.mod | 2 +-
integration-tests/load/go.sum | 4 ++--
integration-tests/smoke/ccip/ccip_rmn_test.go | 2 +-
32 files changed, 72 insertions(+), 57 deletions(-)
create mode 100644 .changeset/fluffy-lizards-laugh.md
create mode 100644 contracts/.changeset/stale-dots-destroy.md
diff --git a/.changeset/fluffy-lizards-laugh.md b/.changeset/fluffy-lizards-laugh.md
new file mode 100644
index 00000000000..3d38170c2d8
--- /dev/null
+++ b/.changeset/fluffy-lizards-laugh.md
@@ -0,0 +1,5 @@
+---
+"chainlink": patch
+---
+
+Changed RMNRemote and RMNHome parameter f to fObserve and fSign #updated
diff --git a/contracts/.changeset/stale-dots-destroy.md b/contracts/.changeset/stale-dots-destroy.md
new file mode 100644
index 00000000000..98cea8292e0
--- /dev/null
+++ b/contracts/.changeset/stale-dots-destroy.md
@@ -0,0 +1,5 @@
+---
+'@chainlink/contracts': patch
+---
+
+replace f with fObserve in RMNHome and RMNRemote and update all tests CCIP-4058
diff --git a/contracts/src/v0.8/ccip/rmn/RMNHome.sol b/contracts/src/v0.8/ccip/rmn/RMNHome.sol
index 4fd01a7115b..b1eb56679f4 100644
--- a/contracts/src/v0.8/ccip/rmn/RMNHome.sol
+++ b/contracts/src/v0.8/ccip/rmn/RMNHome.sol
@@ -81,7 +81,7 @@ contract RMNHome is Ownable2StepMsgSender, ITypeAndVersion {
struct SourceChain {
uint64 chainSelector; // ─╮ The Source chain selector.
- uint64 f; // ─────────────╯ Maximum number of faulty observers; f+1 observers required to agree on an observation for this source chain.
+ uint64 fObserve; // ──────╯ Maximum number of faulty observers; f+1 observers required to agree on an observation for this source chain.
uint256 observerNodesBitmap; // ObserverNodesBitmap & (1<
Date: Thu, 9 Jan 2025 18:43:00 +0200
Subject: [PATCH 26/91] deployment/ccip/changeset: add active/candidate test
(#15863)
* deployment/ccip/changeset: add active/candidate test
* goimports
---
.../changeset/cs_active_candidate_test.go | 252 ++++++++++++++++++
1 file changed, 252 insertions(+)
create mode 100644 deployment/ccip/changeset/cs_active_candidate_test.go
diff --git a/deployment/ccip/changeset/cs_active_candidate_test.go b/deployment/ccip/changeset/cs_active_candidate_test.go
new file mode 100644
index 00000000000..92e3e825620
--- /dev/null
+++ b/deployment/ccip/changeset/cs_active_candidate_test.go
@@ -0,0 +1,252 @@
+package changeset
+
+import (
+ "math/big"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/accounts/abi/bind"
+ "github.com/ethereum/go-ethereum/common"
+ "golang.org/x/exp/maps"
+
+ "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext"
+ "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/internal"
+ commonchangeset "github.com/smartcontractkit/chainlink/deployment/common/changeset"
+
+ "github.com/stretchr/testify/require"
+
+ "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/types"
+ "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/fee_quoter"
+ "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router"
+ "github.com/smartcontractkit/chainlink/v2/core/logger"
+)
+
+func Test_ActiveCandidate(t *testing.T) {
+ // Setup an environment with 2 chains, a source and a dest.
+ // We want to have the active instance execute a few messages
+ // and then setup a candidate instance. The candidate instance
+ // should not be able to transmit anything until we make it active.
+ tenv := NewMemoryEnvironment(t,
+ WithChains(2),
+ WithNodes(4))
+ state, err := LoadOnchainState(tenv.Env)
+ require.NoError(t, err)
+
+ // Deploy to all chains.
+ allChains := maps.Keys(tenv.Env.Chains)
+ source := allChains[0]
+ dest := allChains[1]
+
+ // Connect source to dest
+ sourceState := state.Chains[source]
+ tenv.Env, err = commonchangeset.ApplyChangesets(t, tenv.Env, tenv.TimelockContracts(t), []commonchangeset.ChangesetApplication{
+ {
+ Changeset: commonchangeset.WrapChangeSet(UpdateOnRampsDests),
+ Config: UpdateOnRampDestsConfig{
+ UpdatesByChain: map[uint64]map[uint64]OnRampDestinationUpdate{
+ source: {
+ dest: {
+ IsEnabled: true,
+ AllowListEnabled: false,
+ },
+ },
+ },
+ },
+ },
+ {
+ Changeset: commonchangeset.WrapChangeSet(UpdateFeeQuoterPricesCS),
+ Config: UpdateFeeQuoterPricesConfig{
+ PricesByChain: map[uint64]FeeQuoterPriceUpdatePerSource{
+ source: {
+ TokenPrices: map[common.Address]*big.Int{
+ sourceState.LinkToken.Address(): DefaultLinkPrice,
+ sourceState.Weth9.Address(): DefaultWethPrice,
+ },
+ GasPrices: map[uint64]*big.Int{
+ dest: DefaultGasPrice,
+ },
+ },
+ },
+ },
+ },
+ {
+ Changeset: commonchangeset.WrapChangeSet(UpdateFeeQuoterDests),
+ Config: UpdateFeeQuoterDestsConfig{
+ UpdatesByChain: map[uint64]map[uint64]fee_quoter.FeeQuoterDestChainConfig{
+ source: {
+ dest: DefaultFeeQuoterDestChainConfig(),
+ },
+ },
+ },
+ },
+ {
+ Changeset: commonchangeset.WrapChangeSet(UpdateOffRampSources),
+ Config: UpdateOffRampSourcesConfig{
+ UpdatesByChain: map[uint64]map[uint64]OffRampSourceUpdate{
+ dest: {
+ source: {
+ IsEnabled: true,
+ },
+ },
+ },
+ },
+ },
+ {
+ Changeset: commonchangeset.WrapChangeSet(UpdateRouterRamps),
+ Config: UpdateRouterRampsConfig{
+ UpdatesByChain: map[uint64]RouterUpdates{
+ // onRamp update on source chain
+ source: {
+ OnRampUpdates: map[uint64]bool{
+ dest: true,
+ },
+ },
+ // offramp update on dest chain
+ dest: {
+ OffRampUpdates: map[uint64]bool{
+ source: true,
+ },
+ },
+ },
+ },
+ },
+ })
+ require.NoError(t, err)
+
+ // check that source router has dest enabled
+ onRamp, err := sourceState.Router.GetOnRamp(&bind.CallOpts{
+ Context: testcontext.Get(t),
+ }, dest)
+ require.NoError(t, err)
+ require.NotEqual(t, common.HexToAddress("0x0"), onRamp, "expected onRamp to be set")
+
+ // Transfer ownership so that we can set new candidate configs
+ // and set new config digest on the offramp.
+ _, err = commonchangeset.ApplyChangesets(t, tenv.Env, tenv.TimelockContracts(t), []commonchangeset.ChangesetApplication{
+ {
+ Changeset: commonchangeset.WrapChangeSet(commonchangeset.TransferToMCMSWithTimelock),
+ Config: genTestTransferOwnershipConfig(tenv, allChains, state),
+ },
+ })
+ require.NoError(t, err)
+ assertTimelockOwnership(t, tenv, allChains, state)
+
+ sendMsg := func() {
+ latesthdr, err := tenv.Env.Chains[dest].Client.HeaderByNumber(testcontext.Get(t), nil)
+ require.NoError(t, err)
+ block := latesthdr.Number.Uint64()
+ msgSentEvent := TestSendRequest(t, tenv.Env, state, source, dest, false, router.ClientEVM2AnyMessage{
+ Receiver: common.LeftPadBytes(state.Chains[dest].Receiver.Address().Bytes(), 32),
+ Data: []byte("hello world"),
+ TokenAmounts: nil,
+ FeeToken: common.HexToAddress("0x0"),
+ ExtraArgs: nil,
+ })
+
+ var (
+ startBlocks = map[uint64]*uint64{
+ dest: &block,
+ }
+ expectedSeqNum = map[SourceDestPair]uint64{
+ {
+ SourceChainSelector: source,
+ DestChainSelector: dest,
+ }: msgSentEvent.SequenceNumber,
+ }
+ expectedSeqNumExec = map[SourceDestPair][]uint64{
+ {
+ SourceChainSelector: source,
+ DestChainSelector: dest,
+ }: {msgSentEvent.SequenceNumber},
+ }
+ )
+
+ // Confirm execution of the message
+ ConfirmCommitForAllWithExpectedSeqNums(t, tenv.Env, state, expectedSeqNum, startBlocks)
+ ConfirmExecWithSeqNrsForAll(t, tenv.Env, state, expectedSeqNumExec, startBlocks)
+ }
+
+ // send a message from source to dest and ensure that it gets executed
+ sendMsg()
+
+ var (
+ capReg = state.Chains[tenv.HomeChainSel].CapabilityRegistry
+ ccipHome = state.Chains[tenv.HomeChainSel].CCIPHome
+ )
+ donID, err := internal.DonIDForChain(capReg, ccipHome, dest)
+ require.NoError(t, err)
+ candidateDigestCommitBefore, err := ccipHome.GetCandidateDigest(&bind.CallOpts{
+ Context: testcontext.Get(t),
+ }, donID, uint8(types.PluginTypeCCIPCommit))
+ require.NoError(t, err)
+ require.Equal(t, [32]byte{}, candidateDigestCommitBefore)
+ candidateDigestExecBefore, err := ccipHome.GetCandidateDigest(&bind.CallOpts{
+ Context: testcontext.Get(t),
+ }, donID, uint8(types.PluginTypeCCIPExec))
+ require.NoError(t, err)
+ require.Equal(t, [32]byte{}, candidateDigestExecBefore)
+
+ // Now we can add a candidate config, send another request, and observe behavior.
+ // The candidate config should not be able to execute messages.
+ tokenConfig := NewTestTokenConfig(state.Chains[tenv.FeedChainSel].USDFeeds)
+ _, err = commonchangeset.ApplyChangesets(t, tenv.Env, tenv.TimelockContracts(t), []commonchangeset.ChangesetApplication{
+ {
+ Changeset: commonchangeset.WrapChangeSet(SetCandidateChangeset),
+ Config: SetCandidateChangesetConfig{
+ SetCandidateConfigBase: SetCandidateConfigBase{
+ HomeChainSelector: tenv.HomeChainSel,
+ FeedChainSelector: tenv.FeedChainSel,
+ // NOTE: this is technically not a new chain, but needed for validation.
+ OCRConfigPerRemoteChainSelector: map[uint64]CCIPOCRParams{
+ dest: DefaultOCRParams(
+ tenv.FeedChainSel,
+ tokenConfig.GetTokenInfo(logger.TestLogger(t), state.Chains[dest].LinkToken, state.Chains[dest].Weth9),
+ nil,
+ ),
+ },
+ PluginType: types.PluginTypeCCIPCommit,
+ MCMS: &MCMSConfig{
+ MinDelay: 0,
+ },
+ },
+ },
+ },
+ {
+ Changeset: commonchangeset.WrapChangeSet(SetCandidateChangeset),
+ Config: SetCandidateChangesetConfig{
+ SetCandidateConfigBase: SetCandidateConfigBase{
+ HomeChainSelector: tenv.HomeChainSel,
+ FeedChainSelector: tenv.FeedChainSel,
+ // NOTE: this is technically not a new chain, but needed for validation.
+ OCRConfigPerRemoteChainSelector: map[uint64]CCIPOCRParams{
+ dest: DefaultOCRParams(
+ tenv.FeedChainSel,
+ tokenConfig.GetTokenInfo(logger.TestLogger(t), state.Chains[dest].LinkToken, state.Chains[dest].Weth9),
+ nil,
+ ),
+ },
+ PluginType: types.PluginTypeCCIPExec,
+ MCMS: &MCMSConfig{
+ MinDelay: 0,
+ },
+ },
+ },
+ },
+ })
+ require.NoError(t, err)
+
+ // check that CCIPHome state is updated with the new candidate configs
+ // for the dest chain DON.
+ candidateDigestCommit, err := ccipHome.GetCandidateDigest(&bind.CallOpts{
+ Context: testcontext.Get(t),
+ }, donID, uint8(types.PluginTypeCCIPCommit))
+ require.NoError(t, err)
+ require.NotEqual(t, candidateDigestCommit, candidateDigestCommitBefore)
+ candidateDigestExec, err := ccipHome.GetCandidateDigest(&bind.CallOpts{
+ Context: testcontext.Get(t),
+ }, donID, uint8(types.PluginTypeCCIPExec))
+ require.NoError(t, err)
+ require.NotEqual(t, candidateDigestExec, candidateDigestExecBefore)
+
+ // send a message from source to dest and ensure that it gets executed after the candidate config is set
+ sendMsg()
+}
From 182575a823d8e011f4a03a8a69d5498ef5346019 Mon Sep 17 00:00:00 2001
From: joaoluisam
Date: Fri, 10 Jan 2025 10:05:09 +0000
Subject: [PATCH 27/91] Add soneium config (#15883)
* Add soneium config
* Add changeset
---
.changeset/silver-books-grab.md | 5 +
.../config/toml/defaults/Soneium_Mainnet.toml | 36 ++++++
docs/CONFIG.md | 110 ++++++++++++++++++
3 files changed, 151 insertions(+)
create mode 100644 .changeset/silver-books-grab.md
create mode 100644 core/chains/evm/config/toml/defaults/Soneium_Mainnet.toml
diff --git a/.changeset/silver-books-grab.md b/.changeset/silver-books-grab.md
new file mode 100644
index 00000000000..2aa20e97f27
--- /dev/null
+++ b/.changeset/silver-books-grab.md
@@ -0,0 +1,5 @@
+---
+"chainlink": patch
+---
+
+#add #nops Add soneium config
diff --git a/core/chains/evm/config/toml/defaults/Soneium_Mainnet.toml b/core/chains/evm/config/toml/defaults/Soneium_Mainnet.toml
new file mode 100644
index 00000000000..9dd633123cb
--- /dev/null
+++ b/core/chains/evm/config/toml/defaults/Soneium_Mainnet.toml
@@ -0,0 +1,36 @@
+ChainID = '1868'
+ChainType = 'optimismBedrock'
+LinkContractAddress = '0x7ea13478Ea3961A0e8b538cb05a9DF0477c79Cd2'
+FinalityDepth = 200
+LogPollInterval = '2s'
+NoNewHeadsThreshold = '40s'
+MinIncomingConfirmations = 1
+NoNewFinalizedHeadsThreshold = '120m' # Soneium can take upto 2Hours to finalize
+FinalityTagEnabled = true
+
+[GasEstimator]
+EIP1559DynamicFees = true
+PriceMin = '1 wei'
+BumpMin = '1 mwei'
+
+[GasEstimator.BlockHistory]
+BlockHistorySize = 60
+
+[GasEstimator.DAOracle]
+OracleType = 'opstack'
+OracleAddress = '0x420000000000000000000000000000000000000F'
+
+[Transactions]
+ResendAfterThreshold = '30s'
+
+[HeadTracker]
+HistoryDepth = 300
+
+[NodePool]
+SyncThreshold = 10
+
+[OCR]
+ContractConfirmations = 1
+
+[OCR2.Automation]
+GasLimit = 6500000
diff --git a/docs/CONFIG.md b/docs/CONFIG.md
index fe55f4ad293..6f50a7ab16e 100644
--- a/docs/CONFIG.md
+++ b/docs/CONFIG.md
@@ -5933,6 +5933,116 @@ GasLimitDefault = 400000
+Soneium Sepolia (1946)
```toml
From c57f910327ad2a0cb104a156b289f75b5bb7d972 Mon Sep 17 00:00:00 2001
From: amit-momin <108959691+amit-momin@users.noreply.github.com>
Date: Fri, 10 Jan 2025 05:09:43 -0600
Subject: [PATCH 28/91] Update abandon tx functionality to drop related
attempts (#15616)
* Updated abandon tx to drop related attemtps
* Added changeset
---
.changeset/gorgeous-ants-promise.md | 5 +++
core/chains/evm/txmgr/evm_tx_store.go | 15 +++++--
core/chains/evm/txmgr/evm_tx_store_test.go | 50 +++++++++++++++++++++-
3 files changed, 65 insertions(+), 5 deletions(-)
create mode 100644 .changeset/gorgeous-ants-promise.md
diff --git a/.changeset/gorgeous-ants-promise.md b/.changeset/gorgeous-ants-promise.md
new file mode 100644
index 00000000000..117bc9a85a9
--- /dev/null
+++ b/.changeset/gorgeous-ants-promise.md
@@ -0,0 +1,5 @@
+---
+"chainlink": patch
+---
+
+Updated TXM abandon transaction functionality to drop related attempts. #updated
diff --git a/core/chains/evm/txmgr/evm_tx_store.go b/core/chains/evm/txmgr/evm_tx_store.go
index 95756790cf3..66e0cc84971 100644
--- a/core/chains/evm/txmgr/evm_tx_store.go
+++ b/core/chains/evm/txmgr/evm_tx_store.go
@@ -1786,8 +1786,17 @@ func (o *evmTxStore) Abandon(ctx context.Context, chainID *big.Int, addr common.
var cancel context.CancelFunc
ctx, cancel = o.stopCh.Ctx(ctx)
defer cancel()
- _, err := o.q.ExecContext(ctx, `UPDATE evm.txes SET state='fatal_error', nonce = NULL, error = 'abandoned' WHERE state IN ('unconfirmed', 'in_progress', 'unstarted') AND evm_chain_id = $1 AND from_address = $2`, chainID.String(), addr)
- return err
+ return o.Transact(ctx, false, func(orm *evmTxStore) error {
+ var abandonedIDs []string
+ err := orm.q.SelectContext(ctx, &abandonedIDs, `UPDATE evm.txes SET state='fatal_error', nonce = NULL, error = 'abandoned' WHERE state IN ('unconfirmed', 'in_progress', 'unstarted') AND evm_chain_id = $1 AND from_address = $2 RETURNING id`, chainID.String(), addr)
+ if err != nil {
+ return fmt.Errorf("failed to mark transactions as abandoned: %w", err)
+ }
+ if _, err := orm.q.ExecContext(ctx, `DELETE FROM evm.tx_attempts WHERE eth_tx_id = ANY($1)`, pq.Array(abandonedIDs)); err != nil {
+ return fmt.Errorf("failed to delete attempts related to abandoned transactions: %w", err)
+ }
+ return nil
+ })
}
// Find transactions by a field in the TxMeta blob and transaction states
@@ -1916,7 +1925,7 @@ func (o *evmTxStore) FindAttemptsRequiringReceiptFetch(ctx context.Context, chai
query := `
SELECT evm.tx_attempts.* FROM evm.tx_attempts
JOIN evm.txes ON evm.txes.ID = evm.tx_attempts.eth_tx_id
- WHERE evm.tx_attempts.state = 'broadcast' AND evm.txes.state IN ('confirmed', 'confirmed_missing_receipt', 'fatal_error') AND evm.txes.evm_chain_id = $1 AND evm.txes.ID NOT IN (
+ WHERE evm.tx_attempts.state = 'broadcast' AND evm.txes.nonce IS NOT NULL AND evm.txes.state IN ('confirmed', 'confirmed_missing_receipt', 'fatal_error') AND evm.txes.evm_chain_id = $1 AND evm.txes.ID NOT IN (
SELECT DISTINCT evm.txes.ID FROM evm.txes
JOIN evm.tx_attempts ON evm.tx_attempts.eth_tx_id = evm.txes.ID
JOIN evm.receipts ON evm.receipts.tx_hash = evm.tx_attempts.hash
diff --git a/core/chains/evm/txmgr/evm_tx_store_test.go b/core/chains/evm/txmgr/evm_tx_store_test.go
index a05cf3f9010..9f515c22be4 100644
--- a/core/chains/evm/txmgr/evm_tx_store_test.go
+++ b/core/chains/evm/txmgr/evm_tx_store_test.go
@@ -1796,11 +1796,16 @@ func TestORM_FindAttemptsRequiringReceiptFetch(t *testing.T) {
// Terminally stuck transaction with receipt should NOT be picked up for receipt fetch
stuckTx := mustInsertTerminallyStuckTxWithAttempt(t, txStore, fromAddress, 1, blockNum)
mustInsertEthReceipt(t, txStore, blockNum, utils.NewHash(), stuckTx.TxAttempts[0].Hash)
+ // Fatal transactions with nil nonce and stored attempts should NOT be picked up for receipt fetch
+ fatalTxWithAttempt := mustInsertFatalErrorEthTx(t, txStore, fromAddress)
+ attempt := newBroadcastLegacyEthTxAttempt(t, fatalTxWithAttempt.ID)
+ err := txStore.InsertTxAttempt(ctx, &attempt)
+ require.NoError(t, err)
// Confirmed transaction without receipt should be picked up for receipt fetch
confirmedTx := mustInsertConfirmedEthTx(t, txStore, 0, fromAddress)
- attempt := newBroadcastLegacyEthTxAttempt(t, confirmedTx.ID)
- err := txStore.InsertTxAttempt(ctx, &attempt)
+ attempt = newBroadcastLegacyEthTxAttempt(t, confirmedTx.ID)
+ err = txStore.InsertTxAttempt(ctx, &attempt)
require.NoError(t, err)
attempts, err := txStore.FindAttemptsRequiringReceiptFetch(ctx, testutils.FixtureChainID)
@@ -1823,6 +1828,12 @@ func TestORM_FindAttemptsRequiringReceiptFetch(t *testing.T) {
// Terminally stuck transaction with receipt should NOT be picked up for receipt fetch
stuckTxWithReceipt := mustInsertTerminallyStuckTxWithAttempt(t, txStore, fromAddress, 1, blockNum)
mustInsertEthReceipt(t, txStore, blockNum, utils.NewHash(), stuckTxWithReceipt.TxAttempts[0].Hash)
+ // Fatal transactions with nil nonce and stored attempts should NOT be picked up for receipt fetch
+ fatalTxWithAttempt := mustInsertFatalErrorEthTx(t, txStore, fromAddress)
+ attempt := newBroadcastLegacyEthTxAttempt(t, fatalTxWithAttempt.ID)
+ err := txStore.InsertTxAttempt(ctx, &attempt)
+ require.NoError(t, err)
+
// Terminally stuck transaction without receipt should be picked up for receipt fetch
stuckTxWoutReceipt := mustInsertTerminallyStuckTxWithAttempt(t, txStore, fromAddress, 0, blockNum)
@@ -2008,6 +2019,41 @@ func TestORM_DeleteReceiptsByTxHash(t *testing.T) {
require.Len(t, etx2.TxAttempts[0].Receipts, 1)
}
+func TestORM_Abandon(t *testing.T) {
+ t.Parallel()
+
+ db := pgtest.NewSqlxDB(t)
+ txStore := cltest.NewTestTxStore(t, db)
+ ctx := tests.Context(t)
+ ethKeyStore := cltest.NewKeyStore(t, db).Eth()
+ _, fromAddress := cltest.MustInsertRandomKeyReturningState(t, ethKeyStore)
+ etx1 := mustCreateUnstartedGeneratedTx(t, txStore, fromAddress, testutils.FixtureChainID)
+ etx2 := mustInsertInProgressEthTxWithAttempt(t, txStore, 1, fromAddress)
+ etx3 := mustInsertUnconfirmedEthTxWithAttemptState(t, txStore, 0, fromAddress, txmgrtypes.TxAttemptBroadcast)
+
+ err := txStore.Abandon(ctx, testutils.FixtureChainID, fromAddress)
+ require.NoError(t, err)
+
+ // transactions marked as fatal error with abandon reason, nil sequence, and no attempts
+ etx1, err = txStore.FindTxWithAttempts(ctx, etx1.ID)
+ require.NoError(t, err)
+ require.Equal(t, txmgrcommon.TxFatalError, etx1.State)
+ require.Nil(t, etx1.Sequence)
+ require.Empty(t, etx1.TxAttempts)
+
+ etx2, err = txStore.FindTxWithAttempts(ctx, etx2.ID)
+ require.NoError(t, err)
+ require.Equal(t, txmgrcommon.TxFatalError, etx2.State)
+ require.Nil(t, etx2.Sequence)
+ require.Empty(t, etx2.TxAttempts)
+
+ etx3, err = txStore.FindTxWithAttempts(ctx, etx3.ID)
+ require.NoError(t, err)
+ require.Equal(t, txmgrcommon.TxFatalError, etx3.State)
+ require.Nil(t, etx3.Sequence)
+ require.Empty(t, etx3.TxAttempts)
+}
+
func mustInsertTerminallyStuckTxWithAttempt(t *testing.T, txStore txmgr.TestEvmTxStore, fromAddress common.Address, nonceInt int64, broadcastBeforeBlockNum int64) txmgr.Tx {
ctx := tests.Context(t)
broadcast := time.Now()
From 3dcfd1c591939653d184d9b26972a67bf89f8dd2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?=
Date: Fri, 10 Jan 2025 21:24:35 +0900
Subject: [PATCH 29/91] deployment/memory: Solana support (develop) (#15889)
* deployment: memory: Generate more transmitter key types, expose in JD
* deployment: memory: Configure nodes with solana config too
* Use CTF to spin up the solana validator for in-memory tests
* Use autopatchelf on solana binaries to make them usable on NixOS
* memory: solana: Shut down the container when test terminates
* go mod tidy
* Use latest upstream CTF
* Add missing import
* make modgraph
* Use framework.DefaultNetwork()
---
core/scripts/go.mod | 12 +-
core/scripts/go.sum | 73 +++++--
deployment/environment/memory/chain.go | 92 ++++++++-
deployment/environment/memory/environment.go | 8 +-
deployment/environment/memory/job_client.go | 46 +----
deployment/environment/memory/node.go | 205 ++++++++++++++-----
deployment/environment/memory/node_test.go | 2 +-
deployment/go.mod | 15 +-
deployment/go.sum | 31 +--
deployment/solana_chain.go | 56 ++++-
go.md | 4 +
integration-tests/go.mod | 13 +-
integration-tests/go.sum | 37 ++--
integration-tests/load/go.mod | 12 +-
integration-tests/load/go.sum | 27 +--
shell.nix | 50 +++++
16 files changed, 497 insertions(+), 186 deletions(-)
diff --git a/core/scripts/go.mod b/core/scripts/go.mod
index dcef0492207..79addebee3f 100644
--- a/core/scripts/go.mod
+++ b/core/scripts/go.mod
@@ -82,8 +82,8 @@ require (
github.com/btcsuite/btcd/btcec/v2 v2.3.4 // indirect
github.com/buger/jsonparser v1.1.1 // indirect
github.com/bytecodealliance/wasmtime-go/v23 v23.0.0 // indirect
- github.com/bytedance/sonic v1.11.6 // indirect
- github.com/bytedance/sonic/loader v0.1.1 // indirect
+ github.com/bytedance/sonic v1.12.3 // indirect
+ github.com/bytedance/sonic/loader v0.2.0 // indirect
github.com/cenkalti/backoff v2.2.1+incompatible // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cespare/xxhash v1.1.0 // indirect
@@ -135,7 +135,7 @@ require (
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
- github.com/gabriel-vasile/mimetype v1.4.3 // indirect
+ github.com/gabriel-vasile/mimetype v1.4.6 // indirect
github.com/gagliardetto/binary v0.8.0 // indirect
github.com/gagliardetto/solana-go v1.12.0 // indirect
github.com/gagliardetto/treeout v0.1.4 // indirect
@@ -164,7 +164,7 @@ require (
github.com/go-openapi/swag v0.23.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
- github.com/go-playground/validator/v10 v10.22.0 // indirect
+ github.com/go-playground/validator/v10 v10.22.1 // indirect
github.com/go-viper/mapstructure/v2 v2.1.0 // indirect
github.com/go-webauthn/webauthn v0.9.4 // indirect
github.com/go-webauthn/x v0.1.5 // indirect
@@ -251,7 +251,7 @@ require (
github.com/maruel/natural v1.1.1 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
- github.com/mattn/go-runewidth v0.0.14 // indirect
+ github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/mfridman/interpolate v0.0.2 // indirect
github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
@@ -284,7 +284,7 @@ require (
github.com/prometheus/procfs v0.15.1 // indirect
github.com/prometheus/prometheus v0.54.1 // indirect
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
- github.com/rivo/uniseg v0.4.4 // indirect
+ github.com/rivo/uniseg v0.4.7 // indirect
github.com/robfig/cron/v3 v3.0.1 // indirect
github.com/rogpeppe/go-internal v1.13.1 // indirect
github.com/rs/cors v1.10.1 // indirect
diff --git a/core/scripts/go.sum b/core/scripts/go.sum
index 779360c646d..21d260ebdb9 100644
--- a/core/scripts/go.sum
+++ b/core/scripts/go.sum
@@ -67,6 +67,8 @@ cosmossdk.io/math v1.3.0 h1:RC+jryuKeytIiictDslBP9i1fhkVm6ZDmZEoNP316zE=
cosmossdk.io/math v1.3.0/go.mod h1:vnRTxewy+M7BtXBNFybkuhSH4WfedVAAnERHgVFhp3k=
cosmossdk.io/tools/rosetta v0.2.1 h1:ddOMatOH+pbxWbrGJKRAawdBkPYLfKXutK9IETnjYxw=
cosmossdk.io/tools/rosetta v0.2.1/go.mod h1:Pqdc1FdvkNV3LcNIkYWt2RQY6IP1ge6YWZk8MhhO9Hw=
+dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
+dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
@@ -148,6 +150,34 @@ github.com/avast/retry-go/v4 v4.6.0 h1:K9xNA+KeB8HHc2aWFuLb25Offp+0iVRXEvFx8IinR
github.com/avast/retry-go/v4 v4.6.0/go.mod h1:gvWlPhBVsvBbLkVGDg/KwvBv0bEkCOLRRSHKIr2PyOE=
github.com/aws/aws-sdk-go v1.54.19 h1:tyWV+07jagrNiCcGRzRhdtVjQs7Vy41NwsuOcl0IbVI=
github.com/aws/aws-sdk-go v1.54.19/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
+github.com/aws/aws-sdk-go-v2 v1.32.2 h1:AkNLZEyYMLnx/Q/mSKkcMqwNFXMAvFto9bNsHqcTduI=
+github.com/aws/aws-sdk-go-v2 v1.32.2/go.mod h1:2SK5n0a2karNTv5tbP1SjsX0uhttou00v/HpXKM1ZUo=
+github.com/aws/aws-sdk-go-v2/config v1.28.0 h1:FosVYWcqEtWNxHn8gB/Vs6jOlNwSoyOCA/g/sxyySOQ=
+github.com/aws/aws-sdk-go-v2/config v1.28.0/go.mod h1:pYhbtvg1siOOg8h5an77rXle9tVG8T+BWLWAo7cOukc=
+github.com/aws/aws-sdk-go-v2/credentials v1.17.41 h1:7gXo+Axmp+R4Z+AK8YFQO0ZV3L0gizGINCOWxSLY9W8=
+github.com/aws/aws-sdk-go-v2/credentials v1.17.41/go.mod h1:u4Eb8d3394YLubphT4jLEwN1rLNq2wFOlT6OuxFwPzU=
+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.17 h1:TMH3f/SCAWdNtXXVPPu5D6wrr4G5hI1rAxbcocKfC7Q=
+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.17/go.mod h1:1ZRXLdTpzdJb9fwTMXiLipENRxkGMTn1sfKexGllQCw=
+github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.21 h1:UAsR3xA31QGf79WzpG/ixT9FZvQlh5HY1NRqSHBNOCk=
+github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.21/go.mod h1:JNr43NFf5L9YaG3eKTm7HQzls9J+A9YYcGI5Quh1r2Y=
+github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.21 h1:6jZVETqmYCadGFvrYEQfC5fAQmlo80CeL5psbno6r0s=
+github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.21/go.mod h1:1SR0GbLlnN3QUmYaflZNiH1ql+1qrSiB2vwcJ+4UM60=
+github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ=
+github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc=
+github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.0 h1:TToQNkvGguu209puTojY/ozlqy2d/SFNcoLIqTFi42g=
+github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.0/go.mod h1:0jp+ltwkf+SwG2fm/PKo8t4y8pJSgOCO4D8Lz3k0aHQ=
+github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.2 h1:s7NA1SOw8q/5c0wr8477yOPp0z+uBaXBnLE0XYb0POA=
+github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.2/go.mod h1:fnjjWyAW/Pj5HYOxl9LJqWtEwS7W2qgcRLWP+uWbss0=
+github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.34.2 h1:Rrqru2wYkKQCS2IM5/JrgKUQIoNTqA6y/iuxkjzxC6M=
+github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.34.2/go.mod h1:QuCURO98Sqee2AXmqDNxKXYFm2OEDAVAPApMqO0Vqnc=
+github.com/aws/aws-sdk-go-v2/service/sso v1.24.2 h1:bSYXVyUzoTHoKalBmwaZxs97HU9DWWI3ehHSAMa7xOk=
+github.com/aws/aws-sdk-go-v2/service/sso v1.24.2/go.mod h1:skMqY7JElusiOUjMJMOv1jJsP7YUg7DrhgqZZWuzu1U=
+github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.2 h1:AhmO1fHINP9vFYUE0LHzCWg/LfUWUF+zFPEcY9QXb7o=
+github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.2/go.mod h1:o8aQygT2+MVP0NaV6kbdE1YnnIM8RRVQzoeUH45GOdI=
+github.com/aws/aws-sdk-go-v2/service/sts v1.32.2 h1:CiS7i0+FUe+/YY1GvIBLLrR/XNGZ4CtM1Ll0XavNuVo=
+github.com/aws/aws-sdk-go-v2/service/sts v1.32.2/go.mod h1:HtaiBI8CjYoNVde8arShXb94UbQQi9L4EMr6D+xGBwo=
+github.com/aws/smithy-go v1.22.0 h1:uunKnWlcoL3zO7q+gG2Pk53joueEOsnNB28QdMsmiMM=
+github.com/aws/smithy-go v1.22.0/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59 h1:WWB576BN5zNSZc/M9d/10pqEx5VHNhaQ/yOVAkmj5Yo=
github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I=
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
@@ -184,10 +214,11 @@ github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMU
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
github.com/bytecodealliance/wasmtime-go/v23 v23.0.0 h1:NJvU4S8KEk1GnF6+FvlnzMD/8wXTj/mYJSG6Q4yu3Pw=
github.com/bytecodealliance/wasmtime-go/v23 v23.0.0/go.mod h1:5YIL+Ouiww2zpO7u+iZ1U1G5NvmwQYaXdmCZQGjQM0U=
-github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
-github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
-github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
+github.com/bytedance/sonic v1.12.3 h1:W2MGa7RCU1QTeYRTPE3+88mVC0yXmsRQRChiyVocVjU=
+github.com/bytedance/sonic v1.12.3/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
+github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM=
+github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
@@ -254,6 +285,8 @@ github.com/containerd/continuity v0.4.3 h1:6HVkalIp+2u1ZLH1J/pYX2oBVXlJZvh1X1A7b
github.com/containerd/continuity v0.4.3/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ=
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
+github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A=
+github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
@@ -284,6 +317,8 @@ github.com/cosmos/ledger-cosmos-go v0.12.4 h1:drvWt+GJP7Aiw550yeb3ON/zsrgW0jgh5s
github.com/cosmos/ledger-cosmos-go v0.12.4/go.mod h1:fjfVWRf++Xkygt9wzCsjEBdjcf7wiiY35fv3ctT+k4M=
github.com/cosmos/rosetta-sdk-go v0.10.0 h1:E5RhTruuoA7KTIXUcMicL76cffyeoyvNybzUGSKFTcM=
github.com/cosmos/rosetta-sdk-go v0.10.0/go.mod h1:SImAZkb96YbwvoRkzSMQB6noNJXFgWl/ENIznEoYQI4=
+github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA=
+github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
@@ -378,8 +413,8 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
-github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
-github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
+github.com/gabriel-vasile/mimetype v1.4.6 h1:3+PzJTKLkvgjeTbts6msPJt4DixhT4YtFNf1gtGe3zc=
+github.com/gabriel-vasile/mimetype v1.4.6/go.mod h1:JX1qVKqZd40hUPpAfiNTe0Sne7hdfKSbOqqmkq8GCXc=
github.com/gagliardetto/binary v0.8.0 h1:U9ahc45v9HW0d15LoN++vIXSJyqR/pWw8DDlhd7zvxg=
github.com/gagliardetto/binary v0.8.0/go.mod h1:2tfj51g5o9dnvsc+fL3Jxr22MuWzYXwx9wEoN0XQ7/c=
github.com/gagliardetto/gofuzz v1.2.2 h1:XL/8qDMzcgvR4+CyRQW9UGdwPRPMHVJfqQ/uMvSUuQw=
@@ -466,8 +501,8 @@ github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
-github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao=
-github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
+github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA=
+github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
@@ -846,6 +881,7 @@ github.com/linxGnu/grocksdb v1.7.16 h1:Q2co1xrpdkr5Hx3Fp+f+f7fRGhQFQhvi/+226dtLm
github.com/linxGnu/grocksdb v1.7.16/go.mod h1:JkS7pl5qWpGpuVb3bPqTz8nC12X3YtPZT+Xq7+QfQo4=
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
+github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
@@ -877,8 +913,8 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
-github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
-github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
+github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
+github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
@@ -916,6 +952,14 @@ github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqky
github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU=
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
+github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
+github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
+github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU=
+github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko=
+github.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo=
+github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
+github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g=
+github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -1043,8 +1087,8 @@ github.com/regen-network/protobuf v1.3.3-alpha.regen.1/go.mod h1:2DjTFR1HhMQhiWC
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
-github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
-github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
+github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
+github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
@@ -1088,6 +1132,7 @@ github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKl
github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shirou/gopsutil/v3 v3.24.3 h1:eoUGJSmdfLzJ3mxIhmOAhgKEKgQkeOwKpz1NbhVnuPE=
github.com/shirou/gopsutil/v3 v3.24.3/go.mod h1:JpND7O217xa72ewWz9zN2eIIkPWsDN/3pl0H8Qt0uwg=
+github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
@@ -1133,6 +1178,8 @@ github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241223151630-eac4f1508dc
github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241223151630-eac4f1508dce/go.mod h1:qq+nW0JDnCCGMf2c38ZHjH8xgkAQnXKighjJr5JdDNE=
github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241202202529-2033490e77b8 h1:tNS7U9lrxkFvEuyxQv11HHOiV9LPDGC9wYEy+yM/Jv4=
github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241202202529-2033490e77b8/go.mod h1:EBrEgcdIbwepqguClkv8Ohy7CbyWSJaE4EC9aBJlQK0=
+github.com/smartcontractkit/chainlink-testing-framework/framework v0.4.2-0.20250110073248-456673e8eea2 h1:nTUoe7GZLw17nPLV5t3Vgf4U4pf+VW0Uko5xpNiKdKU=
+github.com/smartcontractkit/chainlink-testing-framework/framework v0.4.2-0.20250110073248-456673e8eea2/go.mod h1:mMUqvS3BZfvN1OfK4OFTYf1+T0X6nwmSXJM2keaPsSM=
github.com/smartcontractkit/chainlink-testing-framework/lib v1.50.13 h1:T0kbw07Vb6xUyA9MIJZfErMgWseWi1zf7cYvRpoq7ug=
github.com/smartcontractkit/chainlink-testing-framework/lib v1.50.13/go.mod h1:1CKUOzoK+Ga19WuhRH9pxZ+qUUnrlIx108VEA6qSzeQ=
github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7 h1:12ijqMM9tvYVEm+nR826WsrNi6zCKpwBhuApq127wHs=
@@ -1212,6 +1259,8 @@ github.com/tendermint/go-amino v0.16.0 h1:GyhmgQKvqF82e2oZeuMSp9JTN0N09emoSZlb2l
github.com/tendermint/go-amino v0.16.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME=
github.com/test-go/testify v1.1.4 h1:Tf9lntrKUMHiXQ07qBScBTSA0dhYQlu83hswqelv1iE=
github.com/test-go/testify v1.1.4/go.mod h1:rH7cfJo/47vWGdi4GPj16x3/t1xGOj2YxzmNQzk2ghU=
+github.com/testcontainers/testcontainers-go v0.34.0 h1:5fbgF0vIN5u+nD3IWabQwRybuB4GY8G2HHgCkbMzMHo=
+github.com/testcontainers/testcontainers-go v0.34.0/go.mod h1:6P/kMkQe8yqPHfPWNulFGdFHTD8HB2vLq/231xY2iPQ=
github.com/theodesp/go-heaps v0.0.0-20190520121037-88e35354fe0a h1:YuO+afVc3eqrjiCUizNCxI53bl/BnPiVwXqLzqYTqgU=
github.com/theodesp/go-heaps v0.0.0-20190520121037-88e35354fe0a/go.mod h1:/sfW47zCZp9FrtGcWyo1VjbgDaodxX9ovZvgLb/MxaA=
github.com/tidwall/btree v1.6.0 h1:LDZfKfQIBHGHWSwckhXI0RPSXzlo+KYdjK7FWSqOzzg=
@@ -1380,7 +1429,6 @@ go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
-golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.11.0 h1:KXV8WWKCXm6tRpLirl2szsO5j/oOODwZf4hATmGVNs4=
golang.org/x/arch v0.11.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@@ -1915,7 +1963,6 @@ nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYm
pgregory.net/rapid v1.1.0 h1:CMa0sjHSru3puNx+J0MIAuiiEV4N0qj8/cMWGBBCsjw=
pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
-rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU=
diff --git a/deployment/environment/memory/chain.go b/deployment/environment/memory/chain.go
index 193def7ba08..d3aa3a614f4 100644
--- a/deployment/environment/memory/chain.go
+++ b/deployment/environment/memory/chain.go
@@ -1,8 +1,14 @@
package memory
import (
+ "encoding/json"
"math/big"
+ "os"
+ "path"
+ "strconv"
+ "sync"
"testing"
+ "time"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
@@ -11,14 +17,20 @@ import (
"github.com/ethereum/go-ethereum/ethclient/simulated"
"github.com/gagliardetto/solana-go"
solRpc "github.com/gagliardetto/solana-go/rpc"
-
+ "github.com/hashicorp/consul/sdk/freeport"
"github.com/stretchr/testify/require"
+ "github.com/testcontainers/testcontainers-go"
solTestUtil "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/testutils"
chainsel "github.com/smartcontractkit/chain-selectors"
"github.com/smartcontractkit/chainlink-common/pkg/utils/tests"
+
+ chainselectors "github.com/smartcontractkit/chain-selectors"
+
+ "github.com/smartcontractkit/chainlink-testing-framework/framework"
+ "github.com/smartcontractkit/chainlink-testing-framework/framework/components/blockchain"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets"
)
@@ -30,7 +42,9 @@ type EVMChain struct {
type SolanaChain struct {
Client *solRpc.Client
- DeployerKey *solana.PrivateKey
+ URL string
+ WSURL string
+ DeployerKey solana.PrivateKey
}
func fundAddress(t *testing.T, from *bind.TransactOpts, to common.Address, amount *big.Int, backend *simulated.Backend) {
@@ -80,13 +94,12 @@ func GenerateChainsSol(t *testing.T, numChains int) map[uint64]SolanaChain {
chains := make(map[uint64]SolanaChain)
for i := 0; i < numChains; i++ {
chainID := testSolanaChainSelectors[i]
- url, _ := solTestUtil.SetupLocalSolNodeWithFlags(t)
- admin, gerr := solana.NewRandomPrivateKey()
- solTestUtil.FundTestAccounts(t, []solana.PublicKey{admin.PublicKey()}, url)
- require.NoError(t, gerr)
+ solChain := solChain(t)
+ admin := solChain.DeployerKey
+ solTestUtil.FundTestAccounts(t, []solana.PublicKey{admin.PublicKey()}, solChain.URL)
chains[chainID] = SolanaChain{
- Client: solRpc.New(url),
- DeployerKey: &admin,
+ Client: solChain.Client,
+ DeployerKey: solChain.DeployerKey,
}
}
return chains
@@ -126,3 +139,66 @@ func evmChain(t *testing.T, numUsers int) EVMChain {
Users: users,
}
}
+
+var once = &sync.Once{}
+
+func solChain(t *testing.T) SolanaChain {
+ t.Helper()
+
+ // initialize the docker network used by CTF
+ err := framework.DefaultNetwork(once)
+ require.NoError(t, err)
+
+ deployerKey, err := solana.NewRandomPrivateKey()
+ require.NoError(t, err)
+
+ t.TempDir()
+ // store the generated keypair somewhere
+ bytes, err := json.Marshal([]byte(deployerKey))
+ require.NoError(t, err)
+ keypairPath := path.Join(t.TempDir(), "solana-keypair.json")
+ err = os.WriteFile(keypairPath, bytes, 0600)
+ require.NoError(t, err)
+
+ port := freeport.GetOne(t)
+
+ bcInput := &blockchain.Input{
+ Type: "solana",
+ ChainID: chainselectors.SOLANA_DEVNET.ChainID,
+ PublicKey: deployerKey.PublicKey().String(),
+ Port: strconv.Itoa(port),
+ // TODO: ContractsDir & SolanaPrograms via env vars
+ }
+ output, err := blockchain.NewBlockchainNetwork(bcInput)
+ require.NoError(t, err)
+ testcontainers.CleanupContainer(t, output.Container)
+
+ url := output.Nodes[0].HostHTTPUrl
+ wsURL := output.Nodes[0].HostWSUrl
+
+ // Wait for api server to boot
+ client := solRpc.New(url)
+ var ready bool
+ for i := 0; i < 30; i++ {
+ time.Sleep(time.Second)
+ out, err := client.GetHealth(tests.Context(t))
+ if err != nil || out != solRpc.HealthOk {
+ t.Logf("API server not ready yet (attempt %d)\n", i+1)
+ continue
+ }
+ ready = true
+ break
+ }
+ if !ready {
+ t.Logf("solana-test-validator is not ready after 30 attempts")
+ }
+ require.True(t, ready)
+ t.Logf("solana-test-validator is ready at %s", url)
+
+ return SolanaChain{
+ Client: client,
+ URL: url,
+ WSURL: wsURL,
+ DeployerKey: deployerKey,
+ }
+}
diff --git a/deployment/environment/memory/environment.go b/deployment/environment/memory/environment.go
index 3c5fdc6e779..c9044792834 100644
--- a/deployment/environment/memory/environment.go
+++ b/deployment/environment/memory/environment.go
@@ -128,10 +128,10 @@ func generateMemoryChainSol(t *testing.T, inputs map[uint64]SolanaChain) map[uin
chains[cid] = deployment.SolChain{
Selector: cid,
Client: chain.Client,
- DeployerKey: chain.DeployerKey,
+ DeployerKey: &chain.DeployerKey,
Confirm: func(instructions []solana.Instruction, opts ...solCommomUtil.TxModifier) error {
_, err := solCommomUtil.SendAndConfirm(
- context.Background(), chain.Client, instructions, *chain.DeployerKey, solRpc.CommitmentConfirmed, opts...,
+ context.Background(), chain.Client, instructions, chain.DeployerKey, solRpc.CommitmentConfirmed, opts...,
)
if err != nil {
return err
@@ -153,13 +153,13 @@ func NewNodes(t *testing.T, logLevel zapcore.Level, chains map[uint64]deployment
// since we won't run a bootstrapper and a plugin oracle on the same
// chainlink node in production.
for i := 0; i < numBootstraps; i++ {
- node := NewNode(t, ports[i], chains, logLevel, true /* bootstrap */, registryConfig)
+ node := NewNode(t, ports[i], chains, nil, logLevel, true /* bootstrap */, registryConfig)
nodesByPeerID[node.Keys.PeerID.String()] = *node
// Note in real env, this ID is allocated by JD.
}
for i := 0; i < numNodes; i++ {
// grab port offset by numBootstraps, since above loop also takes some ports.
- node := NewNode(t, ports[numBootstraps+i], chains, logLevel, false /* bootstrap */, registryConfig)
+ node := NewNode(t, ports[numBootstraps+i], chains, nil, logLevel, false /* bootstrap */, registryConfig)
nodesByPeerID[node.Keys.PeerID.String()] = *node
// Note in real env, this ID is allocated by JD.
}
diff --git a/deployment/environment/memory/job_client.go b/deployment/environment/memory/job_client.go
index e44c664b77e..e025ea18fda 100644
--- a/deployment/environment/memory/job_client.go
+++ b/deployment/environment/memory/job_client.go
@@ -5,7 +5,6 @@ import (
"errors"
"fmt"
"slices"
- "strconv"
"strings"
"github.com/ethereum/go-ethereum/common"
@@ -153,49 +152,12 @@ func (j JobClient) ListNodeChainConfigs(ctx context.Context, in *nodev1.ListNode
if !ok {
return nil, fmt.Errorf("node id not found: %s", in.Filter.NodeIds[0])
}
- evmBundle := n.Keys.OCRKeyBundles[chaintype.EVM]
- offpk := evmBundle.OffchainPublicKey()
- cpk := evmBundle.ConfigEncryptionPublicKey()
-
- evmKeyBundle := &nodev1.OCR2Config_OCRKeyBundle{
- BundleId: evmBundle.ID(),
- ConfigPublicKey: common.Bytes2Hex(cpk[:]),
- OffchainPublicKey: common.Bytes2Hex(offpk[:]),
- OnchainSigningAddress: evmBundle.OnChainPublicKey(),
- }
-
var chainConfigs []*nodev1.ChainConfig
- for evmChainID, transmitter := range n.Keys.TransmittersByEVMChainID {
- chainConfigs = append(chainConfigs, &nodev1.ChainConfig{
- Chain: &nodev1.Chain{
- Id: strconv.Itoa(int(evmChainID)),
- Type: nodev1.ChainType_CHAIN_TYPE_EVM,
- },
- AccountAddress: transmitter.String(),
- AdminAddress: transmitter.String(), // TODO: custom address
- Ocr1Config: nil,
- Ocr2Config: &nodev1.OCR2Config{
- Enabled: true,
- IsBootstrap: n.IsBoostrap,
- P2PKeyBundle: &nodev1.OCR2Config_P2PKeyBundle{
- PeerId: n.Keys.PeerID.String(),
- },
- OcrKeyBundle: evmKeyBundle,
- Multiaddr: n.Addr.String(),
- Plugins: nil,
- ForwarderAddress: ptr(""),
- },
- })
- }
for _, selector := range n.Chains {
family, err := chainsel.GetSelectorFamily(selector)
if err != nil {
return nil, err
}
- if family == chainsel.FamilyEVM {
- // already handled above
- continue
- }
// NOTE: this supports non-EVM too
chainID, err := chainsel.GetChainIDFromSelector(selector)
@@ -220,7 +182,6 @@ func (j JobClient) ListNodeChainConfigs(ctx context.Context, in *nodev1.ListNode
}
bundle := n.Keys.OCRKeyBundles[ocrtype]
-
offpk := bundle.OffchainPublicKey()
cpk := bundle.ConfigEncryptionPublicKey()
@@ -245,13 +206,15 @@ func (j JobClient) ListNodeChainConfigs(ctx context.Context, in *nodev1.ListNode
panic(fmt.Sprintf("Unsupported chain family %v", family))
}
+ transmitter := n.Keys.Transmitters[selector]
+
chainConfigs = append(chainConfigs, &nodev1.ChainConfig{
Chain: &nodev1.Chain{
Id: chainID,
Type: ctype,
},
- AccountAddress: "", // TODO: support AccountAddress
- AdminAddress: "",
+ AccountAddress: transmitter,
+ AdminAddress: transmitter,
Ocr1Config: nil,
Ocr2Config: &nodev1.OCR2Config{
Enabled: true,
@@ -266,7 +229,6 @@ func (j JobClient) ListNodeChainConfigs(ctx context.Context, in *nodev1.ListNode
},
})
}
- // TODO: I think we can pull it from the feeds manager.
return &nodev1.ListNodeChainConfigsResponse{
ChainConfigs: chainConfigs,
}, nil
diff --git a/deployment/environment/memory/node.go b/deployment/environment/memory/node.go
index 84f0d2e443f..606c080be94 100644
--- a/deployment/environment/memory/node.go
+++ b/deployment/environment/memory/node.go
@@ -6,6 +6,7 @@ import (
"math/big"
"net"
"net/http"
+ "slices"
"strconv"
"testing"
"time"
@@ -22,6 +23,8 @@ import (
"github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox"
"github.com/smartcontractkit/chainlink-common/pkg/utils/tests"
+ solcfg "github.com/smartcontractkit/chainlink-solana/pkg/solana/config"
+
"github.com/smartcontractkit/chainlink/deployment"
"github.com/smartcontractkit/chainlink/v2/core/capabilities"
@@ -72,14 +75,19 @@ func NewNode(
t *testing.T,
port int, // Port for the P2P V2 listener.
chains map[uint64]deployment.Chain,
+ solchains map[uint64]deployment.SolChain,
logLevel zapcore.Level,
bootstrap bool,
registryConfig deployment.CapabilityRegistryConfig,
) *Node {
evmchains := make(map[uint64]EVMChain)
for _, chain := range chains {
- // we're only mapping evm chains here
- if family, err := chainsel.GetSelectorFamily(chain.Selector); err != nil || family != chainsel.FamilyEVM {
+ family, err := chainsel.GetSelectorFamily(chain.Selector)
+ if err != nil {
+ t.Fatal(err)
+ }
+ // we're only mapping evm chains here, currently this list could also contain non-EVMs, e.g. Aptos
+ if family != chainsel.FamilyEVM {
continue
}
evmChainID, err := chainsel.ChainIdFromSelector(chain.Selector)
@@ -120,11 +128,21 @@ func NewNode(
c.Log.Level = ptr(configv2.LogLevel(logLevel))
- var chainConfigs v2toml.EVMConfigs
+ var evmConfigs v2toml.EVMConfigs
for chainID := range evmchains {
- chainConfigs = append(chainConfigs, createConfigV2Chain(chainID))
+ evmConfigs = append(evmConfigs, createConfigV2Chain(chainID))
}
- c.EVM = chainConfigs
+ c.EVM = evmConfigs
+
+ var solConfigs solcfg.TOMLConfigs
+ for chainID, chain := range solchains {
+ solanaChainID, err := chainsel.GetChainIDFromSelector(chainID)
+ if err != nil {
+ t.Fatal(err)
+ }
+ solConfigs = append(solConfigs, createSolanaChainConfig(solanaChainID, chain))
+ }
+ c.Solana = solConfigs
})
// Set logging.
@@ -164,6 +182,12 @@ func NewNode(
CSAETHKeystore: kStore,
}
+ solanaOpts := chainlink.SolanaFactoryConfig{
+ Keystore: master.Solana(),
+ TOMLConfigs: cfg.SolanaConfigs(),
+ DS: db,
+ }
+
// Build Beholder auth
ctx := tests.Context(t)
require.NoError(t, master.Unlock(ctx, "password"))
@@ -171,14 +195,19 @@ func NewNode(
beholderAuthHeaders, csaPubKeyHex, err := keystore.BuildBeholderAuth(master)
require.NoError(t, err)
- // Build relayer factory with EVM.
+ loopRegistry := plugins.NewLoopRegistry(lggr.Named("LoopRegistry"), cfg.Tracing(), cfg.Telemetry(), beholderAuthHeaders, csaPubKeyHex)
+
+ // Build relayer factory
relayerFactory := chainlink.RelayerFactory{
Logger: lggr,
- LoopRegistry: plugins.NewLoopRegistry(lggr.Named("LoopRegistry"), cfg.Tracing(), cfg.Telemetry(), beholderAuthHeaders, csaPubKeyHex),
+ LoopRegistry: loopRegistry,
GRPCOpts: loop.GRPCOpts{},
CapabilitiesRegistry: capabilities.NewRegistry(lggr),
}
- initOps := []chainlink.CoreRelayerChainInitFunc{chainlink.InitEVM(context.Background(), relayerFactory, evmOpts)}
+ initOps := []chainlink.CoreRelayerChainInitFunc{
+ chainlink.InitEVM(context.Background(), relayerFactory, evmOpts),
+ chainlink.InitSolana(context.Background(), relayerFactory, solanaOpts),
+ }
rci, err := chainlink.NewCoreRelayerChainInteroperators(initOps...)
require.NoError(t, err)
@@ -194,17 +223,20 @@ func NewNode(
RestrictedHTTPClient: &http.Client{},
AuditLogger: audit.NoopLogger,
MailMon: mailMon,
- LoopRegistry: plugins.NewLoopRegistry(lggr, cfg.Tracing(), cfg.Telemetry(), beholderAuthHeaders, csaPubKeyHex),
+ LoopRegistry: loopRegistry,
})
require.NoError(t, err)
t.Cleanup(func() {
require.NoError(t, db.Close())
})
- keys := CreateKeys(t, app, chains)
+ keys := CreateKeys(t, app, chains, solchains)
return &Node{
- App: app,
- Chains: maps.Keys(chains),
+ App: app,
+ Chains: slices.Concat(
+ maps.Keys(chains),
+ maps.Keys(solchains),
+ ),
Keys: keys,
Addr: net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: port},
IsBoostrap: bootstrap,
@@ -212,14 +244,17 @@ func NewNode(
}
type Keys struct {
- PeerID p2pkey.PeerID
- CSA csakey.KeyV2
- TransmittersByEVMChainID map[uint64]common.Address
- OCRKeyBundles map[chaintype.ChainType]ocr2key.KeyBundle
+ PeerID p2pkey.PeerID
+ CSA csakey.KeyV2
+ Transmitters map[uint64]string // chainSelector => address
+ OCRKeyBundles map[chaintype.ChainType]ocr2key.KeyBundle
}
func CreateKeys(t *testing.T,
- app chainlink.Application, chains map[uint64]deployment.Chain) Keys {
+ app chainlink.Application,
+ chains map[uint64]deployment.Chain,
+ solchains map[uint64]deployment.SolChain,
+) Keys {
ctx := tests.Context(t)
_, err := app.GetKeyStore().P2P().Create(ctx)
require.NoError(t, err)
@@ -235,7 +270,7 @@ func CreateKeys(t *testing.T,
require.Len(t, p2pIDs, 1)
peerID := p2pIDs[0].PeerID()
// create a transmitter for each chain
- transmitters := make(map[uint64]common.Address)
+ transmitters := make(map[uint64]string)
keybundles := make(map[chaintype.ChainType]ocr2key.KeyBundle)
for _, chain := range chains {
family, err := chainsel.GetSelectorFamily(chain.Selector)
@@ -257,44 +292,100 @@ func CreateKeys(t *testing.T,
panic(fmt.Sprintf("Unsupported chain family %v", family))
}
- keybundle, err := app.GetKeyStore().OCR2().Create(ctx, ctype)
+ err = app.GetKeyStore().OCR2().EnsureKeys(ctx, ctype)
+ require.NoError(t, err)
+ keys, err := app.GetKeyStore().OCR2().GetAllOfType(ctype)
require.NoError(t, err)
+ require.Len(t, keys, 1)
+ keybundle := keys[0]
+
keybundles[ctype] = keybundle
- if family != chainsel.FamilyEVM {
- // TODO: only support EVM transmission keys for now
- continue
+ switch family {
+ case chainsel.FamilyEVM:
+ evmChainID, err := chainsel.ChainIdFromSelector(chain.Selector)
+ require.NoError(t, err)
+
+ cid := new(big.Int).SetUint64(evmChainID)
+ addrs, err2 := app.GetKeyStore().Eth().EnabledAddressesForChain(ctx, cid)
+ require.NoError(t, err2)
+ var transmitter common.Address
+ if len(addrs) == 1 {
+ // just fund the address
+ transmitter = addrs[0]
+ } else {
+ // create key and fund it
+ _, err3 := app.GetKeyStore().Eth().Create(ctx, cid)
+ require.NoError(t, err3, "failed to create key for chain", evmChainID)
+ sendingKeys, err3 := app.GetKeyStore().Eth().EnabledAddressesForChain(ctx, cid)
+ require.NoError(t, err3)
+ require.Len(t, sendingKeys, 1)
+ transmitter = sendingKeys[0]
+ }
+ transmitters[chain.Selector] = transmitter.String()
+
+ backend := chain.Client.(*Backend).Sim
+ fundAddress(t, chain.DeployerKey, transmitter, assets.Ether(1000).ToInt(), backend)
+ // need to look more into it, but it seems like with sim chains nodes are sending txs with 0x from address
+ fundAddress(t, chain.DeployerKey, common.Address{}, assets.Ether(1000).ToInt(), backend)
+ case chainsel.FamilyAptos:
+ err = app.GetKeyStore().Aptos().EnsureKey(ctx)
+ require.NoError(t, err, "failed to create key for aptos")
+
+ keys, err := app.GetKeyStore().Aptos().GetAll()
+ require.NoError(t, err)
+ require.Len(t, keys, 1)
+
+ transmitter := keys[0]
+ transmitters[chain.Selector] = transmitter.ID()
+
+ // TODO: funding
+ case chainsel.FamilyStarknet:
+ err = app.GetKeyStore().StarkNet().EnsureKey(ctx)
+ require.NoError(t, err, "failed to create key for starknet")
+
+ keys, err := app.GetKeyStore().StarkNet().GetAll()
+ require.NoError(t, err)
+ require.Len(t, keys, 1)
+
+ transmitter := keys[0]
+ transmitters[chain.Selector] = transmitter.ID()
+
+ // TODO: funding
+ default:
+ // TODO: other transmission keys unsupported for now
}
+ }
- evmChainID, err := chainsel.ChainIdFromSelector(chain.Selector)
+ for chain := range solchains {
+ ctype := chaintype.Solana
+ err = app.GetKeyStore().OCR2().EnsureKeys(ctx, ctype)
+ require.NoError(t, err)
+ keys, err := app.GetKeyStore().OCR2().GetAllOfType(ctype)
require.NoError(t, err)
+ require.Len(t, keys, 1)
+ keybundle := keys[0]
- cid := big.NewInt(int64(evmChainID))
- addrs, err2 := app.GetKeyStore().Eth().EnabledAddressesForChain(ctx, cid)
- require.NoError(t, err2)
- if len(addrs) == 1 {
- // just fund the address
- transmitters[evmChainID] = addrs[0]
- } else {
- // create key and fund it
- _, err3 := app.GetKeyStore().Eth().Create(ctx, cid)
- require.NoError(t, err3, "failed to create key for chain", evmChainID)
- sendingKeys, err3 := app.GetKeyStore().Eth().EnabledAddressesForChain(ctx, cid)
- require.NoError(t, err3)
- require.Len(t, sendingKeys, 1)
- transmitters[evmChainID] = sendingKeys[0]
- }
- backend := chain.Client.(*Backend).Sim
- fundAddress(t, chain.DeployerKey, transmitters[evmChainID], assets.Ether(1000).ToInt(), backend)
- // need to look more into it, but it seems like with sim chains nodes are sending txs with 0x from address
- fundAddress(t, chain.DeployerKey, common.Address{}, assets.Ether(1000).ToInt(), backend)
+ keybundles[ctype] = keybundle
+
+ err = app.GetKeyStore().Solana().EnsureKey(ctx)
+ require.NoError(t, err, "failed to create key for solana")
+
+ solkeys, err := app.GetKeyStore().Solana().GetAll()
+ require.NoError(t, err)
+ require.Len(t, solkeys, 1)
+
+ transmitter := solkeys[0]
+ transmitters[chain] = transmitter.ID()
+
+ // TODO: funding
}
return Keys{
- PeerID: peerID,
- CSA: csaKey,
- TransmittersByEVMChainID: transmitters,
- OCRKeyBundles: keybundles,
+ PeerID: peerID,
+ CSA: csaKey,
+ Transmitters: transmitters,
+ OCRKeyBundles: keybundles,
}
}
@@ -313,6 +404,28 @@ func createConfigV2Chain(chainID uint64) *v2toml.EVMConfig {
}
}
+func createSolanaChainConfig(chainID string, chain deployment.SolChain) *solcfg.TOMLConfig {
+ chainConfig := solcfg.Chain{}
+ chainConfig.SetDefaults()
+
+ url, err := config.ParseURL(chain.URL)
+ if err != nil {
+ panic(err)
+ }
+
+ return &solcfg.TOMLConfig{
+ ChainID: &chainID,
+ Enabled: ptr(true),
+ Chain: chainConfig,
+ MultiNode: solcfg.MultiNodeConfig{},
+ Nodes: []*solcfg.Node{{
+ Name: ptr("primary"),
+ URL: url,
+ SendOnly: false,
+ }},
+ }
+}
+
func ptr[T any](v T) *T { return &v }
var _ keystore.Eth = &EthKeystoreSim{}
diff --git a/deployment/environment/memory/node_test.go b/deployment/environment/memory/node_test.go
index 78bc2db90e5..b9562f0290a 100644
--- a/deployment/environment/memory/node_test.go
+++ b/deployment/environment/memory/node_test.go
@@ -15,7 +15,7 @@ import (
func TestNode(t *testing.T) {
chains, _ := NewMemoryChains(t, 3, 5)
ports := freeport.GetN(t, 1)
- node := NewNode(t, ports[0], chains, zapcore.DebugLevel, false, deployment.CapabilityRegistryConfig{})
+ node := NewNode(t, ports[0], chains, nil, zapcore.DebugLevel, false, deployment.CapabilityRegistryConfig{})
// We expect 3 transmitter keys
keys, err := node.App.GetKeyStore().Eth().GetAll(tests.Context(t))
require.NoError(t, err)
diff --git a/deployment/go.mod b/deployment/go.mod
index 813acf2ec05..f8875c64544 100644
--- a/deployment/go.mod
+++ b/deployment/go.mod
@@ -33,6 +33,8 @@ require (
github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250103152858-8973fd0c912b
github.com/smartcontractkit/chainlink-common v0.4.1-0.20250108194320-2ebd63bbb16e
github.com/smartcontractkit/chainlink-protos/job-distributor v0.6.0
+ github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241223151630-eac4f1508dce
+ github.com/smartcontractkit/chainlink-testing-framework/framework v0.4.2-0.20250110073248-456673e8eea2
github.com/smartcontractkit/chainlink-testing-framework/lib v1.50.13
github.com/smartcontractkit/libocr v0.0.0-20241223215956-e5b78d8e3919
github.com/stretchr/testify v1.10.0
@@ -117,8 +119,8 @@ require (
github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 // indirect
github.com/buger/jsonparser v1.1.1 // indirect
github.com/bytecodealliance/wasmtime-go/v23 v23.0.0 // indirect
- github.com/bytedance/sonic v1.11.6 // indirect
- github.com/bytedance/sonic/loader v0.1.1 // indirect
+ github.com/bytedance/sonic v1.12.3 // indirect
+ github.com/bytedance/sonic/loader v0.2.0 // indirect
github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b // indirect
github.com/c9s/goprocinfo v0.0.0-20210130143923-c95fcf8c64a8 // indirect
github.com/cdk8s-team/cdk8s-core-go/cdk8s/v2 v2.7.5 // indirect
@@ -188,7 +190,7 @@ require (
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
- github.com/gabriel-vasile/mimetype v1.4.3 // indirect
+ github.com/gabriel-vasile/mimetype v1.4.6 // indirect
github.com/gagliardetto/binary v0.8.0 // indirect
github.com/gagliardetto/treeout v0.1.4 // indirect
github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect
@@ -217,7 +219,7 @@ require (
github.com/go-openapi/validate v0.23.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
- github.com/go-playground/validator/v10 v10.22.0 // indirect
+ github.com/go-playground/validator/v10 v10.22.1 // indirect
github.com/go-redis/redis/v8 v8.11.5 // indirect
github.com/go-viper/mapstructure/v2 v2.1.0 // indirect
github.com/go-webauthn/webauthn v0.9.4 // indirect
@@ -331,7 +333,7 @@ require (
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
- github.com/mattn/go-runewidth v0.0.14 // indirect
+ github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/miekg/dns v1.1.61 // indirect
github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
@@ -387,7 +389,7 @@ require (
github.com/prometheus/procfs v0.15.1 // indirect
github.com/prometheus/prometheus v0.54.1 // indirect
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
- github.com/rivo/uniseg v0.4.4 // indirect
+ github.com/rivo/uniseg v0.4.7 // indirect
github.com/robfig/cron/v3 v3.0.1 // indirect
github.com/rogpeppe/go-internal v1.13.1 // indirect
github.com/rs/cors v1.10.1 // indirect
@@ -411,7 +413,6 @@ require (
github.com/smartcontractkit/chainlink-feeds v0.1.1 // indirect
github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20241220173418-09e17ddbeb20 // indirect
github.com/smartcontractkit/chainlink-protos/orchestrator v0.4.0 // indirect
- github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241223151630-eac4f1508dce // indirect
github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241202202529-2033490e77b8 // indirect
github.com/smartcontractkit/chainlink-testing-framework/lib/grafana v1.50.0 // indirect
github.com/smartcontractkit/chainlink-testing-framework/seth v1.50.5 // indirect
diff --git a/deployment/go.sum b/deployment/go.sum
index d7a410108c0..7e7fb74354d 100644
--- a/deployment/go.sum
+++ b/deployment/go.sum
@@ -281,10 +281,11 @@ github.com/bxcodec/faker v2.0.1+incompatible h1:P0KUpUw5w6WJXwrPfv35oc91i4d8nf40
github.com/bxcodec/faker v2.0.1+incompatible/go.mod h1:BNzfpVdTwnFJ6GtfYTcQu6l6rHShT+veBxNCnjCx5XM=
github.com/bytecodealliance/wasmtime-go/v23 v23.0.0 h1:NJvU4S8KEk1GnF6+FvlnzMD/8wXTj/mYJSG6Q4yu3Pw=
github.com/bytecodealliance/wasmtime-go/v23 v23.0.0/go.mod h1:5YIL+Ouiww2zpO7u+iZ1U1G5NvmwQYaXdmCZQGjQM0U=
-github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
-github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
-github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
+github.com/bytedance/sonic v1.12.3 h1:W2MGa7RCU1QTeYRTPE3+88mVC0yXmsRQRChiyVocVjU=
+github.com/bytedance/sonic v1.12.3/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
+github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM=
+github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b h1:6+ZFm0flnudZzdSE0JxlhR2hKnGPcNB35BjQf4RYQDY=
github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M=
github.com/c9s/goprocinfo v0.0.0-20210130143923-c95fcf8c64a8 h1:SjZ2GvvOononHOpK84APFuMvxqsk3tEIaKH/z4Rpu3g=
@@ -412,8 +413,8 @@ github.com/creachadair/taskgroup v0.4.2 h1:jsBLdAJE42asreGss2xZGZ8fJra7WtwnHWeJF
github.com/creachadair/taskgroup v0.4.2/go.mod h1:qiXUOSrbwAY3u0JPGTzObbE3yf9hcXHDKBZ2ZjpCbgM=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
-github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
-github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
+github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0=
+github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0=
github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0=
github.com/danielkov/gin-helmet v0.0.0-20171108135313-1387e224435e h1:5jVSh2l/ho6ajWhSPNN84eHEdq3dp0T7+f6r3Tc6hsk=
@@ -515,8 +516,8 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
-github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
-github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
+github.com/gabriel-vasile/mimetype v1.4.6 h1:3+PzJTKLkvgjeTbts6msPJt4DixhT4YtFNf1gtGe3zc=
+github.com/gabriel-vasile/mimetype v1.4.6/go.mod h1:JX1qVKqZd40hUPpAfiNTe0Sne7hdfKSbOqqmkq8GCXc=
github.com/gagliardetto/binary v0.8.0 h1:U9ahc45v9HW0d15LoN++vIXSJyqR/pWw8DDlhd7zvxg=
github.com/gagliardetto/binary v0.8.0/go.mod h1:2tfj51g5o9dnvsc+fL3Jxr22MuWzYXwx9wEoN0XQ7/c=
github.com/gagliardetto/gofuzz v1.2.2 h1:XL/8qDMzcgvR4+CyRQW9UGdwPRPMHVJfqQ/uMvSUuQw=
@@ -603,8 +604,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
-github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao=
-github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
+github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA=
+github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
github.com/go-resty/resty/v2 v2.15.3 h1:bqff+hcqAflpiF591hhJzNdkRsFhlB96CYfBwSFvql8=
@@ -1090,8 +1091,8 @@ github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
-github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
-github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
+github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
+github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
@@ -1304,8 +1305,8 @@ github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqn
github.com/regen-network/protobuf v1.3.3-alpha.regen.1 h1:OHEc+q5iIAXpqiqFKeLpu5NwTIkVXUs48vFMwzqpqY4=
github.com/regen-network/protobuf v1.3.3-alpha.regen.1/go.mod h1:2DjTFR1HhMQhiWC5sZ4OhQ3+NtdbZ6oBDKQwq5Ou+FI=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
-github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
-github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
+github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
+github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
@@ -1403,6 +1404,8 @@ github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241223151630-eac4f1508dc
github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241223151630-eac4f1508dce/go.mod h1:qq+nW0JDnCCGMf2c38ZHjH8xgkAQnXKighjJr5JdDNE=
github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241202202529-2033490e77b8 h1:tNS7U9lrxkFvEuyxQv11HHOiV9LPDGC9wYEy+yM/Jv4=
github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241202202529-2033490e77b8/go.mod h1:EBrEgcdIbwepqguClkv8Ohy7CbyWSJaE4EC9aBJlQK0=
+github.com/smartcontractkit/chainlink-testing-framework/framework v0.4.2-0.20250110073248-456673e8eea2 h1:nTUoe7GZLw17nPLV5t3Vgf4U4pf+VW0Uko5xpNiKdKU=
+github.com/smartcontractkit/chainlink-testing-framework/framework v0.4.2-0.20250110073248-456673e8eea2/go.mod h1:mMUqvS3BZfvN1OfK4OFTYf1+T0X6nwmSXJM2keaPsSM=
github.com/smartcontractkit/chainlink-testing-framework/lib v1.50.13 h1:T0kbw07Vb6xUyA9MIJZfErMgWseWi1zf7cYvRpoq7ug=
github.com/smartcontractkit/chainlink-testing-framework/lib v1.50.13/go.mod h1:1CKUOzoK+Ga19WuhRH9pxZ+qUUnrlIx108VEA6qSzeQ=
github.com/smartcontractkit/chainlink-testing-framework/lib/grafana v1.50.0 h1:VIxK8u0Jd0Q/VuhmsNm6Bls6Tb31H/sA3A/rbc5hnhg=
@@ -1678,7 +1681,6 @@ go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
go4.org/netipx v0.0.0-20230125063823-8449b0a6169f h1:ketMxHg+vWm3yccyYiq+uK8D3fRmna2Fcj+awpQp84s=
go4.org/netipx v0.0.0-20230125063823-8449b0a6169f/go.mod h1:tgPU4N2u9RByaTN3NC2p9xOzyFpte4jYwsIIRF7XlSc=
-golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.11.0 h1:KXV8WWKCXm6tRpLirl2szsO5j/oOODwZf4hATmGVNs4=
golang.org/x/arch v0.11.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@@ -2232,7 +2234,6 @@ nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYm
pgregory.net/rapid v1.1.0 h1:CMa0sjHSru3puNx+J0MIAuiiEV4N0qj8/cMWGBBCsjw=
pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
-rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU=
diff --git a/deployment/solana_chain.go b/deployment/solana_chain.go
index ba02e74f892..34410c2d06a 100644
--- a/deployment/solana_chain.go
+++ b/deployment/solana_chain.go
@@ -1,11 +1,16 @@
package deployment
import (
+ "bytes"
"fmt"
+ "os/exec"
"strconv"
+ "strings"
+ "time"
"github.com/gagliardetto/solana-go"
solRpc "github.com/gagliardetto/solana-go/rpc"
+ "github.com/pkg/errors"
solCommomUtil "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/common"
)
@@ -14,11 +19,16 @@ import (
type SolChain struct {
// Selectors used as canonical chain identifier.
Selector uint64
- // RPC cient
+ // RPC client
Client *solRpc.Client
+ URL string
+ WSURL string
// TODO: raw private key for now, need to replace with a more secure way
DeployerKey *solana.PrivateKey
- Confirm func(instructions []solana.Instruction, opts ...solCommomUtil.TxModifier) error
+ // deploy uses the solana CLI which needs a keyfile
+ KeypairPath string
+ ProgramsPath string
+ Confirm func(instructions []solana.Instruction, opts ...solCommomUtil.TxModifier) error
}
func (c SolChain) String() string {
@@ -41,3 +51,45 @@ func (c SolChain) Name() string {
}
return chainInfo.ChainName
}
+
+func (c SolChain) DeployProgram(programName string) (string, error) {
+ programFile := fmt.Sprintf("%s/%s.so", c.ProgramsPath, programName)
+ programKeyPair := fmt.Sprintf("%s/%s-keypair.json", c.ProgramsPath, programName)
+
+ // Construct the CLI command: solana program deploy
+ // TODO: @terry doing this on the fly
+ cmd := exec.Command("solana", "program", "deploy", programFile, "--keypair", c.KeypairPath, "--program-id", programKeyPair)
+
+ // Capture the command output
+ var stdout, stderr bytes.Buffer
+ cmd.Stdout = &stdout
+ cmd.Stderr = &stderr
+
+ // Run the command
+ if err := cmd.Run(); err != nil {
+ return "", fmt.Errorf("error deploying program: %s: %s", err.Error(), stderr.String())
+ }
+
+ // Parse and return the program ID
+ output := stdout.String()
+
+ time.Sleep(5 * time.Second) // obviously need to do this better
+ return parseProgramID(output)
+}
+
+// parseProgramID parses the program ID from the deploy output.
+func parseProgramID(output string) (string, error) {
+ // Look for the program ID in the CLI output
+ // Example output: "Program Id: "
+ const prefix = "Program Id: "
+ startIdx := strings.Index(output, prefix)
+ if startIdx == -1 {
+ return "", errors.New("failed to find program ID in output")
+ }
+ startIdx += len(prefix)
+ endIdx := strings.Index(output[startIdx:], "\n")
+ if endIdx == -1 {
+ endIdx = len(output)
+ }
+ return output[startIdx : startIdx+endIdx], nil
+}
diff --git a/go.md b/go.md
index ed41edee2b0..5a9081747f2 100644
--- a/go.md
+++ b/go.md
@@ -129,6 +129,8 @@ flowchart LR
click chainlink-solana href "https://github.com/smartcontractkit/chainlink-solana"
chainlink-starknet/relayer --> chainlink-common
click chainlink-starknet/relayer href "https://github.com/smartcontractkit/chainlink-starknet"
+ chainlink-testing-framework/framework
+ click chainlink-testing-framework/framework href "https://github.com/smartcontractkit/chainlink-testing-framework"
chainlink-testing-framework/havoc --> chainlink-testing-framework/lib/grafana
click chainlink-testing-framework/havoc href "https://github.com/smartcontractkit/chainlink-testing-framework"
chainlink-testing-framework/lib --> chainlink-testing-framework/seth
@@ -145,6 +147,7 @@ flowchart LR
chainlink/deployment --> ccip-owner-contracts
chainlink/deployment --> chainlink-ccip/chains/solana
chainlink/deployment --> chainlink-protos/job-distributor
+ chainlink/deployment --> chainlink-testing-framework/framework
chainlink/deployment --> chainlink-testing-framework/lib
chainlink/deployment --> chainlink/v2
click chainlink/deployment href "https://github.com/smartcontractkit/chainlink"
@@ -200,6 +203,7 @@ flowchart LR
click chainlink-protos-repo href "https://github.com/smartcontractkit/chainlink-protos"
subgraph chainlink-testing-framework-repo[chainlink-testing-framework]
+ chainlink-testing-framework/framework
chainlink-testing-framework/havoc
chainlink-testing-framework/lib
chainlink-testing-framework/lib/grafana
diff --git a/integration-tests/go.mod b/integration-tests/go.mod
index b5cf870b900..5b3322a4a44 100644
--- a/integration-tests/go.mod
+++ b/integration-tests/go.mod
@@ -137,8 +137,8 @@ require (
github.com/btcsuite/btcd/btcec/v2 v2.3.4 // indirect
github.com/buger/jsonparser v1.1.1 // indirect
github.com/bytecodealliance/wasmtime-go/v23 v23.0.0 // indirect
- github.com/bytedance/sonic v1.11.6 // indirect
- github.com/bytedance/sonic/loader v0.1.1 // indirect
+ github.com/bytedance/sonic v1.12.3 // indirect
+ github.com/bytedance/sonic/loader v0.2.0 // indirect
github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b // indirect
github.com/c9s/goprocinfo v0.0.0-20210130143923-c95fcf8c64a8 // indirect
github.com/cdk8s-team/cdk8s-core-go/cdk8s/v2 v2.7.5 // indirect
@@ -208,7 +208,7 @@ require (
github.com/fatih/color v1.17.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
- github.com/gabriel-vasile/mimetype v1.4.3 // indirect
+ github.com/gabriel-vasile/mimetype v1.4.6 // indirect
github.com/gagliardetto/binary v0.8.0 // indirect
github.com/gagliardetto/solana-go v1.12.0 // indirect
github.com/gagliardetto/treeout v0.1.4 // indirect
@@ -238,7 +238,7 @@ require (
github.com/go-openapi/validate v0.23.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
- github.com/go-playground/validator/v10 v10.22.0 // indirect
+ github.com/go-playground/validator/v10 v10.22.1 // indirect
github.com/go-redis/redis/v8 v8.11.5 // indirect
github.com/go-viper/mapstructure/v2 v2.1.0 // indirect
github.com/go-webauthn/webauthn v0.9.4 // indirect
@@ -351,7 +351,7 @@ require (
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
- github.com/mattn/go-runewidth v0.0.14 // indirect
+ github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/miekg/dns v1.1.61 // indirect
github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
@@ -404,7 +404,7 @@ require (
github.com/prometheus/procfs v0.15.1 // indirect
github.com/prometheus/prometheus v0.54.1 // indirect
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
- github.com/rivo/uniseg v0.4.4 // indirect
+ github.com/rivo/uniseg v0.4.7 // indirect
github.com/robfig/cron/v3 v3.0.1 // indirect
github.com/rogpeppe/go-internal v1.13.1 // indirect
github.com/rs/cors v1.10.1 // indirect
@@ -430,6 +430,7 @@ require (
github.com/smartcontractkit/chainlink-protos/orchestrator v0.4.0 // indirect
github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241223151630-eac4f1508dce // indirect
github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241202202529-2033490e77b8 // indirect
+ github.com/smartcontractkit/chainlink-testing-framework/framework v0.4.2-0.20250110073248-456673e8eea2 // indirect
github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7 // indirect
github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20241009055228-33d0c0bf38de // indirect
github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20241009055228-33d0c0bf38de // indirect
diff --git a/integration-tests/go.sum b/integration-tests/go.sum
index b0f5879cb11..ea4e33c1e7b 100644
--- a/integration-tests/go.sum
+++ b/integration-tests/go.sum
@@ -231,6 +231,8 @@ github.com/aws/smithy-go v1.22.0 h1:uunKnWlcoL3zO7q+gG2Pk53joueEOsnNB28QdMsmiMM=
github.com/aws/smithy-go v1.22.0/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59 h1:WWB576BN5zNSZc/M9d/10pqEx5VHNhaQ/yOVAkmj5Yo=
github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I=
+github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
+github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df h1:GSoSVRLoBaFpOOds6QyY1L8AX7uoY+Ln3BHc22W40X0=
@@ -273,10 +275,11 @@ github.com/bxcodec/faker v2.0.1+incompatible h1:P0KUpUw5w6WJXwrPfv35oc91i4d8nf40
github.com/bxcodec/faker v2.0.1+incompatible/go.mod h1:BNzfpVdTwnFJ6GtfYTcQu6l6rHShT+veBxNCnjCx5XM=
github.com/bytecodealliance/wasmtime-go/v23 v23.0.0 h1:NJvU4S8KEk1GnF6+FvlnzMD/8wXTj/mYJSG6Q4yu3Pw=
github.com/bytecodealliance/wasmtime-go/v23 v23.0.0/go.mod h1:5YIL+Ouiww2zpO7u+iZ1U1G5NvmwQYaXdmCZQGjQM0U=
-github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
-github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
-github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
+github.com/bytedance/sonic v1.12.3 h1:W2MGa7RCU1QTeYRTPE3+88mVC0yXmsRQRChiyVocVjU=
+github.com/bytedance/sonic v1.12.3/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
+github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM=
+github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b h1:6+ZFm0flnudZzdSE0JxlhR2hKnGPcNB35BjQf4RYQDY=
github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M=
github.com/c9s/goprocinfo v0.0.0-20210130143923-c95fcf8c64a8 h1:SjZ2GvvOononHOpK84APFuMvxqsk3tEIaKH/z4Rpu3g=
@@ -416,8 +419,8 @@ github.com/creachadair/taskgroup v0.4.2 h1:jsBLdAJE42asreGss2xZGZ8fJra7WtwnHWeJF
github.com/creachadair/taskgroup v0.4.2/go.mod h1:qiXUOSrbwAY3u0JPGTzObbE3yf9hcXHDKBZ2ZjpCbgM=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
-github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
-github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
+github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0=
+github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0=
github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0=
github.com/danielkov/gin-helmet v0.0.0-20171108135313-1387e224435e h1:5jVSh2l/ho6ajWhSPNN84eHEdq3dp0T7+f6r3Tc6hsk=
@@ -517,8 +520,8 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
-github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
-github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
+github.com/gabriel-vasile/mimetype v1.4.6 h1:3+PzJTKLkvgjeTbts6msPJt4DixhT4YtFNf1gtGe3zc=
+github.com/gabriel-vasile/mimetype v1.4.6/go.mod h1:JX1qVKqZd40hUPpAfiNTe0Sne7hdfKSbOqqmkq8GCXc=
github.com/gagliardetto/binary v0.8.0 h1:U9ahc45v9HW0d15LoN++vIXSJyqR/pWw8DDlhd7zvxg=
github.com/gagliardetto/binary v0.8.0/go.mod h1:2tfj51g5o9dnvsc+fL3Jxr22MuWzYXwx9wEoN0XQ7/c=
github.com/gagliardetto/gofuzz v1.2.2 h1:XL/8qDMzcgvR4+CyRQW9UGdwPRPMHVJfqQ/uMvSUuQw=
@@ -605,8 +608,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
-github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao=
-github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
+github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA=
+github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
github.com/go-resty/resty/v2 v2.15.3 h1:bqff+hcqAflpiF591hhJzNdkRsFhlB96CYfBwSFvql8=
@@ -1100,8 +1103,8 @@ github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
-github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
-github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
+github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
+github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
@@ -1181,8 +1184,8 @@ github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs
github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns=
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
-github.com/muesli/termenv v0.12.0 h1:KuQRUE3PgxRFWhq4gHvZtPSLCGDqM5q/cYr1pZ39ytc=
-github.com/muesli/termenv v0.12.0/go.mod h1:WCCv32tusQ/EEZ5S8oUIIrC/nIuBcxCVqlN4Xfkv+7A=
+github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a h1:2MaM6YC3mGu54x+RKAA6JiFFHlHDY1UbkxqppT7wYOg=
+github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a/go.mod h1:hxSnBBYLK21Vtq/PHd0S2FYCxBXzBua8ov5s1RobyRQ=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
@@ -1322,8 +1325,8 @@ github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqn
github.com/regen-network/protobuf v1.3.3-alpha.regen.1 h1:OHEc+q5iIAXpqiqFKeLpu5NwTIkVXUs48vFMwzqpqY4=
github.com/regen-network/protobuf v1.3.3-alpha.regen.1/go.mod h1:2DjTFR1HhMQhiWC5sZ4OhQ3+NtdbZ6oBDKQwq5Ou+FI=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
-github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
-github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
+github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
+github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
@@ -1425,6 +1428,8 @@ github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241223151630-eac4f1508dc
github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241223151630-eac4f1508dce/go.mod h1:qq+nW0JDnCCGMf2c38ZHjH8xgkAQnXKighjJr5JdDNE=
github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241202202529-2033490e77b8 h1:tNS7U9lrxkFvEuyxQv11HHOiV9LPDGC9wYEy+yM/Jv4=
github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241202202529-2033490e77b8/go.mod h1:EBrEgcdIbwepqguClkv8Ohy7CbyWSJaE4EC9aBJlQK0=
+github.com/smartcontractkit/chainlink-testing-framework/framework v0.4.2-0.20250110073248-456673e8eea2 h1:nTUoe7GZLw17nPLV5t3Vgf4U4pf+VW0Uko5xpNiKdKU=
+github.com/smartcontractkit/chainlink-testing-framework/framework v0.4.2-0.20250110073248-456673e8eea2/go.mod h1:mMUqvS3BZfvN1OfK4OFTYf1+T0X6nwmSXJM2keaPsSM=
github.com/smartcontractkit/chainlink-testing-framework/havoc v1.50.2 h1:GDGrC5OGiV0RyM1znYWehSQXyZQWTOzrEeJRYmysPCE=
github.com/smartcontractkit/chainlink-testing-framework/havoc v1.50.2/go.mod h1:DsT43c1oTBmp3iQkMcoZOoKThwZvt8X3Pz6UmznJ4GY=
github.com/smartcontractkit/chainlink-testing-framework/lib v1.50.19 h1:9PMwKNqFKc5FXf4VchyD3CGzZelnSgi13fgVdT2X7T4=
@@ -1704,7 +1709,6 @@ go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
go4.org/netipx v0.0.0-20230125063823-8449b0a6169f h1:ketMxHg+vWm3yccyYiq+uK8D3fRmna2Fcj+awpQp84s=
go4.org/netipx v0.0.0-20230125063823-8449b0a6169f/go.mod h1:tgPU4N2u9RByaTN3NC2p9xOzyFpte4jYwsIIRF7XlSc=
-golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.11.0 h1:KXV8WWKCXm6tRpLirl2szsO5j/oOODwZf4hATmGVNs4=
golang.org/x/arch v0.11.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@@ -2262,7 +2266,6 @@ nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYm
pgregory.net/rapid v1.1.0 h1:CMa0sjHSru3puNx+J0MIAuiiEV4N0qj8/cMWGBBCsjw=
pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
-rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU=
diff --git a/integration-tests/load/go.mod b/integration-tests/load/go.mod
index ddcf26ebb71..da059e156b7 100644
--- a/integration-tests/load/go.mod
+++ b/integration-tests/load/go.mod
@@ -107,8 +107,8 @@ require (
github.com/btcsuite/btcd/btcec/v2 v2.3.4 // indirect
github.com/buger/jsonparser v1.1.1 // indirect
github.com/bytecodealliance/wasmtime-go/v23 v23.0.0 // indirect
- github.com/bytedance/sonic v1.11.6 // indirect
- github.com/bytedance/sonic/loader v0.1.1 // indirect
+ github.com/bytedance/sonic v1.12.3 // indirect
+ github.com/bytedance/sonic/loader v0.2.0 // indirect
github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b // indirect
github.com/c9s/goprocinfo v0.0.0-20210130143923-c95fcf8c64a8 // indirect
github.com/cdk8s-team/cdk8s-core-go/cdk8s/v2 v2.7.5 // indirect
@@ -179,7 +179,7 @@ require (
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
- github.com/gabriel-vasile/mimetype v1.4.3 // indirect
+ github.com/gabriel-vasile/mimetype v1.4.6 // indirect
github.com/gagliardetto/binary v0.8.0 // indirect
github.com/gagliardetto/solana-go v1.12.0 // indirect
github.com/gagliardetto/treeout v0.1.4 // indirect
@@ -209,7 +209,7 @@ require (
github.com/go-openapi/validate v0.23.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
- github.com/go-playground/validator/v10 v10.22.0 // indirect
+ github.com/go-playground/validator/v10 v10.22.1 // indirect
github.com/go-redis/redis/v8 v8.11.5 // indirect
github.com/go-viper/mapstructure/v2 v2.1.0 // indirect
github.com/go-webauthn/webauthn v0.9.4 // indirect
@@ -327,7 +327,7 @@ require (
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
- github.com/mattn/go-runewidth v0.0.14 // indirect
+ github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/miekg/dns v1.1.61 // indirect
github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
@@ -382,7 +382,7 @@ require (
github.com/prometheus/procfs v0.15.1 // indirect
github.com/prometheus/prometheus v0.54.1 // indirect
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
- github.com/rivo/uniseg v0.4.4 // indirect
+ github.com/rivo/uniseg v0.4.7 // indirect
github.com/robfig/cron/v3 v3.0.1 // indirect
github.com/rogpeppe/go-internal v1.13.1 // indirect
github.com/rs/cors v1.10.1 // indirect
diff --git a/integration-tests/load/go.sum b/integration-tests/load/go.sum
index c5dff273180..b592320e882 100644
--- a/integration-tests/load/go.sum
+++ b/integration-tests/load/go.sum
@@ -277,10 +277,11 @@ github.com/bxcodec/faker v2.0.1+incompatible h1:P0KUpUw5w6WJXwrPfv35oc91i4d8nf40
github.com/bxcodec/faker v2.0.1+incompatible/go.mod h1:BNzfpVdTwnFJ6GtfYTcQu6l6rHShT+veBxNCnjCx5XM=
github.com/bytecodealliance/wasmtime-go/v23 v23.0.0 h1:NJvU4S8KEk1GnF6+FvlnzMD/8wXTj/mYJSG6Q4yu3Pw=
github.com/bytecodealliance/wasmtime-go/v23 v23.0.0/go.mod h1:5YIL+Ouiww2zpO7u+iZ1U1G5NvmwQYaXdmCZQGjQM0U=
-github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
-github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
-github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
+github.com/bytedance/sonic v1.12.3 h1:W2MGa7RCU1QTeYRTPE3+88mVC0yXmsRQRChiyVocVjU=
+github.com/bytedance/sonic v1.12.3/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
+github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM=
+github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b h1:6+ZFm0flnudZzdSE0JxlhR2hKnGPcNB35BjQf4RYQDY=
github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M=
github.com/c9s/goprocinfo v0.0.0-20210130143923-c95fcf8c64a8 h1:SjZ2GvvOononHOpK84APFuMvxqsk3tEIaKH/z4Rpu3g=
@@ -511,8 +512,8 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
-github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
-github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
+github.com/gabriel-vasile/mimetype v1.4.6 h1:3+PzJTKLkvgjeTbts6msPJt4DixhT4YtFNf1gtGe3zc=
+github.com/gabriel-vasile/mimetype v1.4.6/go.mod h1:JX1qVKqZd40hUPpAfiNTe0Sne7hdfKSbOqqmkq8GCXc=
github.com/gagliardetto/binary v0.8.0 h1:U9ahc45v9HW0d15LoN++vIXSJyqR/pWw8DDlhd7zvxg=
github.com/gagliardetto/binary v0.8.0/go.mod h1:2tfj51g5o9dnvsc+fL3Jxr22MuWzYXwx9wEoN0XQ7/c=
github.com/gagliardetto/gofuzz v1.2.2 h1:XL/8qDMzcgvR4+CyRQW9UGdwPRPMHVJfqQ/uMvSUuQw=
@@ -599,8 +600,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
-github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao=
-github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
+github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA=
+github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
github.com/go-resty/resty/v2 v2.15.3 h1:bqff+hcqAflpiF591hhJzNdkRsFhlB96CYfBwSFvql8=
@@ -1094,8 +1095,8 @@ github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
-github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
-github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
+github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
+github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
@@ -1312,8 +1313,8 @@ github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqn
github.com/regen-network/protobuf v1.3.3-alpha.regen.1 h1:OHEc+q5iIAXpqiqFKeLpu5NwTIkVXUs48vFMwzqpqY4=
github.com/regen-network/protobuf v1.3.3-alpha.regen.1/go.mod h1:2DjTFR1HhMQhiWC5sZ4OhQ3+NtdbZ6oBDKQwq5Ou+FI=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
-github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
-github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
+github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
+github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
@@ -1416,6 +1417,8 @@ github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241223151630-eac4f1508dc
github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241223151630-eac4f1508dce/go.mod h1:qq+nW0JDnCCGMf2c38ZHjH8xgkAQnXKighjJr5JdDNE=
github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241202202529-2033490e77b8 h1:tNS7U9lrxkFvEuyxQv11HHOiV9LPDGC9wYEy+yM/Jv4=
github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241202202529-2033490e77b8/go.mod h1:EBrEgcdIbwepqguClkv8Ohy7CbyWSJaE4EC9aBJlQK0=
+github.com/smartcontractkit/chainlink-testing-framework/framework v0.4.2-0.20250110073248-456673e8eea2 h1:nTUoe7GZLw17nPLV5t3Vgf4U4pf+VW0Uko5xpNiKdKU=
+github.com/smartcontractkit/chainlink-testing-framework/framework v0.4.2-0.20250110073248-456673e8eea2/go.mod h1:mMUqvS3BZfvN1OfK4OFTYf1+T0X6nwmSXJM2keaPsSM=
github.com/smartcontractkit/chainlink-testing-framework/havoc v1.50.2 h1:GDGrC5OGiV0RyM1znYWehSQXyZQWTOzrEeJRYmysPCE=
github.com/smartcontractkit/chainlink-testing-framework/havoc v1.50.2/go.mod h1:DsT43c1oTBmp3iQkMcoZOoKThwZvt8X3Pz6UmznJ4GY=
github.com/smartcontractkit/chainlink-testing-framework/lib v1.50.19 h1:9PMwKNqFKc5FXf4VchyD3CGzZelnSgi13fgVdT2X7T4=
@@ -1695,7 +1698,6 @@ go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
go4.org/netipx v0.0.0-20230125063823-8449b0a6169f h1:ketMxHg+vWm3yccyYiq+uK8D3fRmna2Fcj+awpQp84s=
go4.org/netipx v0.0.0-20230125063823-8449b0a6169f/go.mod h1:tgPU4N2u9RByaTN3NC2p9xOzyFpte4jYwsIIRF7XlSc=
-golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.11.0 h1:KXV8WWKCXm6tRpLirl2szsO5j/oOODwZf4hATmGVNs4=
golang.org/x/arch v0.11.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@@ -2251,7 +2253,6 @@ nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYm
pgregory.net/rapid v1.1.0 h1:CMa0sjHSru3puNx+J0MIAuiiEV4N0qj8/cMWGBBCsjw=
pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
-rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU=
diff --git a/shell.nix b/shell.nix
index 4065e7e3def..456bbd8a9c1 100644
--- a/shell.nix
+++ b/shell.nix
@@ -6,6 +6,53 @@ with pkgs; let
nodePackages = pkgs.nodePackages.override {inherit nodejs;};
pnpm = pnpm_9;
+ version = "v2.0.18";
+ getBinDerivation =
+ {
+ name,
+ filename,
+ sha256,
+ }:
+ pkgs.stdenv.mkDerivation rec {
+ inherit name;
+ url = "https://github.com/anza-xyz/agave/releases/download/${version}/${filename}";
+
+ nativeBuildInputs = [
+ autoPatchelfHook
+ ];
+
+ autoPatchelfIgnoreMissingDeps = true;
+
+ buildInputs = with pkgs; [stdenv.cc.cc.libgcc stdenv.cc.cc.lib] ++ lib.optionals stdenv.isLinux [ libudev-zero ];
+
+ src = pkgs.fetchzip {
+ inherit url sha256;
+ };
+
+ installPhase = ''
+ mkdir -p $out/bin
+ ls -lah $src
+ cp -r $src/bin/* $out/bin
+ '';
+ };
+
+ solanaBinaries = {
+ x86_64-linux = getBinDerivation {
+ name = "solana-cli-x86_64-linux";
+ filename = "solana-release-x86_64-unknown-linux-gnu.tar.bz2";
+ ### BEGIN_LINUX_SHA256 ###
+ sha256 = "sha256-3FW6IMZeDtyU4GTsRIwT9BFLNzLPEuP+oiQdur7P13s=";
+ ### END_LINUX_SHA256 ###
+ };
+ aarch64-apple-darwin = getBinDerivation {
+ name = "solana-cli-aarch64-apple-darwin";
+ filename = "solana-release-aarch64-apple-darwin.tar.bz2";
+ ### BEGIN_DARWIN_SHA256 ###
+ sha256 = "sha256-6VjycYU0NU0evXoqtGAZMYGHQEKijofnFQnBJNVsb6Q=";
+ ### END_DARWIN_SHA256 ###
+ };
+ };
+
mkShell' = mkShell.override {
# The current nix default sdk for macOS fails to compile go projects, so we use a newer one for now.
stdenv =
@@ -50,9 +97,12 @@ in
pkg-config
libudev-zero
libusb1
+ solanaBinaries.x86_64-linux
] ++ lib.optionals isCrib [
nur.repos.goreleaser.goreleaser-pro
patchelf
+ ] ++ pkgs.lib.optionals (pkgs.stdenv.isDarwin && pkgs.stdenv.hostPlatform.isAarch64) [
+ solanaBinaries.aarch64-apple-darwin
];
shellHook = ''
From 437ef640db1d4455e7b4d90868c9e5c4d62054df Mon Sep 17 00:00:00 2001
From: Suryansh <39276431+0xsuryansh@users.noreply.github.com>
Date: Fri, 10 Jan 2025 22:48:41 +0530
Subject: [PATCH 30/91] CCIP-4223 bubble up revert for estimation (#15504)
* feat: fix estimation by adding a reverting clause
* CCIP-4223 changeset
* [Bot] Update changeset file with jira issues
* Update gethwrappers
* updates wrapper and gas snapshot
* add tests
* fix test vars
* add comments reasoning for address(1)
* reorder import
* fix comments
* updated code after via ir merge
* update sender with C11 address
* update wrappers
* fmt
* move GAS_ESTIMATION_SENDER to Internal.sol + minor refactoring
* make gas estimator const public and use encodeCall
* lint fix
* fix test
---------
Co-authored-by: app-token-issuer-infra-releng[bot] <120227048+app-token-issuer-infra-releng[bot]@users.noreply.github.com>
---
contracts/.changeset/twelve-pianos-chew.md | 10 ++++
contracts/gas-snapshots/ccip.gas-snapshot | 31 +++++-----
.../src/v0.8/ccip/libraries/Internal.sol | 5 ++
contracts/src/v0.8/ccip/ocr/MultiOCR3Base.sol | 6 +-
contracts/src/v0.8/ccip/offRamp/OffRamp.sol | 8 +++
.../OffRamp/OffRamp.trialExecute.t.sol | 57 +++++++++++++++++++
.../multi_ocr3_helper/multi_ocr3_helper.go | 4 +-
.../ccip/generated/offramp/offramp.go | 4 +-
...rapper-dependency-versions-do-not-edit.txt | 4 +-
9 files changed, 108 insertions(+), 21 deletions(-)
create mode 100644 contracts/.changeset/twelve-pianos-chew.md
diff --git a/contracts/.changeset/twelve-pianos-chew.md b/contracts/.changeset/twelve-pianos-chew.md
new file mode 100644
index 00000000000..9aa4fa8f2b2
--- /dev/null
+++ b/contracts/.changeset/twelve-pianos-chew.md
@@ -0,0 +1,10 @@
+---
+'@chainlink/contracts': minor
+---
+
+#internal Fix gas estimation by adding a reverting clause
+
+PR issue: CCIP-4223
+
+
+Solidity Review issue: CCIP-3966
\ No newline at end of file
diff --git a/contracts/gas-snapshots/ccip.gas-snapshot b/contracts/gas-snapshots/ccip.gas-snapshot
index 019626e6a67..afc2768185f 100644
--- a/contracts/gas-snapshots/ccip.gas-snapshot
+++ b/contracts/gas-snapshots/ccip.gas-snapshot
@@ -226,18 +226,18 @@ OffRamp_commit:test_ReportOnlyRootSuccess_gas() (gas: 141051)
OffRamp_commit:test_RootWithRMNDisabled() (gas: 153873)
OffRamp_commit:test_StaleReportWithRoot() (gas: 232101)
OffRamp_commit:test_ValidPriceUpdateThenStaleReportWithRoot() (gas: 206722)
-OffRamp_constructor:test_Constructor() (gas: 6269512)
+OffRamp_constructor:test_Constructor() (gas: 6311247)
OffRamp_execute:test_LargeBatch() (gas: 3373860)
OffRamp_execute:test_MultipleReports() (gas: 291458)
-OffRamp_execute:test_MultipleReportsWithPartialValidationFailures() (gas: 364776)
+OffRamp_execute:test_MultipleReportsWithPartialValidationFailures() (gas: 364826)
OffRamp_execute:test_SingleReport() (gas: 168850)
OffRamp_executeSingleMessage:test_executeSingleMessage_NoTokens() (gas: 51610)
OffRamp_executeSingleMessage:test_executeSingleMessage_NonContract() (gas: 20514)
OffRamp_executeSingleMessage:test_executeSingleMessage_NonContractWithTokens() (gas: 230418)
OffRamp_executeSingleMessage:test_executeSingleMessage_WithMessageInterceptor() (gas: 87465)
OffRamp_executeSingleMessage:test_executeSingleMessage_WithTokens() (gas: 259935)
-OffRamp_executeSingleReport:test_InvalidSourcePoolAddress() (gas: 455358)
-OffRamp_executeSingleReport:test_ReceiverError() (gas: 180797)
+OffRamp_executeSingleReport:test_InvalidSourcePoolAddress() (gas: 455383)
+OffRamp_executeSingleReport:test_ReceiverError() (gas: 180822)
OffRamp_executeSingleReport:test_SingleMessageNoTokens() (gas: 205270)
OffRamp_executeSingleReport:test_SingleMessageNoTokensOtherChain() (gas: 241357)
OffRamp_executeSingleReport:test_SingleMessageNoTokensUnordered() (gas: 185263)
@@ -254,23 +254,26 @@ OffRamp_executeSingleReport:test__execute_SkippedAlreadyExecutedMessageUnordered
OffRamp_getExecutionState:test_FillExecutionState() (gas: 3955662)
OffRamp_getExecutionState:test_GetDifferentChainExecutionState() (gas: 121311)
OffRamp_getExecutionState:test_GetExecutionState() (gas: 90102)
-OffRamp_manuallyExecute:test_manuallyExecute() (gas: 212368)
-OffRamp_manuallyExecute:test_manuallyExecute_DoesNotRevertIfUntouched() (gas: 165742)
-OffRamp_manuallyExecute:test_manuallyExecute_LowGasLimit() (gas: 479145)
+OffRamp_manuallyExecute:test_manuallyExecute() (gas: 212393)
+OffRamp_manuallyExecute:test_manuallyExecute_DoesNotRevertIfUntouched() (gas: 165767)
+OffRamp_manuallyExecute:test_manuallyExecute_LowGasLimit() (gas: 479170)
OffRamp_manuallyExecute:test_manuallyExecute_ReentrancyFails() (gas: 2229662)
-OffRamp_manuallyExecute:test_manuallyExecute_WithGasOverride() (gas: 212918)
-OffRamp_manuallyExecute:test_manuallyExecute_WithMultiReportGasOverride() (gas: 732218)
-OffRamp_manuallyExecute:test_manuallyExecute_WithPartialMessages() (gas: 337015)
+OffRamp_manuallyExecute:test_manuallyExecute_WithGasOverride() (gas: 212943)
+OffRamp_manuallyExecute:test_manuallyExecute_WithMultiReportGasOverride() (gas: 732343)
+OffRamp_manuallyExecute:test_manuallyExecute_WithPartialMessages() (gas: 337040)
OffRamp_releaseOrMintSingleToken:test__releaseOrMintSingleToken() (gas: 94629)
OffRamp_releaseOrMintTokens:test_releaseOrMintTokens() (gas: 161157)
OffRamp_releaseOrMintTokens:test_releaseOrMintTokens_WithGasOverride() (gas: 163023)
OffRamp_releaseOrMintTokens:test_releaseOrMintTokens_destDenominatedDecimals() (gas: 174276)
OffRamp_setDynamicConfig:test_SetDynamicConfig() (gas: 25442)
OffRamp_setDynamicConfig:test_SetDynamicConfigWithInterceptor() (gas: 47493)
-OffRamp_trialExecute:test_trialExecute() (gas: 263635)
-OffRamp_trialExecute:test_trialExecute_RateLimitError() (gas: 120721)
-OffRamp_trialExecute:test_trialExecute_TokenHandlingErrorIsCaught() (gas: 132031)
-OffRamp_trialExecute:test_trialExecute_TokenPoolIsNotAContract() (gas: 281380)
+OffRamp_trialExecute:test_trialExecute() (gas: 263614)
+OffRamp_trialExecute:test_trialExecute_CallWithExactGasRevertsAndSenderIsNotGasEstimator() (gas: 24490)
+OffRamp_trialExecute:test_trialExecute_RateLimitError() (gas: 120710)
+OffRamp_trialExecute:test_trialExecute_RevertsWhen_NoEnoughGasForCallSigAndSenderIsGasEstimator() (gas: 29391)
+OffRamp_trialExecute:test_trialExecute_RevertsWhen_NoGasForCallExactCheckAndSenderIsGasEstimator() (gas: 29539)
+OffRamp_trialExecute:test_trialExecute_TokenHandlingErrorIsCaught() (gas: 131932)
+OffRamp_trialExecute:test_trialExecute_TokenPoolIsNotAContract() (gas: 281327)
OnRampTokenPoolReentrancy:test_OnRampTokenPoolReentrancy() (gas: 244294)
OnRamp_applyAllowlistUpdates:test_applyAllowlistUpdates() (gas: 325979)
OnRamp_applyAllowlistUpdates:test_applyAllowlistUpdates_InvalidAllowListRequestDisabledAllowListWithAdds() (gas: 17190)
diff --git a/contracts/src/v0.8/ccip/libraries/Internal.sol b/contracts/src/v0.8/ccip/libraries/Internal.sol
index 25d923ee1ed..443ea22a107 100644
--- a/contracts/src/v0.8/ccip/libraries/Internal.sol
+++ b/contracts/src/v0.8/ccip/libraries/Internal.sol
@@ -17,6 +17,11 @@ library Internal {
/// @dev The expected number of bytes returned by the balanceOf function.
uint256 internal constant MAX_BALANCE_OF_RET_BYTES = 32;
+ /// @dev The address used to send calls for gas estimation.
+ /// You only need to use this address if the minimum gas limit specified by the user is not actually enough to execute the
+ /// given message and you're attempting to estimate the actual necessary gas limit
+ address public constant GAS_ESTIMATION_SENDER = address(0xC11C11C11C11C11C11C11C11C11C11C11C11C1);
+
/// @notice A collection of token price and gas price updates.
/// @dev RMN depends on this struct, if changing, please notify the RMN maintainers.
struct PriceUpdates {
diff --git a/contracts/src/v0.8/ccip/ocr/MultiOCR3Base.sol b/contracts/src/v0.8/ccip/ocr/MultiOCR3Base.sol
index b6741f78fbe..a6feb0b069c 100644
--- a/contracts/src/v0.8/ccip/ocr/MultiOCR3Base.sol
+++ b/contracts/src/v0.8/ccip/ocr/MultiOCR3Base.sol
@@ -3,6 +3,7 @@ pragma solidity ^0.8.4;
import {Ownable2StepMsgSender} from "../../shared/access/Ownable2StepMsgSender.sol";
import {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol";
+import {Internal} from "../libraries/Internal.sol";
/// @notice Onchain verification of reports from the offchain reporting protocol with multiple OCR plugin support.
abstract contract MultiOCR3Base is ITypeAndVersion, Ownable2StepMsgSender {
@@ -42,6 +43,7 @@ abstract contract MultiOCR3Base is ITypeAndVersion, Ownable2StepMsgSender {
error NonUniqueSignatures();
error OracleCannotBeZeroAddress();
error StaticConfigCannotBeChanged(uint8 ocrPluginType);
+ error InsufficientGasForCallWithExact();
/// @dev Packing these fields used on the hot path in a ConfigInfo variable reduces the retrieval of all
/// of them to a minimum number of SLOADs.
@@ -274,7 +276,9 @@ abstract contract MultiOCR3Base is ITypeAndVersion, Ownable2StepMsgSender {
&& msg.sender == s_ocrConfigs[ocrPluginType].transmitters[transmitter.index]
)
) {
- revert UnauthorizedTransmitter();
+ if (msg.sender != Internal.GAS_ESTIMATION_SENDER) {
+ revert UnauthorizedTransmitter();
+ }
}
}
diff --git a/contracts/src/v0.8/ccip/offRamp/OffRamp.sol b/contracts/src/v0.8/ccip/offRamp/OffRamp.sol
index 2b6f075bc7a..9ecf41272ab 100644
--- a/contracts/src/v0.8/ccip/offRamp/OffRamp.sol
+++ b/contracts/src/v0.8/ccip/offRamp/OffRamp.sol
@@ -544,6 +544,14 @@ contract OffRamp is ITypeAndVersion, MultiOCR3Base {
) internal returns (Internal.MessageExecutionState executionState, bytes memory) {
try this.executeSingleMessage(message, offchainTokenData, tokenGasOverrides) {}
catch (bytes memory err) {
+ if (msg.sender == Internal.GAS_ESTIMATION_SENDER) {
+ if (
+ CallWithExactGas.NOT_ENOUGH_GAS_FOR_CALL_SIG == bytes4(err)
+ || CallWithExactGas.NO_GAS_FOR_CALL_EXACT_CHECK_SIG == bytes4(err)
+ ) {
+ revert InsufficientGasForCallWithExact();
+ }
+ }
// return the message execution state as FAILURE and the revert data.
// Max length of revert data is Router.MAX_RET_BYTES, max length of err is 4 + Router.MAX_RET_BYTES.
return (Internal.MessageExecutionState.FAILURE, err);
diff --git a/contracts/src/v0.8/ccip/test/offRamp/OffRamp/OffRamp.trialExecute.t.sol b/contracts/src/v0.8/ccip/test/offRamp/OffRamp/OffRamp.trialExecute.t.sol
index 1acb4c4ee0a..6b72b25bb61 100644
--- a/contracts/src/v0.8/ccip/test/offRamp/OffRamp/OffRamp.trialExecute.t.sol
+++ b/contracts/src/v0.8/ccip/test/offRamp/OffRamp/OffRamp.trialExecute.t.sol
@@ -1,8 +1,10 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
+import {CallWithExactGas} from "../../../../shared/call/CallWithExactGas.sol";
import {Internal} from "../../../libraries/Internal.sol";
import {RateLimiter} from "../../../libraries/RateLimiter.sol";
+import {MultiOCR3Base} from "../../../ocr/MultiOCR3Base.sol";
import {OffRamp} from "../../../offRamp/OffRamp.sol";
import {OffRampSetup} from "./OffRampSetup.t.sol";
@@ -117,4 +119,59 @@ contract OffRamp_trialExecute is OffRampSetup {
assertEq(uint256(Internal.MessageExecutionState.FAILURE), uint256(newState));
assertEq(abi.encodeWithSelector(OffRamp.NotACompatiblePool.selector, address(0)), err);
}
+
+ function test_trialExecute_CallWithExactGasRevertsAndSenderIsNotGasEstimator() public {
+ Internal.Any2EVMRampMessage memory message =
+ _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1);
+
+ bytes[] memory offchainTokenData = new bytes[](message.tokenAmounts.length);
+ uint32[] memory tokenGasOverrides = new uint32[](0);
+
+ vm.mockCallRevert(
+ address(s_offRamp),
+ abi.encodeCall(s_offRamp.executeSingleMessage, (message, offchainTokenData, tokenGasOverrides)),
+ abi.encodeWithSelector(CallWithExactGas.NOT_ENOUGH_GAS_FOR_CALL_SIG, "")
+ );
+
+ (Internal.MessageExecutionState newState, bytes memory err) =
+ s_offRamp.trialExecute(message, offchainTokenData, tokenGasOverrides);
+ assertEq(uint256(Internal.MessageExecutionState.FAILURE), uint256(newState));
+ assertEq(CallWithExactGas.NotEnoughGasForCall.selector, bytes4(err));
+ }
+
+ function test_trialExecute_RevertsWhen_NoGasForCallExactCheckAndSenderIsGasEstimator() public {
+ Internal.Any2EVMRampMessage memory message =
+ _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1);
+
+ bytes[] memory offchainTokenData = new bytes[](message.tokenAmounts.length);
+ uint32[] memory tokenGasOverrides = new uint32[](0);
+
+ vm.mockCallRevert(
+ address(s_offRamp),
+ abi.encodeCall(s_offRamp.executeSingleMessage, (message, offchainTokenData, tokenGasOverrides)),
+ abi.encodeWithSelector(CallWithExactGas.NO_GAS_FOR_CALL_EXACT_CHECK_SIG, "")
+ );
+
+ changePrank(Internal.GAS_ESTIMATION_SENDER);
+ vm.expectRevert(MultiOCR3Base.InsufficientGasForCallWithExact.selector);
+ s_offRamp.trialExecute(message, offchainTokenData, tokenGasOverrides);
+ }
+
+ function test_trialExecute_RevertsWhen_NoEnoughGasForCallSigAndSenderIsGasEstimator() public {
+ Internal.Any2EVMRampMessage memory message =
+ _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1);
+
+ bytes[] memory offchainTokenData = new bytes[](message.tokenAmounts.length);
+ uint32[] memory tokenGasOverrides = new uint32[](0);
+
+ vm.mockCallRevert(
+ address(s_offRamp),
+ abi.encodeCall(s_offRamp.executeSingleMessage, (message, offchainTokenData, tokenGasOverrides)),
+ abi.encodeWithSelector(CallWithExactGas.NOT_ENOUGH_GAS_FOR_CALL_SIG, "")
+ );
+
+ changePrank(Internal.GAS_ESTIMATION_SENDER);
+ vm.expectRevert(MultiOCR3Base.InsufficientGasForCallWithExact.selector);
+ s_offRamp.trialExecute(message, offchainTokenData, tokenGasOverrides);
+ }
}
diff --git a/core/gethwrappers/ccip/generated/multi_ocr3_helper/multi_ocr3_helper.go b/core/gethwrappers/ccip/generated/multi_ocr3_helper/multi_ocr3_helper.go
index 5a06ea5bea5..3b0b885126a 100644
--- a/core/gethwrappers/ccip/generated/multi_ocr3_helper/multi_ocr3_helper.go
+++ b/core/gethwrappers/ccip/generated/multi_ocr3_helper/multi_ocr3_helper.go
@@ -58,8 +58,8 @@ type MultiOCR3BaseOracle struct {
}
var MultiOCR3HelperMetaData = &bind.MetaData{
- ABI: "[{\"type\":\"function\",\"name\":\"acceptOwnership\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"getOracle\",\"inputs\":[{\"name\":\"ocrPluginType\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"oracleAddress\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[{\"name\":\"\",\"type\":\"tuple\",\"internalType\":\"structMultiOCR3Base.Oracle\",\"components\":[{\"name\":\"index\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"role\",\"type\":\"uint8\",\"internalType\":\"enumMultiOCR3Base.Role\"}]}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"latestConfigDetails\",\"inputs\":[{\"name\":\"ocrPluginType\",\"type\":\"uint8\",\"internalType\":\"uint8\"}],\"outputs\":[{\"name\":\"ocrConfig\",\"type\":\"tuple\",\"internalType\":\"structMultiOCR3Base.OCRConfig\",\"components\":[{\"name\":\"configInfo\",\"type\":\"tuple\",\"internalType\":\"structMultiOCR3Base.ConfigInfo\",\"components\":[{\"name\":\"configDigest\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"F\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"n\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"isSignatureVerificationEnabled\",\"type\":\"bool\",\"internalType\":\"bool\"}]},{\"name\":\"signers\",\"type\":\"address[]\",\"internalType\":\"address[]\"},{\"name\":\"transmitters\",\"type\":\"address[]\",\"internalType\":\"address[]\"}]}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"owner\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"setOCR3Configs\",\"inputs\":[{\"name\":\"ocrConfigArgs\",\"type\":\"tuple[]\",\"internalType\":\"structMultiOCR3Base.OCRConfigArgs[]\",\"components\":[{\"name\":\"configDigest\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"ocrPluginType\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"F\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"isSignatureVerificationEnabled\",\"type\":\"bool\",\"internalType\":\"bool\"},{\"name\":\"signers\",\"type\":\"address[]\",\"internalType\":\"address[]\"},{\"name\":\"transmitters\",\"type\":\"address[]\",\"internalType\":\"address[]\"}]}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setTransmitOcrPluginType\",\"inputs\":[{\"name\":\"ocrPluginType\",\"type\":\"uint8\",\"internalType\":\"uint8\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"transferOwnership\",\"inputs\":[{\"name\":\"to\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"transmitWithSignatures\",\"inputs\":[{\"name\":\"reportContext\",\"type\":\"bytes32[2]\",\"internalType\":\"bytes32[2]\"},{\"name\":\"report\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"rs\",\"type\":\"bytes32[]\",\"internalType\":\"bytes32[]\"},{\"name\":\"ss\",\"type\":\"bytes32[]\",\"internalType\":\"bytes32[]\"},{\"name\":\"rawVs\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"transmitWithoutSignatures\",\"inputs\":[{\"name\":\"reportContext\",\"type\":\"bytes32[2]\",\"internalType\":\"bytes32[2]\"},{\"name\":\"report\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"typeAndVersion\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"string\",\"internalType\":\"string\"}],\"stateMutability\":\"pure\"},{\"type\":\"event\",\"name\":\"AfterConfigSet\",\"inputs\":[{\"name\":\"ocrPluginType\",\"type\":\"uint8\",\"indexed\":false,\"internalType\":\"uint8\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"ConfigSet\",\"inputs\":[{\"name\":\"ocrPluginType\",\"type\":\"uint8\",\"indexed\":false,\"internalType\":\"uint8\"},{\"name\":\"configDigest\",\"type\":\"bytes32\",\"indexed\":false,\"internalType\":\"bytes32\"},{\"name\":\"signers\",\"type\":\"address[]\",\"indexed\":false,\"internalType\":\"address[]\"},{\"name\":\"transmitters\",\"type\":\"address[]\",\"indexed\":false,\"internalType\":\"address[]\"},{\"name\":\"F\",\"type\":\"uint8\",\"indexed\":false,\"internalType\":\"uint8\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"OwnershipTransferRequested\",\"inputs\":[{\"name\":\"from\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"to\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"OwnershipTransferred\",\"inputs\":[{\"name\":\"from\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"to\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Transmitted\",\"inputs\":[{\"name\":\"ocrPluginType\",\"type\":\"uint8\",\"indexed\":true,\"internalType\":\"uint8\"},{\"name\":\"configDigest\",\"type\":\"bytes32\",\"indexed\":false,\"internalType\":\"bytes32\"},{\"name\":\"sequenceNumber\",\"type\":\"uint64\",\"indexed\":false,\"internalType\":\"uint64\"}],\"anonymous\":false},{\"type\":\"error\",\"name\":\"CannotTransferToSelf\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ConfigDigestMismatch\",\"inputs\":[{\"name\":\"expected\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"actual\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}]},{\"type\":\"error\",\"name\":\"ForkedChain\",\"inputs\":[{\"name\":\"expected\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"actual\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"type\":\"error\",\"name\":\"InvalidConfig\",\"inputs\":[{\"name\":\"errorType\",\"type\":\"uint8\",\"internalType\":\"enumMultiOCR3Base.InvalidConfigErrorType\"}]},{\"type\":\"error\",\"name\":\"MustBeProposedOwner\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"NonUniqueSignatures\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"OnlyCallableByOwner\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"OracleCannotBeZeroAddress\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"OwnerCannotBeZero\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"SignaturesOutOfRegistration\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"StaticConfigCannotBeChanged\",\"inputs\":[{\"name\":\"ocrPluginType\",\"type\":\"uint8\",\"internalType\":\"uint8\"}]},{\"type\":\"error\",\"name\":\"UnauthorizedSigner\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"UnauthorizedTransmitter\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"WrongMessageLength\",\"inputs\":[{\"name\":\"expected\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"actual\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"type\":\"error\",\"name\":\"WrongNumberOfSignatures\",\"inputs\":[]}]",
- Bin: "0x60a080604052346051573315604057600180546001600160a01b0319163317905546608052611f2090816100578239608051818181610f2f015261169b0152f35b639b15e16f60e01b60005260046000fd5b600080fdfe6080604052600436101561001257600080fd5b60003560e01c806310061068146115df578063181f5a771461150f57806334a9c92e146114285780633ecdb95b14610e2d57806379ba509714610d445780637ac0aa1a14610cdc5780638da5cb5b14610c8a578063c673e58414610b2e578063f2fde38b14610a3b5763f716f99f1461008a57600080fd5b34610a365760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610a365760043567ffffffffffffffff8111610a365736602382011215610a365780600401356100e481611c1a565b916100f26040519384611bd9565b8183526024602084019260051b82010190368211610a365760248101925b82841061093b5784610120611e58565b6002906000805b82518110156109395761013a8184611dc8565b5190604082019060ff8251161561090a5760ff60208401511692836000528660205260406000206001810190815460ff8116156000146108c6577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffff62ff00006060860151151560101b1691161782555b60a08301928351956101008751116107dc578651156108975760038301996101eb6101de6101e58d60405192838092611d78565b0382611bd9565b8a611ea3565b6060830151610566575b60005b88518110156103b55773ffffffffffffffffffffffffffffffffffffffff610220828b611dc8565b5116908a600052600360205260ff60408060002060009073ffffffffffffffffffffffffffffffffffffffff86168252602052205460081c16600381101561032e5761038757811561035d578b6040519261027a84611b85565b60ff83168452602084019161032e578f604060ff928f8493865260005260036020528160002073ffffffffffffffffffffffffffffffffffffffff60009216825260205220945116167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008454161783555191600383101561032e576001927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff61ff0083549260081b169116179055016101f8565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b7fd6c62c9b0000000000000000000000000000000000000000000000000000000060005260046000fd5b7f367f56a2000000000000000000000000000000000000000000000000000000006000526004805260246000fd5b509997969091929394959781519167ffffffffffffffff83116105375768010000000000000000831161053757815483835580841061050e575b509060208d989796959493920190600052602060002060005b8381106104e157505050509360019796936104cb7fab8b1b57514019638d7b5ce9c638fe71366fe8e2be1c40a7a80f1733d0e9f547946104bd7f897ac1b2c12867721b284f3eb147bd4ab046d4eef1cf31c1d8988bfcfb962b53999560ff60209a51169460ff86167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055519485835551916040519687968a88528b88015260a0604088015260a087019101611d78565b908482036060860152611b3b565b9060808301520390a1604051908152a101610127565b825173ffffffffffffffffffffffffffffffffffffffff16818301558e9950602090920191600101610408565b8260005283602060002091820191015b81811061052b57506103ef565b6000815560010161051e565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b8b840161058360405161057d816101de8186611d78565b8b611ea3565b60808401519061010082511161086957815160ff8551166003029060ff821691820361083a57111561080b5781518a51116107dc5781519087547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff61ff008460081b16911617885567ffffffffffffffff8211610537576801000000000000000082116105375780548282558083106107b3575b506020830190600052602060002060005b8381106107895750600193600093508392509050835b61064c575b505050506101f5565b80518310156107845773ffffffffffffffffffffffffffffffffffffffff6106748483611dc8565b5116928d600052600360205260ff60408060002060009073ffffffffffffffffffffffffffffffffffffffff88168252602052205460081c16600381101561032e5761038757831561035d5782604051946106ce86611b85565b60ff83168652602086019161032e578f604060ff9283928a865260005260036020528160002073ffffffffffffffffffffffffffffffffffffffff60009216825260205220965116167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008654161785555190600382101561032e57859485927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff61ff0083549260081b169116179055019261063e565b610643565b600190602073ffffffffffffffffffffffffffffffffffffffff8551169401938184015501610628565b8160005282602060002091820191015b8181106107d05750610617565b600081556001016107c3565b7f367f56a200000000000000000000000000000000000000000000000000000000600052600160045260246000fd5b7f367f56a200000000000000000000000000000000000000000000000000000000600052600360045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8d7f367f56a20000000000000000000000000000000000000000000000000000000060005260045260246000fd5b7f367f56a200000000000000000000000000000000000000000000000000000000600052600560045260246000fd5b60ff606085015115159160101c16151503156101aa57857f87f6037c0000000000000000000000000000000000000000000000000000000060005260045260246000fd5b7f367f56a200000000000000000000000000000000000000000000000000000000600052600060045260246000fd5b005b833567ffffffffffffffff8111610a3657820160c07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc8236030112610a36576040519160c0830183811067ffffffffffffffff82111761053757604052602482013583526109ab60448301611afc565b60208401526109bc60648301611afc565b604084015260848201358015158103610a3657606084015260a482013567ffffffffffffffff8111610a36576109f89060243691850101611c32565b608084015260c48201359267ffffffffffffffff8411610a3657610a26602094936024869536920101611c32565b60a0820152815201930192610110565b600080fd5b34610a365760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610a365760043573ffffffffffffffffffffffffffffffffffffffff8116809103610a3657610a93611e58565b338114610b0457807fffffffffffffffffffffffff0000000000000000000000000000000000000000600054161760005573ffffffffffffffffffffffffffffffffffffffff600154167fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae1278600080a3005b7fdad89dca0000000000000000000000000000000000000000000000000000000060005260046000fd5b34610a365760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610a365760ff610b67611aec565b606060408051610b7681611ba1565b8151610b8181611bbd565b60008152600060208201526000838201526000848201528152826020820152015216600052600260205260606040600020610c866003610c5560405193610bc785611ba1565b610bd081611d3f565b8552610c0860405191610bf183610bea8160028501611d78565b0384611bd9565b60208701928352610bea6040518096819301611d78565b6040850192835260405195869560208752518051602088015260ff602082015116604088015260ff604082015116828801520151151560808601525160c060a086015260e0850190611b3b565b90517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08483030160c0850152611b3b565b0390f35b34610a365760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610a3657602073ffffffffffffffffffffffffffffffffffffffff60015416604051908152f35b34610a365760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610a365760ff610d15611aec565b167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff006004541617600455600080f35b34610a365760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610a365760005473ffffffffffffffffffffffffffffffffffffffff81163303610e03577fffffffffffffffffffffffff00000000000000000000000000000000000000006001549133828416176001551660005573ffffffffffffffffffffffffffffffffffffffff3391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a3005b7f02b543c60000000000000000000000000000000000000000000000000000000060005260046000fd5b34610a365760c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610a365736604411610a365760443567ffffffffffffffff8111610a3657610e84903690600401611abe565b9060643567ffffffffffffffff8111610a3657610ea5903690600401611b0a565b919060843567ffffffffffffffff8111610a3657610eca610ee9913690600401611b0a565b9190610ee160a4359460ff60045416973691611cf3565b923691611cf3565b90846000526002602052610f006040600020611d3f565b9560043594610f0e82611e0b565b97606081019889516113db575b8036036113aa5750805187810361137857507f00000000000000000000000000000000000000000000000000000000000000004681036113475750876000526003602052604060002073ffffffffffffffffffffffffffffffffffffffff331660005260205260406000209860405199610f948b611b85565b5460ff81168b52610faf60ff60208d019260081c1682611ce7565b519960038b101561032e57600260009b1490816112d2575b50156112aa5751611014575b88887f198d6990ef96613a9026203077e422916918b03ff47f0be6bee7b02d8e139ef060408a815190815267ffffffffffffffff602435166020820152a280f35b60ff611027816020875194015116611e34565b160361128257825184510361125a578761104083611cad565b9161104e6040519384611bd9565b8383526020830193368183011161125657806020928637830101525190206040516020810191825260406004818301376060815261108d608082611bd9565b51902090869281519488945b8686106110a65750610fd3565b60208610156112295760208a60806110bf858a1a611e46565b6110c98a89611dc8565b516110d48b89611dc8565b519060ff604051938c855216868401526040830152606082015282805260015afa1561121e578951898b52600360205273ffffffffffffffffffffffffffffffffffffffff60408c2091168b5260205260408a206040519061113582611b85565b5460ff8116825261115060ff602084019260081c1682611ce7565b5160038110156111f1577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff016111c957600160ff8251161b82166111a15790600160ff819351161b17950194611099565b60048b7ff67bc7c4000000000000000000000000000000000000000000000000000000008152fd5b60048b7fca31867a000000000000000000000000000000000000000000000000000000008152fd5b60248c7f4e487b710000000000000000000000000000000000000000000000000000000081526021600452fd5b6040513d8b823e3d90fd5b60248a7f4e487b710000000000000000000000000000000000000000000000000000000081526032600452fd5b8280fd5b6004887fa75d88af000000000000000000000000000000000000000000000000000000008152fd5b6004887f71253a25000000000000000000000000000000000000000000000000000000008152fd5b60048a7fda0f08e8000000000000000000000000000000000000000000000000000000008152fd5b9050898b52600260205260ff600360408d200191511690805482101561131a579073ffffffffffffffffffffffffffffffffffffffff918c5260208c2001541633148b610fc7565b60248c7f4e487b710000000000000000000000000000000000000000000000000000000081526032600452fd5b7f0f01ce85000000000000000000000000000000000000000000000000000000006000526004524660245260446000fd5b87907f93df584c0000000000000000000000000000000000000000000000000000000060005260045260245260446000fd5b7f8e1192e1000000000000000000000000000000000000000000000000000000006000526004523660245260446000fd5b84518060051b908082046020149015171561083a576113f990611e19565b908651918260051b928084046020149015171561083a576114239261141d91611e27565b90611e27565b610f1b565b34610a365760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610a365761145f611aec565b6024359073ffffffffffffffffffffffffffffffffffffffff82168203610a365760ff906000602060405161149381611b85565b828152015216600052600360205273ffffffffffffffffffffffffffffffffffffffff604060002091166000526020526040600020604051906114d582611b85565b5460ff811682526114f060ff602084019260081c1682611ce7565b60ff60405192511682525190600382101561032e576040916020820152f35b34610a365760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610a3657604080519061154d8183611bd9565b601982527f4d756c74694f4352334261736548656c70657220312e302e30000000000000006020830152805180926020825280519081602084015260005b8281106115c85750506000828201840152601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168101030190f35b60208282018101518783018701528694500161158b565b34610a365760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610a365736604411610a365760443567ffffffffffffffff8111610a3657611636903690600401611abe565b604051602092916116478483611bd9565b60008252600036813760ff6004541691826000526002855261166c6040600020611d3f565b936004359261167a81611e0b565b9560608101968751611a79575b8036036113aa57508051858103611a4757507f000000000000000000000000000000000000000000000000000000000000000046810361134757508560005260038852604060002073ffffffffffffffffffffffffffffffffffffffff33166000528852604060002096604051976116fe89611b85565b5460ff8116895261171860ff8b8b019260081c1682611ce7565b5197600389101561032e576002600099149081611a01575b50156119d9575161177d575b86867f198d6990ef96613a9026203077e422916918b03ff47f0be6bee7b02d8e139ef06040888c825191825267ffffffffffffffff6024351690820152a280f35b60ff61178f818a875194015116611e34565b16036119b15761179e81611cad565b906117ac6040519283611bd9565b8082528782019236828201116119ad578188928a928637830101525190206040518681019182526040600481830137606081526117ea608082611bd9565b519020849082519286925b848410611802575061173c565b88841015611980578888608061181982881a611e46565b6118238887611dc8565b5161182e8988611dc8565b519060ff604051938a855216868401526040830152606082015282805260015afa1561197557875187895260038a5273ffffffffffffffffffffffffffffffffffffffff60408a20911689528952604088206040519061188d82611b85565b5460ff811682526118a760ff8c84019260081c1682611ce7565b516003811015611948577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0161192057600160ff8251161b82166118f85790600160ff819351161b179301926117f5565b6004897ff67bc7c4000000000000000000000000000000000000000000000000000000008152fd5b6004897fca31867a000000000000000000000000000000000000000000000000000000008152fd5b60248a7f4e487b710000000000000000000000000000000000000000000000000000000081526021600452fd5b6040513d89823e3d90fd5b6024887f4e487b710000000000000000000000000000000000000000000000000000000081526032600452fd5b8780fd5b6004867f71253a25000000000000000000000000000000000000000000000000000000008152fd5b6004887fda0f08e8000000000000000000000000000000000000000000000000000000008152fd5b905087895260028a5260ff600360408b2001915116908054821015611229579073ffffffffffffffffffffffffffffffffffffffff918a528a8a2001541633148a611730565b85907f93df584c0000000000000000000000000000000000000000000000000000000060005260045260245260446000fd5b84518060051b908082048b149015171561083a57611a9690611e19565b908551918260051b928084048c149015171561083a57611ab99261141d91611e27565b611687565b9181601f84011215610a365782359167ffffffffffffffff8311610a365760208381860195010111610a3657565b6004359060ff82168203610a3657565b359060ff82168203610a3657565b9181601f84011215610a365782359167ffffffffffffffff8311610a36576020808501948460051b010111610a3657565b906020808351928381520192019060005b818110611b595750505090565b825173ffffffffffffffffffffffffffffffffffffffff16845260209384019390920191600101611b4c565b6040810190811067ffffffffffffffff82111761053757604052565b6060810190811067ffffffffffffffff82111761053757604052565b6080810190811067ffffffffffffffff82111761053757604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761053757604052565b67ffffffffffffffff81116105375760051b60200190565b9080601f83011215610a3657813590611c4a82611c1a565b92611c586040519485611bd9565b82845260208085019360051b820101918211610a3657602001915b818310611c805750505090565b823573ffffffffffffffffffffffffffffffffffffffff81168103610a3657815260209283019201611c73565b67ffffffffffffffff811161053757601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b600382101561032e5752565b929190611cff81611c1a565b93611d0d6040519586611bd9565b602085838152019160051b8101928311610a3657905b828210611d2f57505050565b8135815260209182019101611d23565b90604051611d4c81611bbd565b606060ff600183958054855201548181166020850152818160081c16604085015260101c161515910152565b906020825491828152019160005260206000209060005b818110611d9c5750505090565b825473ffffffffffffffffffffffffffffffffffffffff16845260209093019260019283019201611d8f565b8051821015611ddc5760209160051b010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b608401908160841161083a57565b60a001908160a01161083a57565b9190820180921161083a57565b60ff60019116019060ff821161083a57565b60ff601b9116019060ff821161083a57565b73ffffffffffffffffffffffffffffffffffffffff600154163303611e7957565b7f2b5c74de0000000000000000000000000000000000000000000000000000000060005260046000fd5b91909160005b8351811015611f0d5760019060ff831660005260036020526000604080822073ffffffffffffffffffffffffffffffffffffffff611ee7858a611dc8565b511673ffffffffffffffffffffffffffffffffffffffff16835260205281205501611ea9565b5050905056fea164736f6c634300081a000a",
+ ABI: "[{\"type\":\"function\",\"name\":\"acceptOwnership\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"getOracle\",\"inputs\":[{\"name\":\"ocrPluginType\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"oracleAddress\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[{\"name\":\"\",\"type\":\"tuple\",\"internalType\":\"structMultiOCR3Base.Oracle\",\"components\":[{\"name\":\"index\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"role\",\"type\":\"uint8\",\"internalType\":\"enumMultiOCR3Base.Role\"}]}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"latestConfigDetails\",\"inputs\":[{\"name\":\"ocrPluginType\",\"type\":\"uint8\",\"internalType\":\"uint8\"}],\"outputs\":[{\"name\":\"ocrConfig\",\"type\":\"tuple\",\"internalType\":\"structMultiOCR3Base.OCRConfig\",\"components\":[{\"name\":\"configInfo\",\"type\":\"tuple\",\"internalType\":\"structMultiOCR3Base.ConfigInfo\",\"components\":[{\"name\":\"configDigest\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"F\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"n\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"isSignatureVerificationEnabled\",\"type\":\"bool\",\"internalType\":\"bool\"}]},{\"name\":\"signers\",\"type\":\"address[]\",\"internalType\":\"address[]\"},{\"name\":\"transmitters\",\"type\":\"address[]\",\"internalType\":\"address[]\"}]}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"owner\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"setOCR3Configs\",\"inputs\":[{\"name\":\"ocrConfigArgs\",\"type\":\"tuple[]\",\"internalType\":\"structMultiOCR3Base.OCRConfigArgs[]\",\"components\":[{\"name\":\"configDigest\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"ocrPluginType\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"F\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"isSignatureVerificationEnabled\",\"type\":\"bool\",\"internalType\":\"bool\"},{\"name\":\"signers\",\"type\":\"address[]\",\"internalType\":\"address[]\"},{\"name\":\"transmitters\",\"type\":\"address[]\",\"internalType\":\"address[]\"}]}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setTransmitOcrPluginType\",\"inputs\":[{\"name\":\"ocrPluginType\",\"type\":\"uint8\",\"internalType\":\"uint8\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"transferOwnership\",\"inputs\":[{\"name\":\"to\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"transmitWithSignatures\",\"inputs\":[{\"name\":\"reportContext\",\"type\":\"bytes32[2]\",\"internalType\":\"bytes32[2]\"},{\"name\":\"report\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"rs\",\"type\":\"bytes32[]\",\"internalType\":\"bytes32[]\"},{\"name\":\"ss\",\"type\":\"bytes32[]\",\"internalType\":\"bytes32[]\"},{\"name\":\"rawVs\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"transmitWithoutSignatures\",\"inputs\":[{\"name\":\"reportContext\",\"type\":\"bytes32[2]\",\"internalType\":\"bytes32[2]\"},{\"name\":\"report\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"typeAndVersion\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"string\",\"internalType\":\"string\"}],\"stateMutability\":\"pure\"},{\"type\":\"event\",\"name\":\"AfterConfigSet\",\"inputs\":[{\"name\":\"ocrPluginType\",\"type\":\"uint8\",\"indexed\":false,\"internalType\":\"uint8\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"ConfigSet\",\"inputs\":[{\"name\":\"ocrPluginType\",\"type\":\"uint8\",\"indexed\":false,\"internalType\":\"uint8\"},{\"name\":\"configDigest\",\"type\":\"bytes32\",\"indexed\":false,\"internalType\":\"bytes32\"},{\"name\":\"signers\",\"type\":\"address[]\",\"indexed\":false,\"internalType\":\"address[]\"},{\"name\":\"transmitters\",\"type\":\"address[]\",\"indexed\":false,\"internalType\":\"address[]\"},{\"name\":\"F\",\"type\":\"uint8\",\"indexed\":false,\"internalType\":\"uint8\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"OwnershipTransferRequested\",\"inputs\":[{\"name\":\"from\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"to\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"OwnershipTransferred\",\"inputs\":[{\"name\":\"from\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"to\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Transmitted\",\"inputs\":[{\"name\":\"ocrPluginType\",\"type\":\"uint8\",\"indexed\":true,\"internalType\":\"uint8\"},{\"name\":\"configDigest\",\"type\":\"bytes32\",\"indexed\":false,\"internalType\":\"bytes32\"},{\"name\":\"sequenceNumber\",\"type\":\"uint64\",\"indexed\":false,\"internalType\":\"uint64\"}],\"anonymous\":false},{\"type\":\"error\",\"name\":\"CannotTransferToSelf\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ConfigDigestMismatch\",\"inputs\":[{\"name\":\"expected\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"actual\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}]},{\"type\":\"error\",\"name\":\"ForkedChain\",\"inputs\":[{\"name\":\"expected\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"actual\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"type\":\"error\",\"name\":\"InsufficientGasForCallWithExact\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidConfig\",\"inputs\":[{\"name\":\"errorType\",\"type\":\"uint8\",\"internalType\":\"enumMultiOCR3Base.InvalidConfigErrorType\"}]},{\"type\":\"error\",\"name\":\"MustBeProposedOwner\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"NonUniqueSignatures\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"OnlyCallableByOwner\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"OracleCannotBeZeroAddress\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"OwnerCannotBeZero\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"SignaturesOutOfRegistration\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"StaticConfigCannotBeChanged\",\"inputs\":[{\"name\":\"ocrPluginType\",\"type\":\"uint8\",\"internalType\":\"uint8\"}]},{\"type\":\"error\",\"name\":\"UnauthorizedSigner\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"UnauthorizedTransmitter\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"WrongMessageLength\",\"inputs\":[{\"name\":\"expected\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"actual\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"type\":\"error\",\"name\":\"WrongNumberOfSignatures\",\"inputs\":[]}]",
+ Bin: "0x60a080604052346051573315604057600180546001600160a01b0319163317905546608052611f5890816100578239608051818181610f2f01526116b70152f35b639b15e16f60e01b60005260046000fd5b600080fdfe6080604052600436101561001257600080fd5b60003560e01c806310061068146115fb578063181f5a771461152b57806334a9c92e146114445780633ecdb95b14610e2d57806379ba509714610d445780637ac0aa1a14610cdc5780638da5cb5b14610c8a578063c673e58414610b2e578063f2fde38b14610a3b5763f716f99f1461008a57600080fd5b34610a365760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610a365760043567ffffffffffffffff8111610a365736602382011215610a365780600401356100e481611c52565b916100f26040519384611c11565b8183526024602084019260051b82010190368211610a365760248101925b82841061093b5784610120611e90565b6002906000805b82518110156109395761013a8184611e00565b5190604082019060ff8251161561090a5760ff60208401511692836000528660205260406000206001810190815460ff8116156000146108c6577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffff62ff00006060860151151560101b1691161782555b60a08301928351956101008751116107dc578651156108975760038301996101eb6101de6101e58d60405192838092611db0565b0382611c11565b8a611edb565b6060830151610566575b60005b88518110156103b55773ffffffffffffffffffffffffffffffffffffffff610220828b611e00565b5116908a600052600360205260ff60408060002060009073ffffffffffffffffffffffffffffffffffffffff86168252602052205460081c16600381101561032e5761038757811561035d578b6040519261027a84611bbd565b60ff83168452602084019161032e578f604060ff928f8493865260005260036020528160002073ffffffffffffffffffffffffffffffffffffffff60009216825260205220945116167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008454161783555191600383101561032e576001927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff61ff0083549260081b169116179055016101f8565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b7fd6c62c9b0000000000000000000000000000000000000000000000000000000060005260046000fd5b7f367f56a2000000000000000000000000000000000000000000000000000000006000526004805260246000fd5b509997969091929394959781519167ffffffffffffffff83116105375768010000000000000000831161053757815483835580841061050e575b509060208d989796959493920190600052602060002060005b8381106104e157505050509360019796936104cb7fab8b1b57514019638d7b5ce9c638fe71366fe8e2be1c40a7a80f1733d0e9f547946104bd7f897ac1b2c12867721b284f3eb147bd4ab046d4eef1cf31c1d8988bfcfb962b53999560ff60209a51169460ff86167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055519485835551916040519687968a88528b88015260a0604088015260a087019101611db0565b908482036060860152611b73565b9060808301520390a1604051908152a101610127565b825173ffffffffffffffffffffffffffffffffffffffff16818301558e9950602090920191600101610408565b8260005283602060002091820191015b81811061052b57506103ef565b6000815560010161051e565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b8b840161058360405161057d816101de8186611db0565b8b611edb565b60808401519061010082511161086957815160ff8551166003029060ff821691820361083a57111561080b5781518a51116107dc5781519087547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff61ff008460081b16911617885567ffffffffffffffff8211610537576801000000000000000082116105375780548282558083106107b3575b506020830190600052602060002060005b8381106107895750600193600093508392509050835b61064c575b505050506101f5565b80518310156107845773ffffffffffffffffffffffffffffffffffffffff6106748483611e00565b5116928d600052600360205260ff60408060002060009073ffffffffffffffffffffffffffffffffffffffff88168252602052205460081c16600381101561032e5761038757831561035d5782604051946106ce86611bbd565b60ff83168652602086019161032e578f604060ff9283928a865260005260036020528160002073ffffffffffffffffffffffffffffffffffffffff60009216825260205220965116167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008654161785555190600382101561032e57859485927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff61ff0083549260081b169116179055019261063e565b610643565b600190602073ffffffffffffffffffffffffffffffffffffffff8551169401938184015501610628565b8160005282602060002091820191015b8181106107d05750610617565b600081556001016107c3565b7f367f56a200000000000000000000000000000000000000000000000000000000600052600160045260246000fd5b7f367f56a200000000000000000000000000000000000000000000000000000000600052600360045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8d7f367f56a20000000000000000000000000000000000000000000000000000000060005260045260246000fd5b7f367f56a200000000000000000000000000000000000000000000000000000000600052600560045260246000fd5b60ff606085015115159160101c16151503156101aa57857f87f6037c0000000000000000000000000000000000000000000000000000000060005260045260246000fd5b7f367f56a200000000000000000000000000000000000000000000000000000000600052600060045260246000fd5b005b833567ffffffffffffffff8111610a3657820160c07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc8236030112610a36576040519160c0830183811067ffffffffffffffff82111761053757604052602482013583526109ab60448301611b34565b60208401526109bc60648301611b34565b604084015260848201358015158103610a3657606084015260a482013567ffffffffffffffff8111610a36576109f89060243691850101611c6a565b608084015260c48201359267ffffffffffffffff8411610a3657610a26602094936024869536920101611c6a565b60a0820152815201930192610110565b600080fd5b34610a365760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610a365760043573ffffffffffffffffffffffffffffffffffffffff8116809103610a3657610a93611e90565b338114610b0457807fffffffffffffffffffffffff0000000000000000000000000000000000000000600054161760005573ffffffffffffffffffffffffffffffffffffffff600154167fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae1278600080a3005b7fdad89dca0000000000000000000000000000000000000000000000000000000060005260046000fd5b34610a365760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610a365760ff610b67611b24565b606060408051610b7681611bd9565b8151610b8181611bf5565b60008152600060208201526000838201526000848201528152826020820152015216600052600260205260606040600020610c866003610c5560405193610bc785611bd9565b610bd081611d77565b8552610c0860405191610bf183610bea8160028501611db0565b0384611c11565b60208701928352610bea6040518096819301611db0565b6040850192835260405195869560208752518051602088015260ff602082015116604088015260ff604082015116828801520151151560808601525160c060a086015260e0850190611b73565b90517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08483030160c0850152611b73565b0390f35b34610a365760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610a3657602073ffffffffffffffffffffffffffffffffffffffff60015416604051908152f35b34610a365760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610a365760ff610d15611b24565b167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff006004541617600455600080f35b34610a365760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610a365760005473ffffffffffffffffffffffffffffffffffffffff81163303610e03577fffffffffffffffffffffffff00000000000000000000000000000000000000006001549133828416176001551660005573ffffffffffffffffffffffffffffffffffffffff3391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a3005b7f02b543c60000000000000000000000000000000000000000000000000000000060005260046000fd5b34610a365760c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610a365736604411610a365760443567ffffffffffffffff8111610a3657610e84903690600401611af6565b9060643567ffffffffffffffff8111610a3657610ea5903690600401611b42565b919060843567ffffffffffffffff8111610a3657610eca610ee9913690600401611b42565b9190610ee160a4359460ff60045416973691611d2b565b923691611d2b565b90846000526002602052610f006040600020611d77565b9560043594610f0e82611e43565b97606081019889516113f7575b8036036113c65750805187810361139457507f00000000000000000000000000000000000000000000000000000000000000004681036113635750876000526003602052604060002073ffffffffffffffffffffffffffffffffffffffff331660005260205260406000209860405199610f948b611bbd565b5460ff81168b52610faf60ff60208d019260081c1682611d1f565b519960038b101561032e57600260009b1490816112ee575b50156112ab575b51611015575b88887f198d6990ef96613a9026203077e422916918b03ff47f0be6bee7b02d8e139ef060408a815190815267ffffffffffffffff602435166020820152a280f35b60ff611028816020875194015116611e6c565b160361128357825184510361125b578761104183611ce5565b9161104f6040519384611c11565b8383526020830193368183011161125757806020928637830101525190206040516020810191825260406004818301376060815261108e608082611c11565b51902090869281519488945b8686106110a75750610fd4565b602086101561122a5760208a60806110c0858a1a611e7e565b6110ca8a89611e00565b516110d58b89611e00565b519060ff604051938c855216868401526040830152606082015282805260015afa1561121f578951898b52600360205273ffffffffffffffffffffffffffffffffffffffff60408c2091168b5260205260408a206040519061113682611bbd565b5460ff8116825261115160ff602084019260081c1682611d1f565b5160038110156111f2577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff016111ca57600160ff8251161b82166111a25790600160ff819351161b1795019461109a565b60048b7ff67bc7c4000000000000000000000000000000000000000000000000000000008152fd5b60048b7fca31867a000000000000000000000000000000000000000000000000000000008152fd5b60248c7f4e487b710000000000000000000000000000000000000000000000000000000081526021600452fd5b6040513d8b823e3d90fd5b60248a7f4e487b710000000000000000000000000000000000000000000000000000000081526032600452fd5b8280fd5b6004887fa75d88af000000000000000000000000000000000000000000000000000000008152fd5b6004887f71253a25000000000000000000000000000000000000000000000000000000008152fd5b72c11c11c11c11c11c11c11c11c11c11c11c11c1330315610fce5760048a7fda0f08e8000000000000000000000000000000000000000000000000000000008152fd5b9050898b52600260205260ff600360408d2001915116908054821015611336579073ffffffffffffffffffffffffffffffffffffffff918c5260208c2001541633148b610fc7565b60248c7f4e487b710000000000000000000000000000000000000000000000000000000081526032600452fd5b7f0f01ce85000000000000000000000000000000000000000000000000000000006000526004524660245260446000fd5b87907f93df584c0000000000000000000000000000000000000000000000000000000060005260045260245260446000fd5b7f8e1192e1000000000000000000000000000000000000000000000000000000006000526004523660245260446000fd5b84518060051b908082046020149015171561083a5761141590611e51565b908651918260051b928084046020149015171561083a5761143f9261143991611e5f565b90611e5f565b610f1b565b34610a365760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610a365761147b611b24565b6024359073ffffffffffffffffffffffffffffffffffffffff82168203610a365760ff90600060206040516114af81611bbd565b828152015216600052600360205273ffffffffffffffffffffffffffffffffffffffff604060002091166000526020526040600020604051906114f182611bbd565b5460ff8116825261150c60ff602084019260081c1682611d1f565b60ff60405192511682525190600382101561032e576040916020820152f35b34610a365760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610a365760408051906115698183611c11565b601982527f4d756c74694f4352334261736548656c70657220312e302e30000000000000006020830152805180926020825280519081602084015260005b8281106115e45750506000828201840152601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168101030190f35b6020828201810151878301870152869450016115a7565b34610a365760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610a365736604411610a365760443567ffffffffffffffff8111610a3657611652903690600401611af6565b604051602092916116638483611c11565b60008252600036813760ff600454169182600052600285526116886040600020611d77565b936004359261169681611e43565b9560608101968751611ab1575b8036036113c657508051858103611a7f57507f000000000000000000000000000000000000000000000000000000000000000046810361136357508560005260038852604060002073ffffffffffffffffffffffffffffffffffffffff331660005288526040600020966040519761171a89611bbd565b5460ff8116895261173460ff8b8b019260081c1682611d1f565b5197600389101561032e576002600099149081611a39575b50156119f6575b5161179a575b86867f198d6990ef96613a9026203077e422916918b03ff47f0be6bee7b02d8e139ef06040888c825191825267ffffffffffffffff6024351690820152a280f35b60ff6117ac818a875194015116611e6c565b16036119ce576117bb81611ce5565b906117c96040519283611c11565b8082528782019236828201116119ca578188928a92863783010152519020604051868101918252604060048183013760608152611807608082611c11565b519020849082519286925b84841061181f5750611759565b8884101561199d578888608061183682881a611e7e565b6118408887611e00565b5161184b8988611e00565b519060ff604051938a855216868401526040830152606082015282805260015afa1561199257875187895260038a5273ffffffffffffffffffffffffffffffffffffffff60408a2091168952895260408820604051906118aa82611bbd565b5460ff811682526118c460ff8c84019260081c1682611d1f565b516003811015611965577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0161193d57600160ff8251161b82166119155790600160ff819351161b17930192611812565b6004897ff67bc7c4000000000000000000000000000000000000000000000000000000008152fd5b6004897fca31867a000000000000000000000000000000000000000000000000000000008152fd5b60248a7f4e487b710000000000000000000000000000000000000000000000000000000081526021600452fd5b6040513d89823e3d90fd5b6024887f4e487b710000000000000000000000000000000000000000000000000000000081526032600452fd5b8780fd5b6004867f71253a25000000000000000000000000000000000000000000000000000000008152fd5b72c11c11c11c11c11c11c11c11c11c11c11c11c1330315611753576004887fda0f08e8000000000000000000000000000000000000000000000000000000008152fd5b905087895260028a5260ff600360408b200191511690805482101561122a579073ffffffffffffffffffffffffffffffffffffffff918a528a8a2001541633148a61174c565b85907f93df584c0000000000000000000000000000000000000000000000000000000060005260045260245260446000fd5b84518060051b908082048b149015171561083a57611ace90611e51565b908551918260051b928084048c149015171561083a57611af19261143991611e5f565b6116a3565b9181601f84011215610a365782359167ffffffffffffffff8311610a365760208381860195010111610a3657565b6004359060ff82168203610a3657565b359060ff82168203610a3657565b9181601f84011215610a365782359167ffffffffffffffff8311610a36576020808501948460051b010111610a3657565b906020808351928381520192019060005b818110611b915750505090565b825173ffffffffffffffffffffffffffffffffffffffff16845260209384019390920191600101611b84565b6040810190811067ffffffffffffffff82111761053757604052565b6060810190811067ffffffffffffffff82111761053757604052565b6080810190811067ffffffffffffffff82111761053757604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761053757604052565b67ffffffffffffffff81116105375760051b60200190565b9080601f83011215610a3657813590611c8282611c52565b92611c906040519485611c11565b82845260208085019360051b820101918211610a3657602001915b818310611cb85750505090565b823573ffffffffffffffffffffffffffffffffffffffff81168103610a3657815260209283019201611cab565b67ffffffffffffffff811161053757601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b600382101561032e5752565b929190611d3781611c52565b93611d456040519586611c11565b602085838152019160051b8101928311610a3657905b828210611d6757505050565b8135815260209182019101611d5b565b90604051611d8481611bf5565b606060ff600183958054855201548181166020850152818160081c16604085015260101c161515910152565b906020825491828152019160005260206000209060005b818110611dd45750505090565b825473ffffffffffffffffffffffffffffffffffffffff16845260209093019260019283019201611dc7565b8051821015611e145760209160051b010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b608401908160841161083a57565b60a001908160a01161083a57565b9190820180921161083a57565b60ff60019116019060ff821161083a57565b60ff601b9116019060ff821161083a57565b73ffffffffffffffffffffffffffffffffffffffff600154163303611eb157565b7f2b5c74de0000000000000000000000000000000000000000000000000000000060005260046000fd5b91909160005b8351811015611f455760019060ff831660005260036020526000604080822073ffffffffffffffffffffffffffffffffffffffff611f1f858a611e00565b511673ffffffffffffffffffffffffffffffffffffffff16835260205281205501611ee1565b5050905056fea164736f6c634300081a000a",
}
var MultiOCR3HelperABI = MultiOCR3HelperMetaData.ABI
diff --git a/core/gethwrappers/ccip/generated/offramp/offramp.go b/core/gethwrappers/ccip/generated/offramp/offramp.go
index 3370efd2261..08a10e52878 100644
--- a/core/gethwrappers/ccip/generated/offramp/offramp.go
+++ b/core/gethwrappers/ccip/generated/offramp/offramp.go
@@ -156,8 +156,8 @@ type OffRampStaticConfig struct {
}
var OffRampMetaData = &bind.MetaData{
- ABI: "[{\"type\":\"constructor\",\"inputs\":[{\"name\":\"staticConfig\",\"type\":\"tuple\",\"internalType\":\"structOffRamp.StaticConfig\",\"components\":[{\"name\":\"chainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"gasForCallExactCheck\",\"type\":\"uint16\",\"internalType\":\"uint16\"},{\"name\":\"rmnRemote\",\"type\":\"address\",\"internalType\":\"contractIRMNRemote\"},{\"name\":\"tokenAdminRegistry\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"nonceManager\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"name\":\"dynamicConfig\",\"type\":\"tuple\",\"internalType\":\"structOffRamp.DynamicConfig\",\"components\":[{\"name\":\"feeQuoter\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"permissionLessExecutionThresholdSeconds\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"isRMNVerificationDisabled\",\"type\":\"bool\",\"internalType\":\"bool\"},{\"name\":\"messageInterceptor\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"name\":\"sourceChainConfigs\",\"type\":\"tuple[]\",\"internalType\":\"structOffRamp.SourceChainConfigArgs[]\",\"components\":[{\"name\":\"router\",\"type\":\"address\",\"internalType\":\"contractIRouter\"},{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"isEnabled\",\"type\":\"bool\",\"internalType\":\"bool\"},{\"name\":\"onRamp\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]}],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"acceptOwnership\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"applySourceChainConfigUpdates\",\"inputs\":[{\"name\":\"sourceChainConfigUpdates\",\"type\":\"tuple[]\",\"internalType\":\"structOffRamp.SourceChainConfigArgs[]\",\"components\":[{\"name\":\"router\",\"type\":\"address\",\"internalType\":\"contractIRouter\"},{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"isEnabled\",\"type\":\"bool\",\"internalType\":\"bool\"},{\"name\":\"onRamp\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"ccipReceive\",\"inputs\":[{\"name\":\"\",\"type\":\"tuple\",\"internalType\":\"structClient.Any2EVMMessage\",\"components\":[{\"name\":\"messageId\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"sender\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"data\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"destTokenAmounts\",\"type\":\"tuple[]\",\"internalType\":\"structClient.EVMTokenAmount[]\",\"components\":[{\"name\":\"token\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"amount\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]}]}],\"outputs\":[],\"stateMutability\":\"pure\"},{\"type\":\"function\",\"name\":\"commit\",\"inputs\":[{\"name\":\"reportContext\",\"type\":\"bytes32[2]\",\"internalType\":\"bytes32[2]\"},{\"name\":\"report\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"rs\",\"type\":\"bytes32[]\",\"internalType\":\"bytes32[]\"},{\"name\":\"ss\",\"type\":\"bytes32[]\",\"internalType\":\"bytes32[]\"},{\"name\":\"rawVs\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"execute\",\"inputs\":[{\"name\":\"reportContext\",\"type\":\"bytes32[2]\",\"internalType\":\"bytes32[2]\"},{\"name\":\"report\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"executeSingleMessage\",\"inputs\":[{\"name\":\"message\",\"type\":\"tuple\",\"internalType\":\"structInternal.Any2EVMRampMessage\",\"components\":[{\"name\":\"header\",\"type\":\"tuple\",\"internalType\":\"structInternal.RampMessageHeader\",\"components\":[{\"name\":\"messageId\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"destChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"sequenceNumber\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"nonce\",\"type\":\"uint64\",\"internalType\":\"uint64\"}]},{\"name\":\"sender\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"data\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"receiver\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"gasLimit\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"tokenAmounts\",\"type\":\"tuple[]\",\"internalType\":\"structInternal.Any2EVMTokenTransfer[]\",\"components\":[{\"name\":\"sourcePoolAddress\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"destTokenAddress\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"destGasAmount\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"extraData\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"amount\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]}]},{\"name\":\"offchainTokenData\",\"type\":\"bytes[]\",\"internalType\":\"bytes[]\"},{\"name\":\"tokenGasOverrides\",\"type\":\"uint32[]\",\"internalType\":\"uint32[]\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"getAllSourceChainConfigs\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint64[]\",\"internalType\":\"uint64[]\"},{\"name\":\"\",\"type\":\"tuple[]\",\"internalType\":\"structOffRamp.SourceChainConfig[]\",\"components\":[{\"name\":\"router\",\"type\":\"address\",\"internalType\":\"contractIRouter\"},{\"name\":\"isEnabled\",\"type\":\"bool\",\"internalType\":\"bool\"},{\"name\":\"minSeqNr\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"onRamp\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getDynamicConfig\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"tuple\",\"internalType\":\"structOffRamp.DynamicConfig\",\"components\":[{\"name\":\"feeQuoter\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"permissionLessExecutionThresholdSeconds\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"isRMNVerificationDisabled\",\"type\":\"bool\",\"internalType\":\"bool\"},{\"name\":\"messageInterceptor\",\"type\":\"address\",\"internalType\":\"address\"}]}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getExecutionState\",\"inputs\":[{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"sequenceNumber\",\"type\":\"uint64\",\"internalType\":\"uint64\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint8\",\"internalType\":\"enumInternal.MessageExecutionState\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getLatestPriceSequenceNumber\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint64\",\"internalType\":\"uint64\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getMerkleRoot\",\"inputs\":[{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"root\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getSourceChainConfig\",\"inputs\":[{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"}],\"outputs\":[{\"name\":\"\",\"type\":\"tuple\",\"internalType\":\"structOffRamp.SourceChainConfig\",\"components\":[{\"name\":\"router\",\"type\":\"address\",\"internalType\":\"contractIRouter\"},{\"name\":\"isEnabled\",\"type\":\"bool\",\"internalType\":\"bool\"},{\"name\":\"minSeqNr\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"onRamp\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getStaticConfig\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"tuple\",\"internalType\":\"structOffRamp.StaticConfig\",\"components\":[{\"name\":\"chainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"gasForCallExactCheck\",\"type\":\"uint16\",\"internalType\":\"uint16\"},{\"name\":\"rmnRemote\",\"type\":\"address\",\"internalType\":\"contractIRMNRemote\"},{\"name\":\"tokenAdminRegistry\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"nonceManager\",\"type\":\"address\",\"internalType\":\"address\"}]}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"latestConfigDetails\",\"inputs\":[{\"name\":\"ocrPluginType\",\"type\":\"uint8\",\"internalType\":\"uint8\"}],\"outputs\":[{\"name\":\"ocrConfig\",\"type\":\"tuple\",\"internalType\":\"structMultiOCR3Base.OCRConfig\",\"components\":[{\"name\":\"configInfo\",\"type\":\"tuple\",\"internalType\":\"structMultiOCR3Base.ConfigInfo\",\"components\":[{\"name\":\"configDigest\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"F\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"n\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"isSignatureVerificationEnabled\",\"type\":\"bool\",\"internalType\":\"bool\"}]},{\"name\":\"signers\",\"type\":\"address[]\",\"internalType\":\"address[]\"},{\"name\":\"transmitters\",\"type\":\"address[]\",\"internalType\":\"address[]\"}]}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"manuallyExecute\",\"inputs\":[{\"name\":\"reports\",\"type\":\"tuple[]\",\"internalType\":\"structInternal.ExecutionReport[]\",\"components\":[{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"messages\",\"type\":\"tuple[]\",\"internalType\":\"structInternal.Any2EVMRampMessage[]\",\"components\":[{\"name\":\"header\",\"type\":\"tuple\",\"internalType\":\"structInternal.RampMessageHeader\",\"components\":[{\"name\":\"messageId\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"destChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"sequenceNumber\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"nonce\",\"type\":\"uint64\",\"internalType\":\"uint64\"}]},{\"name\":\"sender\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"data\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"receiver\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"gasLimit\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"tokenAmounts\",\"type\":\"tuple[]\",\"internalType\":\"structInternal.Any2EVMTokenTransfer[]\",\"components\":[{\"name\":\"sourcePoolAddress\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"destTokenAddress\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"destGasAmount\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"extraData\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"amount\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]}]},{\"name\":\"offchainTokenData\",\"type\":\"bytes[][]\",\"internalType\":\"bytes[][]\"},{\"name\":\"proofs\",\"type\":\"bytes32[]\",\"internalType\":\"bytes32[]\"},{\"name\":\"proofFlagBits\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"name\":\"gasLimitOverrides\",\"type\":\"tuple[][]\",\"internalType\":\"structOffRamp.GasLimitOverride[][]\",\"components\":[{\"name\":\"receiverExecutionGasLimit\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"tokenGasOverrides\",\"type\":\"uint32[]\",\"internalType\":\"uint32[]\"}]}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"owner\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"setDynamicConfig\",\"inputs\":[{\"name\":\"dynamicConfig\",\"type\":\"tuple\",\"internalType\":\"structOffRamp.DynamicConfig\",\"components\":[{\"name\":\"feeQuoter\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"permissionLessExecutionThresholdSeconds\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"isRMNVerificationDisabled\",\"type\":\"bool\",\"internalType\":\"bool\"},{\"name\":\"messageInterceptor\",\"type\":\"address\",\"internalType\":\"address\"}]}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setOCR3Configs\",\"inputs\":[{\"name\":\"ocrConfigArgs\",\"type\":\"tuple[]\",\"internalType\":\"structMultiOCR3Base.OCRConfigArgs[]\",\"components\":[{\"name\":\"configDigest\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"ocrPluginType\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"F\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"isSignatureVerificationEnabled\",\"type\":\"bool\",\"internalType\":\"bool\"},{\"name\":\"signers\",\"type\":\"address[]\",\"internalType\":\"address[]\"},{\"name\":\"transmitters\",\"type\":\"address[]\",\"internalType\":\"address[]\"}]}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"transferOwnership\",\"inputs\":[{\"name\":\"to\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"typeAndVersion\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"string\",\"internalType\":\"string\"}],\"stateMutability\":\"view\"},{\"type\":\"event\",\"name\":\"AlreadyAttempted\",\"inputs\":[{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"indexed\":false,\"internalType\":\"uint64\"},{\"name\":\"sequenceNumber\",\"type\":\"uint64\",\"indexed\":false,\"internalType\":\"uint64\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"CommitReportAccepted\",\"inputs\":[{\"name\":\"merkleRoots\",\"type\":\"tuple[]\",\"indexed\":false,\"internalType\":\"structInternal.MerkleRoot[]\",\"components\":[{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"onRampAddress\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"minSeqNr\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"maxSeqNr\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"merkleRoot\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}]},{\"name\":\"priceUpdates\",\"type\":\"tuple\",\"indexed\":false,\"internalType\":\"structInternal.PriceUpdates\",\"components\":[{\"name\":\"tokenPriceUpdates\",\"type\":\"tuple[]\",\"internalType\":\"structInternal.TokenPriceUpdate[]\",\"components\":[{\"name\":\"sourceToken\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"usdPerToken\",\"type\":\"uint224\",\"internalType\":\"uint224\"}]},{\"name\":\"gasPriceUpdates\",\"type\":\"tuple[]\",\"internalType\":\"structInternal.GasPriceUpdate[]\",\"components\":[{\"name\":\"destChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"usdPerUnitGas\",\"type\":\"uint224\",\"internalType\":\"uint224\"}]}]}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"ConfigSet\",\"inputs\":[{\"name\":\"ocrPluginType\",\"type\":\"uint8\",\"indexed\":false,\"internalType\":\"uint8\"},{\"name\":\"configDigest\",\"type\":\"bytes32\",\"indexed\":false,\"internalType\":\"bytes32\"},{\"name\":\"signers\",\"type\":\"address[]\",\"indexed\":false,\"internalType\":\"address[]\"},{\"name\":\"transmitters\",\"type\":\"address[]\",\"indexed\":false,\"internalType\":\"address[]\"},{\"name\":\"F\",\"type\":\"uint8\",\"indexed\":false,\"internalType\":\"uint8\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"DynamicConfigSet\",\"inputs\":[{\"name\":\"dynamicConfig\",\"type\":\"tuple\",\"indexed\":false,\"internalType\":\"structOffRamp.DynamicConfig\",\"components\":[{\"name\":\"feeQuoter\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"permissionLessExecutionThresholdSeconds\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"isRMNVerificationDisabled\",\"type\":\"bool\",\"internalType\":\"bool\"},{\"name\":\"messageInterceptor\",\"type\":\"address\",\"internalType\":\"address\"}]}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"ExecutionStateChanged\",\"inputs\":[{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"indexed\":true,\"internalType\":\"uint64\"},{\"name\":\"sequenceNumber\",\"type\":\"uint64\",\"indexed\":true,\"internalType\":\"uint64\"},{\"name\":\"messageId\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"bytes32\"},{\"name\":\"messageHash\",\"type\":\"bytes32\",\"indexed\":false,\"internalType\":\"bytes32\"},{\"name\":\"state\",\"type\":\"uint8\",\"indexed\":false,\"internalType\":\"enumInternal.MessageExecutionState\"},{\"name\":\"returnData\",\"type\":\"bytes\",\"indexed\":false,\"internalType\":\"bytes\"},{\"name\":\"gasUsed\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"OwnershipTransferRequested\",\"inputs\":[{\"name\":\"from\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"to\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"OwnershipTransferred\",\"inputs\":[{\"name\":\"from\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"to\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"RootRemoved\",\"inputs\":[{\"name\":\"root\",\"type\":\"bytes32\",\"indexed\":false,\"internalType\":\"bytes32\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"SkippedAlreadyExecutedMessage\",\"inputs\":[{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"indexed\":false,\"internalType\":\"uint64\"},{\"name\":\"sequenceNumber\",\"type\":\"uint64\",\"indexed\":false,\"internalType\":\"uint64\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"SkippedReportExecution\",\"inputs\":[{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"indexed\":false,\"internalType\":\"uint64\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"SourceChainConfigSet\",\"inputs\":[{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"indexed\":true,\"internalType\":\"uint64\"},{\"name\":\"sourceConfig\",\"type\":\"tuple\",\"indexed\":false,\"internalType\":\"structOffRamp.SourceChainConfig\",\"components\":[{\"name\":\"router\",\"type\":\"address\",\"internalType\":\"contractIRouter\"},{\"name\":\"isEnabled\",\"type\":\"bool\",\"internalType\":\"bool\"},{\"name\":\"minSeqNr\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"onRamp\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"SourceChainSelectorAdded\",\"inputs\":[{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"indexed\":false,\"internalType\":\"uint64\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"StaticConfigSet\",\"inputs\":[{\"name\":\"staticConfig\",\"type\":\"tuple\",\"indexed\":false,\"internalType\":\"structOffRamp.StaticConfig\",\"components\":[{\"name\":\"chainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"gasForCallExactCheck\",\"type\":\"uint16\",\"internalType\":\"uint16\"},{\"name\":\"rmnRemote\",\"type\":\"address\",\"internalType\":\"contractIRMNRemote\"},{\"name\":\"tokenAdminRegistry\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"nonceManager\",\"type\":\"address\",\"internalType\":\"address\"}]}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Transmitted\",\"inputs\":[{\"name\":\"ocrPluginType\",\"type\":\"uint8\",\"indexed\":true,\"internalType\":\"uint8\"},{\"name\":\"configDigest\",\"type\":\"bytes32\",\"indexed\":false,\"internalType\":\"bytes32\"},{\"name\":\"sequenceNumber\",\"type\":\"uint64\",\"indexed\":false,\"internalType\":\"uint64\"}],\"anonymous\":false},{\"type\":\"error\",\"name\":\"CanOnlySelfCall\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"CannotTransferToSelf\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"CommitOnRampMismatch\",\"inputs\":[{\"name\":\"reportOnRamp\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"configOnRamp\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]},{\"type\":\"error\",\"name\":\"ConfigDigestMismatch\",\"inputs\":[{\"name\":\"expected\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"actual\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}]},{\"type\":\"error\",\"name\":\"CursedByRMN\",\"inputs\":[{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"}]},{\"type\":\"error\",\"name\":\"EmptyBatch\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"EmptyReport\",\"inputs\":[{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"}]},{\"type\":\"error\",\"name\":\"ExecutionError\",\"inputs\":[{\"name\":\"messageId\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"err\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]},{\"type\":\"error\",\"name\":\"ForkedChain\",\"inputs\":[{\"name\":\"expected\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"actual\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"type\":\"error\",\"name\":\"InvalidConfig\",\"inputs\":[{\"name\":\"errorType\",\"type\":\"uint8\",\"internalType\":\"enumMultiOCR3Base.InvalidConfigErrorType\"}]},{\"type\":\"error\",\"name\":\"InvalidDataLength\",\"inputs\":[{\"name\":\"expected\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"got\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"type\":\"error\",\"name\":\"InvalidInterval\",\"inputs\":[{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"min\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"max\",\"type\":\"uint64\",\"internalType\":\"uint64\"}]},{\"type\":\"error\",\"name\":\"InvalidManualExecutionGasLimit\",\"inputs\":[{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"messageId\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"newLimit\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"type\":\"error\",\"name\":\"InvalidManualExecutionTokenGasOverride\",\"inputs\":[{\"name\":\"messageId\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"tokenIndex\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"oldLimit\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"tokenGasOverride\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"type\":\"error\",\"name\":\"InvalidMessageDestChainSelector\",\"inputs\":[{\"name\":\"messageDestChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"}]},{\"type\":\"error\",\"name\":\"InvalidNewState\",\"inputs\":[{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"sequenceNumber\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"newState\",\"type\":\"uint8\",\"internalType\":\"enumInternal.MessageExecutionState\"}]},{\"type\":\"error\",\"name\":\"InvalidOnRampUpdate\",\"inputs\":[{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"}]},{\"type\":\"error\",\"name\":\"InvalidProof\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidRoot\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"LeavesCannotBeEmpty\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ManualExecutionGasAmountCountMismatch\",\"inputs\":[{\"name\":\"messageId\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"sequenceNumber\",\"type\":\"uint64\",\"internalType\":\"uint64\"}]},{\"type\":\"error\",\"name\":\"ManualExecutionGasLimitMismatch\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ManualExecutionNotYetEnabled\",\"inputs\":[{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"}]},{\"type\":\"error\",\"name\":\"MessageValidationError\",\"inputs\":[{\"name\":\"errorReason\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]},{\"type\":\"error\",\"name\":\"MustBeProposedOwner\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"NonUniqueSignatures\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"NotACompatiblePool\",\"inputs\":[{\"name\":\"notPool\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"OnlyCallableByOwner\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"OracleCannotBeZeroAddress\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"OwnerCannotBeZero\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ReceiverError\",\"inputs\":[{\"name\":\"err\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]},{\"type\":\"error\",\"name\":\"ReleaseOrMintBalanceMismatch\",\"inputs\":[{\"name\":\"amountReleased\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"balancePre\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"balancePost\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"type\":\"error\",\"name\":\"RootAlreadyCommitted\",\"inputs\":[{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"merkleRoot\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}]},{\"type\":\"error\",\"name\":\"RootNotCommitted\",\"inputs\":[{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"}]},{\"type\":\"error\",\"name\":\"SignatureVerificationNotAllowedInExecutionPlugin\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"SignatureVerificationRequiredInCommitPlugin\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"SignaturesOutOfRegistration\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"SourceChainNotEnabled\",\"inputs\":[{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"}]},{\"type\":\"error\",\"name\":\"SourceChainSelectorMismatch\",\"inputs\":[{\"name\":\"reportSourceChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"messageSourceChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"}]},{\"type\":\"error\",\"name\":\"StaleCommitReport\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"StaticConfigCannotBeChanged\",\"inputs\":[{\"name\":\"ocrPluginType\",\"type\":\"uint8\",\"internalType\":\"uint8\"}]},{\"type\":\"error\",\"name\":\"TokenDataMismatch\",\"inputs\":[{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"sequenceNumber\",\"type\":\"uint64\",\"internalType\":\"uint64\"}]},{\"type\":\"error\",\"name\":\"TokenHandlingError\",\"inputs\":[{\"name\":\"target\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"err\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]},{\"type\":\"error\",\"name\":\"UnauthorizedSigner\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"UnauthorizedTransmitter\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"UnexpectedTokenData\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"WrongMessageLength\",\"inputs\":[{\"name\":\"expected\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"actual\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"type\":\"error\",\"name\":\"WrongNumberOfSignatures\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ZeroAddressNotAllowed\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ZeroChainSelectorNotAllowed\",\"inputs\":[]}]",
- Bin: "0x6101408060405234610848576162ef803803809161001d828561087e565b833981019080820361014081126108485760a08112610848576040519060a082016001600160401b0381118382101761084d5760405261005c836108a1565b825260208301519261ffff84168403610848576020830193845260408101516001600160a01b0381168103610848576040840190815261009e606083016108b5565b946060850195865260806100b38185016108b5565b86820190815294609f19011261084857604051946100d086610863565b6100dc60a085016108b5565b865260c08401519463ffffffff86168603610848576020870195865261010460e086016108c9565b976040880198895261011961010087016108b5565b6060890190815261012087015190966001600160401b03821161084857018a601f820112156108485780519a6001600160401b038c1161084d578b60051b916020806040519e8f9061016d8388018361087e565b81520193820101908282116108485760208101935b828510610748575050505050331561073757600180546001600160a01b031916331790554660805284516001600160a01b0316158015610725575b8015610713575b6106f15782516001600160401b0316156107025782516001600160401b0390811660a090815286516001600160a01b0390811660c0528351811660e0528451811661010052865161ffff90811661012052604080519751909416875296519096166020860152955185169084015251831660608301525190911660808201527fb0fa1fb01508c5097c502ad056fd77018870c9be9a86d9e56b6b471862d7c5b79190a182516001600160a01b0316156106f1579151600480548351865160ff60c01b90151560c01b1663ffffffff60a01b60a09290921b919091166001600160a01b039485166001600160c81b0319909316831717179091558351600580549184166001600160a01b031990921691909117905560408051918252925163ffffffff166020820152935115159184019190915290511660608201527fcbb53bda7106a610de67df506ac86b65c44d5afac0fd2b11070dc2d61a6f2dee90608090a160005b815181101561066b576020600582901b8301810151908101516001600160401b031690600090821561065c5780516001600160a01b03161561064d57828252600860205260408220906060810151600183019361038585546108d6565b6105ee578354600160a81b600160e81b031916600160a81b1784556040518681527ff4c1390c70e5c0f491ae1ccbc06f9117cbbadf2767b247b3bc203280f24c0fb990602090a15b815180159081156105c3575b506105b4578151916001600160401b0383116105a0576103f986546108d6565b601f811161055b575b50602091601f84116001146104e257926001989796949281926000805160206162cf8339815191529795926104d7575b5050600019600383901b1c191690881b1783555b60408101518254915160a089811b8a9003801960ff60a01b1990951693151590911b60ff60a01b169290921792909216911617815561048484610993565b506104ce6040519283926020845254888060a01b038116602085015260ff8160a01c1615156040850152888060401b039060a81c16606084015260808084015260a0830190610910565b0390a201610328565b015190503880610432565b9190601f198416878452828420935b818110610543575092600199989795939285926000805160206162cf83398151915298968c951061052a575b505050811b018355610446565b015160001960f88460031b161c1916905538808061051d565b929360206001819287860151815501950193016104f1565b86835260208320601f850160051c81019160208610610596575b601f0160051c01905b81811061058b5750610402565b83815560010161057e565b9091508190610575565b634e487b7160e01b82526041600452602482fd5b6342bcdf7f60e11b8152600490fd5b905060208301206040516020810190838252602081526105e460408261087e565b51902014386103d9565b835460a81c6001600160401b0316600114158061061f575b156103cd57632105803760e11b81526004869052602490fd5b50604051610638816106318189610910565b038261087e565b60208151910120825160208401201415610606565b6342bcdf7f60e11b8252600482fd5b63c656089560e01b8252600482fd5b6040516158a89081610a2782396080518161368c015260a05181818161048e01526141a1015260c0518181816104e401528181612cba0152818161310e015261413b015260e051818181610513015261497e01526101005181818161054201526145640152610120518181816104b50152818161243401528181614a7101526155dd0152f35b6342bcdf7f60e11b60005260046000fd5b63c656089560e01b60005260046000fd5b5081516001600160a01b0316156101c4565b5080516001600160a01b0316156101bd565b639b15e16f60e01b60005260046000fd5b84516001600160401b0381116108485782016080818603601f190112610848576040519061077582610863565b60208101516001600160a01b0381168103610848578252610798604082016108a1565b60208301526107a9606082016108c9565b604083015260808101516001600160401b03811161084857602091010185601f820112156108485780516001600160401b03811161084d57604051916107f9601f8301601f19166020018461087e565b81835287602083830101116108485760005b8281106108335750509181600060208096949581960101526060820152815201940193610182565b8060208092840101518282870101520161080b565b600080fd5b634e487b7160e01b600052604160045260246000fd5b608081019081106001600160401b0382111761084d57604052565b601f909101601f19168101906001600160401b0382119082101761084d57604052565b51906001600160401b038216820361084857565b51906001600160a01b038216820361084857565b5190811515820361084857565b90600182811c92168015610906575b60208310146108f057565b634e487b7160e01b600052602260045260246000fd5b91607f16916108e5565b60009291815491610920836108d6565b8083529260018116908115610976575060011461093c57505050565b60009081526020812093945091925b83831061095c575060209250010190565b60018160209294939454838587010152019101919061094b565b915050602093945060ff929192191683830152151560051b010190565b80600052600760205260406000205415600014610a20576006546801000000000000000081101561084d576001810180600655811015610a0a577ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f0181905560065460009182526007602052604090912055600190565b634e487b7160e01b600052603260045260246000fd5b5060009056fe6080604052600436101561001257600080fd5b60003560e01c806304666f9c1461015757806306285c6914610152578063181f5a771461014d5780633f4b04aa146101485780635215505b146101435780635e36480c1461013e5780635e7bb0081461013957806360987c20146101345780637437ff9f1461012f57806379ba50971461012a5780637edf52f41461012557806385572ffb146101205780638da5cb5b1461011b578063c673e58414610116578063ccd37ba314610111578063de5e0b9a1461010c578063e9d68a8e14610107578063f2fde38b14610102578063f58e03fc146100fd5763f716f99f146100f857600080fd5b6118d4565b6117b7565b61172c565b611691565b6115f5565b611571565b6114c6565b6113de565b6113a8565b6111e2565b611162565b6110b9565b61103e565b610e39565b6108ce565b610789565b61067c565b61061d565b61043d565b61031f565b634e487b7160e01b600052604160045260246000fd5b608081019081106001600160401b0382111761018d57604052565b61015c565b60a081019081106001600160401b0382111761018d57604052565b604081019081106001600160401b0382111761018d57604052565b606081019081106001600160401b0382111761018d57604052565b90601f801991011681019081106001600160401b0382111761018d57604052565b6040519061021360c0836101e3565b565b6040519061021360a0836101e3565b60405190610213610100836101e3565b604051906102136040836101e3565b6001600160401b03811161018d5760051b60200190565b6001600160a01b0381160361026b57565b600080fd5b600435906001600160401b038216820361026b57565b35906001600160401b038216820361026b57565b8015150361026b57565b35906102138261029a565b6001600160401b03811161018d57601f01601f191660200190565b9291926102d6826102af565b916102e460405193846101e3565b82948184528183011161026b578281602093846000960137010152565b9080601f8301121561026b5781602061031c933591016102ca565b90565b3461026b57602036600319011261026b576004356001600160401b03811161026b573660238201121561026b5780600401359061035b82610243565b9061036960405192836101e3565b8282526024602083019360051b8201019036821161026b5760248101935b8285106103995761039784611a0f565b005b84356001600160401b03811161026b5782016080602319823603011261026b57604051916103c683610172565b60248201356103d48161025a565b83526103e260448301610286565b602084015260648201356103f58161029a565b60408401526084820135926001600160401b03841161026b57610422602094936024869536920101610301565b6060820152815201940193610387565b600091031261026b57565b3461026b57600036600319011261026b576000608060405161045e81610192565b82815282602082015282604082015282606082015201526105bc60405161048481610192565b6001600160401b037f000000000000000000000000000000000000000000000000000000000000000016815261ffff7f00000000000000000000000000000000000000000000000000000000000000001660208201526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660408201526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660608201526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660808201526040519182918291909160806001600160a01b038160a08401956001600160401b03815116855261ffff6020820151166020860152826040820151166040860152826060820151166060860152015116910152565b0390f35b604051906105cf6020836101e3565b60008252565b60005b8381106105e85750506000910152565b81810151838201526020016105d8565b90602091610611815180928185528580860191016105d5565b601f01601f1916010190565b3461026b57600036600319011261026b576105bc604080519061064081836101e3565b601182527f4f666652616d7020312e362e302d6465760000000000000000000000000000006020830152519182916020835260208301906105f8565b3461026b57600036600319011261026b5760206001600160401b03600b5416604051908152f35b906080606061031c936001600160a01b0381511684526020810151151560208501526001600160401b03604082015116604085015201519181606082015201906105f8565b6040810160408252825180915260206060830193019060005b81811061076a575050506020818303910152815180825260208201916020808360051b8301019401926000915b83831061073d57505050505090565b909192939460208061075b600193601f1986820301875289516106a3565b9701930193019193929061072e565b82516001600160401b0316855260209485019490920191600101610701565b3461026b57600036600319011261026b576006546107a681610243565b906107b460405192836101e3565b808252601f196107c382610243565b0160005b8181106108855750506107d981611ce2565b9060005b8181106107f55750506105bc604051928392836106e8565b8061082b610813610807600194614022565b6001600160401b031690565b61081d8387611d3c565b906001600160401b03169052565b61086961086461084b61083e8488611d3c565b516001600160401b031690565b6001600160401b03166000526008602052604060002090565b611e28565b6108738287611d3c565b5261087e8186611d3c565b50016107dd565b602090610890611cbb565b828287010152016107c7565b634e487b7160e01b600052602160045260246000fd5b600411156108bc57565b61089c565b9060048210156108bc5752565b3461026b57604036600319011261026b576108e7610270565b602435906001600160401b038216820361026b5760209161090791611ec4565b61091460405180926108c1565bf35b91908260a091031261026b5760405161092e81610192565b60806109738183958035855261094660208201610286565b602086015261095760408201610286565b604086015261096860608201610286565b606086015201610286565b910152565b35906102138261025a565b63ffffffff81160361026b57565b359061021382610983565b81601f8201121561026b578035906109b382610243565b926109c160405194856101e3565b82845260208085019360051b8301019181831161026b5760208101935b8385106109ed57505050505090565b84356001600160401b03811161026b57820160a0818503601f19011261026b5760405191610a1a83610192565b60208201356001600160401b03811161026b57856020610a3c92850101610301565b83526040820135610a4c8161025a565b6020840152610a5d60608301610991565b60408401526080820135926001600160401b03841161026b5760a083610a8a886020809881980101610301565b6060840152013560808201528152019401936109de565b9190916101408184031261026b57610ab7610204565b92610ac28183610916565b845260a08201356001600160401b03811161026b5781610ae3918401610301565b602085015260c08201356001600160401b03811161026b5781610b07918401610301565b6040850152610b1860e08301610978565b606085015261010082013560808501526101208201356001600160401b03811161026b57610b46920161099c565b60a0830152565b9080601f8301121561026b578135610b6481610243565b92610b7260405194856101e3565b81845260208085019260051b8201019183831161026b5760208201905b838210610b9e57505050505090565b81356001600160401b03811161026b57602091610bc087848094880101610aa1565b815201910190610b8f565b81601f8201121561026b57803590610be282610243565b92610bf060405194856101e3565b82845260208085019360051b8301019181831161026b5760208101935b838510610c1c57505050505090565b84356001600160401b03811161026b57820183603f8201121561026b576020810135610c4781610243565b91610c5560405193846101e3565b8183526020808085019360051b830101019186831161026b5760408201905b838210610c8e575050509082525060209485019401610c0d565b81356001600160401b03811161026b57602091610cb28a8480809589010101610301565b815201910190610c74565b929190610cc981610243565b93610cd760405195866101e3565b602085838152019160051b810192831161026b57905b828210610cf957505050565b8135815260209182019101610ced565b9080601f8301121561026b5781602061031c93359101610cbd565b81601f8201121561026b57803590610d3b82610243565b92610d4960405194856101e3565b82845260208085019360051b8301019181831161026b5760208101935b838510610d7557505050505090565b84356001600160401b03811161026b57820160a0818503601f19011261026b57610d9d610215565b91610daa60208301610286565b835260408201356001600160401b03811161026b57856020610dce92850101610b4d565b602084015260608201356001600160401b03811161026b57856020610df592850101610bcb565b60408401526080820135926001600160401b03841161026b5760a083610e22886020809881980101610d09565b606084015201356080820152815201940193610d66565b3461026b57604036600319011261026b576004356001600160401b03811161026b57610e69903690600401610d24565b6024356001600160401b03811161026b573660238201121561026b57806004013591610e9483610243565b91610ea260405193846101e3565b8383526024602084019460051b8201019036821161026b5760248101945b828610610ed1576103978585611f0c565b85356001600160401b03811161026b5782013660438201121561026b576024810135610efc81610243565b91610f0a60405193846101e3565b818352602060248185019360051b830101019036821161026b5760448101925b828410610f44575050509082525060209586019501610ec0565b83356001600160401b03811161026b576024908301016040601f19823603011261026b5760405190610f75826101ad565b6020810135825260408101356001600160401b03811161026b57602091010136601f8201121561026b57803590610fab82610243565b91610fb960405193846101e3565b80835260208084019160051b8301019136831161026b57602001905b828210610ff45750505091816020938480940152815201930192610f2a565b60208091833561100381610983565b815201910190610fd5565b9181601f8401121561026b578235916001600160401b03831161026b576020808501948460051b01011161026b57565b3461026b57606036600319011261026b576004356001600160401b03811161026b5761106e903690600401610aa1565b6024356001600160401b03811161026b5761108d90369060040161100e565b91604435926001600160401b03841161026b576110b161039794369060040161100e565b939092612318565b3461026b57600036600319011261026b576110d26125e5565b506105bc6040516110e281610172565b60ff6004546001600160a01b038116835263ffffffff8160a01c16602084015260c01c16151560408201526001600160a01b036005541660608201526040519182918291909160606001600160a01b0381608084019582815116855263ffffffff6020820151166020860152604081015115156040860152015116910152565b3461026b57600036600319011261026b576000546001600160a01b03811633036111d1576001600160a01b0319600154913382841617600155166000556001600160a01b033391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a3005b63015aa1e360e11b60005260046000fd5b3461026b57608036600319011261026b57600060405161120181610172565b60043561120d8161025a565b815260243561121b81610983565b602082015260443561122c8161029a565b604082015260643561123d8161025a565b606082015261124a613489565b6001600160a01b038151161561139957611393816112a96001600160a01b037fcbb53bda7106a610de67df506ac86b65c44d5afac0fd2b11070dc2d61a6f2dee9451166001600160a01b03166001600160a01b03196004541617600455565b60208101516004547fffffffffffffff0000000000ffffffffffffffffffffffffffffffffffffffff77ffffffff000000000000000000000000000000000000000078ff0000000000000000000000000000000000000000000000006040860151151560c01b169360a01b169116171760045561134f61133360608301516001600160a01b031690565b6001600160a01b03166001600160a01b03196005541617600555565b6040519182918291909160606001600160a01b0381608084019582815116855263ffffffff6020820151166020860152604081015115156040860152015116910152565b0390a180f35b6342bcdf7f60e11b8252600482fd5b3461026b57602036600319011261026b576004356001600160401b03811161026b5760a090600319903603011261026b57600080fd5b3461026b57600036600319011261026b5760206001600160a01b0360015416604051908152f35b6004359060ff8216820361026b57565b359060ff8216820361026b57565b906020808351928381520192019060005b8181106114415750505090565b82516001600160a01b0316845260209384019390920191600101611434565b9061031c9160208152606082518051602084015260ff602082015116604084015260ff6040820151168284015201511515608082015260406114b1602084015160c060a085015260e0840190611423565b9201519060c0601f1982850301910152611423565b3461026b57602036600319011261026b5760ff6114e1611405565b6060604080516114f0816101c8565b6114f86125e5565b815282602082015201521660005260026020526105bc6040600020600361156060405192611525846101c8565b61152e8161260a565b845260405161154b816115448160028601612643565b03826101e3565b60208501526115446040518094819301612643565b604082015260405191829182611460565b3461026b57604036600319011261026b5761158a610270565b6001600160401b036024359116600052600a6020526040600020906000526020526020604060002054604051908152f35b9060049160441161026b57565b9181601f8401121561026b578235916001600160401b03831161026b576020838186019501011161026b57565b3461026b5760c036600319011261026b5761160f366115bb565b6044356001600160401b03811161026b5761162e9036906004016115c8565b6064929192356001600160401b03811161026b5761165090369060040161100e565b60843594916001600160401b03861161026b5761167461039796369060040161100e565b94909360a43596612c75565b90602061031c9281815201906106a3565b3461026b57602036600319011261026b576001600160401b036116b2610270565b6116ba611cbb565b501660005260086020526105bc6040600020600161171b604051926116de84610172565b6001600160401b0381546001600160a01b038116865260ff8160a01c161515602087015260a81c1660408501526115446040518094819301611d8a565b606082015260405191829182611680565b3461026b57602036600319011261026b576001600160a01b036004356117518161025a565b611759613489565b163381146117a657806001600160a01b031960005416176000556001600160a01b03600154167fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae1278600080a3005b636d6c4ee560e11b60005260046000fd5b3461026b57606036600319011261026b576117d1366115bb565b6044356001600160401b03811161026b576117f09036906004016115c8565b9182820160208382031261026b578235906001600160401b03821161026b5761181a918401610d24565b60405190602061182a81846101e3565b60008352601f19810160005b81811061185e57505050610397949161184e916136cd565b611856613184565b928392613a33565b60608582018401528201611836565b9080601f8301121561026b57813561188481610243565b9261189260405194856101e3565b81845260208085019260051b82010192831161026b57602001905b8282106118ba5750505090565b6020809183356118c98161025a565b8152019101906118ad565b3461026b57602036600319011261026b576004356001600160401b03811161026b573660238201121561026b5780600401359061191082610243565b9061191e60405192836101e3565b8282526024602083019360051b8201019036821161026b5760248101935b82851061194c57610397846131a0565b84356001600160401b03811161026b57820160c0602319823603011261026b57611974610204565b916024820135835261198860448301611415565b602084015261199960648301611415565b60408401526119aa608483016102a4565b606084015260a48201356001600160401b03811161026b576119d2906024369185010161186d565b608084015260c4820135926001600160401b03841161026b576119ff60209493602486953692010161186d565b60a082015281520194019361193c565b611a17613489565b60005b8151811015611cb757611a2d8183611d3c565b5190611a4360208301516001600160401b031690565b916001600160401b038316908115611ca657611a78611a6c611a6c83516001600160a01b031690565b6001600160a01b031690565b15611c0d57611a9a846001600160401b03166000526008602052604060002090565b906060810151916001810195611ab08754611d50565b611c3457611b237ff4c1390c70e5c0f491ae1ccbc06f9117cbbadf2767b247b3bc203280f24c0fb991611b0984750100000000000000000000000000000000000000000067ffffffffffffffff60a81b19825416179055565b6040516001600160401b0390911681529081906020820190565b0390a15b82518015908115611c1e575b50611c0d57611bee611bd2611c0493611b6f7f49f51971edd25182e97182d6ea372a0488ce2ab639f6a3a7ab4df0d2636fe56b9660019a61352b565b611bc5611b7f6040830151151590565b85547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1690151560a01b74ff000000000000000000000000000000000000000016178555565b516001600160a01b031690565b82906001600160a01b03166001600160a01b0319825416179055565b611bf78461504a565b50604051918291826135fc565b0390a201611a1a565b6342bcdf7f60e11b60005260046000fd5b90506020840120611c2d6134ae565b1438611b33565b60016001600160401b03611c5384546001600160401b039060a81c1690565b16141580611c87575b611c665750611b27565b632105803760e11b6000526001600160401b031660045260246000fd5b6000fd5b50611c9187611e0d565b60208151910120845160208601201415611c5c565b63c656089560e01b60005260046000fd5b5050565b60405190611cc882610172565b606080836000815260006020820152600060408201520152565b90611cec82610243565b611cf960405191826101e3565b8281528092611d0a601f1991610243565b0190602036910137565b634e487b7160e01b600052603260045260246000fd5b805115611d375760200190565b611d14565b8051821015611d375760209160051b010190565b90600182811c92168015611d80575b6020831014611d6a57565b634e487b7160e01b600052602260045260246000fd5b91607f1691611d5f565b60009291815491611d9a83611d50565b8083529260018116908115611df05750600114611db657505050565b60009081526020812093945091925b838310611dd6575060209250010190565b600181602092949394548385870101520191019190611dc5565b915050602093945060ff929192191683830152151560051b010190565b90610213611e219260405193848092611d8a565b03836101e3565b9060016060604051611e3981610172565b611e8281956001600160401b0381546001600160a01b038116855260ff8160a01c161515602086015260a81c166040840152611e7b6040518096819301611d8a565b03846101e3565b0152565b634e487b7160e01b600052601160045260246000fd5b908160051b9180830460201490151715611eb257565b611e86565b91908203918211611eb257565b611ed082607f92613646565b9116906801fffffffffffffffe6001600160401b0383169260011b169180830460021490151715611eb2576003911c1660048110156108bc5790565b611f1461368a565b80518251810361210b5760005b818110611f3457505090610213916136cd565b611f3e8184611d3c565b516020810190815151611f518488611d3c565b51928351820361210b5790916000925b808410611f75575050505050600101611f21565b91949398611f87848b98939598611d3c565b515198611f95888851611d3c565b5199806120c2575b5060a08a01988b6020611fb38b8d515193611d3c565b51015151036120855760005b8a515181101561207057611ffb611ff2611fe88f6020611fe08f8793611d3c565b510151611d3c565b5163ffffffff1690565b63ffffffff1690565b8b8161200c575b5050600101611fbf565b611ff2604061201f8561202b9451611d3c565b51015163ffffffff1690565b9081811061203a57508b612002565b8d51516040516348e617b360e01b81526004810191909152602481019390935260448301919091526064820152608490fd5b0390fd5b50985098509893949095600101929091611f61565b611c838b516120a0606082519201516001600160401b031690565b6370a193fd60e01b6000526004919091526001600160401b0316602452604490565b60808b0151811015611f9d57611c83908b6120e488516001600160401b031690565b905151633a98d46360e11b6000526001600160401b03909116600452602452604452606490565b6320f8fd5960e21b60005260046000fd5b60405190612129826101ad565b60006020838281520152565b604051906121446020836101e3565b600080835282815b82811061215857505050565b60209061216361211c565b8282850101520161214c565b805182526001600160401b03602082015116602083015260806121b66121a4604084015160a0604087015260a08601906105f8565b606084015185820360608701526105f8565b9101519160808183039101526020808351928381520192019060005b8181106121df5750505090565b825180516001600160a01b0316855260209081015181860152604090940193909201916001016121d2565b90602061031c92818152019061216f565b6040513d6000823e3d90fd5b3d15612252573d90612238826102af565b9161224660405193846101e3565b82523d6000602084013e565b606090565b90602061031c9281815201906105f8565b909160608284031261026b57815161227f8161029a565b9260208301516001600160401b03811161026b5783019080601f8301121561026b578151916122ad836102af565b916122bb60405193846101e3565b8383526020848301011161026b576040926122dc91602080850191016105d5565b92015190565b9293606092959461ffff6123066001600160a01b039460808852608088019061216f565b97166020860152604085015216910152565b929093913033036125d45761232b612135565b9460a0850151805161258d575b5050505050805191612356602084519401516001600160401b031690565b906020830151916040840192612383845192612370610215565b9788526001600160401b03166020880152565b6040860152606085015260808401526001600160a01b036123ac6005546001600160a01b031690565b1680612510575b5051511580612504575b80156124ee575b80156124c5575b611cb75761245d9181612402611a6c6123f561084b602060009751016001600160401b0390511690565b546001600160a01b031690565b908361241d606060808401519301516001600160a01b031690565b604051633cf9798360e01b815296879586948593917f000000000000000000000000000000000000000000000000000000000000000090600486016122e2565b03925af19081156124c057600090600092612499575b501561247c5750565b6040516302a35ba360e21b815290819061206c9060048301612257565b90506124b891503d806000833e6124b081836101e3565b810190612268565b509038612473565b61221b565b506124e96124e56124e060608401516001600160a01b031690565b6138f4565b1590565b6123cb565b5060608101516001600160a01b03163b156123c4565b506080810151156123bd565b803b1561026b57600060405180926308d450a160e01b82528183816125388a6004830161220a565b03925af19081612572575b5061256c5761206c612553612227565b6040516309c2532560e01b815291829160048301612257565b386123b3565b806125816000612587936101e3565b80610432565b38612543565b85965060206125c99601516125ac60608901516001600160a01b031690565b906125c360208a51016001600160401b0390511690565b926137db565b903880808080612338565b6306e34e6560e31b60005260046000fd5b604051906125f282610172565b60006060838281528260208201528260408201520152565b9060405161261781610172565b606060ff600183958054855201548181166020850152818160081c16604085015260101c161515910152565b906020825491828152019160005260206000209060005b8181106126675750505090565b82546001600160a01b031684526020909301926001928301920161265a565b90610213611e219260405193848092612643565b35906001600160e01b038216820361026b57565b81601f8201121561026b578035906126c582610243565b926126d360405194856101e3565b82845260208085019360061b8301019181831161026b57602001925b8284106126fd575050505090565b60408483031261026b5760206040918251612717816101ad565b61272087610286565b815261272d83880161269a565b838201528152019301926126ef565b81601f8201121561026b5780359061275382610243565b9261276160405194856101e3565b82845260208085019360051b8301019181831161026b5760208101935b83851061278d57505050505090565b84356001600160401b03811161026b57820160a0818503601f19011261026b57604051916127ba83610192565b6127c660208301610286565b83526040820135926001600160401b03841161026b5760a0836127f0886020809881980101610301565b8584015261280060608201610286565b604084015261281160808201610286565b60608401520135608082015281520194019361277e565b81601f8201121561026b5780359061283f82610243565b9261284d60405194856101e3565b82845260208085019360061b8301019181831161026b57602001925b828410612877575050505090565b60408483031261026b5760206040918251612891816101ad565b863581528287013583820152815201930192612869565b60208183031261026b578035906001600160401b03821161026b570160608183031261026b57604051916128db836101c8565b81356001600160401b03811161026b57820160408183031261026b5760405190612904826101ad565b80356001600160401b03811161026b57810183601f8201121561026b57803561292c81610243565b9161293a60405193846101e3565b81835260208084019260061b8201019086821161026b57602001915b8183106129d25750505082526020810135906001600160401b03821161026b57612982918491016126ae565b6020820152835260208201356001600160401b03811161026b57816129a891840161273c565b602084015260408201356001600160401b03811161026b576129ca9201612828565b604082015290565b60408388031261026b57602060409182516129ec816101ad565b85356129f78161025a565b8152612a0483870161269a565b83820152815201920191612956565b9080602083519182815201916020808360051b8301019401926000915b838310612a3f57505050505090565b9091929394602080600192601f198582030186528851906001600160401b038251168152608080612a7d8585015160a08786015260a08501906105f8565b936001600160401b0360408201511660408501526001600160401b036060820151166060850152015191015297019301930191939290612a30565b916001600160a01b03612ad992168352606060208401526060830190612a13565b9060408183039101526020808351928381520192019060005b818110612aff5750505090565b8251805185526020908101518186015260409094019390920191600101612af2565b906020808351928381520192019060005b818110612b3f5750505090565b825180516001600160401b031685526020908101516001600160e01b03168186015260409094019390920191600101612b32565b9190604081019083519160408252825180915260206060830193019060005b818110612bb357505050602061031c93940151906020818403910152612b21565b825180516001600160a01b031686526020908101516001600160e01b03168187015260409095019490920191600101612b92565b90602061031c928181520190612b73565b9081602091031261026b575161031c8161029a565b9091612c2461031c936040845260408401906105f8565b916020818403910152611d8a565b6001600160401b036001911601906001600160401b038211611eb257565b9091612c6761031c93604084526040840190612a13565b916020818403910152612b73565b929693959190979497612c8a828201826128a8565b98612c9e6124e560045460ff9060c01c1690565b6130f2575b895180515115908115916130e3575b5061300a575b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316999860208a019860005b8a518051821015612fa85781612d0191611d3c565b518d612d1482516001600160401b031690565b604051632cbc26bb60e01b815267ffffffffffffffff60801b608083901b1660048201529091602090829060249082905afa9081156124c057600091612f7a575b50612f5d57612d6390613942565b60208201805160208151910120906001830191612d7f83611e0d565b6020815191012003612f40575050805460408301516001600160401b039081169160a81c168114801590612f18575b612ec657506080820151908115612eb557612dff82612df0612dd786516001600160401b031690565b6001600160401b0316600052600a602052604060002090565b90600052602052604060002090565b54612e81578291612e65612e7a92612e2c612e2760606001999801516001600160401b031690565b612c32565b67ffffffffffffffff60a81b197cffffffffffffffff00000000000000000000000000000000000000000083549260a81b169116179055565b612df0612dd74294516001600160401b031690565b5501612cec565b50612e96611c8392516001600160401b031690565b6332cf0cbf60e01b6000526001600160401b0316600452602452604490565b63504570e360e01b60005260046000fd5b82611c8391612ef06060612ee184516001600160401b031690565b9301516001600160401b031690565b636af0786b60e11b6000526001600160401b0392831660045290821660245216604452606490565b50612f3061080760608501516001600160401b031690565b6001600160401b03821611612dae565b5161206c60405192839263b80d8fa960e01b845260048401612c0d565b637edeb53960e11b6000526001600160401b031660045260246000fd5b612f9b915060203d8111612fa1575b612f9381836101e3565b810190612bf8565b38612d55565b503d612f89565b50506130049496989b507f35c02761bcd3ef995c6a601a1981f4ed3934dcbe5041e24e286c89f5531d17e46102139b612ffc949597999b51905190612ff260405192839283612c50565b0390a13691610cbd565b943691610cbd565b93613d11565b61301f602086015b356001600160401b031690565b600b546001600160401b03828116911610156130c757613055906001600160401b03166001600160401b0319600b541617600b55565b61306d611a6c611a6c6004546001600160a01b031690565b8a5190803b1561026b57604051633937306f60e01b815291600091839182908490829061309d9060048301612be7565b03925af180156124c0576130b2575b50612cb8565b8061258160006130c1936101e3565b386130ac565b5060208a015151612cb857632261116760e01b60005260046000fd5b60209150015151151538612cb2565b60208a01518051613104575b50612ca3565b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169060408c0151823b1561026b57604051633854844f60e11b815292600092849283918291613160913060048501612ab8565b03915afa80156124c057156130fe5780612581600061317e936101e3565b386130fe565b604051906131936020836101e3565b6000808352366020840137565b6131a8613489565b60005b8151811015611cb7576131be8183611d3c565b51906040820160ff6131d1825160ff1690565b161561347357602083015160ff16926131f78460ff166000526002602052604060002090565b916001830191825461321261320c8260ff1690565b60ff1690565b613438575061323f6132276060830151151590565b845462ff0000191690151560101b62ff000016178455565b60a081019182516101008151116133e057805115613422576003860161326d61326782612686565b8a614df8565b60608401516132fd575b947fab8b1b57514019638d7b5ce9c638fe71366fe8e2be1c40a7a80f1733d0e9f547946002946132d96132c96132f79a966132c28760019f9c6132bd6132ef9a8f614f59565b613f25565b5160ff1690565b845460ff191660ff821617909455565b5190818555519060405195869501908886613fab565b0390a1614fdb565b016131ab565b9794600287939597019661331961331389612686565b88614df8565b60808501519461010086511161340c57855161334161320c61333c8a5160ff1690565b613f11565b10156133f65785518451116133e0576132d96132c97fab8b1b57514019638d7b5ce9c638fe71366fe8e2be1c40a7a80f1733d0e9f547986132c28760019f6132bd6132f79f9a8f6133c860029f6133c26132ef9f8f906132bd84926133a7845160ff1690565b908054909161ff001990911660089190911b61ff0016179055565b82614e8c565b505050979c9f50975050969a50505094509450613277565b631b3fab5160e11b600052600160045260246000fd5b631b3fab5160e11b600052600360045260246000fd5b631b3fab5160e11b600052600260045260246000fd5b631b3fab5160e11b600052600560045260246000fd5b60101c60ff1661345361344e6060840151151590565b151590565b9015151461323f576321fd80df60e21b60005260ff861660045260246000fd5b631b3fab5160e11b600090815260045260246000fd5b6001600160a01b0360015416330361349d57565b6315ae3a6f60e11b60005260046000fd5b604051602081019060008252602081526134c96040826101e3565b51902090565b8181106134da575050565b600081556001016134cf565b9190601f81116134f557505050565b610213926000526020600020906020601f840160051c83019310613521575b601f0160051c01906134cf565b9091508190613514565b91909182516001600160401b03811161018d576135528161354c8454611d50565b846134e6565b6020601f8211600114613593578190613584939495600092613588575b50508160011b916000199060031b1c19161790565b9055565b01519050388061356f565b601f198216906135a884600052602060002090565b9160005b8181106135e4575095836001959697106135cb575b505050811b019055565b015160001960f88460031b161c191690553880806135c1565b9192602060018192868b0151815501940192016135ac565b90600160a061031c93602081526001600160401b0384546001600160a01b038116602084015260ff81851c161515604084015260a81c166060820152608080820152019101611d8a565b906001600160401b03613686921660005260096020526701ffffffffffffff60406000209160071c166001600160401b0316600052602052604060002090565b5490565b7f00000000000000000000000000000000000000000000000000000000000000004681036136b55750565b630f01ce8560e01b6000526004524660245260446000fd5b91909180511561376f5782511592602091604051926136ec81856101e3565b60008452601f19810160005b81811061374b5750505060005b8151811015613743578061372c61371e60019385611d3c565b5188156137325786906140ea565b01613705565b61373c8387611d3c565b51906140ea565b505050509050565b8290604051613759816101ad565b60008152606083820152828289010152016136f8565b63c2e5347d60e01b60005260046000fd5b9190811015611d375760051b0190565b3561031c81610983565b9190811015611d375760051b81013590601e198136030182121561026b5701908135916001600160401b03831161026b57602001823603811361026b579190565b909294919397968151966137ee88610243565b976137fc604051998a6101e3565b80895261380b601f1991610243565b0160005b8181106138dd57505060005b83518110156138d057806138628c8a8a8a61385c613855878d61384e828f8f9d8f9e60019f8161387e575b505050611d3c565b519761379a565b36916102ca565b9361492f565b61386c828c611d3c565b52613877818b611d3c565b500161381b565b63ffffffff613896613891858585613780565b613790565b1615613846576138c6926138ad9261389192613780565b60406138b98585611d3c565b51019063ffffffff169052565b8f8f908391613846565b5096985050505050505050565b6020906138e861211c565b82828d0101520161380f565b6139056385572ffb60e01b82614c92565b908161391f575b81613915575090565b61031c9150614c64565b905061392a81614be9565b159061390c565b61390563aff2afbf60e01b82614c92565b6001600160401b031680600052600860205260406000209060ff825460a01c161561396b575090565b63ed053c5960e01b60005260045260246000fd5b6084019081608411611eb257565b60a001908160a011611eb257565b91908201809211611eb257565b600311156108bc57565b60038210156108bc5752565b906102136040516139ce816101ad565b602060ff829554818116845260081c1691016139b2565b8054821015611d375760005260206000200190600090565b60ff60019116019060ff8211611eb257565b60ff601b9116019060ff8211611eb257565b90606092604091835260208301370190565b6001600052600260205293613a677fe90b7bceb6e7df5418fb78d8ee546e97c83a08bbccc01a0644d599ccd2a7c2e061260a565b93853594613a748561397f565b6060820190613a838251151590565b613ce3575b803603613ccb57508151878103613cb25750613aa261368a565b60016000526003602052613af1613aec7fa15bc60c955c405d20d9149c709e2460f1c2d9a497496a7f46004d1772c3054c5b336001600160a01b0316600052602052604060002090565b6139be565b60026020820151613b01816139a8565b613b0a816139a8565b149081613c4a575b5015613c395751613b70575b50505050507f198d6990ef96613a9026203077e422916918b03ff47f0be6bee7b02d8e139ef090613b5461301260019460200190565b604080519283526001600160401b0391909116602083015290a2565b613b9161320c613b8c602085969799989a955194015160ff1690565b6139fd565b03613c28578151835103613c1757613c0f6000613b549461301294613bdb7f198d6990ef96613a9026203077e422916918b03ff47f0be6bee7b02d8e139ef09960019b36916102ca565b60208151910120604051613c0681613bf889602083019586613a21565b03601f1981018352826101e3565b5190208a614cc2565b948394613b1e565b63a75d88af60e01b60005260046000fd5b6371253a2560e01b60005260046000fd5b631b41e11d60e31b60005260046000fd5b60016000526002602052613caa9150611a6c90613c9790613c9160037fe90b7bceb6e7df5418fb78d8ee546e97c83a08bbccc01a0644d599ccd2a7c2e05b01915160ff1690565b906139e5565b90546001600160a01b039160031b1c1690565b331438613b12565b6324f7d61360e21b600052600452602487905260446000fd5b638e1192e160e01b6000526004523660245260446000fd5b613d0c90613d06613cfc613cf78751611e9c565b61398d565b613d068851611e9c565b9061399b565b613a88565b60008052600260205294909390929091613d4a7fac33ff75c19e70fe83507db0d683fd3465c996598dc972688b7ace676c89077b61260a565b94863595613d578361397f565b6060820190613d668251151590565b613eee575b803603613ccb57508151888103613ed55750613d8561368a565b600080526003602052613dba613aec7f3617319a054d772f909f7c479a2cebe5066e836a939412e32403c99029b92eff613ad4565b60026020820151613dca816139a8565b613dd3816139a8565b149081613e8c575b5015613c395751613e1e575b5050505050507f198d6990ef96613a9026203077e422916918b03ff47f0be6bee7b02d8e139ef090613b5461301260009460200190565b613e3a61320c613b8c602087989a999b96975194015160ff1690565b03613c28578351865103613c17576000967f198d6990ef96613a9026203077e422916918b03ff47f0be6bee7b02d8e139ef096613b5495613bdb613e83946130129736916102ca565b94839438613de7565b600080526002602052613ecd9150611a6c90613c9790613c9160037fac33ff75c19e70fe83507db0d683fd3465c996598dc972688b7ace676c89077b613c88565b331438613ddb565b6324f7d61360e21b600052600452602488905260446000fd5b613f0c90613d06613f02613cf78951611e9c565b613d068a51611e9c565b613d6b565b60ff166003029060ff8216918203611eb257565b8151916001600160401b03831161018d5768010000000000000000831161018d576020908254848455808510613f8e575b500190600052602060002060005b838110613f715750505050565b60019060206001600160a01b038551169401938184015501613f64565b613fa59084600052858460002091820191016134cf565b38613f56565b95949392909160ff613fd093168752602087015260a0604087015260a0860190612643565b84810360608601526020808351928381520192019060005b818110614003575050509060806102139294019060ff169052565b82516001600160a01b0316845260209384019390920191600101613fe8565b600654811015611d375760066000527ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f015490565b6001600160401b0361031c94938160609416835216602082015281604082015201906105f8565b60409061031c9392815281602082015201906105f8565b9291906001600160401b039081606495166004521660245260048110156108bc57604452565b9493926140d46060936140e593885260208801906108c1565b6080604087015260808601906105f8565b930152565b906140fc82516001600160401b031690565b8151604051632cbc26bb60e01b815267ffffffffffffffff60801b608084901b1660048201529015159391906001600160401b038216906020816024817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa9081156124c057600091614818575b506147d65760208301918251519485156147a6576040850180515187036147955761419e87611ce2565b957f00000000000000000000000000000000000000000000000000000000000000006141d460016141ce87613942565b01611e0d565b6020815191012060405161423481613bf86020820194868b876001600160401b036060929594938160808401977f2425b0b9f9054c76ff151b0a175b18f37a4a4e82013a72e9f15c9caa095ed21f85521660208401521660408201520152565b519020906001600160401b031660005b8a81106146fd57505050806080606061426493015191015190888661528b565b9788156146df5760005b8881106142815750505050505050505050565b5a61428d828951611d3c565b518051606001516142a7906001600160401b031688611ec4565b6142b0816108b2565b8015908d82831593846146cc575b15614689576060881561460c57506142e560206142db898d611d3c565b5101519242611eb7565b6004546142fa9060a01c63ffffffff16611ff2565b1080156145f9575b156145db57614311878b611d3c565b51516145c5575b845160800151614330906001600160401b0316610807565b61450d575b50614341868951611d3c565b5160a0850151518151036144d157936143a69695938c938f966143868e958c9261438061437a60608951016001600160401b0390511690565b896152bd565b86615464565b9a9080966143a060608851016001600160401b0390511690565b90615345565b61447f575b50506143b6826108b2565b60028203614437575b60019661442d7f05665fe9ad095383d018353f4cbcba77e84db27dd215081bbf7cdf9ae6fbe48b936001600160401b0393519261441e6144158b61440d60608801516001600160401b031690565b96519b611d3c565b51985a90611eb7565b916040519586951698856140bb565b0390a45b0161426e565b91509193949250614447826108b2565b6003820361445b578b929493918a916143bf565b51606001516349362d1f60e11b600052611c8391906001600160401b031689614095565b614488846108b2565b600384036143ab5790929495506144a09193506108b2565b6144b0578b92918a9138806143ab565b5151604051632b11b8d960e01b815290819061206c9087906004840161407e565b611c838b6144eb60608851016001600160401b0390511690565b631cfe6d8b60e01b6000526001600160401b0391821660045216602452604490565b614516836108b2565b614521575b38614335565b8351608001516001600160401b0316602080860151918c61455660405194859384936370701e5760e11b855260048501614057565b038160006001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165af19081156124c0576000916145a7575b5061451b575050505050600190614431565b6145bf915060203d8111612fa157612f9381836101e3565b38614595565b6145cf878b611d3c565b51516080860152614318565b6354e7e43160e11b6000526001600160401b038b1660045260246000fd5b50614603836108b2565b60038314614302565b915083614618846108b2565b1561431857506001959450614681925061465f91507f3ef2a99c550a751d4b0b261268f05a803dfb049ab43616a1ffb388f61fe651209351016001600160401b0390511690565b604080516001600160401b03808c168252909216602083015290918291820190565b0390a1614431565b50505050600192915061468161465f60607f3b575419319662b2a6f5e2467d84521517a3382b908eb3d557bb3fdb0c50e23c9351016001600160401b0390511690565b506146d6836108b2565b600383146142be565b633ee8bd3f60e11b6000526001600160401b03841660045260246000fd5b614708818a51611d3c565b518051604001516001600160401b031683810361477857508051602001516001600160401b031689810361475557509061474484600193615183565b61474e828d611d3c565b5201614244565b636c95f1eb60e01b6000526001600160401b03808a166004521660245260446000fd5b631c21951160e11b6000526001600160401b031660045260246000fd5b6357e0e08360e01b60005260046000fd5b611c836147ba86516001600160401b031690565b63676cf24b60e11b6000526001600160401b0316600452602490565b5092915050612f5d576040516001600160401b039190911681527faab522ed53d887e56ed53dd37398a01aeef6a58e0fa77c2173beb9512d89493390602090a1565b614831915060203d602011612fa157612f9381836101e3565b38614174565b9081602091031261026b575161031c8161025a565b9061031c916020815260e06148ea6148d5614875855161010060208701526101208601906105f8565b60208601516001600160401b0316604086015260408601516001600160a01b03166060860152606086015160808601526148bf608087015160a08701906001600160a01b03169052565b60a0860151858203601f190160c08701526105f8565b60c0850151848203601f1901848601526105f8565b92015190610100601f19828503019101526105f8565b6040906001600160a01b0361031c949316815281602082015201906105f8565b9081602091031261026b575190565b9193929361493b61211c565b5060208301516001600160a01b031660405163bbe4f6db60e01b81526001600160a01b038216600482015290959092602084806024810103816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa9384156124c057600094614bb8575b506001600160a01b0384169586158015614ba6575b614b8857614a6d614a9692613bf8926149f16149ea611ff260408c015163ffffffff1690565b8c896155a5565b9690996080810151614a1f6060835193015193614a0c610224565b9687526001600160401b03166020870152565b6001600160a01b038a16604086015260608501526001600160a01b038d16608085015260a084015260c083015260e0820152604051633907753760e01b60208201529283916024830161484c565b82857f000000000000000000000000000000000000000000000000000000000000000092615633565b94909115614b6c5750805160208103614b53575090614abf826020808a95518301019101614920565b956001600160a01b03841603614af7575b5050505050614aef614ae0610234565b6001600160a01b039093168352565b602082015290565b614b0a93614b0491611eb7565b916155a5565b50908082108015614b40575b614b2257808481614ad0565b63a966e21f60e01b6000908152600493909352602452604452606490fd5b5082614b4c8284611eb7565b1415614b16565b631e3be00960e21b600052602060045260245260446000fd5b61206c604051928392634ff17cad60e11b845260048401614900565b63ae9b4ce960e01b6000526001600160a01b03851660045260246000fd5b50614bb36124e586613931565b6149c4565b614bdb91945060203d602011614be2575b614bd381836101e3565b810190614837565b92386149af565b503d614bc9565b60405160208101916301ffc9a760e01b835263ffffffff60e01b602483015260248252614c176044836101e3565b6179185a10614c53576020926000925191617530fa6000513d82614c47575b5081614c40575090565b9050151590565b60201115915038614c36565b63753fa58960e11b60005260046000fd5b60405160208101916301ffc9a760e01b83526301ffc9a760e01b602483015260248252614c176044836101e3565b6040519060208201926301ffc9a760e01b845263ffffffff60e01b16602483015260248252614c176044836101e3565b919390926000948051946000965b868810614ce1575050505050505050565b6020881015611d375760206000614cf9878b1a613a0f565b614d038b87611d3c565b5190614d3a614d128d8a611d3c565b5160405193849389859094939260ff6060936080840197845216602083015260408201520152565b838052039060015afa156124c057614d80613aec600051614d688960ff166000526003602052604060002090565b906001600160a01b0316600052602052604060002090565b9060016020830151614d91816139a8565b614d9a816139a8565b03614de757614db7614dad835160ff1690565b60ff600191161b90565b8116614dd657614dcd614dad6001935160ff1690565b17970196614cd0565b633d9ef1f160e21b60005260046000fd5b636518c33d60e11b60005260046000fd5b91909160005b8351811015614e515760019060ff831660005260036020526000614e4a604082206001600160a01b03614e31858a611d3c565b51166001600160a01b0316600052602052604060002090565b5501614dfe565b50509050565b8151815460ff191660ff91909116178155906020015160038110156108bc57815461ff00191660089190911b61ff0016179055565b919060005b8151811015614e5157614ea7611bc58284611d3c565b90614ed0614ec683614d688860ff166000526003602052604060002090565b5460081c60ff1690565b614ed9816139a8565b614f44576001600160a01b03821615614f3357614f2d600192614f28614efd610234565b60ff8516815291614f1186602085016139b2565b614d688960ff166000526003602052604060002090565b614e57565b01614e91565b63d6c62c9b60e01b60005260046000fd5b631b3fab5160e11b6000526004805260246000fd5b919060005b8151811015614e5157614f74611bc58284611d3c565b90614f93614ec683614d688860ff166000526003602052604060002090565b614f9c816139a8565b614f44576001600160a01b03821615614f3357614fd5600192614f28614fc0610234565b60ff8516815291614f116002602085016139b2565b01614f5e565b60ff1680600052600260205260ff60016040600020015460101c16908015600014615029575015615018576001600160401b0319600b5416600b55565b6317bd8dd160e11b60005260046000fd5b6001146150335750565b61503957565b6307b8c74d60e51b60005260046000fd5b806000526007602052604060002054156000146150c8576006546801000000000000000081101561018d57600181016006556000600654821015611d3757600690527ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f01819055600654906000526007602052604060002055600190565b50600090565b9080602083519182815201916020808360051b8301019401926000915b8383106150fa57505050505090565b9091929394602080600192601f1985820301865288519060808061515d61512a855160a0865260a08601906105f8565b6001600160a01b0387870151168786015263ffffffff6040870151166040860152606086015185820360608701526105f8565b930151910152970193019301919392906150eb565b90602061031c9281815201906150ce565b6134c981518051906152176151a260608601516001600160a01b031690565b613bf86151b960608501516001600160401b031690565b936151d26080808a01519201516001600160401b031690565b90604051958694602086019889936001600160401b036080946001600160a01b0382959998949960a089019a8952166020880152166040860152606085015216910152565b519020613bf86020840151602081519101209360a060408201516020815191012091015160405161525081613bf8602082019485615172565b51902090604051958694602086019889919260a093969594919660c08401976000855260208501526040840152606083015260808201520152565b926001600160401b039261529e926156f0565b9116600052600a60205260406000209060005260205260406000205490565b607f8216906801fffffffffffffffe6001600160401b0383169260011b169180830460021490151715611eb257615342916001600160401b036153008584613646565b921660005260096020526701ffffffffffffff60406000209460071c169160036001831b921b19161792906001600160401b0316600052602052604060002090565b55565b9091607f83166801fffffffffffffffe6001600160401b0382169160011b169080820460021490151715611eb25761537d8484613646565b60048310156108bc576001600160401b036153429416600052600960205260036701ffffffffffffff60406000209660071c1693831b921b19161792906001600160401b0316600052602052604060002090565b9080602083519182815201916020808360051b8301019401926000915b8383106153fd57505050505090565b909192939460208061541b600193601f1986820301875289516105f8565b970193019301919392906153ee565b906020808351928381520192019060005b8181106154485750505090565b825163ffffffff1684526020938401939092019160010161543b565b91606092303b1561026b5761556660a0926155546000956155426040519889978897630304c3e160e51b89528260048a01526001600160401b0360808251805160648d01528260208201511660848d01528260408201511660a48d015282868201511660c48d015201511660e48a015261552361550e6154f78b61014061010460208701519201526101a48d01906105f8565b60408401518c8203606319016101248e01526105f8565b938201516001600160a01b03166101448b0152565b60808101516101648a01520151878203606319016101848901526150ce565b858103600319016024870152906153d1565b8381036003190160448501529061542a565b038183305af19081615590575b5061558557615580612227565b600391565b60029061031c6105c0565b80612581600061559f936101e3565b38615573565b6040516370a0823160e01b60208201526001600160a01b039091166024820152919291615602906155d98160448101613bf8565b84837f000000000000000000000000000000000000000000000000000000000000000092615633565b92909115614b6c5750805160208103614b5357509061562d8260208061031c95518301019101614920565b93611eb7565b93919361564060846102af565b9461564e60405196876101e3565b6084865261565c60846102af565b602087019590601f1901368737833b156156df575a908082106156ce578291038060061c900311156156bd576000918291825a9560208451940192f1905a9003923d90608482116156b4575b6000908287523e929190565b608491506156a8565b6337c3be2960e01b60005260046000fd5b632be8ca8b60e21b60005260046000fd5b63030ed58f60e21b60005260046000fd5b805192825190841561584c5761010185111580615840575b1561576f5781850194600019860195610100871161576f5786156158305761572f87611ce2565b9660009586978795885b84811061579457505050505060011901809514938461578a575b505082615780575b50501561576f5761576b91611d3c565b5190565b6309bde33960e01b60005260046000fd5b149050388061575b565b1492503880615753565b6001811b8281160361582257868a101561580d576157b660018b019a85611d3c565b51905b8c888c10156157f957506157d160018c019b86611d3c565b515b818d1161576f576157f2828f926157ec9060019661585d565b92611d3c565b5201615739565b60018d019c61580791611d3c565b516157d3565b61581b60018c019b8d611d3c565b51906157b9565b61581b600189019884611d3c565b50505050905061576b9150611d2a565b50610101821115615708565b630469ac9960e21b60005260046000fd5b8181101561586f579061031c91615874565b61031c915b906040519060208201926001845260408301526060820152606081526134c96080826101e356fea164736f6c634300081a000a49f51971edd25182e97182d6ea372a0488ce2ab639f6a3a7ab4df0d2636fe56b",
+ ABI: "[{\"type\":\"constructor\",\"inputs\":[{\"name\":\"staticConfig\",\"type\":\"tuple\",\"internalType\":\"structOffRamp.StaticConfig\",\"components\":[{\"name\":\"chainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"gasForCallExactCheck\",\"type\":\"uint16\",\"internalType\":\"uint16\"},{\"name\":\"rmnRemote\",\"type\":\"address\",\"internalType\":\"contractIRMNRemote\"},{\"name\":\"tokenAdminRegistry\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"nonceManager\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"name\":\"dynamicConfig\",\"type\":\"tuple\",\"internalType\":\"structOffRamp.DynamicConfig\",\"components\":[{\"name\":\"feeQuoter\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"permissionLessExecutionThresholdSeconds\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"isRMNVerificationDisabled\",\"type\":\"bool\",\"internalType\":\"bool\"},{\"name\":\"messageInterceptor\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"name\":\"sourceChainConfigs\",\"type\":\"tuple[]\",\"internalType\":\"structOffRamp.SourceChainConfigArgs[]\",\"components\":[{\"name\":\"router\",\"type\":\"address\",\"internalType\":\"contractIRouter\"},{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"isEnabled\",\"type\":\"bool\",\"internalType\":\"bool\"},{\"name\":\"onRamp\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]}],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"acceptOwnership\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"applySourceChainConfigUpdates\",\"inputs\":[{\"name\":\"sourceChainConfigUpdates\",\"type\":\"tuple[]\",\"internalType\":\"structOffRamp.SourceChainConfigArgs[]\",\"components\":[{\"name\":\"router\",\"type\":\"address\",\"internalType\":\"contractIRouter\"},{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"isEnabled\",\"type\":\"bool\",\"internalType\":\"bool\"},{\"name\":\"onRamp\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"ccipReceive\",\"inputs\":[{\"name\":\"\",\"type\":\"tuple\",\"internalType\":\"structClient.Any2EVMMessage\",\"components\":[{\"name\":\"messageId\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"sender\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"data\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"destTokenAmounts\",\"type\":\"tuple[]\",\"internalType\":\"structClient.EVMTokenAmount[]\",\"components\":[{\"name\":\"token\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"amount\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]}]}],\"outputs\":[],\"stateMutability\":\"pure\"},{\"type\":\"function\",\"name\":\"commit\",\"inputs\":[{\"name\":\"reportContext\",\"type\":\"bytes32[2]\",\"internalType\":\"bytes32[2]\"},{\"name\":\"report\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"rs\",\"type\":\"bytes32[]\",\"internalType\":\"bytes32[]\"},{\"name\":\"ss\",\"type\":\"bytes32[]\",\"internalType\":\"bytes32[]\"},{\"name\":\"rawVs\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"execute\",\"inputs\":[{\"name\":\"reportContext\",\"type\":\"bytes32[2]\",\"internalType\":\"bytes32[2]\"},{\"name\":\"report\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"executeSingleMessage\",\"inputs\":[{\"name\":\"message\",\"type\":\"tuple\",\"internalType\":\"structInternal.Any2EVMRampMessage\",\"components\":[{\"name\":\"header\",\"type\":\"tuple\",\"internalType\":\"structInternal.RampMessageHeader\",\"components\":[{\"name\":\"messageId\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"destChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"sequenceNumber\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"nonce\",\"type\":\"uint64\",\"internalType\":\"uint64\"}]},{\"name\":\"sender\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"data\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"receiver\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"gasLimit\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"tokenAmounts\",\"type\":\"tuple[]\",\"internalType\":\"structInternal.Any2EVMTokenTransfer[]\",\"components\":[{\"name\":\"sourcePoolAddress\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"destTokenAddress\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"destGasAmount\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"extraData\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"amount\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]}]},{\"name\":\"offchainTokenData\",\"type\":\"bytes[]\",\"internalType\":\"bytes[]\"},{\"name\":\"tokenGasOverrides\",\"type\":\"uint32[]\",\"internalType\":\"uint32[]\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"getAllSourceChainConfigs\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint64[]\",\"internalType\":\"uint64[]\"},{\"name\":\"\",\"type\":\"tuple[]\",\"internalType\":\"structOffRamp.SourceChainConfig[]\",\"components\":[{\"name\":\"router\",\"type\":\"address\",\"internalType\":\"contractIRouter\"},{\"name\":\"isEnabled\",\"type\":\"bool\",\"internalType\":\"bool\"},{\"name\":\"minSeqNr\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"onRamp\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getDynamicConfig\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"tuple\",\"internalType\":\"structOffRamp.DynamicConfig\",\"components\":[{\"name\":\"feeQuoter\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"permissionLessExecutionThresholdSeconds\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"isRMNVerificationDisabled\",\"type\":\"bool\",\"internalType\":\"bool\"},{\"name\":\"messageInterceptor\",\"type\":\"address\",\"internalType\":\"address\"}]}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getExecutionState\",\"inputs\":[{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"sequenceNumber\",\"type\":\"uint64\",\"internalType\":\"uint64\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint8\",\"internalType\":\"enumInternal.MessageExecutionState\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getLatestPriceSequenceNumber\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint64\",\"internalType\":\"uint64\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getMerkleRoot\",\"inputs\":[{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"root\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getSourceChainConfig\",\"inputs\":[{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"}],\"outputs\":[{\"name\":\"\",\"type\":\"tuple\",\"internalType\":\"structOffRamp.SourceChainConfig\",\"components\":[{\"name\":\"router\",\"type\":\"address\",\"internalType\":\"contractIRouter\"},{\"name\":\"isEnabled\",\"type\":\"bool\",\"internalType\":\"bool\"},{\"name\":\"minSeqNr\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"onRamp\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getStaticConfig\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"tuple\",\"internalType\":\"structOffRamp.StaticConfig\",\"components\":[{\"name\":\"chainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"gasForCallExactCheck\",\"type\":\"uint16\",\"internalType\":\"uint16\"},{\"name\":\"rmnRemote\",\"type\":\"address\",\"internalType\":\"contractIRMNRemote\"},{\"name\":\"tokenAdminRegistry\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"nonceManager\",\"type\":\"address\",\"internalType\":\"address\"}]}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"latestConfigDetails\",\"inputs\":[{\"name\":\"ocrPluginType\",\"type\":\"uint8\",\"internalType\":\"uint8\"}],\"outputs\":[{\"name\":\"ocrConfig\",\"type\":\"tuple\",\"internalType\":\"structMultiOCR3Base.OCRConfig\",\"components\":[{\"name\":\"configInfo\",\"type\":\"tuple\",\"internalType\":\"structMultiOCR3Base.ConfigInfo\",\"components\":[{\"name\":\"configDigest\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"F\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"n\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"isSignatureVerificationEnabled\",\"type\":\"bool\",\"internalType\":\"bool\"}]},{\"name\":\"signers\",\"type\":\"address[]\",\"internalType\":\"address[]\"},{\"name\":\"transmitters\",\"type\":\"address[]\",\"internalType\":\"address[]\"}]}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"manuallyExecute\",\"inputs\":[{\"name\":\"reports\",\"type\":\"tuple[]\",\"internalType\":\"structInternal.ExecutionReport[]\",\"components\":[{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"messages\",\"type\":\"tuple[]\",\"internalType\":\"structInternal.Any2EVMRampMessage[]\",\"components\":[{\"name\":\"header\",\"type\":\"tuple\",\"internalType\":\"structInternal.RampMessageHeader\",\"components\":[{\"name\":\"messageId\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"destChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"sequenceNumber\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"nonce\",\"type\":\"uint64\",\"internalType\":\"uint64\"}]},{\"name\":\"sender\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"data\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"receiver\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"gasLimit\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"tokenAmounts\",\"type\":\"tuple[]\",\"internalType\":\"structInternal.Any2EVMTokenTransfer[]\",\"components\":[{\"name\":\"sourcePoolAddress\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"destTokenAddress\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"destGasAmount\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"extraData\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"amount\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]}]},{\"name\":\"offchainTokenData\",\"type\":\"bytes[][]\",\"internalType\":\"bytes[][]\"},{\"name\":\"proofs\",\"type\":\"bytes32[]\",\"internalType\":\"bytes32[]\"},{\"name\":\"proofFlagBits\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"name\":\"gasLimitOverrides\",\"type\":\"tuple[][]\",\"internalType\":\"structOffRamp.GasLimitOverride[][]\",\"components\":[{\"name\":\"receiverExecutionGasLimit\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"tokenGasOverrides\",\"type\":\"uint32[]\",\"internalType\":\"uint32[]\"}]}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"owner\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"setDynamicConfig\",\"inputs\":[{\"name\":\"dynamicConfig\",\"type\":\"tuple\",\"internalType\":\"structOffRamp.DynamicConfig\",\"components\":[{\"name\":\"feeQuoter\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"permissionLessExecutionThresholdSeconds\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"isRMNVerificationDisabled\",\"type\":\"bool\",\"internalType\":\"bool\"},{\"name\":\"messageInterceptor\",\"type\":\"address\",\"internalType\":\"address\"}]}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setOCR3Configs\",\"inputs\":[{\"name\":\"ocrConfigArgs\",\"type\":\"tuple[]\",\"internalType\":\"structMultiOCR3Base.OCRConfigArgs[]\",\"components\":[{\"name\":\"configDigest\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"ocrPluginType\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"F\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"isSignatureVerificationEnabled\",\"type\":\"bool\",\"internalType\":\"bool\"},{\"name\":\"signers\",\"type\":\"address[]\",\"internalType\":\"address[]\"},{\"name\":\"transmitters\",\"type\":\"address[]\",\"internalType\":\"address[]\"}]}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"transferOwnership\",\"inputs\":[{\"name\":\"to\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"typeAndVersion\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"string\",\"internalType\":\"string\"}],\"stateMutability\":\"view\"},{\"type\":\"event\",\"name\":\"AlreadyAttempted\",\"inputs\":[{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"indexed\":false,\"internalType\":\"uint64\"},{\"name\":\"sequenceNumber\",\"type\":\"uint64\",\"indexed\":false,\"internalType\":\"uint64\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"CommitReportAccepted\",\"inputs\":[{\"name\":\"merkleRoots\",\"type\":\"tuple[]\",\"indexed\":false,\"internalType\":\"structInternal.MerkleRoot[]\",\"components\":[{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"onRampAddress\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"minSeqNr\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"maxSeqNr\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"merkleRoot\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}]},{\"name\":\"priceUpdates\",\"type\":\"tuple\",\"indexed\":false,\"internalType\":\"structInternal.PriceUpdates\",\"components\":[{\"name\":\"tokenPriceUpdates\",\"type\":\"tuple[]\",\"internalType\":\"structInternal.TokenPriceUpdate[]\",\"components\":[{\"name\":\"sourceToken\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"usdPerToken\",\"type\":\"uint224\",\"internalType\":\"uint224\"}]},{\"name\":\"gasPriceUpdates\",\"type\":\"tuple[]\",\"internalType\":\"structInternal.GasPriceUpdate[]\",\"components\":[{\"name\":\"destChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"usdPerUnitGas\",\"type\":\"uint224\",\"internalType\":\"uint224\"}]}]}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"ConfigSet\",\"inputs\":[{\"name\":\"ocrPluginType\",\"type\":\"uint8\",\"indexed\":false,\"internalType\":\"uint8\"},{\"name\":\"configDigest\",\"type\":\"bytes32\",\"indexed\":false,\"internalType\":\"bytes32\"},{\"name\":\"signers\",\"type\":\"address[]\",\"indexed\":false,\"internalType\":\"address[]\"},{\"name\":\"transmitters\",\"type\":\"address[]\",\"indexed\":false,\"internalType\":\"address[]\"},{\"name\":\"F\",\"type\":\"uint8\",\"indexed\":false,\"internalType\":\"uint8\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"DynamicConfigSet\",\"inputs\":[{\"name\":\"dynamicConfig\",\"type\":\"tuple\",\"indexed\":false,\"internalType\":\"structOffRamp.DynamicConfig\",\"components\":[{\"name\":\"feeQuoter\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"permissionLessExecutionThresholdSeconds\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"isRMNVerificationDisabled\",\"type\":\"bool\",\"internalType\":\"bool\"},{\"name\":\"messageInterceptor\",\"type\":\"address\",\"internalType\":\"address\"}]}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"ExecutionStateChanged\",\"inputs\":[{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"indexed\":true,\"internalType\":\"uint64\"},{\"name\":\"sequenceNumber\",\"type\":\"uint64\",\"indexed\":true,\"internalType\":\"uint64\"},{\"name\":\"messageId\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"bytes32\"},{\"name\":\"messageHash\",\"type\":\"bytes32\",\"indexed\":false,\"internalType\":\"bytes32\"},{\"name\":\"state\",\"type\":\"uint8\",\"indexed\":false,\"internalType\":\"enumInternal.MessageExecutionState\"},{\"name\":\"returnData\",\"type\":\"bytes\",\"indexed\":false,\"internalType\":\"bytes\"},{\"name\":\"gasUsed\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"OwnershipTransferRequested\",\"inputs\":[{\"name\":\"from\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"to\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"OwnershipTransferred\",\"inputs\":[{\"name\":\"from\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"to\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"RootRemoved\",\"inputs\":[{\"name\":\"root\",\"type\":\"bytes32\",\"indexed\":false,\"internalType\":\"bytes32\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"SkippedAlreadyExecutedMessage\",\"inputs\":[{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"indexed\":false,\"internalType\":\"uint64\"},{\"name\":\"sequenceNumber\",\"type\":\"uint64\",\"indexed\":false,\"internalType\":\"uint64\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"SkippedReportExecution\",\"inputs\":[{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"indexed\":false,\"internalType\":\"uint64\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"SourceChainConfigSet\",\"inputs\":[{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"indexed\":true,\"internalType\":\"uint64\"},{\"name\":\"sourceConfig\",\"type\":\"tuple\",\"indexed\":false,\"internalType\":\"structOffRamp.SourceChainConfig\",\"components\":[{\"name\":\"router\",\"type\":\"address\",\"internalType\":\"contractIRouter\"},{\"name\":\"isEnabled\",\"type\":\"bool\",\"internalType\":\"bool\"},{\"name\":\"minSeqNr\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"onRamp\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"SourceChainSelectorAdded\",\"inputs\":[{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"indexed\":false,\"internalType\":\"uint64\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"StaticConfigSet\",\"inputs\":[{\"name\":\"staticConfig\",\"type\":\"tuple\",\"indexed\":false,\"internalType\":\"structOffRamp.StaticConfig\",\"components\":[{\"name\":\"chainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"gasForCallExactCheck\",\"type\":\"uint16\",\"internalType\":\"uint16\"},{\"name\":\"rmnRemote\",\"type\":\"address\",\"internalType\":\"contractIRMNRemote\"},{\"name\":\"tokenAdminRegistry\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"nonceManager\",\"type\":\"address\",\"internalType\":\"address\"}]}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Transmitted\",\"inputs\":[{\"name\":\"ocrPluginType\",\"type\":\"uint8\",\"indexed\":true,\"internalType\":\"uint8\"},{\"name\":\"configDigest\",\"type\":\"bytes32\",\"indexed\":false,\"internalType\":\"bytes32\"},{\"name\":\"sequenceNumber\",\"type\":\"uint64\",\"indexed\":false,\"internalType\":\"uint64\"}],\"anonymous\":false},{\"type\":\"error\",\"name\":\"CanOnlySelfCall\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"CannotTransferToSelf\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"CommitOnRampMismatch\",\"inputs\":[{\"name\":\"reportOnRamp\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"configOnRamp\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]},{\"type\":\"error\",\"name\":\"ConfigDigestMismatch\",\"inputs\":[{\"name\":\"expected\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"actual\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}]},{\"type\":\"error\",\"name\":\"CursedByRMN\",\"inputs\":[{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"}]},{\"type\":\"error\",\"name\":\"EmptyBatch\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"EmptyReport\",\"inputs\":[{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"}]},{\"type\":\"error\",\"name\":\"ExecutionError\",\"inputs\":[{\"name\":\"messageId\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"err\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]},{\"type\":\"error\",\"name\":\"ForkedChain\",\"inputs\":[{\"name\":\"expected\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"actual\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"type\":\"error\",\"name\":\"InsufficientGasForCallWithExact\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidConfig\",\"inputs\":[{\"name\":\"errorType\",\"type\":\"uint8\",\"internalType\":\"enumMultiOCR3Base.InvalidConfigErrorType\"}]},{\"type\":\"error\",\"name\":\"InvalidDataLength\",\"inputs\":[{\"name\":\"expected\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"got\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"type\":\"error\",\"name\":\"InvalidInterval\",\"inputs\":[{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"min\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"max\",\"type\":\"uint64\",\"internalType\":\"uint64\"}]},{\"type\":\"error\",\"name\":\"InvalidManualExecutionGasLimit\",\"inputs\":[{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"messageId\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"newLimit\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"type\":\"error\",\"name\":\"InvalidManualExecutionTokenGasOverride\",\"inputs\":[{\"name\":\"messageId\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"tokenIndex\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"oldLimit\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"tokenGasOverride\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"type\":\"error\",\"name\":\"InvalidMessageDestChainSelector\",\"inputs\":[{\"name\":\"messageDestChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"}]},{\"type\":\"error\",\"name\":\"InvalidNewState\",\"inputs\":[{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"sequenceNumber\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"newState\",\"type\":\"uint8\",\"internalType\":\"enumInternal.MessageExecutionState\"}]},{\"type\":\"error\",\"name\":\"InvalidOnRampUpdate\",\"inputs\":[{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"}]},{\"type\":\"error\",\"name\":\"InvalidProof\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidRoot\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"LeavesCannotBeEmpty\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ManualExecutionGasAmountCountMismatch\",\"inputs\":[{\"name\":\"messageId\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"sequenceNumber\",\"type\":\"uint64\",\"internalType\":\"uint64\"}]},{\"type\":\"error\",\"name\":\"ManualExecutionGasLimitMismatch\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ManualExecutionNotYetEnabled\",\"inputs\":[{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"}]},{\"type\":\"error\",\"name\":\"MessageValidationError\",\"inputs\":[{\"name\":\"errorReason\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]},{\"type\":\"error\",\"name\":\"MustBeProposedOwner\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"NonUniqueSignatures\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"NotACompatiblePool\",\"inputs\":[{\"name\":\"notPool\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"OnlyCallableByOwner\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"OracleCannotBeZeroAddress\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"OwnerCannotBeZero\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ReceiverError\",\"inputs\":[{\"name\":\"err\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]},{\"type\":\"error\",\"name\":\"ReleaseOrMintBalanceMismatch\",\"inputs\":[{\"name\":\"amountReleased\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"balancePre\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"balancePost\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"type\":\"error\",\"name\":\"RootAlreadyCommitted\",\"inputs\":[{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"merkleRoot\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}]},{\"type\":\"error\",\"name\":\"RootNotCommitted\",\"inputs\":[{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"}]},{\"type\":\"error\",\"name\":\"SignatureVerificationNotAllowedInExecutionPlugin\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"SignatureVerificationRequiredInCommitPlugin\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"SignaturesOutOfRegistration\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"SourceChainNotEnabled\",\"inputs\":[{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"}]},{\"type\":\"error\",\"name\":\"SourceChainSelectorMismatch\",\"inputs\":[{\"name\":\"reportSourceChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"messageSourceChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"}]},{\"type\":\"error\",\"name\":\"StaleCommitReport\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"StaticConfigCannotBeChanged\",\"inputs\":[{\"name\":\"ocrPluginType\",\"type\":\"uint8\",\"internalType\":\"uint8\"}]},{\"type\":\"error\",\"name\":\"TokenDataMismatch\",\"inputs\":[{\"name\":\"sourceChainSelector\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"sequenceNumber\",\"type\":\"uint64\",\"internalType\":\"uint64\"}]},{\"type\":\"error\",\"name\":\"TokenHandlingError\",\"inputs\":[{\"name\":\"target\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"err\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]},{\"type\":\"error\",\"name\":\"UnauthorizedSigner\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"UnauthorizedTransmitter\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"UnexpectedTokenData\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"WrongMessageLength\",\"inputs\":[{\"name\":\"expected\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"actual\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"type\":\"error\",\"name\":\"WrongNumberOfSignatures\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ZeroAddressNotAllowed\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ZeroChainSelectorNotAllowed\",\"inputs\":[]}]",
+ Bin: "0x6101408060405234610848576163ea803803809161001d828561087e565b833981019080820361014081126108485760a08112610848576040519060a082016001600160401b0381118382101761084d5760405261005c836108a1565b825260208301519261ffff84168403610848576020830193845260408101516001600160a01b0381168103610848576040840190815261009e606083016108b5565b946060850195865260806100b38185016108b5565b86820190815294609f19011261084857604051946100d086610863565b6100dc60a085016108b5565b865260c08401519463ffffffff86168603610848576020870195865261010460e086016108c9565b976040880198895261011961010087016108b5565b6060890190815261012087015190966001600160401b03821161084857018a601f820112156108485780519a6001600160401b038c1161084d578b60051b916020806040519e8f9061016d8388018361087e565b81520193820101908282116108485760208101935b828510610748575050505050331561073757600180546001600160a01b031916331790554660805284516001600160a01b0316158015610725575b8015610713575b6106f15782516001600160401b0316156107025782516001600160401b0390811660a090815286516001600160a01b0390811660c0528351811660e0528451811661010052865161ffff90811661012052604080519751909416875296519096166020860152955185169084015251831660608301525190911660808201527fb0fa1fb01508c5097c502ad056fd77018870c9be9a86d9e56b6b471862d7c5b79190a182516001600160a01b0316156106f1579151600480548351865160ff60c01b90151560c01b1663ffffffff60a01b60a09290921b919091166001600160a01b039485166001600160c81b0319909316831717179091558351600580549184166001600160a01b031990921691909117905560408051918252925163ffffffff166020820152935115159184019190915290511660608201527fcbb53bda7106a610de67df506ac86b65c44d5afac0fd2b11070dc2d61a6f2dee90608090a160005b815181101561066b576020600582901b8301810151908101516001600160401b031690600090821561065c5780516001600160a01b03161561064d57828252600860205260408220906060810151600183019361038585546108d6565b6105ee578354600160a81b600160e81b031916600160a81b1784556040518681527ff4c1390c70e5c0f491ae1ccbc06f9117cbbadf2767b247b3bc203280f24c0fb990602090a15b815180159081156105c3575b506105b4578151916001600160401b0383116105a0576103f986546108d6565b601f811161055b575b50602091601f84116001146104e257926001989796949281926000805160206163ca8339815191529795926104d7575b5050600019600383901b1c191690881b1783555b60408101518254915160a089811b8a9003801960ff60a01b1990951693151590911b60ff60a01b169290921792909216911617815561048484610993565b506104ce6040519283926020845254888060a01b038116602085015260ff8160a01c1615156040850152888060401b039060a81c16606084015260808084015260a0830190610910565b0390a201610328565b015190503880610432565b9190601f198416878452828420935b818110610543575092600199989795939285926000805160206163ca83398151915298968c951061052a575b505050811b018355610446565b015160001960f88460031b161c1916905538808061051d565b929360206001819287860151815501950193016104f1565b86835260208320601f850160051c81019160208610610596575b601f0160051c01905b81811061058b5750610402565b83815560010161057e565b9091508190610575565b634e487b7160e01b82526041600452602482fd5b6342bcdf7f60e11b8152600490fd5b905060208301206040516020810190838252602081526105e460408261087e565b51902014386103d9565b835460a81c6001600160401b0316600114158061061f575b156103cd57632105803760e11b81526004869052602490fd5b50604051610638816106318189610910565b038261087e565b60208151910120825160208401201415610606565b6342bcdf7f60e11b8252600482fd5b63c656089560e01b8252600482fd5b6040516159a39081610a2782396080518161368c015260a05181818161048e01526141ea015260c0518181816104e401528181612cba0152818161310e0152614184015260e05181818161051301526149c701526101005181818161054201526145ad0152610120518181816104b50152818161243401528181614aba01526156d80152f35b6342bcdf7f60e11b60005260046000fd5b63c656089560e01b60005260046000fd5b5081516001600160a01b0316156101c4565b5080516001600160a01b0316156101bd565b639b15e16f60e01b60005260046000fd5b84516001600160401b0381116108485782016080818603601f190112610848576040519061077582610863565b60208101516001600160a01b0381168103610848578252610798604082016108a1565b60208301526107a9606082016108c9565b604083015260808101516001600160401b03811161084857602091010185601f820112156108485780516001600160401b03811161084d57604051916107f9601f8301601f19166020018461087e565b81835287602083830101116108485760005b8281106108335750509181600060208096949581960101526060820152815201940193610182565b8060208092840101518282870101520161080b565b600080fd5b634e487b7160e01b600052604160045260246000fd5b608081019081106001600160401b0382111761084d57604052565b601f909101601f19168101906001600160401b0382119082101761084d57604052565b51906001600160401b038216820361084857565b51906001600160a01b038216820361084857565b5190811515820361084857565b90600182811c92168015610906575b60208310146108f057565b634e487b7160e01b600052602260045260246000fd5b91607f16916108e5565b60009291815491610920836108d6565b8083529260018116908115610976575060011461093c57505050565b60009081526020812093945091925b83831061095c575060209250010190565b60018160209294939454838587010152019101919061094b565b915050602093945060ff929192191683830152151560051b010190565b80600052600760205260406000205415600014610a20576006546801000000000000000081101561084d576001810180600655811015610a0a577ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f0181905560065460009182526007602052604090912055600190565b634e487b7160e01b600052603260045260246000fd5b5060009056fe6080604052600436101561001257600080fd5b60003560e01c806304666f9c1461015757806306285c6914610152578063181f5a771461014d5780633f4b04aa146101485780635215505b146101435780635e36480c1461013e5780635e7bb0081461013957806360987c20146101345780637437ff9f1461012f57806379ba50971461012a5780637edf52f41461012557806385572ffb146101205780638da5cb5b1461011b578063c673e58414610116578063ccd37ba314610111578063de5e0b9a1461010c578063e9d68a8e14610107578063f2fde38b14610102578063f58e03fc146100fd5763f716f99f146100f857600080fd5b6118d4565b6117b7565b61172c565b611691565b6115f5565b611571565b6114c6565b6113de565b6113a8565b6111e2565b611162565b6110b9565b61103e565b610e39565b6108ce565b610789565b61067c565b61061d565b61043d565b61031f565b634e487b7160e01b600052604160045260246000fd5b608081019081106001600160401b0382111761018d57604052565b61015c565b60a081019081106001600160401b0382111761018d57604052565b604081019081106001600160401b0382111761018d57604052565b606081019081106001600160401b0382111761018d57604052565b90601f801991011681019081106001600160401b0382111761018d57604052565b6040519061021360c0836101e3565b565b6040519061021360a0836101e3565b60405190610213610100836101e3565b604051906102136040836101e3565b6001600160401b03811161018d5760051b60200190565b6001600160a01b0381160361026b57565b600080fd5b600435906001600160401b038216820361026b57565b35906001600160401b038216820361026b57565b8015150361026b57565b35906102138261029a565b6001600160401b03811161018d57601f01601f191660200190565b9291926102d6826102af565b916102e460405193846101e3565b82948184528183011161026b578281602093846000960137010152565b9080601f8301121561026b5781602061031c933591016102ca565b90565b3461026b57602036600319011261026b576004356001600160401b03811161026b573660238201121561026b5780600401359061035b82610243565b9061036960405192836101e3565b8282526024602083019360051b8201019036821161026b5760248101935b8285106103995761039784611a0f565b005b84356001600160401b03811161026b5782016080602319823603011261026b57604051916103c683610172565b60248201356103d48161025a565b83526103e260448301610286565b602084015260648201356103f58161029a565b60408401526084820135926001600160401b03841161026b57610422602094936024869536920101610301565b6060820152815201940193610387565b600091031261026b57565b3461026b57600036600319011261026b576000608060405161045e81610192565b82815282602082015282604082015282606082015201526105bc60405161048481610192565b6001600160401b037f000000000000000000000000000000000000000000000000000000000000000016815261ffff7f00000000000000000000000000000000000000000000000000000000000000001660208201526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660408201526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660608201526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660808201526040519182918291909160806001600160a01b038160a08401956001600160401b03815116855261ffff6020820151166020860152826040820151166040860152826060820151166060860152015116910152565b0390f35b604051906105cf6020836101e3565b60008252565b60005b8381106105e85750506000910152565b81810151838201526020016105d8565b90602091610611815180928185528580860191016105d5565b601f01601f1916010190565b3461026b57600036600319011261026b576105bc604080519061064081836101e3565b601182527f4f666652616d7020312e362e302d6465760000000000000000000000000000006020830152519182916020835260208301906105f8565b3461026b57600036600319011261026b5760206001600160401b03600b5416604051908152f35b906080606061031c936001600160a01b0381511684526020810151151560208501526001600160401b03604082015116604085015201519181606082015201906105f8565b6040810160408252825180915260206060830193019060005b81811061076a575050506020818303910152815180825260208201916020808360051b8301019401926000915b83831061073d57505050505090565b909192939460208061075b600193601f1986820301875289516106a3565b9701930193019193929061072e565b82516001600160401b0316855260209485019490920191600101610701565b3461026b57600036600319011261026b576006546107a681610243565b906107b460405192836101e3565b808252601f196107c382610243565b0160005b8181106108855750506107d981611ce2565b9060005b8181106107f55750506105bc604051928392836106e8565b8061082b61081361080760019461406b565b6001600160401b031690565b61081d8387611d3c565b906001600160401b03169052565b61086961086461084b61083e8488611d3c565b516001600160401b031690565b6001600160401b03166000526008602052604060002090565b611e28565b6108738287611d3c565b5261087e8186611d3c565b50016107dd565b602090610890611cbb565b828287010152016107c7565b634e487b7160e01b600052602160045260246000fd5b600411156108bc57565b61089c565b9060048210156108bc5752565b3461026b57604036600319011261026b576108e7610270565b602435906001600160401b038216820361026b5760209161090791611ec4565b61091460405180926108c1565bf35b91908260a091031261026b5760405161092e81610192565b60806109738183958035855261094660208201610286565b602086015261095760408201610286565b604086015261096860608201610286565b606086015201610286565b910152565b35906102138261025a565b63ffffffff81160361026b57565b359061021382610983565b81601f8201121561026b578035906109b382610243565b926109c160405194856101e3565b82845260208085019360051b8301019181831161026b5760208101935b8385106109ed57505050505090565b84356001600160401b03811161026b57820160a0818503601f19011261026b5760405191610a1a83610192565b60208201356001600160401b03811161026b57856020610a3c92850101610301565b83526040820135610a4c8161025a565b6020840152610a5d60608301610991565b60408401526080820135926001600160401b03841161026b5760a083610a8a886020809881980101610301565b6060840152013560808201528152019401936109de565b9190916101408184031261026b57610ab7610204565b92610ac28183610916565b845260a08201356001600160401b03811161026b5781610ae3918401610301565b602085015260c08201356001600160401b03811161026b5781610b07918401610301565b6040850152610b1860e08301610978565b606085015261010082013560808501526101208201356001600160401b03811161026b57610b46920161099c565b60a0830152565b9080601f8301121561026b578135610b6481610243565b92610b7260405194856101e3565b81845260208085019260051b8201019183831161026b5760208201905b838210610b9e57505050505090565b81356001600160401b03811161026b57602091610bc087848094880101610aa1565b815201910190610b8f565b81601f8201121561026b57803590610be282610243565b92610bf060405194856101e3565b82845260208085019360051b8301019181831161026b5760208101935b838510610c1c57505050505090565b84356001600160401b03811161026b57820183603f8201121561026b576020810135610c4781610243565b91610c5560405193846101e3565b8183526020808085019360051b830101019186831161026b5760408201905b838210610c8e575050509082525060209485019401610c0d565b81356001600160401b03811161026b57602091610cb28a8480809589010101610301565b815201910190610c74565b929190610cc981610243565b93610cd760405195866101e3565b602085838152019160051b810192831161026b57905b828210610cf957505050565b8135815260209182019101610ced565b9080601f8301121561026b5781602061031c93359101610cbd565b81601f8201121561026b57803590610d3b82610243565b92610d4960405194856101e3565b82845260208085019360051b8301019181831161026b5760208101935b838510610d7557505050505090565b84356001600160401b03811161026b57820160a0818503601f19011261026b57610d9d610215565b91610daa60208301610286565b835260408201356001600160401b03811161026b57856020610dce92850101610b4d565b602084015260608201356001600160401b03811161026b57856020610df592850101610bcb565b60408401526080820135926001600160401b03841161026b5760a083610e22886020809881980101610d09565b606084015201356080820152815201940193610d66565b3461026b57604036600319011261026b576004356001600160401b03811161026b57610e69903690600401610d24565b6024356001600160401b03811161026b573660238201121561026b57806004013591610e9483610243565b91610ea260405193846101e3565b8383526024602084019460051b8201019036821161026b5760248101945b828610610ed1576103978585611f0c565b85356001600160401b03811161026b5782013660438201121561026b576024810135610efc81610243565b91610f0a60405193846101e3565b818352602060248185019360051b830101019036821161026b5760448101925b828410610f44575050509082525060209586019501610ec0565b83356001600160401b03811161026b576024908301016040601f19823603011261026b5760405190610f75826101ad565b6020810135825260408101356001600160401b03811161026b57602091010136601f8201121561026b57803590610fab82610243565b91610fb960405193846101e3565b80835260208084019160051b8301019136831161026b57602001905b828210610ff45750505091816020938480940152815201930192610f2a565b60208091833561100381610983565b815201910190610fd5565b9181601f8401121561026b578235916001600160401b03831161026b576020808501948460051b01011161026b57565b3461026b57606036600319011261026b576004356001600160401b03811161026b5761106e903690600401610aa1565b6024356001600160401b03811161026b5761108d90369060040161100e565b91604435926001600160401b03841161026b576110b161039794369060040161100e565b939092612318565b3461026b57600036600319011261026b576110d26125e5565b506105bc6040516110e281610172565b60ff6004546001600160a01b038116835263ffffffff8160a01c16602084015260c01c16151560408201526001600160a01b036005541660608201526040519182918291909160606001600160a01b0381608084019582815116855263ffffffff6020820151166020860152604081015115156040860152015116910152565b3461026b57600036600319011261026b576000546001600160a01b03811633036111d1576001600160a01b0319600154913382841617600155166000556001600160a01b033391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a3005b63015aa1e360e11b60005260046000fd5b3461026b57608036600319011261026b57600060405161120181610172565b60043561120d8161025a565b815260243561121b81610983565b602082015260443561122c8161029a565b604082015260643561123d8161025a565b606082015261124a613489565b6001600160a01b038151161561139957611393816112a96001600160a01b037fcbb53bda7106a610de67df506ac86b65c44d5afac0fd2b11070dc2d61a6f2dee9451166001600160a01b03166001600160a01b03196004541617600455565b60208101516004547fffffffffffffff0000000000ffffffffffffffffffffffffffffffffffffffff77ffffffff000000000000000000000000000000000000000078ff0000000000000000000000000000000000000000000000006040860151151560c01b169360a01b169116171760045561134f61133360608301516001600160a01b031690565b6001600160a01b03166001600160a01b03196005541617600555565b6040519182918291909160606001600160a01b0381608084019582815116855263ffffffff6020820151166020860152604081015115156040860152015116910152565b0390a180f35b6342bcdf7f60e11b8252600482fd5b3461026b57602036600319011261026b576004356001600160401b03811161026b5760a090600319903603011261026b57600080fd5b3461026b57600036600319011261026b5760206001600160a01b0360015416604051908152f35b6004359060ff8216820361026b57565b359060ff8216820361026b57565b906020808351928381520192019060005b8181106114415750505090565b82516001600160a01b0316845260209384019390920191600101611434565b9061031c9160208152606082518051602084015260ff602082015116604084015260ff6040820151168284015201511515608082015260406114b1602084015160c060a085015260e0840190611423565b9201519060c0601f1982850301910152611423565b3461026b57602036600319011261026b5760ff6114e1611405565b6060604080516114f0816101c8565b6114f86125e5565b815282602082015201521660005260026020526105bc6040600020600361156060405192611525846101c8565b61152e8161260a565b845260405161154b816115448160028601612643565b03826101e3565b60208501526115446040518094819301612643565b604082015260405191829182611460565b3461026b57604036600319011261026b5761158a610270565b6001600160401b036024359116600052600a6020526040600020906000526020526020604060002054604051908152f35b9060049160441161026b57565b9181601f8401121561026b578235916001600160401b03831161026b576020838186019501011161026b57565b3461026b5760c036600319011261026b5761160f366115bb565b6044356001600160401b03811161026b5761162e9036906004016115c8565b6064929192356001600160401b03811161026b5761165090369060040161100e565b60843594916001600160401b03861161026b5761167461039796369060040161100e565b94909360a43596612c75565b90602061031c9281815201906106a3565b3461026b57602036600319011261026b576001600160401b036116b2610270565b6116ba611cbb565b501660005260086020526105bc6040600020600161171b604051926116de84610172565b6001600160401b0381546001600160a01b038116865260ff8160a01c161515602087015260a81c1660408501526115446040518094819301611d8a565b606082015260405191829182611680565b3461026b57602036600319011261026b576001600160a01b036004356117518161025a565b611759613489565b163381146117a657806001600160a01b031960005416176000556001600160a01b03600154167fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae1278600080a3005b636d6c4ee560e11b60005260046000fd5b3461026b57606036600319011261026b576117d1366115bb565b6044356001600160401b03811161026b576117f09036906004016115c8565b9182820160208382031261026b578235906001600160401b03821161026b5761181a918401610d24565b60405190602061182a81846101e3565b60008352601f19810160005b81811061185e57505050610397949161184e916136cd565b611856613184565b928392613a33565b60608582018401528201611836565b9080601f8301121561026b57813561188481610243565b9261189260405194856101e3565b81845260208085019260051b82010192831161026b57602001905b8282106118ba5750505090565b6020809183356118c98161025a565b8152019101906118ad565b3461026b57602036600319011261026b576004356001600160401b03811161026b573660238201121561026b5780600401359061191082610243565b9061191e60405192836101e3565b8282526024602083019360051b8201019036821161026b5760248101935b82851061194c57610397846131a0565b84356001600160401b03811161026b57820160c0602319823603011261026b57611974610204565b916024820135835261198860448301611415565b602084015261199960648301611415565b60408401526119aa608483016102a4565b606084015260a48201356001600160401b03811161026b576119d2906024369185010161186d565b608084015260c4820135926001600160401b03841161026b576119ff60209493602486953692010161186d565b60a082015281520194019361193c565b611a17613489565b60005b8151811015611cb757611a2d8183611d3c565b5190611a4360208301516001600160401b031690565b916001600160401b038316908115611ca657611a78611a6c611a6c83516001600160a01b031690565b6001600160a01b031690565b15611c0d57611a9a846001600160401b03166000526008602052604060002090565b906060810151916001810195611ab08754611d50565b611c3457611b237ff4c1390c70e5c0f491ae1ccbc06f9117cbbadf2767b247b3bc203280f24c0fb991611b0984750100000000000000000000000000000000000000000067ffffffffffffffff60a81b19825416179055565b6040516001600160401b0390911681529081906020820190565b0390a15b82518015908115611c1e575b50611c0d57611bee611bd2611c0493611b6f7f49f51971edd25182e97182d6ea372a0488ce2ab639f6a3a7ab4df0d2636fe56b9660019a61352b565b611bc5611b7f6040830151151590565b85547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1690151560a01b74ff000000000000000000000000000000000000000016178555565b516001600160a01b031690565b82906001600160a01b03166001600160a01b0319825416179055565b611bf784615093565b50604051918291826135fc565b0390a201611a1a565b6342bcdf7f60e11b60005260046000fd5b90506020840120611c2d6134ae565b1438611b33565b60016001600160401b03611c5384546001600160401b039060a81c1690565b16141580611c87575b611c665750611b27565b632105803760e11b6000526001600160401b031660045260246000fd5b6000fd5b50611c9187611e0d565b60208151910120845160208601201415611c5c565b63c656089560e01b60005260046000fd5b5050565b60405190611cc882610172565b606080836000815260006020820152600060408201520152565b90611cec82610243565b611cf960405191826101e3565b8281528092611d0a601f1991610243565b0190602036910137565b634e487b7160e01b600052603260045260246000fd5b805115611d375760200190565b611d14565b8051821015611d375760209160051b010190565b90600182811c92168015611d80575b6020831014611d6a57565b634e487b7160e01b600052602260045260246000fd5b91607f1691611d5f565b60009291815491611d9a83611d50565b8083529260018116908115611df05750600114611db657505050565b60009081526020812093945091925b838310611dd6575060209250010190565b600181602092949394548385870101520191019190611dc5565b915050602093945060ff929192191683830152151560051b010190565b90610213611e219260405193848092611d8a565b03836101e3565b9060016060604051611e3981610172565b611e8281956001600160401b0381546001600160a01b038116855260ff8160a01c161515602086015260a81c166040840152611e7b6040518096819301611d8a565b03846101e3565b0152565b634e487b7160e01b600052601160045260246000fd5b908160051b9180830460201490151715611eb257565b611e86565b91908203918211611eb257565b611ed082607f92613646565b9116906801fffffffffffffffe6001600160401b0383169260011b169180830460021490151715611eb2576003911c1660048110156108bc5790565b611f1461368a565b80518251810361210b5760005b818110611f3457505090610213916136cd565b611f3e8184611d3c565b516020810190815151611f518488611d3c565b51928351820361210b5790916000925b808410611f75575050505050600101611f21565b91949398611f87848b98939598611d3c565b515198611f95888851611d3c565b5199806120c2575b5060a08a01988b6020611fb38b8d515193611d3c565b51015151036120855760005b8a515181101561207057611ffb611ff2611fe88f6020611fe08f8793611d3c565b510151611d3c565b5163ffffffff1690565b63ffffffff1690565b8b8161200c575b5050600101611fbf565b611ff2604061201f8561202b9451611d3c565b51015163ffffffff1690565b9081811061203a57508b612002565b8d51516040516348e617b360e01b81526004810191909152602481019390935260448301919091526064820152608490fd5b0390fd5b50985098509893949095600101929091611f61565b611c838b516120a0606082519201516001600160401b031690565b6370a193fd60e01b6000526004919091526001600160401b0316602452604490565b60808b0151811015611f9d57611c83908b6120e488516001600160401b031690565b905151633a98d46360e11b6000526001600160401b03909116600452602452604452606490565b6320f8fd5960e21b60005260046000fd5b60405190612129826101ad565b60006020838281520152565b604051906121446020836101e3565b600080835282815b82811061215857505050565b60209061216361211c565b8282850101520161214c565b805182526001600160401b03602082015116602083015260806121b66121a4604084015160a0604087015260a08601906105f8565b606084015185820360608701526105f8565b9101519160808183039101526020808351928381520192019060005b8181106121df5750505090565b825180516001600160a01b0316855260209081015181860152604090940193909201916001016121d2565b90602061031c92818152019061216f565b6040513d6000823e3d90fd5b3d15612252573d90612238826102af565b9161224660405193846101e3565b82523d6000602084013e565b606090565b90602061031c9281815201906105f8565b909160608284031261026b57815161227f8161029a565b9260208301516001600160401b03811161026b5783019080601f8301121561026b578151916122ad836102af565b916122bb60405193846101e3565b8383526020848301011161026b576040926122dc91602080850191016105d5565b92015190565b9293606092959461ffff6123066001600160a01b039460808852608088019061216f565b97166020860152604085015216910152565b929093913033036125d45761232b612135565b9460a0850151805161258d575b5050505050805191612356602084519401516001600160401b031690565b906020830151916040840192612383845192612370610215565b9788526001600160401b03166020880152565b6040860152606085015260808401526001600160a01b036123ac6005546001600160a01b031690565b1680612510575b5051511580612504575b80156124ee575b80156124c5575b611cb75761245d9181612402611a6c6123f561084b602060009751016001600160401b0390511690565b546001600160a01b031690565b908361241d606060808401519301516001600160a01b031690565b604051633cf9798360e01b815296879586948593917f000000000000000000000000000000000000000000000000000000000000000090600486016122e2565b03925af19081156124c057600090600092612499575b501561247c5750565b6040516302a35ba360e21b815290819061206c9060048301612257565b90506124b891503d806000833e6124b081836101e3565b810190612268565b509038612473565b61221b565b506124e96124e56124e060608401516001600160a01b031690565b6138f4565b1590565b6123cb565b5060608101516001600160a01b03163b156123c4565b506080810151156123bd565b803b1561026b57600060405180926308d450a160e01b82528183816125388a6004830161220a565b03925af19081612572575b5061256c5761206c612553612227565b6040516309c2532560e01b815291829160048301612257565b386123b3565b806125816000612587936101e3565b80610432565b38612543565b85965060206125c99601516125ac60608901516001600160a01b031690565b906125c360208a51016001600160401b0390511690565b926137db565b903880808080612338565b6306e34e6560e31b60005260046000fd5b604051906125f282610172565b60006060838281528260208201528260408201520152565b9060405161261781610172565b606060ff600183958054855201548181166020850152818160081c16604085015260101c161515910152565b906020825491828152019160005260206000209060005b8181106126675750505090565b82546001600160a01b031684526020909301926001928301920161265a565b90610213611e219260405193848092612643565b35906001600160e01b038216820361026b57565b81601f8201121561026b578035906126c582610243565b926126d360405194856101e3565b82845260208085019360061b8301019181831161026b57602001925b8284106126fd575050505090565b60408483031261026b5760206040918251612717816101ad565b61272087610286565b815261272d83880161269a565b838201528152019301926126ef565b81601f8201121561026b5780359061275382610243565b9261276160405194856101e3565b82845260208085019360051b8301019181831161026b5760208101935b83851061278d57505050505090565b84356001600160401b03811161026b57820160a0818503601f19011261026b57604051916127ba83610192565b6127c660208301610286565b83526040820135926001600160401b03841161026b5760a0836127f0886020809881980101610301565b8584015261280060608201610286565b604084015261281160808201610286565b60608401520135608082015281520194019361277e565b81601f8201121561026b5780359061283f82610243565b9261284d60405194856101e3565b82845260208085019360061b8301019181831161026b57602001925b828410612877575050505090565b60408483031261026b5760206040918251612891816101ad565b863581528287013583820152815201930192612869565b60208183031261026b578035906001600160401b03821161026b570160608183031261026b57604051916128db836101c8565b81356001600160401b03811161026b57820160408183031261026b5760405190612904826101ad565b80356001600160401b03811161026b57810183601f8201121561026b57803561292c81610243565b9161293a60405193846101e3565b81835260208084019260061b8201019086821161026b57602001915b8183106129d25750505082526020810135906001600160401b03821161026b57612982918491016126ae565b6020820152835260208201356001600160401b03811161026b57816129a891840161273c565b602084015260408201356001600160401b03811161026b576129ca9201612828565b604082015290565b60408388031261026b57602060409182516129ec816101ad565b85356129f78161025a565b8152612a0483870161269a565b83820152815201920191612956565b9080602083519182815201916020808360051b8301019401926000915b838310612a3f57505050505090565b9091929394602080600192601f198582030186528851906001600160401b038251168152608080612a7d8585015160a08786015260a08501906105f8565b936001600160401b0360408201511660408501526001600160401b036060820151166060850152015191015297019301930191939290612a30565b916001600160a01b03612ad992168352606060208401526060830190612a13565b9060408183039101526020808351928381520192019060005b818110612aff5750505090565b8251805185526020908101518186015260409094019390920191600101612af2565b906020808351928381520192019060005b818110612b3f5750505090565b825180516001600160401b031685526020908101516001600160e01b03168186015260409094019390920191600101612b32565b9190604081019083519160408252825180915260206060830193019060005b818110612bb357505050602061031c93940151906020818403910152612b21565b825180516001600160a01b031686526020908101516001600160e01b03168187015260409095019490920191600101612b92565b90602061031c928181520190612b73565b9081602091031261026b575161031c8161029a565b9091612c2461031c936040845260408401906105f8565b916020818403910152611d8a565b6001600160401b036001911601906001600160401b038211611eb257565b9091612c6761031c93604084526040840190612a13565b916020818403910152612b73565b929693959190979497612c8a828201826128a8565b98612c9e6124e560045460ff9060c01c1690565b6130f2575b895180515115908115916130e3575b5061300a575b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316999860208a019860005b8a518051821015612fa85781612d0191611d3c565b518d612d1482516001600160401b031690565b604051632cbc26bb60e01b815267ffffffffffffffff60801b608083901b1660048201529091602090829060249082905afa9081156124c057600091612f7a575b50612f5d57612d6390613942565b60208201805160208151910120906001830191612d7f83611e0d565b6020815191012003612f40575050805460408301516001600160401b039081169160a81c168114801590612f18575b612ec657506080820151908115612eb557612dff82612df0612dd786516001600160401b031690565b6001600160401b0316600052600a602052604060002090565b90600052602052604060002090565b54612e81578291612e65612e7a92612e2c612e2760606001999801516001600160401b031690565b612c32565b67ffffffffffffffff60a81b197cffffffffffffffff00000000000000000000000000000000000000000083549260a81b169116179055565b612df0612dd74294516001600160401b031690565b5501612cec565b50612e96611c8392516001600160401b031690565b6332cf0cbf60e01b6000526001600160401b0316600452602452604490565b63504570e360e01b60005260046000fd5b82611c8391612ef06060612ee184516001600160401b031690565b9301516001600160401b031690565b636af0786b60e11b6000526001600160401b0392831660045290821660245216604452606490565b50612f3061080760608501516001600160401b031690565b6001600160401b03821611612dae565b5161206c60405192839263b80d8fa960e01b845260048401612c0d565b637edeb53960e11b6000526001600160401b031660045260246000fd5b612f9b915060203d8111612fa1575b612f9381836101e3565b810190612bf8565b38612d55565b503d612f89565b50506130049496989b507f35c02761bcd3ef995c6a601a1981f4ed3934dcbe5041e24e286c89f5531d17e46102139b612ffc949597999b51905190612ff260405192839283612c50565b0390a13691610cbd565b943691610cbd565b93613d2d565b61301f602086015b356001600160401b031690565b600b546001600160401b03828116911610156130c757613055906001600160401b03166001600160401b0319600b541617600b55565b61306d611a6c611a6c6004546001600160a01b031690565b8a5190803b1561026b57604051633937306f60e01b815291600091839182908490829061309d9060048301612be7565b03925af180156124c0576130b2575b50612cb8565b8061258160006130c1936101e3565b386130ac565b5060208a015151612cb857632261116760e01b60005260046000fd5b60209150015151151538612cb2565b60208a01518051613104575b50612ca3565b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169060408c0151823b1561026b57604051633854844f60e11b815292600092849283918291613160913060048501612ab8565b03915afa80156124c057156130fe5780612581600061317e936101e3565b386130fe565b604051906131936020836101e3565b6000808352366020840137565b6131a8613489565b60005b8151811015611cb7576131be8183611d3c565b51906040820160ff6131d1825160ff1690565b161561347357602083015160ff16926131f78460ff166000526002602052604060002090565b916001830191825461321261320c8260ff1690565b60ff1690565b613438575061323f6132276060830151151590565b845462ff0000191690151560101b62ff000016178455565b60a081019182516101008151116133e057805115613422576003860161326d61326782612686565b8a614e41565b60608401516132fd575b947fab8b1b57514019638d7b5ce9c638fe71366fe8e2be1c40a7a80f1733d0e9f547946002946132d96132c96132f79a966132c28760019f9c6132bd6132ef9a8f614fa2565b613f6e565b5160ff1690565b845460ff191660ff821617909455565b5190818555519060405195869501908886613ff4565b0390a1615024565b016131ab565b9794600287939597019661331961331389612686565b88614e41565b60808501519461010086511161340c57855161334161320c61333c8a5160ff1690565b613f5a565b10156133f65785518451116133e0576132d96132c97fab8b1b57514019638d7b5ce9c638fe71366fe8e2be1c40a7a80f1733d0e9f547986132c28760019f6132bd6132f79f9a8f6133c860029f6133c26132ef9f8f906132bd84926133a7845160ff1690565b908054909161ff001990911660089190911b61ff0016179055565b82614ed5565b505050979c9f50975050969a50505094509450613277565b631b3fab5160e11b600052600160045260246000fd5b631b3fab5160e11b600052600360045260246000fd5b631b3fab5160e11b600052600260045260246000fd5b631b3fab5160e11b600052600560045260246000fd5b60101c60ff1661345361344e6060840151151590565b151590565b9015151461323f576321fd80df60e21b60005260ff861660045260246000fd5b631b3fab5160e11b600090815260045260246000fd5b6001600160a01b0360015416330361349d57565b6315ae3a6f60e11b60005260046000fd5b604051602081019060008252602081526134c96040826101e3565b51902090565b8181106134da575050565b600081556001016134cf565b9190601f81116134f557505050565b610213926000526020600020906020601f840160051c83019310613521575b601f0160051c01906134cf565b9091508190613514565b91909182516001600160401b03811161018d576135528161354c8454611d50565b846134e6565b6020601f8211600114613593578190613584939495600092613588575b50508160011b916000199060031b1c19161790565b9055565b01519050388061356f565b601f198216906135a884600052602060002090565b9160005b8181106135e4575095836001959697106135cb575b505050811b019055565b015160001960f88460031b161c191690553880806135c1565b9192602060018192868b0151815501940192016135ac565b90600160a061031c93602081526001600160401b0384546001600160a01b038116602084015260ff81851c161515604084015260a81c166060820152608080820152019101611d8a565b906001600160401b03613686921660005260096020526701ffffffffffffff60406000209160071c166001600160401b0316600052602052604060002090565b5490565b7f00000000000000000000000000000000000000000000000000000000000000004681036136b55750565b630f01ce8560e01b6000526004524660245260446000fd5b91909180511561376f5782511592602091604051926136ec81856101e3565b60008452601f19810160005b81811061374b5750505060005b8151811015613743578061372c61371e60019385611d3c565b518815613732578690614133565b01613705565b61373c8387611d3c565b5190614133565b505050509050565b8290604051613759816101ad565b60008152606083820152828289010152016136f8565b63c2e5347d60e01b60005260046000fd5b9190811015611d375760051b0190565b3561031c81610983565b9190811015611d375760051b81013590601e198136030182121561026b5701908135916001600160401b03831161026b57602001823603811361026b579190565b909294919397968151966137ee88610243565b976137fc604051998a6101e3565b80895261380b601f1991610243565b0160005b8181106138dd57505060005b83518110156138d057806138628c8a8a8a61385c613855878d61384e828f8f9d8f9e60019f8161387e575b505050611d3c565b519761379a565b36916102ca565b93614978565b61386c828c611d3c565b52613877818b611d3c565b500161381b565b63ffffffff613896613891858585613780565b613790565b1615613846576138c6926138ad9261389192613780565b60406138b98585611d3c565b51019063ffffffff169052565b8f8f908391613846565b5096985050505050505050565b6020906138e861211c565b82828d0101520161380f565b6139056385572ffb60e01b82614cdb565b908161391f575b81613915575090565b61031c9150614cad565b905061392a81614c32565b159061390c565b61390563aff2afbf60e01b82614cdb565b6001600160401b031680600052600860205260406000209060ff825460a01c161561396b575090565b63ed053c5960e01b60005260045260246000fd5b6084019081608411611eb257565b60a001908160a011611eb257565b91908201809211611eb257565b600311156108bc57565b60038210156108bc5752565b906102136040516139ce816101ad565b602060ff829554818116845260081c1691016139b2565b8054821015611d375760005260206000200190600090565b60ff60019116019060ff8211611eb257565b60ff601b9116019060ff8211611eb257565b90606092604091835260208301370190565b6001600052600260205293613a677fe90b7bceb6e7df5418fb78d8ee546e97c83a08bbccc01a0644d599ccd2a7c2e061260a565b93853594613a748561397f565b6060820190613a838251151590565b613cff575b803603613ce757508151878103613cce5750613aa261368a565b60016000526003602052613af1613aec7fa15bc60c955c405d20d9149c709e2460f1c2d9a497496a7f46004d1772c3054c5b336001600160a01b0316600052602052604060002090565b6139be565b60026020820151613b01816139a8565b613b0a816139a8565b149081613c66575b5015613c3a575b51613b71575b50505050507f198d6990ef96613a9026203077e422916918b03ff47f0be6bee7b02d8e139ef090613b5561301260019460200190565b604080519283526001600160401b0391909116602083015290a2565b613b9261320c613b8d602085969799989a955194015160ff1690565b6139fd565b03613c29578151835103613c1857613c106000613b559461301294613bdc7f198d6990ef96613a9026203077e422916918b03ff47f0be6bee7b02d8e139ef09960019b36916102ca565b60208151910120604051613c0781613bf989602083019586613a21565b03601f1981018352826101e3565b5190208a614d0b565b948394613b1f565b63a75d88af60e01b60005260046000fd5b6371253a2560e01b60005260046000fd5b72c11c11c11c11c11c11c11c11c11c11c11c11c1330315613b1957631b41e11d60e31b60005260046000fd5b60016000526002602052613cc69150611a6c90613cb390613cad60037fe90b7bceb6e7df5418fb78d8ee546e97c83a08bbccc01a0644d599ccd2a7c2e05b01915160ff1690565b906139e5565b90546001600160a01b039160031b1c1690565b331438613b12565b6324f7d61360e21b600052600452602487905260446000fd5b638e1192e160e01b6000526004523660245260446000fd5b613d2890613d22613d18613d138751611e9c565b61398d565b613d228851611e9c565b9061399b565b613a88565b60008052600260205294909390929091613d667fac33ff75c19e70fe83507db0d683fd3465c996598dc972688b7ace676c89077b61260a565b94863595613d738361397f565b6060820190613d828251151590565b613f37575b803603613ce757508151888103613f1e5750613da161368a565b600080526003602052613dd6613aec7f3617319a054d772f909f7c479a2cebe5066e836a939412e32403c99029b92eff613ad4565b60026020820151613de6816139a8565b613def816139a8565b149081613ed5575b5015613ea9575b51613e3b575b5050505050507f198d6990ef96613a9026203077e422916918b03ff47f0be6bee7b02d8e139ef090613b5561301260009460200190565b613e5761320c613b8d602087989a999b96975194015160ff1690565b03613c29578351865103613c18576000967f198d6990ef96613a9026203077e422916918b03ff47f0be6bee7b02d8e139ef096613b5595613bdc613ea0946130129736916102ca565b94839438613e04565b72c11c11c11c11c11c11c11c11c11c11c11c11c1330315613dfe57631b41e11d60e31b60005260046000fd5b600080526002602052613f169150611a6c90613cb390613cad60037fac33ff75c19e70fe83507db0d683fd3465c996598dc972688b7ace676c89077b613ca4565b331438613df7565b6324f7d61360e21b600052600452602488905260446000fd5b613f5590613d22613f4b613d138951611e9c565b613d228a51611e9c565b613d87565b60ff166003029060ff8216918203611eb257565b8151916001600160401b03831161018d5768010000000000000000831161018d576020908254848455808510613fd7575b500190600052602060002060005b838110613fba5750505050565b60019060206001600160a01b038551169401938184015501613fad565b613fee9084600052858460002091820191016134cf565b38613f9f565b95949392909160ff61401993168752602087015260a0604087015260a0860190612643565b84810360608601526020808351928381520192019060005b81811061404c575050509060806102139294019060ff169052565b82516001600160a01b0316845260209384019390920191600101614031565b600654811015611d375760066000527ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f015490565b6001600160401b0361031c94938160609416835216602082015281604082015201906105f8565b60409061031c9392815281602082015201906105f8565b9291906001600160401b039081606495166004521660245260048110156108bc57604452565b94939261411d60609361412e93885260208801906108c1565b6080604087015260808601906105f8565b930152565b9061414582516001600160401b031690565b8151604051632cbc26bb60e01b815267ffffffffffffffff60801b608084901b1660048201529015159391906001600160401b038216906020816024817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa9081156124c057600091614861575b5061481f5760208301918251519485156147ef576040850180515187036147de576141e787611ce2565b957f000000000000000000000000000000000000000000000000000000000000000061421d600161421787613942565b01611e0d565b6020815191012060405161427d81613bf96020820194868b876001600160401b036060929594938160808401977f2425b0b9f9054c76ff151b0a175b18f37a4a4e82013a72e9f15c9caa095ed21f85521660208401521660408201520152565b519020906001600160401b031660005b8a81106147465750505080608060606142ad9301519101519088866152d4565b9788156147285760005b8881106142ca5750505050505050505050565b5a6142d6828951611d3c565b518051606001516142f0906001600160401b031688611ec4565b6142f9816108b2565b8015908d8283159384614715575b156146d25760608815614655575061432e6020614324898d611d3c565b5101519242611eb7565b6004546143439060a01c63ffffffff16611ff2565b108015614642575b156146245761435a878b611d3c565b515161460e575b845160800151614379906001600160401b0316610807565b614556575b5061438a868951611d3c565b5160a08501515181510361451a57936143ef9695938c938f966143cf8e958c926143c96143c360608951016001600160401b0390511690565b89615306565b866155c1565b9a9080966143e960608851016001600160401b0390511690565b9061538e565b6144c8575b50506143ff826108b2565b60028203614480575b6001966144767f05665fe9ad095383d018353f4cbcba77e84db27dd215081bbf7cdf9ae6fbe48b936001600160401b0393519261446761445e8b61445660608801516001600160401b031690565b96519b611d3c565b51985a90611eb7565b91604051958695169885614104565b0390a45b016142b7565b91509193949250614490826108b2565b600382036144a4578b929493918a91614408565b51606001516349362d1f60e11b600052611c8391906001600160401b0316896140de565b6144d1846108b2565b600384036143f45790929495506144e99193506108b2565b6144f9578b92918a9138806143f4565b5151604051632b11b8d960e01b815290819061206c908790600484016140c7565b611c838b61453460608851016001600160401b0390511690565b631cfe6d8b60e01b6000526001600160401b0391821660045216602452604490565b61455f836108b2565b61456a575b3861437e565b8351608001516001600160401b0316602080860151918c61459f60405194859384936370701e5760e11b8552600485016140a0565b038160006001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165af19081156124c0576000916145f0575b5061456457505050505060019061447a565b614608915060203d8111612fa157612f9381836101e3565b386145de565b614618878b611d3c565b51516080860152614361565b6354e7e43160e11b6000526001600160401b038b1660045260246000fd5b5061464c836108b2565b6003831461434b565b915083614661846108b2565b15614361575060019594506146ca92506146a891507f3ef2a99c550a751d4b0b261268f05a803dfb049ab43616a1ffb388f61fe651209351016001600160401b0390511690565b604080516001600160401b03808c168252909216602083015290918291820190565b0390a161447a565b5050505060019291506146ca6146a860607f3b575419319662b2a6f5e2467d84521517a3382b908eb3d557bb3fdb0c50e23c9351016001600160401b0390511690565b5061471f836108b2565b60038314614307565b633ee8bd3f60e11b6000526001600160401b03841660045260246000fd5b614751818a51611d3c565b518051604001516001600160401b03168381036147c157508051602001516001600160401b031689810361479e57509061478d846001936151cc565b614797828d611d3c565b520161428d565b636c95f1eb60e01b6000526001600160401b03808a166004521660245260446000fd5b631c21951160e11b6000526001600160401b031660045260246000fd5b6357e0e08360e01b60005260046000fd5b611c8361480386516001600160401b031690565b63676cf24b60e11b6000526001600160401b0316600452602490565b5092915050612f5d576040516001600160401b039190911681527faab522ed53d887e56ed53dd37398a01aeef6a58e0fa77c2173beb9512d89493390602090a1565b61487a915060203d602011612fa157612f9381836101e3565b386141bd565b9081602091031261026b575161031c8161025a565b9061031c916020815260e061493361491e6148be855161010060208701526101208601906105f8565b60208601516001600160401b0316604086015260408601516001600160a01b0316606086015260608601516080860152614908608087015160a08701906001600160a01b03169052565b60a0860151858203601f190160c08701526105f8565b60c0850151848203601f1901848601526105f8565b92015190610100601f19828503019101526105f8565b6040906001600160a01b0361031c949316815281602082015201906105f8565b9081602091031261026b575190565b9193929361498461211c565b5060208301516001600160a01b031660405163bbe4f6db60e01b81526001600160a01b038216600482015290959092602084806024810103816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa9384156124c057600094614c01575b506001600160a01b0384169586158015614bef575b614bd157614ab6614adf92613bf992614a3a614a33611ff260408c015163ffffffff1690565b8c896156a0565b9690996080810151614a686060835193015193614a55610224565b9687526001600160401b03166020870152565b6001600160a01b038a16604086015260608501526001600160a01b038d16608085015260a084015260c083015260e0820152604051633907753760e01b602082015292839160248301614895565b82857f00000000000000000000000000000000000000000000000000000000000000009261572e565b94909115614bb55750805160208103614b9c575090614b08826020808a95518301019101614969565b956001600160a01b03841603614b40575b5050505050614b38614b29610234565b6001600160a01b039093168352565b602082015290565b614b5393614b4d91611eb7565b916156a0565b50908082108015614b89575b614b6b57808481614b19565b63a966e21f60e01b6000908152600493909352602452604452606490fd5b5082614b958284611eb7565b1415614b5f565b631e3be00960e21b600052602060045260245260446000fd5b61206c604051928392634ff17cad60e11b845260048401614949565b63ae9b4ce960e01b6000526001600160a01b03851660045260246000fd5b50614bfc6124e586613931565b614a0d565b614c2491945060203d602011614c2b575b614c1c81836101e3565b810190614880565b92386149f8565b503d614c12565b60405160208101916301ffc9a760e01b835263ffffffff60e01b602483015260248252614c606044836101e3565b6179185a10614c9c576020926000925191617530fa6000513d82614c90575b5081614c89575090565b9050151590565b60201115915038614c7f565b63753fa58960e11b60005260046000fd5b60405160208101916301ffc9a760e01b83526301ffc9a760e01b602483015260248252614c606044836101e3565b6040519060208201926301ffc9a760e01b845263ffffffff60e01b16602483015260248252614c606044836101e3565b919390926000948051946000965b868810614d2a575050505050505050565b6020881015611d375760206000614d42878b1a613a0f565b614d4c8b87611d3c565b5190614d83614d5b8d8a611d3c565b5160405193849389859094939260ff6060936080840197845216602083015260408201520152565b838052039060015afa156124c057614dc9613aec600051614db18960ff166000526003602052604060002090565b906001600160a01b0316600052602052604060002090565b9060016020830151614dda816139a8565b614de3816139a8565b03614e3057614e00614df6835160ff1690565b60ff600191161b90565b8116614e1f57614e16614df66001935160ff1690565b17970196614d19565b633d9ef1f160e21b60005260046000fd5b636518c33d60e11b60005260046000fd5b91909160005b8351811015614e9a5760019060ff831660005260036020526000614e93604082206001600160a01b03614e7a858a611d3c565b51166001600160a01b0316600052602052604060002090565b5501614e47565b50509050565b8151815460ff191660ff91909116178155906020015160038110156108bc57815461ff00191660089190911b61ff0016179055565b919060005b8151811015614e9a57614ef0611bc58284611d3c565b90614f19614f0f83614db18860ff166000526003602052604060002090565b5460081c60ff1690565b614f22816139a8565b614f8d576001600160a01b03821615614f7c57614f76600192614f71614f46610234565b60ff8516815291614f5a86602085016139b2565b614db18960ff166000526003602052604060002090565b614ea0565b01614eda565b63d6c62c9b60e01b60005260046000fd5b631b3fab5160e11b6000526004805260246000fd5b919060005b8151811015614e9a57614fbd611bc58284611d3c565b90614fdc614f0f83614db18860ff166000526003602052604060002090565b614fe5816139a8565b614f8d576001600160a01b03821615614f7c5761501e600192614f71615009610234565b60ff8516815291614f5a6002602085016139b2565b01614fa7565b60ff1680600052600260205260ff60016040600020015460101c16908015600014615072575015615061576001600160401b0319600b5416600b55565b6317bd8dd160e11b60005260046000fd5b60011461507c5750565b61508257565b6307b8c74d60e51b60005260046000fd5b80600052600760205260406000205415600014615111576006546801000000000000000081101561018d57600181016006556000600654821015611d3757600690527ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f01819055600654906000526007602052604060002055600190565b50600090565b9080602083519182815201916020808360051b8301019401926000915b83831061514357505050505090565b9091929394602080600192601f198582030186528851906080806151a6615173855160a0865260a08601906105f8565b6001600160a01b0387870151168786015263ffffffff6040870151166040860152606086015185820360608701526105f8565b93015191015297019301930191939290615134565b90602061031c928181520190615117565b6134c981518051906152606151eb60608601516001600160a01b031690565b613bf961520260608501516001600160401b031690565b9361521b6080808a01519201516001600160401b031690565b90604051958694602086019889936001600160401b036080946001600160a01b0382959998949960a089019a8952166020880152166040860152606085015216910152565b519020613bf96020840151602081519101209360a060408201516020815191012091015160405161529981613bf96020820194856151bb565b51902090604051958694602086019889919260a093969594919660c08401976000855260208501526040840152606083015260808201520152565b926001600160401b03926152e7926157eb565b9116600052600a60205260406000209060005260205260406000205490565b607f8216906801fffffffffffffffe6001600160401b0383169260011b169180830460021490151715611eb25761538b916001600160401b036153498584613646565b921660005260096020526701ffffffffffffff60406000209460071c169160036001831b921b19161792906001600160401b0316600052602052604060002090565b55565b9091607f83166801fffffffffffffffe6001600160401b0382169160011b169080820460021490151715611eb2576153c68484613646565b60048310156108bc576001600160401b0361538b9416600052600960205260036701ffffffffffffff60406000209660071c1693831b921b19161792906001600160401b0316600052602052604060002090565b9080602083519182815201916020808360051b8301019401926000915b83831061544657505050505090565b9091929394602080615464600193601f1986820301875289516105f8565b97019301930191939290615437565b906020808351928381520192019060005b8181106154915750505090565b825163ffffffff16845260209384019390920191600101615484565b916155769061556861031c9593606086526001600160401b0360808251805160608a015282602082015116828a01528260408201511660a08a01528260608201511660c08a015201511660e087015260a061553461551d60208401516101406101008b01526101a08a01906105f8565b6040840151898203605f19016101208b01526105f8565b60608301516001600160a01b03166101408901529160808101516101608901520151868203605f1901610180880152615117565b90848203602086015261541a565b916040818403910152615473565b80516020909101516001600160e01b03198116929190600482106155a6575050565b6001600160e01b031960049290920360031b82901b16169150565b90303b1561026b576000916155ea6040519485938493630304c3e160e51b8552600485016154ad565b038183305af1908161568b575b5061568057615604612227565b9072c11c11c11c11c11c11c11c11c11c11c11c11c13314615626575b60039190565b61563f61563283615584565b6001600160e01b03191690565b6337c3be2960e01b148015615665575b1561562057631d1ccf9f60e01b60005260046000fd5b5061567261563283615584565b632be8ca8b60e21b1461564f565b60029061031c6105c0565b80612581600061569a936101e3565b386155f7565b6040516370a0823160e01b60208201526001600160a01b0390911660248201529192916156fd906156d48160448101613bf9565b84837f00000000000000000000000000000000000000000000000000000000000000009261572e565b92909115614bb55750805160208103614b9c5750906157288260208061031c95518301019101614969565b93611eb7565b93919361573b60846102af565b9461574960405196876101e3565b6084865261575760846102af565b602087019590601f1901368737833b156157da575a908082106157c9578291038060061c900311156157b8576000918291825a9560208451940192f1905a9003923d90608482116157af575b6000908287523e929190565b608491506157a3565b6337c3be2960e01b60005260046000fd5b632be8ca8b60e21b60005260046000fd5b63030ed58f60e21b60005260046000fd5b8051928251908415615947576101018511158061593b575b1561586a5781850194600019860195610100871161586a57861561592b5761582a87611ce2565b9660009586978795885b84811061588f575050505050600119018095149384615885575b50508261587b575b50501561586a5761586691611d3c565b5190565b6309bde33960e01b60005260046000fd5b1490503880615856565b149250388061584e565b6001811b8281160361591d57868a1015615908576158b160018b019a85611d3c565b51905b8c888c10156158f457506158cc60018c019b86611d3c565b515b818d1161586a576158ed828f926158e790600196615958565b92611d3c565b5201615834565b60018d019c61590291611d3c565b516158ce565b61591660018c019b8d611d3c565b51906158b4565b615916600189019884611d3c565b5050505090506158669150611d2a565b50610101821115615803565b630469ac9960e21b60005260046000fd5b8181101561596a579061031c9161596f565b61031c915b906040519060208201926001845260408301526060820152606081526134c96080826101e356fea164736f6c634300081a000a49f51971edd25182e97182d6ea372a0488ce2ab639f6a3a7ab4df0d2636fe56b",
}
var OffRampABI = OffRampMetaData.ABI
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 d7adcbef142..9d10c76b0ad 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
@@ -13,9 +13,9 @@ message_hasher: ../../../contracts/solc/ccip/MessageHasher/MessageHasher.sol/Mes
mock_usdc_token_messenger: ../../../contracts/solc/ccip/MockE2EUSDCTokenMessenger/MockE2EUSDCTokenMessenger.sol/MockE2EUSDCTokenMessenger.abi.json ../../../contracts/solc/ccip/MockE2EUSDCTokenMessenger/MockE2EUSDCTokenMessenger.sol/MockE2EUSDCTokenMessenger.bin ad7902d63667e582b93b2fad139aa53111f9fddcedf92b1d6d122d1ab7ec4bab
mock_usdc_token_transmitter: ../../../contracts/solc/ccip/MockE2EUSDCTransmitter/MockE2EUSDCTransmitter.sol/MockE2EUSDCTransmitter.abi.json ../../../contracts/solc/ccip/MockE2EUSDCTransmitter/MockE2EUSDCTransmitter.sol/MockE2EUSDCTransmitter.bin ae0d090105bc248f4eccd337836ec1db760c506d6f5578e662305abbbc520fcd
multi_aggregate_rate_limiter: ../../../contracts/solc/ccip/MultiAggregateRateLimiter/MultiAggregateRateLimiter.sol/MultiAggregateRateLimiter.abi.json ../../../contracts/solc/ccip/MultiAggregateRateLimiter/MultiAggregateRateLimiter.sol/MultiAggregateRateLimiter.bin d462b10c87ad74b73502c3c97a7fc53771b915adb9a0fbee781e744f3827d179
-multi_ocr3_helper: ../../../contracts/solc/ccip/MultiOCR3Helper/MultiOCR3Helper.sol/MultiOCR3Helper.abi.json ../../../contracts/solc/ccip/MultiOCR3Helper/MultiOCR3Helper.sol/MultiOCR3Helper.bin 6fa79484ac09342282df342055494853521ea5332adaee0b709c6e21bcd17869
+multi_ocr3_helper: ../../../contracts/solc/ccip/MultiOCR3Helper/MultiOCR3Helper.sol/MultiOCR3Helper.abi.json ../../../contracts/solc/ccip/MultiOCR3Helper/MultiOCR3Helper.sol/MultiOCR3Helper.bin 71514db63a2ac5e3bebe0e6b393061ee1b414c9405084f4bbd890cfc76c21b0f
nonce_manager: ../../../contracts/solc/ccip/NonceManager/NonceManager.sol/NonceManager.abi.json ../../../contracts/solc/ccip/NonceManager/NonceManager.sol/NonceManager.bin ac76c64749ce07dd2ec1b9346d3401dcc5538253e516aecc4767c4308817ea59
-offramp: ../../../contracts/solc/ccip/OffRamp/OffRamp.sol/OffRamp.abi.json ../../../contracts/solc/ccip/OffRamp/OffRamp.sol/OffRamp.bin 0b6526e1dfc331b45fe742560622a6538d9f134b0aeb560e84cccc7802425be1
+offramp: ../../../contracts/solc/ccip/OffRamp/OffRamp.sol/OffRamp.abi.json ../../../contracts/solc/ccip/OffRamp/OffRamp.sol/OffRamp.bin 61002b1524baea33d1d6a71d007511ccebc4cf8c3105385774cc27e9c00f046e
onramp: ../../../contracts/solc/ccip/OnRamp/OnRamp.sol/OnRamp.abi.json ../../../contracts/solc/ccip/OnRamp/OnRamp.sol/OnRamp.bin a829e4efe4d8f600dc20589505701fbce872b09bc763b1a5abded28ef4a3afc9
ping_pong_demo: ../../../contracts/solc/ccip/PingPongDemo/PingPongDemo.sol/PingPongDemo.abi.json ../../../contracts/solc/ccip/PingPongDemo/PingPongDemo.sol/PingPongDemo.bin c87b6e1a8961a9dd2fab1eced0df12d0c1ef47bb1b2511f372b7e33443a20683
registry_module_owner_custom: ../../../contracts/solc/ccip/RegistryModuleOwnerCustom/RegistryModuleOwnerCustom.sol/RegistryModuleOwnerCustom.abi.json ../../../contracts/solc/ccip/RegistryModuleOwnerCustom/RegistryModuleOwnerCustom.sol/RegistryModuleOwnerCustom.bin ce04722cdea2e96d791e48c6a99f64559125d34cd24e19cfd5281892d2ed8ef0
From 6e09ac75d556c8b05099f7dba78e1db79324ac3f Mon Sep 17 00:00:00 2001
From: Juan Farber
Date: Fri, 10 Jan 2025 14:26:56 -0300
Subject: [PATCH 31/91] [NONEVM-984] Solana TXM Reorg Detection reference bump
(#15888)
* solana bump
* bump ref after solana was merged
---
.changeset/large-ants-occur.md | 5 +++++
core/scripts/go.mod | 8 +++++++-
core/scripts/go.sum | 9 +++++++--
deployment/go.mod | 7 ++++++-
deployment/go.sum | 8 ++++++--
go.mod | 8 +++++++-
go.sum | 7 +++++--
integration-tests/go.mod | 7 ++++++-
integration-tests/go.sum | 7 +++++--
integration-tests/load/go.mod | 7 ++++++-
integration-tests/load/go.sum | 7 +++++--
11 files changed, 65 insertions(+), 15 deletions(-)
create mode 100644 .changeset/large-ants-occur.md
diff --git a/.changeset/large-ants-occur.md b/.changeset/large-ants-occur.md
new file mode 100644
index 00000000000..81bf4ed5728
--- /dev/null
+++ b/.changeset/large-ants-occur.md
@@ -0,0 +1,5 @@
+---
+"chainlink": minor
+---
+
+add reorg detection for Solana TXM. #added
diff --git a/core/scripts/go.mod b/core/scripts/go.mod
index 79addebee3f..7d32653538d 100644
--- a/core/scripts/go.mod
+++ b/core/scripts/go.mod
@@ -73,6 +73,7 @@ require (
github.com/atombender/go-jsonschema v0.16.1-0.20240916205339-a74cd4e2851c // indirect
github.com/avast/retry-go/v4 v4.6.0 // indirect
github.com/aws/aws-sdk-go v1.54.19 // indirect
+ github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59 // indirect
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/benbjohnson/clock v1.3.5 // indirect
github.com/beorn7/perks v1.0.1 // indirect
@@ -80,6 +81,7 @@ require (
github.com/bits-and-blooms/bitset v1.13.0 // indirect
github.com/blendle/zapdriver v1.3.1 // indirect
github.com/btcsuite/btcd/btcec/v2 v2.3.4 // indirect
+ github.com/buger/goterm v0.0.0-20200322175922-2f3e71b85129 // indirect
github.com/buger/jsonparser v1.1.1 // indirect
github.com/bytecodealliance/wasmtime-go/v23 v23.0.0 // indirect
github.com/bytedance/sonic v1.12.3 // indirect
@@ -139,6 +141,7 @@ require (
github.com/gagliardetto/binary v0.8.0 // indirect
github.com/gagliardetto/solana-go v1.12.0 // indirect
github.com/gagliardetto/treeout v0.1.4 // indirect
+ github.com/gagliardetto/utilz v0.1.1 // indirect
github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect
github.com/gedex/inflector v0.0.0-20170307190818-16278e9db813 // indirect
github.com/getsentry/sentry-go v0.27.0 // indirect
@@ -199,6 +202,7 @@ require (
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect
github.com/gtank/merlin v0.1.1 // indirect
github.com/gtank/ristretto255 v0.1.2 // indirect
+ github.com/hako/durafmt v0.0.0-20200710122514-c0fb7b4da026 // indirect
github.com/hashicorp/consul/sdk v0.16.1 // indirect
github.com/hashicorp/go-bexpr v0.1.10 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
@@ -253,6 +257,7 @@ require (
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/mfridman/interpolate v0.0.2 // indirect
+ github.com/miekg/dns v1.1.61 // indirect
github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
@@ -289,6 +294,7 @@ require (
github.com/rogpeppe/go-internal v1.13.1 // indirect
github.com/rs/cors v1.10.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
+ github.com/ryanuber/go-glob v1.0.0 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sanity-io/litter v1.5.5 // indirect
@@ -308,7 +314,7 @@ require (
github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20241220173418-09e17ddbeb20 // indirect
github.com/smartcontractkit/chainlink-protos/job-distributor v0.6.0 // indirect
github.com/smartcontractkit/chainlink-protos/orchestrator v0.4.0 // indirect
- github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241223151630-eac4f1508dce // indirect
+ github.com/smartcontractkit/chainlink-solana v1.1.1-0.20250110142550-e2a9566d39f3 // indirect
github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241202202529-2033490e77b8 // indirect
github.com/smartcontractkit/chainlink-testing-framework/lib v1.50.13 // indirect
github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7 // indirect
diff --git a/core/scripts/go.sum b/core/scripts/go.sum
index 21d260ebdb9..6494f578fdf 100644
--- a/core/scripts/go.sum
+++ b/core/scripts/go.sum
@@ -419,6 +419,7 @@ github.com/gagliardetto/binary v0.8.0 h1:U9ahc45v9HW0d15LoN++vIXSJyqR/pWw8DDlhd7
github.com/gagliardetto/binary v0.8.0/go.mod h1:2tfj51g5o9dnvsc+fL3Jxr22MuWzYXwx9wEoN0XQ7/c=
github.com/gagliardetto/gofuzz v1.2.2 h1:XL/8qDMzcgvR4+CyRQW9UGdwPRPMHVJfqQ/uMvSUuQw=
github.com/gagliardetto/gofuzz v1.2.2/go.mod h1:bkH/3hYLZrMLbfYWA0pWzXmi5TTRZnu4pMGZBkqMKvY=
+github.com/gagliardetto/hashsearch v0.0.0-20191005111333-09dd671e19f9/go.mod h1:513DXpQPzeRo7d4dsCP3xO3XI8hgvruMl9njxyQeraQ=
github.com/gagliardetto/solana-go v1.12.0 h1:rzsbilDPj6p+/DOPXBMLhwMZeBgeRuXjm5zQFCoXgsg=
github.com/gagliardetto/solana-go v1.12.0/go.mod h1:l/qqqIN6qJJPtxW/G1PF4JtcE3Zg2vD2EliZrr9Gn5k=
github.com/gagliardetto/treeout v0.1.4 h1:ozeYerrLCmCubo1TcIjFiOWTTGteOOHND1twdFpgwaw=
@@ -922,6 +923,7 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5
github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6BbAxPY=
github.com/mfridman/interpolate v0.0.2/go.mod h1:p+7uk6oE07mpE/Ik1b8EckO0O4ZXiGAfshKBWLUM9Xg=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
+github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
github.com/miekg/dns v1.1.61 h1:nLxbwF3XxhwVSm8g9Dghm9MHPaUZuqhPiGL+675ZmEs=
github.com/miekg/dns v1.1.61/go.mod h1:mnAarhS3nWaW+NVP2wTkYVIZyHNJ098SJZUki3eykwQ=
github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM=
@@ -1174,8 +1176,8 @@ github.com/smartcontractkit/chainlink-protos/job-distributor v0.6.0 h1:0ewLMbAz3
github.com/smartcontractkit/chainlink-protos/job-distributor v0.6.0/go.mod h1:/dVVLXrsp+V0AbcYGJo3XMzKg3CkELsweA/TTopCsKE=
github.com/smartcontractkit/chainlink-protos/orchestrator v0.4.0 h1:ZBat8EBvE2LpSQR9U1gEbRV6PfAkiFdINmQ8nVnXIAQ=
github.com/smartcontractkit/chainlink-protos/orchestrator v0.4.0/go.mod h1:m/A3lqD7ms/RsQ9BT5P2uceYY0QX5mIt4KQxT2G6qEo=
-github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241223151630-eac4f1508dce h1:Mvpbr/Fi2IdU2EcmqCxhlCzs5ncnx+BwV80YweA4DZs=
-github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241223151630-eac4f1508dce/go.mod h1:qq+nW0JDnCCGMf2c38ZHjH8xgkAQnXKighjJr5JdDNE=
+github.com/smartcontractkit/chainlink-solana v1.1.1-0.20250110142550-e2a9566d39f3 h1:L6UDu0GzSKfz+/nMSz9yfYrgOpW1/OXhiVK60PxVsnY=
+github.com/smartcontractkit/chainlink-solana v1.1.1-0.20250110142550-e2a9566d39f3/go.mod h1:52U0UH8K0Qwu+HB1LMc+5V27ru2Tgy29YPT+2HkMevY=
github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241202202529-2033490e77b8 h1:tNS7U9lrxkFvEuyxQv11HHOiV9LPDGC9wYEy+yM/Jv4=
github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241202202529-2033490e77b8/go.mod h1:EBrEgcdIbwepqguClkv8Ohy7CbyWSJaE4EC9aBJlQK0=
github.com/smartcontractkit/chainlink-testing-framework/framework v0.4.2-0.20250110073248-456673e8eea2 h1:nTUoe7GZLw17nPLV5t3Vgf4U4pf+VW0Uko5xpNiKdKU=
@@ -1513,6 +1515,7 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -1599,6 +1602,7 @@ golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1713,6 +1717,7 @@ golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
diff --git a/deployment/go.mod b/deployment/go.mod
index f8875c64544..9595794978c 100644
--- a/deployment/go.mod
+++ b/deployment/go.mod
@@ -33,7 +33,7 @@ require (
github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250103152858-8973fd0c912b
github.com/smartcontractkit/chainlink-common v0.4.1-0.20250108194320-2ebd63bbb16e
github.com/smartcontractkit/chainlink-protos/job-distributor v0.6.0
- github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241223151630-eac4f1508dce
+ github.com/smartcontractkit/chainlink-solana v1.1.1-0.20250110142550-e2a9566d39f3
github.com/smartcontractkit/chainlink-testing-framework/framework v0.4.2-0.20250110073248-456673e8eea2
github.com/smartcontractkit/chainlink-testing-framework/lib v1.50.13
github.com/smartcontractkit/libocr v0.0.0-20241223215956-e5b78d8e3919
@@ -107,6 +107,7 @@ require (
github.com/aws/constructs-go/constructs/v10 v10.4.2 // indirect
github.com/aws/jsii-runtime-go v1.104.0 // indirect
github.com/aws/smithy-go v1.22.0 // indirect
+ github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59 // indirect
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/bboreham/go-loser v0.0.0-20230920113527-fcc2c21820a3 // indirect
github.com/benbjohnson/clock v1.3.5 // indirect
@@ -117,6 +118,7 @@ require (
github.com/blendle/zapdriver v1.3.1 // indirect
github.com/btcsuite/btcd/btcec/v2 v2.3.4 // indirect
github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 // indirect
+ github.com/buger/goterm v0.0.0-20200322175922-2f3e71b85129 // indirect
github.com/buger/jsonparser v1.1.1 // indirect
github.com/bytecodealliance/wasmtime-go/v23 v23.0.0 // indirect
github.com/bytedance/sonic v1.12.3 // indirect
@@ -193,6 +195,7 @@ require (
github.com/gabriel-vasile/mimetype v1.4.6 // indirect
github.com/gagliardetto/binary v0.8.0 // indirect
github.com/gagliardetto/treeout v0.1.4 // indirect
+ github.com/gagliardetto/utilz v0.1.1 // indirect
github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect
github.com/getsentry/sentry-go v0.27.0 // indirect
github.com/gin-contrib/sessions v0.0.5 // indirect
@@ -269,6 +272,7 @@ require (
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect
github.com/gtank/merlin v0.1.1 // indirect
github.com/gtank/ristretto255 v0.1.2 // indirect
+ github.com/hako/durafmt v0.0.0-20200710122514-c0fb7b4da026 // indirect
github.com/hashicorp/consul/api v1.29.2 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-bexpr v0.1.10 // indirect
@@ -394,6 +398,7 @@ require (
github.com/rogpeppe/go-internal v1.13.1 // indirect
github.com/rs/cors v1.10.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
+ github.com/ryanuber/go-glob v1.0.0 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sanity-io/litter v1.5.5 // indirect
diff --git a/deployment/go.sum b/deployment/go.sum
index 7e7fb74354d..4800155620c 100644
--- a/deployment/go.sum
+++ b/deployment/go.sum
@@ -522,6 +522,7 @@ github.com/gagliardetto/binary v0.8.0 h1:U9ahc45v9HW0d15LoN++vIXSJyqR/pWw8DDlhd7
github.com/gagliardetto/binary v0.8.0/go.mod h1:2tfj51g5o9dnvsc+fL3Jxr22MuWzYXwx9wEoN0XQ7/c=
github.com/gagliardetto/gofuzz v1.2.2 h1:XL/8qDMzcgvR4+CyRQW9UGdwPRPMHVJfqQ/uMvSUuQw=
github.com/gagliardetto/gofuzz v1.2.2/go.mod h1:bkH/3hYLZrMLbfYWA0pWzXmi5TTRZnu4pMGZBkqMKvY=
+github.com/gagliardetto/hashsearch v0.0.0-20191005111333-09dd671e19f9/go.mod h1:513DXpQPzeRo7d4dsCP3xO3XI8hgvruMl9njxyQeraQ=
github.com/gagliardetto/solana-go v1.12.0 h1:rzsbilDPj6p+/DOPXBMLhwMZeBgeRuXjm5zQFCoXgsg=
github.com/gagliardetto/solana-go v1.12.0/go.mod h1:l/qqqIN6qJJPtxW/G1PF4JtcE3Zg2vD2EliZrr9Gn5k=
github.com/gagliardetto/treeout v0.1.4 h1:ozeYerrLCmCubo1TcIjFiOWTTGteOOHND1twdFpgwaw=
@@ -1101,6 +1102,7 @@ github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6B
github.com/mfridman/interpolate v0.0.2/go.mod h1:p+7uk6oE07mpE/Ik1b8EckO0O4ZXiGAfshKBWLUM9Xg=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
+github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
github.com/miekg/dns v1.1.61 h1:nLxbwF3XxhwVSm8g9Dghm9MHPaUZuqhPiGL+675ZmEs=
github.com/miekg/dns v1.1.61/go.mod h1:mnAarhS3nWaW+NVP2wTkYVIZyHNJ098SJZUki3eykwQ=
@@ -1400,8 +1402,8 @@ github.com/smartcontractkit/chainlink-protos/job-distributor v0.6.0 h1:0ewLMbAz3
github.com/smartcontractkit/chainlink-protos/job-distributor v0.6.0/go.mod h1:/dVVLXrsp+V0AbcYGJo3XMzKg3CkELsweA/TTopCsKE=
github.com/smartcontractkit/chainlink-protos/orchestrator v0.4.0 h1:ZBat8EBvE2LpSQR9U1gEbRV6PfAkiFdINmQ8nVnXIAQ=
github.com/smartcontractkit/chainlink-protos/orchestrator v0.4.0/go.mod h1:m/A3lqD7ms/RsQ9BT5P2uceYY0QX5mIt4KQxT2G6qEo=
-github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241223151630-eac4f1508dce h1:Mvpbr/Fi2IdU2EcmqCxhlCzs5ncnx+BwV80YweA4DZs=
-github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241223151630-eac4f1508dce/go.mod h1:qq+nW0JDnCCGMf2c38ZHjH8xgkAQnXKighjJr5JdDNE=
+github.com/smartcontractkit/chainlink-solana v1.1.1-0.20250110142550-e2a9566d39f3 h1:L6UDu0GzSKfz+/nMSz9yfYrgOpW1/OXhiVK60PxVsnY=
+github.com/smartcontractkit/chainlink-solana v1.1.1-0.20250110142550-e2a9566d39f3/go.mod h1:52U0UH8K0Qwu+HB1LMc+5V27ru2Tgy29YPT+2HkMevY=
github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241202202529-2033490e77b8 h1:tNS7U9lrxkFvEuyxQv11HHOiV9LPDGC9wYEy+yM/Jv4=
github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241202202529-2033490e77b8/go.mod h1:EBrEgcdIbwepqguClkv8Ohy7CbyWSJaE4EC9aBJlQK0=
github.com/smartcontractkit/chainlink-testing-framework/framework v0.4.2-0.20250110073248-456673e8eea2 h1:nTUoe7GZLw17nPLV5t3Vgf4U4pf+VW0Uko5xpNiKdKU=
@@ -1698,6 +1700,7 @@ golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
+golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
@@ -1989,6 +1992,7 @@ golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
diff --git a/go.mod b/go.mod
index eecb2533829..ccedade99b3 100644
--- a/go.mod
+++ b/go.mod
@@ -85,7 +85,7 @@ require (
github.com/smartcontractkit/chainlink-feeds v0.1.1
github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20241220173418-09e17ddbeb20
github.com/smartcontractkit/chainlink-protos/orchestrator v0.4.0
- github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241223151630-eac4f1508dce
+ github.com/smartcontractkit/chainlink-solana v1.1.1-0.20250110142550-e2a9566d39f3
github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241202202529-2033490e77b8
github.com/smartcontractkit/libocr v0.0.0-20241223215956-e5b78d8e3919
github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20241009055228-33d0c0bf38de
@@ -153,12 +153,14 @@ require (
github.com/apache/arrow-go/v18 v18.0.0 // indirect
github.com/armon/go-metrics v0.4.1 // indirect
github.com/atombender/go-jsonschema v0.16.1-0.20240916205339-a74cd4e2851c // indirect
+ github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59 // indirect
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/benbjohnson/clock v1.3.5 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect
github.com/bits-and-blooms/bitset v1.13.0 // indirect
github.com/blendle/zapdriver v1.3.1 // indirect
+ github.com/buger/goterm v0.0.0-20200322175922-2f3e71b85129 // indirect
github.com/buger/jsonparser v1.1.1 // indirect
github.com/bytecodealliance/wasmtime-go/v23 v23.0.0 // indirect
github.com/bytedance/sonic v1.11.6 // indirect
@@ -208,6 +210,7 @@ require (
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/gagliardetto/binary v0.7.7 // indirect
github.com/gagliardetto/treeout v0.1.4 // indirect
+ github.com/gagliardetto/utilz v0.1.1 // indirect
github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect
github.com/gedex/inflector v0.0.0-20170307190818-16278e9db813 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
@@ -250,6 +253,7 @@ require (
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect
github.com/gtank/merlin v0.1.1 // indirect
github.com/gtank/ristretto255 v0.1.2 // indirect
+ github.com/hako/durafmt v0.0.0-20200710122514-c0fb7b4da026 // indirect
github.com/hashicorp/go-bexpr v0.1.10 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-hclog v1.6.3 // indirect
@@ -289,6 +293,7 @@ require (
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/mfridman/interpolate v0.0.2 // indirect
+ github.com/miekg/dns v1.1.61 // indirect
github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
@@ -316,6 +321,7 @@ require (
github.com/rivo/uniseg v0.4.4 // indirect
github.com/rs/cors v1.9.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
+ github.com/ryanuber/go-glob v1.0.0 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sanity-io/litter v1.5.5 // indirect
diff --git a/go.sum b/go.sum
index 0f5735df451..6e8c1b4b5f3 100644
--- a/go.sum
+++ b/go.sum
@@ -406,6 +406,7 @@ github.com/gagliardetto/binary v0.7.7 h1:QZpT38+sgoPg+TIQjH94sLbl/vX+nlIRA37pEyO
github.com/gagliardetto/binary v0.7.7/go.mod h1:mUuay5LL8wFVnIlecHakSZMvcdqfs+CsotR5n77kyjM=
github.com/gagliardetto/gofuzz v1.2.2 h1:XL/8qDMzcgvR4+CyRQW9UGdwPRPMHVJfqQ/uMvSUuQw=
github.com/gagliardetto/gofuzz v1.2.2/go.mod h1:bkH/3hYLZrMLbfYWA0pWzXmi5TTRZnu4pMGZBkqMKvY=
+github.com/gagliardetto/hashsearch v0.0.0-20191005111333-09dd671e19f9/go.mod h1:513DXpQPzeRo7d4dsCP3xO3XI8hgvruMl9njxyQeraQ=
github.com/gagliardetto/solana-go v1.8.4 h1:vmD/JmTlonyXGy39bAo0inMhmbdAwV7rXZtLDMZeodE=
github.com/gagliardetto/solana-go v1.8.4/go.mod h1:i+7aAyNDTHG0jK8GZIBSI4OVvDqkt2Qx+LklYclRNG8=
github.com/gagliardetto/treeout v0.1.4 h1:ozeYerrLCmCubo1TcIjFiOWTTGteOOHND1twdFpgwaw=
@@ -913,6 +914,7 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5
github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6BbAxPY=
github.com/mfridman/interpolate v0.0.2/go.mod h1:p+7uk6oE07mpE/Ik1b8EckO0O4ZXiGAfshKBWLUM9Xg=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
+github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
github.com/miekg/dns v1.1.61 h1:nLxbwF3XxhwVSm8g9Dghm9MHPaUZuqhPiGL+675ZmEs=
github.com/miekg/dns v1.1.61/go.mod h1:mnAarhS3nWaW+NVP2wTkYVIZyHNJ098SJZUki3eykwQ=
github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM=
@@ -1162,8 +1164,8 @@ github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20241220173418-
github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20241220173418-09e17ddbeb20/go.mod h1:4JqpgFy01LaqG1yM2iFTzwX3ZgcAvW9WdstBZQgPHzU=
github.com/smartcontractkit/chainlink-protos/orchestrator v0.4.0 h1:ZBat8EBvE2LpSQR9U1gEbRV6PfAkiFdINmQ8nVnXIAQ=
github.com/smartcontractkit/chainlink-protos/orchestrator v0.4.0/go.mod h1:m/A3lqD7ms/RsQ9BT5P2uceYY0QX5mIt4KQxT2G6qEo=
-github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241223151630-eac4f1508dce h1:Mvpbr/Fi2IdU2EcmqCxhlCzs5ncnx+BwV80YweA4DZs=
-github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241223151630-eac4f1508dce/go.mod h1:qq+nW0JDnCCGMf2c38ZHjH8xgkAQnXKighjJr5JdDNE=
+github.com/smartcontractkit/chainlink-solana v1.1.1-0.20250110142550-e2a9566d39f3 h1:L6UDu0GzSKfz+/nMSz9yfYrgOpW1/OXhiVK60PxVsnY=
+github.com/smartcontractkit/chainlink-solana v1.1.1-0.20250110142550-e2a9566d39f3/go.mod h1:52U0UH8K0Qwu+HB1LMc+5V27ru2Tgy29YPT+2HkMevY=
github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241202202529-2033490e77b8 h1:tNS7U9lrxkFvEuyxQv11HHOiV9LPDGC9wYEy+yM/Jv4=
github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241202202529-2033490e77b8/go.mod h1:EBrEgcdIbwepqguClkv8Ohy7CbyWSJaE4EC9aBJlQK0=
github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7 h1:12ijqMM9tvYVEm+nR826WsrNi6zCKpwBhuApq127wHs=
@@ -1612,6 +1614,7 @@ golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
diff --git a/integration-tests/go.mod b/integration-tests/go.mod
index 5b3322a4a44..bb1952af8f3 100644
--- a/integration-tests/go.mod
+++ b/integration-tests/go.mod
@@ -126,6 +126,7 @@ require (
github.com/aws/constructs-go/constructs/v10 v10.4.2 // indirect
github.com/aws/jsii-runtime-go v1.104.0 // indirect
github.com/aws/smithy-go v1.22.0 // indirect
+ github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59 // indirect
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/bboreham/go-loser v0.0.0-20230920113527-fcc2c21820a3 // indirect
github.com/benbjohnson/clock v1.3.5 // indirect
@@ -135,6 +136,7 @@ require (
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/blendle/zapdriver v1.3.1 // indirect
github.com/btcsuite/btcd/btcec/v2 v2.3.4 // indirect
+ github.com/buger/goterm v0.0.0-20200322175922-2f3e71b85129 // indirect
github.com/buger/jsonparser v1.1.1 // indirect
github.com/bytecodealliance/wasmtime-go/v23 v23.0.0 // indirect
github.com/bytedance/sonic v1.12.3 // indirect
@@ -212,6 +214,7 @@ require (
github.com/gagliardetto/binary v0.8.0 // indirect
github.com/gagliardetto/solana-go v1.12.0 // indirect
github.com/gagliardetto/treeout v0.1.4 // indirect
+ github.com/gagliardetto/utilz v0.1.1 // indirect
github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect
github.com/getsentry/sentry-go v0.27.0 // indirect
github.com/gin-contrib/sessions v0.0.5 // indirect
@@ -287,6 +290,7 @@ require (
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect
github.com/gtank/merlin v0.1.1 // indirect
github.com/gtank/ristretto255 v0.1.2 // indirect
+ github.com/hako/durafmt v0.0.0-20200710122514-c0fb7b4da026 // indirect
github.com/hashicorp/consul/api v1.29.2 // indirect
github.com/hashicorp/consul/sdk v0.16.1 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
@@ -409,6 +413,7 @@ require (
github.com/rogpeppe/go-internal v1.13.1 // indirect
github.com/rs/cors v1.10.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
+ github.com/ryanuber/go-glob v1.0.0 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sanity-io/litter v1.5.5 // indirect
@@ -428,7 +433,7 @@ require (
github.com/smartcontractkit/chainlink-feeds v0.1.1 // indirect
github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20241220173418-09e17ddbeb20 // indirect
github.com/smartcontractkit/chainlink-protos/orchestrator v0.4.0 // indirect
- github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241223151630-eac4f1508dce // indirect
+ github.com/smartcontractkit/chainlink-solana v1.1.1-0.20250110142550-e2a9566d39f3 // indirect
github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241202202529-2033490e77b8 // indirect
github.com/smartcontractkit/chainlink-testing-framework/framework v0.4.2-0.20250110073248-456673e8eea2 // indirect
github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7 // indirect
diff --git a/integration-tests/go.sum b/integration-tests/go.sum
index ea4e33c1e7b..2377fe29ce3 100644
--- a/integration-tests/go.sum
+++ b/integration-tests/go.sum
@@ -526,6 +526,7 @@ github.com/gagliardetto/binary v0.8.0 h1:U9ahc45v9HW0d15LoN++vIXSJyqR/pWw8DDlhd7
github.com/gagliardetto/binary v0.8.0/go.mod h1:2tfj51g5o9dnvsc+fL3Jxr22MuWzYXwx9wEoN0XQ7/c=
github.com/gagliardetto/gofuzz v1.2.2 h1:XL/8qDMzcgvR4+CyRQW9UGdwPRPMHVJfqQ/uMvSUuQw=
github.com/gagliardetto/gofuzz v1.2.2/go.mod h1:bkH/3hYLZrMLbfYWA0pWzXmi5TTRZnu4pMGZBkqMKvY=
+github.com/gagliardetto/hashsearch v0.0.0-20191005111333-09dd671e19f9/go.mod h1:513DXpQPzeRo7d4dsCP3xO3XI8hgvruMl9njxyQeraQ=
github.com/gagliardetto/solana-go v1.12.0 h1:rzsbilDPj6p+/DOPXBMLhwMZeBgeRuXjm5zQFCoXgsg=
github.com/gagliardetto/solana-go v1.12.0/go.mod h1:l/qqqIN6qJJPtxW/G1PF4JtcE3Zg2vD2EliZrr9Gn5k=
github.com/gagliardetto/treeout v0.1.4 h1:ozeYerrLCmCubo1TcIjFiOWTTGteOOHND1twdFpgwaw=
@@ -1113,6 +1114,7 @@ github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6B
github.com/mfridman/interpolate v0.0.2/go.mod h1:p+7uk6oE07mpE/Ik1b8EckO0O4ZXiGAfshKBWLUM9Xg=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
+github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
github.com/miekg/dns v1.1.61 h1:nLxbwF3XxhwVSm8g9Dghm9MHPaUZuqhPiGL+675ZmEs=
github.com/miekg/dns v1.1.61/go.mod h1:mnAarhS3nWaW+NVP2wTkYVIZyHNJ098SJZUki3eykwQ=
@@ -1424,8 +1426,8 @@ github.com/smartcontractkit/chainlink-protos/job-distributor v0.6.0 h1:0ewLMbAz3
github.com/smartcontractkit/chainlink-protos/job-distributor v0.6.0/go.mod h1:/dVVLXrsp+V0AbcYGJo3XMzKg3CkELsweA/TTopCsKE=
github.com/smartcontractkit/chainlink-protos/orchestrator v0.4.0 h1:ZBat8EBvE2LpSQR9U1gEbRV6PfAkiFdINmQ8nVnXIAQ=
github.com/smartcontractkit/chainlink-protos/orchestrator v0.4.0/go.mod h1:m/A3lqD7ms/RsQ9BT5P2uceYY0QX5mIt4KQxT2G6qEo=
-github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241223151630-eac4f1508dce h1:Mvpbr/Fi2IdU2EcmqCxhlCzs5ncnx+BwV80YweA4DZs=
-github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241223151630-eac4f1508dce/go.mod h1:qq+nW0JDnCCGMf2c38ZHjH8xgkAQnXKighjJr5JdDNE=
+github.com/smartcontractkit/chainlink-solana v1.1.1-0.20250110142550-e2a9566d39f3 h1:L6UDu0GzSKfz+/nMSz9yfYrgOpW1/OXhiVK60PxVsnY=
+github.com/smartcontractkit/chainlink-solana v1.1.1-0.20250110142550-e2a9566d39f3/go.mod h1:52U0UH8K0Qwu+HB1LMc+5V27ru2Tgy29YPT+2HkMevY=
github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241202202529-2033490e77b8 h1:tNS7U9lrxkFvEuyxQv11HHOiV9LPDGC9wYEy+yM/Jv4=
github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241202202529-2033490e77b8/go.mod h1:EBrEgcdIbwepqguClkv8Ohy7CbyWSJaE4EC9aBJlQK0=
github.com/smartcontractkit/chainlink-testing-framework/framework v0.4.2-0.20250110073248-456673e8eea2 h1:nTUoe7GZLw17nPLV5t3Vgf4U4pf+VW0Uko5xpNiKdKU=
@@ -2020,6 +2022,7 @@ golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
diff --git a/integration-tests/load/go.mod b/integration-tests/load/go.mod
index da059e156b7..2c0703ed3b5 100644
--- a/integration-tests/load/go.mod
+++ b/integration-tests/load/go.mod
@@ -95,6 +95,7 @@ require (
github.com/aws/constructs-go/constructs/v10 v10.4.2 // indirect
github.com/aws/jsii-runtime-go v1.104.0 // indirect
github.com/aws/smithy-go v1.22.0 // indirect
+ github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59 // indirect
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df // indirect
github.com/bboreham/go-loser v0.0.0-20230920113527-fcc2c21820a3 // indirect
@@ -105,6 +106,7 @@ require (
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/blendle/zapdriver v1.3.1 // indirect
github.com/btcsuite/btcd/btcec/v2 v2.3.4 // indirect
+ github.com/buger/goterm v0.0.0-20200322175922-2f3e71b85129 // indirect
github.com/buger/jsonparser v1.1.1 // indirect
github.com/bytecodealliance/wasmtime-go/v23 v23.0.0 // indirect
github.com/bytedance/sonic v1.12.3 // indirect
@@ -183,6 +185,7 @@ require (
github.com/gagliardetto/binary v0.8.0 // indirect
github.com/gagliardetto/solana-go v1.12.0 // indirect
github.com/gagliardetto/treeout v0.1.4 // indirect
+ github.com/gagliardetto/utilz v0.1.1 // indirect
github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect
github.com/getsentry/sentry-go v0.27.0 // indirect
github.com/gin-contrib/sessions v0.0.5 // indirect
@@ -261,6 +264,7 @@ require (
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect
github.com/gtank/merlin v0.1.1 // indirect
github.com/gtank/ristretto255 v0.1.2 // indirect
+ github.com/hako/durafmt v0.0.0-20200710122514-c0fb7b4da026 // indirect
github.com/hashicorp/consul/api v1.29.2 // indirect
github.com/hashicorp/consul/sdk v0.16.1 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
@@ -387,6 +391,7 @@ require (
github.com/rogpeppe/go-internal v1.13.1 // indirect
github.com/rs/cors v1.10.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
+ github.com/ryanuber/go-glob v1.0.0 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sanity-io/litter v1.5.5 // indirect
@@ -411,7 +416,7 @@ require (
github.com/smartcontractkit/chainlink-feeds v0.1.1 // indirect
github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20241220173418-09e17ddbeb20 // indirect
github.com/smartcontractkit/chainlink-protos/orchestrator v0.4.0 // indirect
- github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241223151630-eac4f1508dce // indirect
+ github.com/smartcontractkit/chainlink-solana v1.1.1-0.20250110142550-e2a9566d39f3 // indirect
github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241202202529-2033490e77b8 // indirect
github.com/smartcontractkit/chainlink-testing-framework/havoc v1.50.2 // indirect
github.com/smartcontractkit/chainlink-testing-framework/lib/grafana v1.50.0 // indirect
diff --git a/integration-tests/load/go.sum b/integration-tests/load/go.sum
index b592320e882..036b3992aaf 100644
--- a/integration-tests/load/go.sum
+++ b/integration-tests/load/go.sum
@@ -518,6 +518,7 @@ github.com/gagliardetto/binary v0.8.0 h1:U9ahc45v9HW0d15LoN++vIXSJyqR/pWw8DDlhd7
github.com/gagliardetto/binary v0.8.0/go.mod h1:2tfj51g5o9dnvsc+fL3Jxr22MuWzYXwx9wEoN0XQ7/c=
github.com/gagliardetto/gofuzz v1.2.2 h1:XL/8qDMzcgvR4+CyRQW9UGdwPRPMHVJfqQ/uMvSUuQw=
github.com/gagliardetto/gofuzz v1.2.2/go.mod h1:bkH/3hYLZrMLbfYWA0pWzXmi5TTRZnu4pMGZBkqMKvY=
+github.com/gagliardetto/hashsearch v0.0.0-20191005111333-09dd671e19f9/go.mod h1:513DXpQPzeRo7d4dsCP3xO3XI8hgvruMl9njxyQeraQ=
github.com/gagliardetto/solana-go v1.12.0 h1:rzsbilDPj6p+/DOPXBMLhwMZeBgeRuXjm5zQFCoXgsg=
github.com/gagliardetto/solana-go v1.12.0/go.mod h1:l/qqqIN6qJJPtxW/G1PF4JtcE3Zg2vD2EliZrr9Gn5k=
github.com/gagliardetto/treeout v0.1.4 h1:ozeYerrLCmCubo1TcIjFiOWTTGteOOHND1twdFpgwaw=
@@ -1105,6 +1106,7 @@ github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6B
github.com/mfridman/interpolate v0.0.2/go.mod h1:p+7uk6oE07mpE/Ik1b8EckO0O4ZXiGAfshKBWLUM9Xg=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
+github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
github.com/miekg/dns v1.1.61 h1:nLxbwF3XxhwVSm8g9Dghm9MHPaUZuqhPiGL+675ZmEs=
github.com/miekg/dns v1.1.61/go.mod h1:mnAarhS3nWaW+NVP2wTkYVIZyHNJ098SJZUki3eykwQ=
@@ -1413,8 +1415,8 @@ github.com/smartcontractkit/chainlink-protos/job-distributor v0.6.0 h1:0ewLMbAz3
github.com/smartcontractkit/chainlink-protos/job-distributor v0.6.0/go.mod h1:/dVVLXrsp+V0AbcYGJo3XMzKg3CkELsweA/TTopCsKE=
github.com/smartcontractkit/chainlink-protos/orchestrator v0.4.0 h1:ZBat8EBvE2LpSQR9U1gEbRV6PfAkiFdINmQ8nVnXIAQ=
github.com/smartcontractkit/chainlink-protos/orchestrator v0.4.0/go.mod h1:m/A3lqD7ms/RsQ9BT5P2uceYY0QX5mIt4KQxT2G6qEo=
-github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241223151630-eac4f1508dce h1:Mvpbr/Fi2IdU2EcmqCxhlCzs5ncnx+BwV80YweA4DZs=
-github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241223151630-eac4f1508dce/go.mod h1:qq+nW0JDnCCGMf2c38ZHjH8xgkAQnXKighjJr5JdDNE=
+github.com/smartcontractkit/chainlink-solana v1.1.1-0.20250110142550-e2a9566d39f3 h1:L6UDu0GzSKfz+/nMSz9yfYrgOpW1/OXhiVK60PxVsnY=
+github.com/smartcontractkit/chainlink-solana v1.1.1-0.20250110142550-e2a9566d39f3/go.mod h1:52U0UH8K0Qwu+HB1LMc+5V27ru2Tgy29YPT+2HkMevY=
github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241202202529-2033490e77b8 h1:tNS7U9lrxkFvEuyxQv11HHOiV9LPDGC9wYEy+yM/Jv4=
github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241202202529-2033490e77b8/go.mod h1:EBrEgcdIbwepqguClkv8Ohy7CbyWSJaE4EC9aBJlQK0=
github.com/smartcontractkit/chainlink-testing-framework/framework v0.4.2-0.20250110073248-456673e8eea2 h1:nTUoe7GZLw17nPLV5t3Vgf4U4pf+VW0Uko5xpNiKdKU=
@@ -2007,6 +2009,7 @@ golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
From 5156cfe5ce0f8e060255b39e6b6ce2160da4a3b5 Mon Sep 17 00:00:00 2001
From: Mateusz Sekara
Date: Fri, 10 Jan 2025 20:21:17 +0100
Subject: [PATCH 32/91] CCIP-4420 Merging back CCIP codebase (#15890) (#15894)
---
.mockery.yaml | 8 +
ccip/config/evm/Sei_Mainnet.toml | 18 +
core/chains/evm/client/errors.go | 8 +-
core/chains/evm/client/errors_test.go | 3 +
.../evm/gas/block_history_estimator_test.go | 1 +
core/chains/evm/gas/rollups/op_l1_oracle.go | 2 +
core/chains/evm/types/models_test.go | 17 +-
.../mock_lbtc_token_pool.go | 3042 +++++++++++++++++
core/services/ocr2/delegate.go | 96 +-
.../ocr2/plugins/ccip/ccipcommit/factory.go | 29 +-
.../plugins/ccip/ccipcommit/factory_test.go | 3 +
.../plugins/ccip/ccipcommit/initializers.go | 73 +-
.../ocr2/plugins/ccip/ccipcommit/ocr2.go | 76 +-
.../ocr2/plugins/ccip/ccipcommit/ocr2_test.go | 162 +-
.../ocr2/plugins/ccip/ccipexec/batching.go | 19 +-
.../plugins/ccip/ccipexec/batching_test.go | 11 +-
.../ocr2/plugins/ccip/ccipexec/factory.go | 17 +-
.../plugins/ccip/ccipexec/factory_test.go | 3 +
.../plugins/ccip/ccipexec/initializers.go | 32 +-
.../ocr2/plugins/ccip/ccipexec/ocr2.go | 23 +-
.../ocr2/plugins/ccip/ccipexec/ocr2_test.go | 93 +-
.../ocr2/plugins/ccip/config/config.go | 31 +-
.../plugins/ccip/config/type_and_version.go | 7 +
.../ccip/config/type_and_version_test.go | 50 +
.../plugins/ccip/estimatorconfig/config.go | 83 +
.../ccip/estimatorconfig/config_test.go | 112 +
.../interceptors/mantle/interceptor.go | 82 +
.../interceptors/mantle/interceptor_test.go | 96 +
.../mocks/gas_price_interceptor_mock.go | 106 +
.../ocr2/plugins/ccip/exportinternal.go | 38 +-
.../ocr2/plugins/ccip/integration_test.go | 6 +-
.../ccip/internal/cache/commit_roots_test.go | 14 +-
.../ccip/internal/ccipcommon/shortcuts.go | 13 +-
.../internal/ccipcommon/shortcuts_test.go | 15 +-
.../token_pool_batch_reader_test.go | 4 +-
.../ccipdata/ccipdataprovider/provider.go | 1 -
.../ccipdata/commit_store_reader_test.go | 20 +-
.../internal/ccipdata/factory/commit_store.go | 14 +-
.../ccipdata/factory/commit_store_test.go | 7 +-
.../ccip/internal/ccipdata/factory/offramp.go | 14 +-
.../internal/ccipdata/factory/offramp_test.go | 7 +-
.../ccip/internal/ccipdata/factory/onramp.go | 1 -
.../internal/ccipdata/fee_estimator_config.go | 11 +
.../mocks/fee_estimator_config_mock.go | 177 +
.../internal/ccipdata/offramp_reader_test.go | 14 +-
.../plugins/ccip/internal/ccipdata/reader.go | 1 -
.../ccip/internal/ccipdata/reader_test.go | 7 +-
.../ccip/internal/ccipdata/retry_config.go | 4 +-
.../internal/ccipdata/v1_2_0/commit_store.go | 20 +-
.../ccipdata/v1_2_0/commit_store_test.go | 7 +-
.../ccip/internal/ccipdata/v1_2_0/offramp.go | 20 +-
.../ccipdata/v1_2_0/offramp_reader_test.go | 8 +-
.../v1_2_0/offramp_reader_unit_test.go | 2 +-
.../ccip/internal/ccipdata/v1_2_0/onramp.go | 79 +-
.../internal/ccipdata/v1_2_0/onramp_test.go | 5 +-
.../ccipdata/v1_2_0/price_registry.go | 1 -
.../internal/ccipdata/v1_5_0/commit_store.go | 12 +-
.../ccip/internal/ccipdata/v1_5_0/offramp.go | 20 +-
.../ccip/internal/ccipdata/v1_5_0/onramp.go | 80 +-
.../internal/ccipdata/v1_5_0/onramp_test.go | 8 +-
.../plugins/ccip/internal/pricegetter/evm.go | 158 +-
.../ccip/internal/pricegetter/evm_test.go | 195 +-
.../ccip/internal/pricegetter/pipeline.go | 2 +-
.../ocr2/plugins/ccip/internal/rpclib/evm.go | 4 +
core/services/ocr2/plugins/ccip/metrics.go | 14 +
.../ocr2/plugins/ccip/observations.go | 8 +
.../plugins/ccip/prices/da_price_estimator.go | 35 +-
.../ccip/prices/da_price_estimator_test.go | 158 +-
.../ccip/prices/gas_price_estimator.go | 5 +-
.../ccip/testhelpers/ccip_contracts.go | 26 +-
.../ccip/testhelpers/integration/chainlink.go | 5 +-
.../ccip/testhelpers/integration/jobspec.go | 6 +
.../testhelpers_1_4_0/ccip_contracts_1_4_0.go | 1601 +++++++++
.../testhelpers_1_4_0/chainlink.go | 1054 ++++++
.../testhelpers_1_4_0/config_1_4_0.go | 76 +
.../ocr2/plugins/ccip/tokendata/bgworker.go | 2 +-
.../ccip/tokendata/http/http_client.go | 17 +-
.../tokendata/http/observed_http_client.go | 18 +-
.../ocr2/plugins/ccip/tokendata/lbtc/lbtc.go | 275 ++
.../plugins/ccip/tokendata/lbtc/lbtc_test.go | 490 +++
.../ocr2/plugins/ccip/tokendata/usdc/usdc.go | 2 +-
core/services/relay/evm/ccip.go | 30 +-
core/services/relay/evm/commit_provider.go | 47 +-
core/services/relay/evm/evm.go | 330 +-
core/services/relay/evm/exec_provider.go | 135 +-
.../evm/interceptors/mantle/interceptor.go | 81 +
.../interceptors/mantle/interceptor_test.go | 96 +
87 files changed, 9041 insertions(+), 762 deletions(-)
create mode 100644 ccip/config/evm/Sei_Mainnet.toml
create mode 100644 core/gethwrappers/ccip/generated/mock_lbtc_token_pool/mock_lbtc_token_pool.go
create mode 100644 core/services/ocr2/plugins/ccip/config/type_and_version_test.go
create mode 100644 core/services/ocr2/plugins/ccip/estimatorconfig/config.go
create mode 100644 core/services/ocr2/plugins/ccip/estimatorconfig/config_test.go
create mode 100644 core/services/ocr2/plugins/ccip/estimatorconfig/interceptors/mantle/interceptor.go
create mode 100644 core/services/ocr2/plugins/ccip/estimatorconfig/interceptors/mantle/interceptor_test.go
create mode 100644 core/services/ocr2/plugins/ccip/estimatorconfig/mocks/gas_price_interceptor_mock.go
create mode 100644 core/services/ocr2/plugins/ccip/internal/ccipdata/fee_estimator_config.go
create mode 100644 core/services/ocr2/plugins/ccip/internal/ccipdata/mocks/fee_estimator_config_mock.go
create mode 100644 core/services/ocr2/plugins/ccip/testhelpers/testhelpers_1_4_0/ccip_contracts_1_4_0.go
create mode 100644 core/services/ocr2/plugins/ccip/testhelpers/testhelpers_1_4_0/chainlink.go
create mode 100644 core/services/ocr2/plugins/ccip/testhelpers/testhelpers_1_4_0/config_1_4_0.go
create mode 100644 core/services/ocr2/plugins/ccip/tokendata/lbtc/lbtc.go
create mode 100644 core/services/ocr2/plugins/ccip/tokendata/lbtc/lbtc_test.go
create mode 100644 core/services/relay/evm/interceptors/mantle/interceptor.go
create mode 100644 core/services/relay/evm/interceptors/mantle/interceptor_test.go
diff --git a/.mockery.yaml b/.mockery.yaml
index 7fd4ff242bb..b7dbb8a1e85 100644
--- a/.mockery.yaml
+++ b/.mockery.yaml
@@ -490,12 +490,20 @@ packages:
PriceRegistryReader:
config:
filename: price_registry_reader_mock.go
+ FeeEstimatorConfigReader:
+ config:
+ filename: fee_estimator_config_mock.go
TokenPoolReader:
config:
filename: token_pool_reader_mock.go
USDCReader:
config:
filename: usdc_reader_mock.go
+ github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/estimatorconfig:
+ interfaces:
+ GasPriceInterceptor:
+ config:
+ filename: gas_price_interceptor_mock.go
github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata/batchreader:
config:
filename: token_pool_batched_reader_mock.go
diff --git a/ccip/config/evm/Sei_Mainnet.toml b/ccip/config/evm/Sei_Mainnet.toml
new file mode 100644
index 00000000000..23977756ac1
--- /dev/null
+++ b/ccip/config/evm/Sei_Mainnet.toml
@@ -0,0 +1,18 @@
+ChainID = '1329'
+ChainType = 'sei'
+# finality_depth: instant
+FinalityDepth = 10
+# block_time: ~0.4s, adding 1 second buffer
+LogPollInterval = '2s'
+# finality_depth * block_time / 60 secs = ~0.8 min (finality time)
+NoNewFinalizedHeadsThreshold = '5m'
+# "RPC node returned multiple missing blocks on query for block numbers [31592085 31592084] even though the WS subscription already sent us these blocks. It might help to increase EVM.RPCBlockQueryDelay (currently 1)"
+RPCBlockQueryDelay = 5
+
+[GasEstimator]
+EIP1559DynamicFees = false
+Mode = 'BlockHistory'
+PriceMax = '3000 gwei' # recommended by ds&a
+
+[GasEstimator.BlockHistory]
+BlockHistorySize = 200
diff --git a/core/chains/evm/client/errors.go b/core/chains/evm/client/errors.go
index 871e574aea2..a19e03d50cb 100644
--- a/core/chains/evm/client/errors.go
+++ b/core/chains/evm/client/errors.go
@@ -265,9 +265,11 @@ var aStar = ClientErrors{
}
var mantle = ClientErrors{
- InsufficientEth: regexp.MustCompile(`(: |^)'*insufficient funds for gas \* price \+ value`),
- Fatal: regexp.MustCompile(`(: |^)'*invalid sender`),
- NonceTooLow: regexp.MustCompile(`(: |^)'*nonce too low`),
+ InsufficientEth: regexp.MustCompile(`(: |^)'*insufficient funds for gas \* price \+ value`),
+ Fatal: regexp.MustCompile(`(: |^)'*invalid sender`),
+ NonceTooLow: regexp.MustCompile(`(: |^)'*nonce too low`),
+ ReplacementTransactionUnderpriced: regexp.MustCompile(`(: |^)'*replacement transaction underpriced`),
+ TransactionAlreadyInMempool: regexp.MustCompile(`(: |^)'*already known`),
}
var hederaFatal = regexp.MustCompile(`(: |^)(execution reverted)(:|$) | ^Transaction gas limit '(\d+)' exceeds block gas limit '(\d+)' | ^Transaction gas limit provided '(\d+)' is insufficient of intrinsic gas required '(\d+)' | ^Oversized data:|status INVALID_SIGNATURE`)
diff --git a/core/chains/evm/client/errors_test.go b/core/chains/evm/client/errors_test.go
index 9a046922abb..2a28fe3c2c8 100644
--- a/core/chains/evm/client/errors_test.go
+++ b/core/chains/evm/client/errors_test.go
@@ -112,6 +112,7 @@ func Test_Eth_Errors(t *testing.T) {
{"gas price too low", false, "Arbitrum"},
{"client error replacement underpriced", true, "tomlConfig"},
{"", false, "tomlConfig"},
+ {"failed to forward tx to sequencer, please try again. Error message: 'replacement transaction underpriced'", true, "Mantle"},
}
for _, test := range tests {
@@ -145,6 +146,8 @@ func Test_Eth_Errors(t *testing.T) {
{"client error transaction already in mempool", true, "tomlConfig"},
{"alreadyknown", true, "Gnosis"},
{"tx already exists in cache", true, "Sei"},
+ {"failed to forward tx to sequencer, please try again. Error message: 'already known'", true, "Mantle"},
+ {"tx already exists in cache", true, "Sei"},
}
for _, test := range tests {
err = evmclient.NewSendErrorS(test.message)
diff --git a/core/chains/evm/gas/block_history_estimator_test.go b/core/chains/evm/gas/block_history_estimator_test.go
index e3df261f2cf..b754132d7c7 100644
--- a/core/chains/evm/gas/block_history_estimator_test.go
+++ b/core/chains/evm/gas/block_history_estimator_test.go
@@ -18,6 +18,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/smartcontractkit/chainlink-common/pkg/logger"
+
"github.com/smartcontractkit/chainlink-common/pkg/utils/tests"
commonfee "github.com/smartcontractkit/chainlink/v2/common/fee"
diff --git a/core/chains/evm/gas/rollups/op_l1_oracle.go b/core/chains/evm/gas/rollups/op_l1_oracle.go
index cd6f2347953..daf67ded259 100644
--- a/core/chains/evm/gas/rollups/op_l1_oracle.go
+++ b/core/chains/evm/gas/rollups/op_l1_oracle.go
@@ -26,6 +26,8 @@ import (
)
// Reads L2-specific precompiles and caches the l1GasPrice set by the L2.
+//
+//nolint:unused // backported from CCIP
type optimismL1Oracle struct {
services.StateMachine
client l1OracleClient
diff --git a/core/chains/evm/types/models_test.go b/core/chains/evm/types/models_test.go
index c06d683651a..a3f40a8bc2d 100644
--- a/core/chains/evm/types/models_test.go
+++ b/core/chains/evm/types/models_test.go
@@ -954,7 +954,7 @@ func TestBlock_UnmarshalJSON(t *testing.T) {
t.Run("unmarshals geth block", func(t *testing.T) {
b := new(evmtypes.Block)
err := b.UnmarshalJSON([]byte(gethSampleBlock))
- assert.NoError(t, err)
+ require.NoError(t, err)
assert.Equal(t, int64(15051090), b.Number)
assert.Equal(t, "0x45eb0a650b6b0b9fd1ee676b870e43fa7614f1034f7404070327a332faed05c0", b.Hash.Hex())
@@ -966,14 +966,25 @@ func TestBlock_UnmarshalJSON(t *testing.T) {
t.Run("handles empty result", func(t *testing.T) {
b := new(evmtypes.Block)
err := b.UnmarshalJSON([]byte("null"))
- assert.Error(t, err)
+ require.Error(t, err)
assert.Equal(t, pkgerrors.Cause(err), evmtypes.ErrMissingBlock)
assert.True(t, pkgerrors.Is(err, evmtypes.ErrMissingBlock))
})
t.Run("unmarshals EIP-4844 block", func(t *testing.T) {
b := new(evmtypes.Block)
err := b.UnmarshalJSON([]byte(eip4844Block))
- assert.NoError(t, err)
+ require.NoError(t, err)
+ assert.Equal(t, int64(5300694), b.Number)
+ assert.Equal(t, "0x3edd900025edab70dde26a52377c3d0a9474c3f540bd0131d58f508711272590", b.Hash.Hex())
+ assert.Equal(t, "0x077c1d68b52f8203cb90a71759a09b11c2a6577f97ea1fd4a8686a387fbedac8", b.ParentHash.Hex())
+ assert.Equal(t, assets.NewWeiI(96436174005), b.BaseFeePerGas)
+ assert.Equal(t, int64(1708087260), b.Timestamp.Unix())
+ assert.Len(t, b.Transactions, 6)
+ })
+ t.Run("unmarshals EIP-4844 block", func(t *testing.T) {
+ b := new(evmtypes.Block)
+ err := b.UnmarshalJSON([]byte(eip4844Block))
+ require.NoError(t, err)
assert.Equal(t, int64(5300694), b.Number)
assert.Equal(t, "0x3edd900025edab70dde26a52377c3d0a9474c3f540bd0131d58f508711272590", b.Hash.Hex())
assert.Equal(t, "0x077c1d68b52f8203cb90a71759a09b11c2a6577f97ea1fd4a8686a387fbedac8", b.ParentHash.Hex())
diff --git a/core/gethwrappers/ccip/generated/mock_lbtc_token_pool/mock_lbtc_token_pool.go b/core/gethwrappers/ccip/generated/mock_lbtc_token_pool/mock_lbtc_token_pool.go
new file mode 100644
index 00000000000..8fa1b3da14e
--- /dev/null
+++ b/core/gethwrappers/ccip/generated/mock_lbtc_token_pool/mock_lbtc_token_pool.go
@@ -0,0 +1,3042 @@
+// Code generated - DO NOT EDIT.
+// This file is a generated binding and any manual changes will be lost.
+
+package mock_lbtc_token_pool
+
+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 PoolLockOrBurnInV1 struct {
+ Receiver []byte
+ RemoteChainSelector uint64
+ OriginalSender common.Address
+ Amount *big.Int
+ LocalToken common.Address
+}
+
+type PoolLockOrBurnOutV1 struct {
+ DestTokenAddress []byte
+ DestPoolData []byte
+}
+
+type PoolReleaseOrMintInV1 struct {
+ OriginalSender []byte
+ RemoteChainSelector uint64
+ Receiver common.Address
+ Amount *big.Int
+ LocalToken common.Address
+ SourcePoolAddress []byte
+ SourcePoolData []byte
+ OffchainTokenData []byte
+}
+
+type PoolReleaseOrMintOutV1 struct {
+ DestinationAmount *big.Int
+}
+
+type RateLimiterConfig struct {
+ IsEnabled bool
+ Capacity *big.Int
+ Rate *big.Int
+}
+
+type RateLimiterTokenBucket struct {
+ Tokens *big.Int
+ LastUpdated uint32
+ IsEnabled bool
+ Capacity *big.Int
+ Rate *big.Int
+}
+
+type TokenPoolChainUpdate struct {
+ RemoteChainSelector uint64
+ RemotePoolAddresses [][]byte
+ RemoteTokenAddress []byte
+ OutboundRateLimiterConfig RateLimiterConfig
+ InboundRateLimiterConfig RateLimiterConfig
+}
+
+var MockLBTCTokenPoolMetaData = &bind.MetaData{
+ ABI: "[{\"inputs\":[{\"internalType\":\"contractIERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"allowlist\",\"type\":\"address[]\"},{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"destPoolData\",\"type\":\"bytes\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"AllowListNotEnabled\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"}],\"name\":\"CallerIsNotARampOnRouter\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CannotTransferToSelf\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"}],\"name\":\"ChainAlreadyExists\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"ChainNotAllowed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CursedByRMN\",\"type\":\"error\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"DisabledNonZeroRateLimit\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"expected\",\"type\":\"uint8\"},{\"internalType\":\"uint8\",\"name\":\"actual\",\"type\":\"uint8\"}],\"name\":\"InvalidDecimalArgs\",\"type\":\"error\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"rateLimiterConfig\",\"type\":\"tuple\"}],\"name\":\"InvalidRateLimitRate\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"sourcePoolData\",\"type\":\"bytes\"}],\"name\":\"InvalidRemoteChainDecimals\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"remotePoolAddress\",\"type\":\"bytes\"}],\"name\":\"InvalidRemotePoolForChain\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"sourcePoolAddress\",\"type\":\"bytes\"}],\"name\":\"InvalidSourcePoolAddress\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"InvalidToken\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MustBeProposedOwner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"NonExistentChain\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByOwner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"remoteDecimals\",\"type\":\"uint8\"},{\"internalType\":\"uint8\",\"name\":\"localDecimals\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"remoteAmount\",\"type\":\"uint256\"}],\"name\":\"OverflowDetected\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OwnerCannotBeZero\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"remotePoolAddress\",\"type\":\"bytes\"}],\"name\":\"PoolAlreadyAdded\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RateLimitMustBeDisabled\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"SenderNotAllowed\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"}],\"name\":\"Unauthorized\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroAddressNotAllowed\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"AllowListAdd\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"AllowListRemove\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Burned\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"remoteToken\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundRateLimiterConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundRateLimiterConfig\",\"type\":\"tuple\"}],\"name\":\"ChainAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundRateLimiterConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundRateLimiterConfig\",\"type\":\"tuple\"}],\"name\":\"ChainConfigured\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"ChainRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"ConfigChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Locked\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Minted\",\"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\":\"rateLimitAdmin\",\"type\":\"address\"}],\"name\":\"RateLimitAdminSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Released\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"remotePoolAddress\",\"type\":\"bytes\"}],\"name\":\"RemotePoolAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"remotePoolAddress\",\"type\":\"bytes\"}],\"name\":\"RemotePoolRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"oldRouter\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newRouter\",\"type\":\"address\"}],\"name\":\"RouterUpdated\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"remotePoolAddress\",\"type\":\"bytes\"}],\"name\":\"addRemotePool\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"removes\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"adds\",\"type\":\"address[]\"}],\"name\":\"applyAllowListUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64[]\",\"name\":\"remoteChainSelectorsToRemove\",\"type\":\"uint64[]\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes[]\",\"name\":\"remotePoolAddresses\",\"type\":\"bytes[]\"},{\"internalType\":\"bytes\",\"name\":\"remoteTokenAddress\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundRateLimiterConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundRateLimiterConfig\",\"type\":\"tuple\"}],\"internalType\":\"structTokenPool.ChainUpdate[]\",\"name\":\"chainsToAdd\",\"type\":\"tuple[]\"}],\"name\":\"applyChainUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAllowList\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAllowListEnabled\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getCurrentInboundRateLimiterState\",\"outputs\":[{\"components\":[{\"internalType\":\"uint128\",\"name\":\"tokens\",\"type\":\"uint128\"},{\"internalType\":\"uint32\",\"name\":\"lastUpdated\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.TokenBucket\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getCurrentOutboundRateLimiterState\",\"outputs\":[{\"components\":[{\"internalType\":\"uint128\",\"name\":\"tokens\",\"type\":\"uint128\"},{\"internalType\":\"uint32\",\"name\":\"lastUpdated\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.TokenBucket\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRateLimitAdmin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getRemotePools\",\"outputs\":[{\"internalType\":\"bytes[]\",\"name\":\"\",\"type\":\"bytes[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getRemoteToken\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRmnProxy\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRouter\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getSupportedChains\",\"outputs\":[{\"internalType\":\"uint64[]\",\"name\":\"\",\"type\":\"uint64[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getToken\",\"outputs\":[{\"internalType\":\"contractIERC20\",\"name\":\"token\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getTokenDecimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"decimals\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"i_destPoolData\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"remotePoolAddress\",\"type\":\"bytes\"}],\"name\":\"isRemotePool\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"isSupportedChain\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"isSupportedToken\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"receiver\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"originalSender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"localToken\",\"type\":\"address\"}],\"internalType\":\"structPool.LockOrBurnInV1\",\"name\":\"lockOrBurnIn\",\"type\":\"tuple\"}],\"name\":\"lockOrBurn\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"destTokenAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"destPoolData\",\"type\":\"bytes\"}],\"internalType\":\"structPool.LockOrBurnOutV1\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"originalSender\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"localToken\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"sourcePoolAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"sourcePoolData\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"offchainTokenData\",\"type\":\"bytes\"}],\"internalType\":\"structPool.ReleaseOrMintInV1\",\"name\":\"releaseOrMintIn\",\"type\":\"tuple\"}],\"name\":\"releaseOrMint\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"destinationAmount\",\"type\":\"uint256\"}],\"internalType\":\"structPool.ReleaseOrMintOutV1\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"remotePoolAddress\",\"type\":\"bytes\"}],\"name\":\"removeRemotePool\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundConfig\",\"type\":\"tuple\"}],\"name\":\"setChainRateLimiterConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"rateLimitAdmin\",\"type\":\"address\"}],\"name\":\"setRateLimitAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newRouter\",\"type\":\"address\"}],\"name\":\"setRouter\",\"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\":\"view\",\"type\":\"function\"}]",
+ Bin: "0x6101006040523480156200001257600080fd5b5060405162003a5138038062003a51833981016040819052620000359162000663565b846008858585336000816200005d57604051639b15e16f60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b038481169190911790915581161562000090576200009081620001fe565b50506001600160a01b0385161580620000b057506001600160a01b038116155b80620000c357506001600160a01b038216155b15620000e2576040516342bcdf7f60e11b815260040160405180910390fd5b6001600160a01b03808616608081905290831660c0526040805163313ce56760e01b8152905163313ce567916004808201926020929091908290030181865afa92505050801562000152575060408051601f3d908101601f191682019092526200014f9181019062000785565b60015b1562000192578060ff168560ff161462000190576040516332ad3e0760e11b815260ff80871660048301528216602482015260440160405180910390fd5b505b60ff841660a052600480546001600160a01b0319166001600160a01b038316179055825115801560e052620001dc57604080516000815260208101909152620001dc908462000278565b505050505080600a9081620001f2919062000841565b5050505050506200095b565b336001600160a01b038216036200022857604051636d6c4ee560e11b815260040160405180910390fd5b600080546001600160a01b0319166001600160a01b03838116918217835560015460405192939116917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60e05162000299576040516335f4a7b360e01b815260040160405180910390fd5b60005b825181101562000324576000838281518110620002bd57620002bd6200090d565b60209081029190910101519050620002d7600282620003d5565b156200031a576040516001600160a01b03821681527f800671136ab6cfee9fbe5ed1fb7ca417811aca3cf864800d127b927adedf75669060200160405180910390a15b506001016200029c565b5060005b8151811015620003d05760008282815181106200034957620003496200090d565b6020026020010151905060006001600160a01b0316816001600160a01b031603620003755750620003c7565b62000382600282620003f5565b15620003c5576040516001600160a01b03821681527f2640d4d76caf8bf478aabfa982fa4e1c4eb71a37f93cd15e80dbc657911546d89060200160405180910390a15b505b60010162000328565b505050565b6000620003ec836001600160a01b0384166200040c565b90505b92915050565b6000620003ec836001600160a01b03841662000510565b60008181526001830160205260408120548015620005055760006200043360018362000923565b8554909150600090620004499060019062000923565b9050808214620004b55760008660000182815481106200046d576200046d6200090d565b90600052602060002001549050808760000184815481106200049357620004936200090d565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080620004c957620004c962000945565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050620003ef565b6000915050620003ef565b60008181526001830160205260408120546200055957508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155620003ef565b506000620003ef565b6001600160a01b03811681146200057857600080fd5b50565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b0381118282101715620005bc57620005bc6200057b565b604052919050565b8051620005d18162000562565b919050565b600082601f830112620005e857600080fd5b81516001600160401b038111156200060457620006046200057b565b60206200061a601f8301601f1916820162000591565b82815285828487010111156200062f57600080fd5b60005b838110156200064f57858101830151828201840152820162000632565b506000928101909101919091529392505050565b600080600080600060a086880312156200067c57600080fd5b8551620006898162000562565b602087810151919650906001600160401b0380821115620006a957600080fd5b818901915089601f830112620006be57600080fd5b815181811115620006d357620006d36200057b565b8060051b620006e485820162000591565b918252838101850191858101908d841115620006ff57600080fd5b948601945b838610156200072d57855192506200071c8362000562565b828252948601949086019062000704565b9950620007419250505060408a01620005c4565b95506200075160608a01620005c4565b945060808901519250808311156200076857600080fd5b50506200077888828901620005d6565b9150509295509295909350565b6000602082840312156200079857600080fd5b815160ff81168114620007aa57600080fd5b9392505050565b600181811c90821680620007c657607f821691505b602082108103620007e757634e487b7160e01b600052602260045260246000fd5b50919050565b601f821115620003d0576000816000526020600020601f850160051c81016020861015620008185750805b601f850160051c820191505b81811015620008395782815560010162000824565b505050505050565b81516001600160401b038111156200085d576200085d6200057b565b62000875816200086e8454620007b1565b84620007ed565b602080601f831160018114620008ad5760008415620008945750858301515b600019600386901b1c1916600185901b17855562000839565b600085815260208120601f198616915b82811015620008de57888601518255948401946001909101908401620008bd565b5085821015620008fd5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b634e487b7160e01b600052603260045260246000fd5b81810381811115620003ef57634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052603160045260246000fd5b60805160a05160c05160e0516130a0620009b160003960008181610562015261199f0152600061053c015260006102eb015260008181610252015281816102a7015281816107450152610ba201526130a06000f3fe608060405234801561001057600080fd5b50600436106101da5760003560e01c80639a4575b911610104578063c0d78655116100a2578063dc0bd97111610071578063dc0bd9711461053a578063e0351e1314610560578063e8a1da1714610586578063f2fde38b1461059957600080fd5b8063c0d78655146104ec578063c4bffe2b146104ff578063c75eea9c14610514578063cf7401f31461052757600080fd5b8063acfecf91116100de578063acfecf9114610439578063af58d59f1461044c578063b0f479a1146104bb578063b7946580146104d957600080fd5b80639a4575b9146103e4578063a42a7b8b14610404578063a7cd63b71461042457600080fd5b80634c5ef0ed1161017c57806379ba50971161014b57806379ba5097146103985780637d54534e146103a05780638926f54f146103b35780638da5cb5b146103c657600080fd5b80634c5ef0ed1461033f57806354c8a4f31461035257806362ddd3c4146103675780636d3d1a581461037a57600080fd5b8063240028e8116101b8578063240028e81461029757806324f65ee7146102e457806332a7a82214610315578063390775371461031d57600080fd5b806301ffc9a7146101df578063181f5a771461020757806321df0da714610250575b600080fd5b6101f26101ed36600461243d565b6105ac565b60405190151581526020015b60405180910390f35b6102436040518060400160405280601781526020017f4d6f636b4c425443546f6b656e506f6f6c20312e352e3100000000000000000081525081565b6040516101fe91906124e3565b7f00000000000000000000000000000000000000000000000000000000000000005b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101fe565b6101f26102a53660046124f6565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff90811691161490565b60405160ff7f00000000000000000000000000000000000000000000000000000000000000001681526020016101fe565b610243610691565b61033061032b36600461252c565b61071f565b604051905181526020016101fe565b6101f261034d366004612585565b610894565b610365610360366004612654565b6108de565b005b610365610375366004612585565b610959565b60095473ffffffffffffffffffffffffffffffffffffffff16610272565b6103656109f6565b6103656103ae3660046124f6565b610ac4565b6101f26103c13660046126c0565b610b45565b60015473ffffffffffffffffffffffffffffffffffffffff16610272565b6103f76103f23660046126db565b610b5c565b6040516101fe9190612716565b6104176104123660046126c0565b610d07565b6040516101fe919061276d565b61042c610e72565b6040516101fe91906127ef565b610365610447366004612585565b610e83565b61045f61045a3660046126c0565b610f9b565b6040516101fe919081516fffffffffffffffffffffffffffffffff908116825260208084015163ffffffff1690830152604080840151151590830152606080840151821690830152608092830151169181019190915260a00190565b60045473ffffffffffffffffffffffffffffffffffffffff16610272565b6102436104e73660046126c0565b611070565b6103656104fa3660046124f6565b611120565b6105076111fb565b6040516101fe9190612849565b61045f6105223660046126c0565b6112b3565b6103656105353660046129c8565b611385565b7f0000000000000000000000000000000000000000000000000000000000000000610272565b7f00000000000000000000000000000000000000000000000000000000000000006101f2565b610365610594366004612654565b611409565b6103656105a73660046124f6565b61191b565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167faff2afbf00000000000000000000000000000000000000000000000000000000148061063f57507fffffffff0000000000000000000000000000000000000000000000000000000082167f0e64dd2900000000000000000000000000000000000000000000000000000000145b8061068b57507fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a700000000000000000000000000000000000000000000000000000000145b92915050565b600a805461069e90612a0d565b80601f01602080910402602001604051908101604052809291908181526020018280546106ca90612a0d565b80156107175780601f106106ec57610100808354040283529160200191610717565b820191906000526020600020905b8154815290600101906020018083116106fa57829003601f168201915b505050505081565b60408051602081019091526000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000166340c10f1961077a60608501604086016124f6565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff909116600482015260608501356024820152604401600060405180830381600087803b1580156107ea57600080fd5b505af11580156107fe573d6000803e3d6000fd5b506108139250505060608301604084016124f6565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f9d228d69b5fdb8d273a2336f8fb8612d039631024ea9bf09c424a9503aa078f0846060013560405161087591815260200190565b60405180910390a3506040805160208101909152606090910135815290565b60006108d683836040516108a9929190612a60565b604080519182900390912067ffffffffffffffff871660009081526007602052919091206005019061192f565b949350505050565b6108e661194a565b6109538484808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152505060408051602080880282810182019093528782529093508792508691829185019084908082843760009201919091525061199d92505050565b50505050565b61096161194a565b61096a83610b45565b6109b1576040517f1e670e4b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff841660048201526024015b60405180910390fd5b6109f18383838080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611b5392505050565b505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314610a47576040517f02b543c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000008082163390811790935560008054909116815560405173ffffffffffffffffffffffffffffffffffffffff909216929183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b610acc61194a565b600980547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040519081527f44676b5284b809a22248eba0da87391d79098be38bb03154be88a58bf4d091749060200160405180910390a150565b600061068b600567ffffffffffffffff841661192f565b60408051808201909152606080825260208201526040517f42966c68000000000000000000000000000000000000000000000000000000008152606083013560048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906342966c6890602401600060405180830381600087803b158015610bfb57600080fd5b505af1158015610c0f573d6000803e3d6000fd5b5050604051606085013581523392507f696de425f79f4a40bc6d2122ca50507f0efbeabbff86a84871b7196ab8ea8df7915060200160405180910390a26040518060400160405280610c6d8460200160208101906104e791906126c0565b8152602001600a8054610c7f90612a0d565b80601f0160208091040260200160405190810160405280929190818152602001828054610cab90612a0d565b8015610cf85780601f10610ccd57610100808354040283529160200191610cf8565b820191906000526020600020905b815481529060010190602001808311610cdb57829003601f168201915b50505050508152509050919050565b67ffffffffffffffff8116600090815260076020526040812060609190610d3090600501611c4d565b90506000815167ffffffffffffffff811115610d4e57610d4e61288b565b604051908082528060200260200182016040528015610d8157816020015b6060815260200190600190039081610d6c5790505b50905060005b8251811015610e6a5760086000848381518110610da657610da6612a70565b602002602001015181526020019081526020016000208054610dc790612a0d565b80601f0160208091040260200160405190810160405280929190818152602001828054610df390612a0d565b8015610e405780601f10610e1557610100808354040283529160200191610e40565b820191906000526020600020905b815481529060010190602001808311610e2357829003601f168201915b5050505050828281518110610e5757610e57612a70565b6020908102919091010152600101610d87565b509392505050565b6060610e7e6002611c4d565b905090565b610e8b61194a565b610e9483610b45565b610ed6576040517f1e670e4b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff841660048201526024016109a8565b610f168282604051610ee9929190612a60565b604080519182900390912067ffffffffffffffff8616600090815260076020529190912060050190611c5a565b610f52578282826040517f74f23c7c0000000000000000000000000000000000000000000000000000000081526004016109a893929190612ae8565b8267ffffffffffffffff167f52d00ee4d9bd51b40168f2afc5848837288ce258784ad914278791464b3f4d768383604051610f8e929190612b0c565b60405180910390a2505050565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915267ffffffffffffffff8216600090815260076020908152604091829020825160a08101845260028201546fffffffffffffffffffffffffffffffff808216835270010000000000000000000000000000000080830463ffffffff16958401959095527401000000000000000000000000000000000000000090910460ff16151594820194909452600390910154808416606083015291909104909116608082015261068b90611c66565b67ffffffffffffffff8116600090815260076020526040902060040180546060919061109b90612a0d565b80601f01602080910402602001604051908101604052809291908181526020018280546110c790612a0d565b80156111145780601f106110e957610100808354040283529160200191611114565b820191906000526020600020905b8154815290600101906020018083116110f757829003601f168201915b50505050509050919050565b61112861194a565b73ffffffffffffffffffffffffffffffffffffffff8116611175576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6004805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff000000000000000000000000000000000000000083168117909355604080519190921680825260208201939093527f02dc5c233404867c793b749c6d644beb2277536d18a7e7974d3f238e4c6f1684910160405180910390a15050565b606060006112096005611c4d565b90506000815167ffffffffffffffff8111156112275761122761288b565b604051908082528060200260200182016040528015611250578160200160208202803683370190505b50905060005b82518110156112ac5782818151811061127157611271612a70565b602002602001015182828151811061128b5761128b612a70565b67ffffffffffffffff90921660209283029190910190910152600101611256565b5092915050565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915267ffffffffffffffff8216600090815260076020908152604091829020825160a08101845281546fffffffffffffffffffffffffffffffff808216835270010000000000000000000000000000000080830463ffffffff16958401959095527401000000000000000000000000000000000000000090910460ff16151594820194909452600190910154808416606083015291909104909116608082015261068b90611c66565b60095473ffffffffffffffffffffffffffffffffffffffff1633148015906113c5575060015473ffffffffffffffffffffffffffffffffffffffff163314155b156113fe576040517f8e4a23d60000000000000000000000000000000000000000000000000000000081523360048201526024016109a8565b6109f1838383611d18565b61141161194a565b60005b838110156115fe57600085858381811061143057611430612a70565b905060200201602081019061144591906126c0565b905061145c600567ffffffffffffffff8316611c5a565b61149e576040517f1e670e4b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff821660048201526024016109a8565b67ffffffffffffffff811660009081526007602052604081206114c390600501611c4d565b905060005b815181101561152f576115268282815181106114e6576114e6612a70565b6020026020010151600760008667ffffffffffffffff1667ffffffffffffffff168152602001908152602001600020600501611c5a90919063ffffffff16565b506001016114c8565b5067ffffffffffffffff8216600090815260076020526040812080547fffffffffffffffffffffff0000000000000000000000000000000000000000009081168255600182018390556002820180549091169055600381018290559061159860048301826123d0565b60058201600081816115aa828261240a565b505060405167ffffffffffffffff871681527f5204aec90a3c794d8e90fded8b46ae9c7c552803e7e832e0c1d358396d859916945060200192506115ec915050565b60405180910390a15050600101611414565b5060005b8181101561191457600083838381811061161e5761161e612a70565b90506020028101906116309190612b20565b61163990612bec565b905061164a81606001516000611e02565b61165981608001516000611e02565b806040015151600003611698576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80516116b09060059067ffffffffffffffff16611f3f565b6116f55780516040517f1d5ad3c500000000000000000000000000000000000000000000000000000000815267ffffffffffffffff90911660048201526024016109a8565b805167ffffffffffffffff16600090815260076020908152604091829020825160a08082018552606080870180518601516fffffffffffffffffffffffffffffffff90811680865263ffffffff42168689018190528351511515878b0181905284518a0151841686890181905294518b0151841660809889018190528954740100000000000000000000000000000000000000009283027fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff7001000000000000000000000000000000008087027fffffffffffffffffffffffff000000000000000000000000000000000000000094851690981788178216929092178d5592810290971760018c01558c519889018d52898e0180518d01518716808b528a8e019590955280515115158a8f018190528151909d01518716988a01899052518d0151909516979098018790526002890180549a9091029990931617179094169590951790925590920290911760038201559082015160048201906118789082612d63565b5060005b8260200151518110156118bc576118b48360000151846020015183815181106118a7576118a7612a70565b6020026020010151611b53565b60010161187c565b507f8d340f17e19058004c20453540862a9c62778504476f6756755cb33bcd6c38c282600001518360400151846060015185608001516040516119029493929190612e7d565b60405180910390a15050600101611602565b5050505050565b61192361194a565b61192c81611f4b565b50565b600081815260018301602052604081205415155b9392505050565b60015473ffffffffffffffffffffffffffffffffffffffff16331461199b576040517f2b5c74de00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b7f00000000000000000000000000000000000000000000000000000000000000006119f4576040517f35f4a7b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b8251811015611a8a576000838281518110611a1457611a14612a70565b60200260200101519050611a3281600261200f90919063ffffffff16565b15611a815760405173ffffffffffffffffffffffffffffffffffffffff821681527f800671136ab6cfee9fbe5ed1fb7ca417811aca3cf864800d127b927adedf75669060200160405180910390a15b506001016119f7565b5060005b81518110156109f1576000828281518110611aab57611aab612a70565b60200260200101519050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603611aef5750611b4b565b611afa600282612031565b15611b495760405173ffffffffffffffffffffffffffffffffffffffff821681527f2640d4d76caf8bf478aabfa982fa4e1c4eb71a37f93cd15e80dbc657911546d89060200160405180910390a15b505b600101611a8e565b8051600003611b8e576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805160208083019190912067ffffffffffffffff8416600090815260079092526040909120611bc09060050182611f3f565b611bfa5782826040517f393b8ad20000000000000000000000000000000000000000000000000000000081526004016109a8929190612f16565b6000818152600860205260409020611c128382612d63565b508267ffffffffffffffff167f7d628c9a1796743d365ab521a8b2a4686e419b3269919dc9145ea2ce853b54ea83604051610f8e91906124e3565b6060600061194383612053565b600061194383836120ae565b6040805160a081018252600080825260208201819052918101829052606081018290526080810191909152611cf482606001516fffffffffffffffffffffffffffffffff1683600001516fffffffffffffffffffffffffffffffff16846020015163ffffffff1642611cd89190612f68565b85608001516fffffffffffffffffffffffffffffffff166121a1565b6fffffffffffffffffffffffffffffffff1682525063ffffffff4216602082015290565b611d2183610b45565b611d63576040517f1e670e4b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff841660048201526024016109a8565b611d6e826000611e02565b67ffffffffffffffff83166000908152600760205260409020611d9190836121c9565b611d9c816000611e02565b67ffffffffffffffff83166000908152600760205260409020611dc290600201826121c9565b7f0350d63aa5f270e01729d00d627eeb8f3429772b1818c016c66a588a864f912b838383604051611df593929190612f7b565b60405180910390a1505050565b815115611ecd5781602001516fffffffffffffffffffffffffffffffff1682604001516fffffffffffffffffffffffffffffffff16101580611e58575060408201516fffffffffffffffffffffffffffffffff16155b15611e9157816040517f8020d1240000000000000000000000000000000000000000000000000000000081526004016109a89190612ffe565b8015611ec9576040517f433fc33d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050565b60408201516fffffffffffffffffffffffffffffffff16151580611f06575060208201516fffffffffffffffffffffffffffffffff1615155b15611ec957816040517fd68af9cc0000000000000000000000000000000000000000000000000000000081526004016109a89190612ffe565b6000611943838361236b565b3373ffffffffffffffffffffffffffffffffffffffff821603611f9a576040517fdad89dca00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff838116918217835560015460405192939116917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60006119438373ffffffffffffffffffffffffffffffffffffffff84166120ae565b60006119438373ffffffffffffffffffffffffffffffffffffffff841661236b565b60608160000180548060200260200160405190810160405280929190818152602001828054801561111457602002820191906000526020600020905b81548152602001906001019080831161208f5750505050509050919050565b600081815260018301602052604081205480156121975760006120d2600183612f68565b85549091506000906120e690600190612f68565b905080821461214b57600086600001828154811061210657612106612a70565b906000526020600020015490508087600001848154811061212957612129612a70565b6000918252602080832090910192909255918252600188019052604090208390555b855486908061215c5761215c61303a565b60019003818190600052602060002001600090559055856001016000868152602001908152602001600020600090556001935050505061068b565b600091505061068b565b60006121c0856121b18486613069565b6121bb9087613080565b6123ba565b95945050505050565b81546000906121f290700100000000000000000000000000000000900463ffffffff1642612f68565b90508015612294576001830154835461223a916fffffffffffffffffffffffffffffffff808216928116918591700100000000000000000000000000000000909104166121a1565b83546fffffffffffffffffffffffffffffffff919091167fffffffffffffffffffffffff0000000000000000000000000000000000000000909116177001000000000000000000000000000000004263ffffffff16021783555b602082015183546122ba916fffffffffffffffffffffffffffffffff90811691166123ba565b83548351151574010000000000000000000000000000000000000000027fffffffffffffffffffffff00ffffffff000000000000000000000000000000009091166fffffffffffffffffffffffffffffffff92831617178455602083015160408085015183167001000000000000000000000000000000000291909216176001850155517f9ea3374b67bf275e6bb9c8ae68f9cae023e1c528b4b27e092f0bb209d3531c1990611df5908490612ffe565b60008181526001830160205260408120546123b25750815460018181018455600084815260208082209093018490558454848252828601909352604090209190915561068b565b50600061068b565b60008183106123c95781611943565b5090919050565b5080546123dc90612a0d565b6000825580601f106123ec575050565b601f01602090049060005260206000209081019061192c9190612424565b508054600082559060005260206000209081019061192c91905b5b808211156124395760008155600101612425565b5090565b60006020828403121561244f57600080fd5b81357fffffffff000000000000000000000000000000000000000000000000000000008116811461194357600080fd5b6000815180845260005b818110156124a557602081850181015186830182015201612489565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b602081526000611943602083018461247f565b60006020828403121561250857600080fd5b813573ffffffffffffffffffffffffffffffffffffffff8116811461194357600080fd5b60006020828403121561253e57600080fd5b813567ffffffffffffffff81111561255557600080fd5b8201610100818503121561194357600080fd5b803567ffffffffffffffff8116811461258057600080fd5b919050565b60008060006040848603121561259a57600080fd5b6125a384612568565b9250602084013567ffffffffffffffff808211156125c057600080fd5b818601915086601f8301126125d457600080fd5b8135818111156125e357600080fd5b8760208285010111156125f557600080fd5b6020830194508093505050509250925092565b60008083601f84011261261a57600080fd5b50813567ffffffffffffffff81111561263257600080fd5b6020830191508360208260051b850101111561264d57600080fd5b9250929050565b6000806000806040858703121561266a57600080fd5b843567ffffffffffffffff8082111561268257600080fd5b61268e88838901612608565b909650945060208701359150808211156126a757600080fd5b506126b487828801612608565b95989497509550505050565b6000602082840312156126d257600080fd5b61194382612568565b6000602082840312156126ed57600080fd5b813567ffffffffffffffff81111561270457600080fd5b820160a0818503121561194357600080fd5b602081526000825160406020840152612732606084018261247f565b905060208401517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08483030160408501526121c0828261247f565b600060208083016020845280855180835260408601915060408160051b87010192506020870160005b828110156127e2577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08886030184526127d085835161247f565b94509285019290850190600101612796565b5092979650505050505050565b6020808252825182820181905260009190848201906040850190845b8181101561283d57835173ffffffffffffffffffffffffffffffffffffffff168352928401929184019160010161280b565b50909695505050505050565b6020808252825182820181905260009190848201906040850190845b8181101561283d57835167ffffffffffffffff1683529284019291840191600101612865565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60405160a0810167ffffffffffffffff811182821017156128dd576128dd61288b565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561292a5761292a61288b565b604052919050565b80356fffffffffffffffffffffffffffffffff8116811461258057600080fd5b60006060828403121561296457600080fd5b6040516060810181811067ffffffffffffffff821117156129875761298761288b565b6040529050808235801515811461299d57600080fd5b81526129ab60208401612932565b60208201526129bc60408401612932565b60408201525092915050565b600080600060e084860312156129dd57600080fd5b6129e684612568565b92506129f58560208601612952565b9150612a048560808601612952565b90509250925092565b600181811c90821680612a2157607f821691505b602082108103612a5a577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b8183823760009101908152919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b67ffffffffffffffff841681526040602082015260006121c0604083018486612a9f565b6020815260006108d6602083018486612a9f565b600082357ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee1833603018112612b5457600080fd5b9190910192915050565b600082601f830112612b6f57600080fd5b813567ffffffffffffffff811115612b8957612b8961288b565b612bba60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116016128e3565b818152846020838601011115612bcf57600080fd5b816020850160208301376000918101602001919091529392505050565b60006101208236031215612bff57600080fd5b612c076128ba565b612c1083612568565b815260208084013567ffffffffffffffff80821115612c2e57600080fd5b9085019036601f830112612c4157600080fd5b813581811115612c5357612c5361288b565b8060051b612c628582016128e3565b9182528381018501918581019036841115612c7c57600080fd5b86860192505b83831015612cb857823585811115612c9a5760008081fd5b612ca83689838a0101612b5e565b8352509186019190860190612c82565b8087890152505050506040860135925080831115612cd557600080fd5b5050612ce336828601612b5e565b604083015250612cf63660608501612952565b6060820152612d083660c08501612952565b608082015292915050565b601f8211156109f1576000816000526020600020601f850160051c81016020861015612d3c5750805b601f850160051c820191505b81811015612d5b57828155600101612d48565b505050505050565b815167ffffffffffffffff811115612d7d57612d7d61288b565b612d9181612d8b8454612a0d565b84612d13565b602080601f831160018114612de45760008415612dae5750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b178555612d5b565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b82811015612e3157888601518255948401946001909101908401612e12565b5085821015612e6d57878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b600061010067ffffffffffffffff87168352806020840152612ea18184018761247f565b8551151560408581019190915260208701516fffffffffffffffffffffffffffffffff9081166060870152908701511660808501529150612edf9050565b8251151560a083015260208301516fffffffffffffffffffffffffffffffff90811660c084015260408401511660e08301526121c0565b67ffffffffffffffff831681526040602082015260006108d6604083018461247f565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8181038181111561068b5761068b612f39565b67ffffffffffffffff8416815260e08101612fc760208301858051151582526020808201516fffffffffffffffffffffffffffffffff9081169184019190915260409182015116910152565b82511515608083015260208301516fffffffffffffffffffffffffffffffff90811660a084015260408401511660c08301526108d6565b6060810161068b82848051151582526020808201516fffffffffffffffffffffffffffffffff9081169184019190915260409182015116910152565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b808202811582820484141761068b5761068b612f39565b8082018082111561068b5761068b612f3956fea164736f6c6343000818000a",
+}
+
+var MockLBTCTokenPoolABI = MockLBTCTokenPoolMetaData.ABI
+
+var MockLBTCTokenPoolBin = MockLBTCTokenPoolMetaData.Bin
+
+func DeployMockLBTCTokenPool(auth *bind.TransactOpts, backend bind.ContractBackend, token common.Address, allowlist []common.Address, rmnProxy common.Address, router common.Address, destPoolData []byte) (common.Address, *types.Transaction, *MockLBTCTokenPool, error) {
+ parsed, err := MockLBTCTokenPoolMetaData.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(MockLBTCTokenPoolBin), backend, token, allowlist, rmnProxy, router, destPoolData)
+ if err != nil {
+ return common.Address{}, nil, nil, err
+ }
+ return address, tx, &MockLBTCTokenPool{address: address, abi: *parsed, MockLBTCTokenPoolCaller: MockLBTCTokenPoolCaller{contract: contract}, MockLBTCTokenPoolTransactor: MockLBTCTokenPoolTransactor{contract: contract}, MockLBTCTokenPoolFilterer: MockLBTCTokenPoolFilterer{contract: contract}}, nil
+}
+
+type MockLBTCTokenPool struct {
+ address common.Address
+ abi abi.ABI
+ MockLBTCTokenPoolCaller
+ MockLBTCTokenPoolTransactor
+ MockLBTCTokenPoolFilterer
+}
+
+type MockLBTCTokenPoolCaller struct {
+ contract *bind.BoundContract
+}
+
+type MockLBTCTokenPoolTransactor struct {
+ contract *bind.BoundContract
+}
+
+type MockLBTCTokenPoolFilterer struct {
+ contract *bind.BoundContract
+}
+
+type MockLBTCTokenPoolSession struct {
+ Contract *MockLBTCTokenPool
+ CallOpts bind.CallOpts
+ TransactOpts bind.TransactOpts
+}
+
+type MockLBTCTokenPoolCallerSession struct {
+ Contract *MockLBTCTokenPoolCaller
+ CallOpts bind.CallOpts
+}
+
+type MockLBTCTokenPoolTransactorSession struct {
+ Contract *MockLBTCTokenPoolTransactor
+ TransactOpts bind.TransactOpts
+}
+
+type MockLBTCTokenPoolRaw struct {
+ Contract *MockLBTCTokenPool
+}
+
+type MockLBTCTokenPoolCallerRaw struct {
+ Contract *MockLBTCTokenPoolCaller
+}
+
+type MockLBTCTokenPoolTransactorRaw struct {
+ Contract *MockLBTCTokenPoolTransactor
+}
+
+func NewMockLBTCTokenPool(address common.Address, backend bind.ContractBackend) (*MockLBTCTokenPool, error) {
+ abi, err := abi.JSON(strings.NewReader(MockLBTCTokenPoolABI))
+ if err != nil {
+ return nil, err
+ }
+ contract, err := bindMockLBTCTokenPool(address, backend, backend, backend)
+ if err != nil {
+ return nil, err
+ }
+ return &MockLBTCTokenPool{address: address, abi: abi, MockLBTCTokenPoolCaller: MockLBTCTokenPoolCaller{contract: contract}, MockLBTCTokenPoolTransactor: MockLBTCTokenPoolTransactor{contract: contract}, MockLBTCTokenPoolFilterer: MockLBTCTokenPoolFilterer{contract: contract}}, nil
+}
+
+func NewMockLBTCTokenPoolCaller(address common.Address, caller bind.ContractCaller) (*MockLBTCTokenPoolCaller, error) {
+ contract, err := bindMockLBTCTokenPool(address, caller, nil, nil)
+ if err != nil {
+ return nil, err
+ }
+ return &MockLBTCTokenPoolCaller{contract: contract}, nil
+}
+
+func NewMockLBTCTokenPoolTransactor(address common.Address, transactor bind.ContractTransactor) (*MockLBTCTokenPoolTransactor, error) {
+ contract, err := bindMockLBTCTokenPool(address, nil, transactor, nil)
+ if err != nil {
+ return nil, err
+ }
+ return &MockLBTCTokenPoolTransactor{contract: contract}, nil
+}
+
+func NewMockLBTCTokenPoolFilterer(address common.Address, filterer bind.ContractFilterer) (*MockLBTCTokenPoolFilterer, error) {
+ contract, err := bindMockLBTCTokenPool(address, nil, nil, filterer)
+ if err != nil {
+ return nil, err
+ }
+ return &MockLBTCTokenPoolFilterer{contract: contract}, nil
+}
+
+func bindMockLBTCTokenPool(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) {
+ parsed, err := MockLBTCTokenPoolMetaData.GetAbi()
+ if err != nil {
+ return nil, err
+ }
+ return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
+ return _MockLBTCTokenPool.Contract.MockLBTCTokenPoolCaller.contract.Call(opts, result, method, params...)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
+ return _MockLBTCTokenPool.Contract.MockLBTCTokenPoolTransactor.contract.Transfer(opts)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
+ return _MockLBTCTokenPool.Contract.MockLBTCTokenPoolTransactor.contract.Transact(opts, method, params...)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
+ return _MockLBTCTokenPool.Contract.contract.Call(opts, result, method, params...)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
+ return _MockLBTCTokenPool.Contract.contract.Transfer(opts)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
+ return _MockLBTCTokenPool.Contract.contract.Transact(opts, method, params...)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolCaller) GetAllowList(opts *bind.CallOpts) ([]common.Address, error) {
+ var out []interface{}
+ err := _MockLBTCTokenPool.contract.Call(opts, &out, "getAllowList")
+
+ if err != nil {
+ return *new([]common.Address), err
+ }
+
+ out0 := *abi.ConvertType(out[0], new([]common.Address)).(*[]common.Address)
+
+ return out0, err
+
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolSession) GetAllowList() ([]common.Address, error) {
+ return _MockLBTCTokenPool.Contract.GetAllowList(&_MockLBTCTokenPool.CallOpts)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolCallerSession) GetAllowList() ([]common.Address, error) {
+ return _MockLBTCTokenPool.Contract.GetAllowList(&_MockLBTCTokenPool.CallOpts)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolCaller) GetAllowListEnabled(opts *bind.CallOpts) (bool, error) {
+ var out []interface{}
+ err := _MockLBTCTokenPool.contract.Call(opts, &out, "getAllowListEnabled")
+
+ if err != nil {
+ return *new(bool), err
+ }
+
+ out0 := *abi.ConvertType(out[0], new(bool)).(*bool)
+
+ return out0, err
+
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolSession) GetAllowListEnabled() (bool, error) {
+ return _MockLBTCTokenPool.Contract.GetAllowListEnabled(&_MockLBTCTokenPool.CallOpts)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolCallerSession) GetAllowListEnabled() (bool, error) {
+ return _MockLBTCTokenPool.Contract.GetAllowListEnabled(&_MockLBTCTokenPool.CallOpts)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolCaller) GetCurrentInboundRateLimiterState(opts *bind.CallOpts, remoteChainSelector uint64) (RateLimiterTokenBucket, error) {
+ var out []interface{}
+ err := _MockLBTCTokenPool.contract.Call(opts, &out, "getCurrentInboundRateLimiterState", remoteChainSelector)
+
+ if err != nil {
+ return *new(RateLimiterTokenBucket), err
+ }
+
+ out0 := *abi.ConvertType(out[0], new(RateLimiterTokenBucket)).(*RateLimiterTokenBucket)
+
+ return out0, err
+
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolSession) GetCurrentInboundRateLimiterState(remoteChainSelector uint64) (RateLimiterTokenBucket, error) {
+ return _MockLBTCTokenPool.Contract.GetCurrentInboundRateLimiterState(&_MockLBTCTokenPool.CallOpts, remoteChainSelector)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolCallerSession) GetCurrentInboundRateLimiterState(remoteChainSelector uint64) (RateLimiterTokenBucket, error) {
+ return _MockLBTCTokenPool.Contract.GetCurrentInboundRateLimiterState(&_MockLBTCTokenPool.CallOpts, remoteChainSelector)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolCaller) GetCurrentOutboundRateLimiterState(opts *bind.CallOpts, remoteChainSelector uint64) (RateLimiterTokenBucket, error) {
+ var out []interface{}
+ err := _MockLBTCTokenPool.contract.Call(opts, &out, "getCurrentOutboundRateLimiterState", remoteChainSelector)
+
+ if err != nil {
+ return *new(RateLimiterTokenBucket), err
+ }
+
+ out0 := *abi.ConvertType(out[0], new(RateLimiterTokenBucket)).(*RateLimiterTokenBucket)
+
+ return out0, err
+
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolSession) GetCurrentOutboundRateLimiterState(remoteChainSelector uint64) (RateLimiterTokenBucket, error) {
+ return _MockLBTCTokenPool.Contract.GetCurrentOutboundRateLimiterState(&_MockLBTCTokenPool.CallOpts, remoteChainSelector)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolCallerSession) GetCurrentOutboundRateLimiterState(remoteChainSelector uint64) (RateLimiterTokenBucket, error) {
+ return _MockLBTCTokenPool.Contract.GetCurrentOutboundRateLimiterState(&_MockLBTCTokenPool.CallOpts, remoteChainSelector)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolCaller) GetRateLimitAdmin(opts *bind.CallOpts) (common.Address, error) {
+ var out []interface{}
+ err := _MockLBTCTokenPool.contract.Call(opts, &out, "getRateLimitAdmin")
+
+ if err != nil {
+ return *new(common.Address), err
+ }
+
+ out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address)
+
+ return out0, err
+
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolSession) GetRateLimitAdmin() (common.Address, error) {
+ return _MockLBTCTokenPool.Contract.GetRateLimitAdmin(&_MockLBTCTokenPool.CallOpts)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolCallerSession) GetRateLimitAdmin() (common.Address, error) {
+ return _MockLBTCTokenPool.Contract.GetRateLimitAdmin(&_MockLBTCTokenPool.CallOpts)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolCaller) GetRemotePools(opts *bind.CallOpts, remoteChainSelector uint64) ([][]byte, error) {
+ var out []interface{}
+ err := _MockLBTCTokenPool.contract.Call(opts, &out, "getRemotePools", remoteChainSelector)
+
+ if err != nil {
+ return *new([][]byte), err
+ }
+
+ out0 := *abi.ConvertType(out[0], new([][]byte)).(*[][]byte)
+
+ return out0, err
+
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolSession) GetRemotePools(remoteChainSelector uint64) ([][]byte, error) {
+ return _MockLBTCTokenPool.Contract.GetRemotePools(&_MockLBTCTokenPool.CallOpts, remoteChainSelector)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolCallerSession) GetRemotePools(remoteChainSelector uint64) ([][]byte, error) {
+ return _MockLBTCTokenPool.Contract.GetRemotePools(&_MockLBTCTokenPool.CallOpts, remoteChainSelector)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolCaller) GetRemoteToken(opts *bind.CallOpts, remoteChainSelector uint64) ([]byte, error) {
+ var out []interface{}
+ err := _MockLBTCTokenPool.contract.Call(opts, &out, "getRemoteToken", remoteChainSelector)
+
+ if err != nil {
+ return *new([]byte), err
+ }
+
+ out0 := *abi.ConvertType(out[0], new([]byte)).(*[]byte)
+
+ return out0, err
+
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolSession) GetRemoteToken(remoteChainSelector uint64) ([]byte, error) {
+ return _MockLBTCTokenPool.Contract.GetRemoteToken(&_MockLBTCTokenPool.CallOpts, remoteChainSelector)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolCallerSession) GetRemoteToken(remoteChainSelector uint64) ([]byte, error) {
+ return _MockLBTCTokenPool.Contract.GetRemoteToken(&_MockLBTCTokenPool.CallOpts, remoteChainSelector)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolCaller) GetRmnProxy(opts *bind.CallOpts) (common.Address, error) {
+ var out []interface{}
+ err := _MockLBTCTokenPool.contract.Call(opts, &out, "getRmnProxy")
+
+ if err != nil {
+ return *new(common.Address), err
+ }
+
+ out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address)
+
+ return out0, err
+
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolSession) GetRmnProxy() (common.Address, error) {
+ return _MockLBTCTokenPool.Contract.GetRmnProxy(&_MockLBTCTokenPool.CallOpts)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolCallerSession) GetRmnProxy() (common.Address, error) {
+ return _MockLBTCTokenPool.Contract.GetRmnProxy(&_MockLBTCTokenPool.CallOpts)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolCaller) GetRouter(opts *bind.CallOpts) (common.Address, error) {
+ var out []interface{}
+ err := _MockLBTCTokenPool.contract.Call(opts, &out, "getRouter")
+
+ if err != nil {
+ return *new(common.Address), err
+ }
+
+ out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address)
+
+ return out0, err
+
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolSession) GetRouter() (common.Address, error) {
+ return _MockLBTCTokenPool.Contract.GetRouter(&_MockLBTCTokenPool.CallOpts)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolCallerSession) GetRouter() (common.Address, error) {
+ return _MockLBTCTokenPool.Contract.GetRouter(&_MockLBTCTokenPool.CallOpts)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolCaller) GetSupportedChains(opts *bind.CallOpts) ([]uint64, error) {
+ var out []interface{}
+ err := _MockLBTCTokenPool.contract.Call(opts, &out, "getSupportedChains")
+
+ if err != nil {
+ return *new([]uint64), err
+ }
+
+ out0 := *abi.ConvertType(out[0], new([]uint64)).(*[]uint64)
+
+ return out0, err
+
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolSession) GetSupportedChains() ([]uint64, error) {
+ return _MockLBTCTokenPool.Contract.GetSupportedChains(&_MockLBTCTokenPool.CallOpts)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolCallerSession) GetSupportedChains() ([]uint64, error) {
+ return _MockLBTCTokenPool.Contract.GetSupportedChains(&_MockLBTCTokenPool.CallOpts)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolCaller) GetToken(opts *bind.CallOpts) (common.Address, error) {
+ var out []interface{}
+ err := _MockLBTCTokenPool.contract.Call(opts, &out, "getToken")
+
+ if err != nil {
+ return *new(common.Address), err
+ }
+
+ out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address)
+
+ return out0, err
+
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolSession) GetToken() (common.Address, error) {
+ return _MockLBTCTokenPool.Contract.GetToken(&_MockLBTCTokenPool.CallOpts)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolCallerSession) GetToken() (common.Address, error) {
+ return _MockLBTCTokenPool.Contract.GetToken(&_MockLBTCTokenPool.CallOpts)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolCaller) GetTokenDecimals(opts *bind.CallOpts) (uint8, error) {
+ var out []interface{}
+ err := _MockLBTCTokenPool.contract.Call(opts, &out, "getTokenDecimals")
+
+ if err != nil {
+ return *new(uint8), err
+ }
+
+ out0 := *abi.ConvertType(out[0], new(uint8)).(*uint8)
+
+ return out0, err
+
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolSession) GetTokenDecimals() (uint8, error) {
+ return _MockLBTCTokenPool.Contract.GetTokenDecimals(&_MockLBTCTokenPool.CallOpts)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolCallerSession) GetTokenDecimals() (uint8, error) {
+ return _MockLBTCTokenPool.Contract.GetTokenDecimals(&_MockLBTCTokenPool.CallOpts)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolCaller) IDestPoolData(opts *bind.CallOpts) ([]byte, error) {
+ var out []interface{}
+ err := _MockLBTCTokenPool.contract.Call(opts, &out, "i_destPoolData")
+
+ if err != nil {
+ return *new([]byte), err
+ }
+
+ out0 := *abi.ConvertType(out[0], new([]byte)).(*[]byte)
+
+ return out0, err
+
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolSession) IDestPoolData() ([]byte, error) {
+ return _MockLBTCTokenPool.Contract.IDestPoolData(&_MockLBTCTokenPool.CallOpts)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolCallerSession) IDestPoolData() ([]byte, error) {
+ return _MockLBTCTokenPool.Contract.IDestPoolData(&_MockLBTCTokenPool.CallOpts)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolCaller) IsRemotePool(opts *bind.CallOpts, remoteChainSelector uint64, remotePoolAddress []byte) (bool, error) {
+ var out []interface{}
+ err := _MockLBTCTokenPool.contract.Call(opts, &out, "isRemotePool", remoteChainSelector, remotePoolAddress)
+
+ if err != nil {
+ return *new(bool), err
+ }
+
+ out0 := *abi.ConvertType(out[0], new(bool)).(*bool)
+
+ return out0, err
+
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolSession) IsRemotePool(remoteChainSelector uint64, remotePoolAddress []byte) (bool, error) {
+ return _MockLBTCTokenPool.Contract.IsRemotePool(&_MockLBTCTokenPool.CallOpts, remoteChainSelector, remotePoolAddress)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolCallerSession) IsRemotePool(remoteChainSelector uint64, remotePoolAddress []byte) (bool, error) {
+ return _MockLBTCTokenPool.Contract.IsRemotePool(&_MockLBTCTokenPool.CallOpts, remoteChainSelector, remotePoolAddress)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolCaller) IsSupportedChain(opts *bind.CallOpts, remoteChainSelector uint64) (bool, error) {
+ var out []interface{}
+ err := _MockLBTCTokenPool.contract.Call(opts, &out, "isSupportedChain", remoteChainSelector)
+
+ if err != nil {
+ return *new(bool), err
+ }
+
+ out0 := *abi.ConvertType(out[0], new(bool)).(*bool)
+
+ return out0, err
+
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolSession) IsSupportedChain(remoteChainSelector uint64) (bool, error) {
+ return _MockLBTCTokenPool.Contract.IsSupportedChain(&_MockLBTCTokenPool.CallOpts, remoteChainSelector)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolCallerSession) IsSupportedChain(remoteChainSelector uint64) (bool, error) {
+ return _MockLBTCTokenPool.Contract.IsSupportedChain(&_MockLBTCTokenPool.CallOpts, remoteChainSelector)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolCaller) IsSupportedToken(opts *bind.CallOpts, token common.Address) (bool, error) {
+ var out []interface{}
+ err := _MockLBTCTokenPool.contract.Call(opts, &out, "isSupportedToken", token)
+
+ if err != nil {
+ return *new(bool), err
+ }
+
+ out0 := *abi.ConvertType(out[0], new(bool)).(*bool)
+
+ return out0, err
+
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolSession) IsSupportedToken(token common.Address) (bool, error) {
+ return _MockLBTCTokenPool.Contract.IsSupportedToken(&_MockLBTCTokenPool.CallOpts, token)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolCallerSession) IsSupportedToken(token common.Address) (bool, error) {
+ return _MockLBTCTokenPool.Contract.IsSupportedToken(&_MockLBTCTokenPool.CallOpts, token)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolCaller) Owner(opts *bind.CallOpts) (common.Address, error) {
+ var out []interface{}
+ err := _MockLBTCTokenPool.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 (_MockLBTCTokenPool *MockLBTCTokenPoolSession) Owner() (common.Address, error) {
+ return _MockLBTCTokenPool.Contract.Owner(&_MockLBTCTokenPool.CallOpts)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolCallerSession) Owner() (common.Address, error) {
+ return _MockLBTCTokenPool.Contract.Owner(&_MockLBTCTokenPool.CallOpts)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolCaller) SupportsInterface(opts *bind.CallOpts, interfaceId [4]byte) (bool, error) {
+ var out []interface{}
+ err := _MockLBTCTokenPool.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 (_MockLBTCTokenPool *MockLBTCTokenPoolSession) SupportsInterface(interfaceId [4]byte) (bool, error) {
+ return _MockLBTCTokenPool.Contract.SupportsInterface(&_MockLBTCTokenPool.CallOpts, interfaceId)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolCallerSession) SupportsInterface(interfaceId [4]byte) (bool, error) {
+ return _MockLBTCTokenPool.Contract.SupportsInterface(&_MockLBTCTokenPool.CallOpts, interfaceId)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolCaller) TypeAndVersion(opts *bind.CallOpts) (string, error) {
+ var out []interface{}
+ err := _MockLBTCTokenPool.contract.Call(opts, &out, "typeAndVersion")
+
+ if err != nil {
+ return *new(string), err
+ }
+
+ out0 := *abi.ConvertType(out[0], new(string)).(*string)
+
+ return out0, err
+
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolSession) TypeAndVersion() (string, error) {
+ return _MockLBTCTokenPool.Contract.TypeAndVersion(&_MockLBTCTokenPool.CallOpts)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolCallerSession) TypeAndVersion() (string, error) {
+ return _MockLBTCTokenPool.Contract.TypeAndVersion(&_MockLBTCTokenPool.CallOpts)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolTransactor) AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) {
+ return _MockLBTCTokenPool.contract.Transact(opts, "acceptOwnership")
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolSession) AcceptOwnership() (*types.Transaction, error) {
+ return _MockLBTCTokenPool.Contract.AcceptOwnership(&_MockLBTCTokenPool.TransactOpts)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolTransactorSession) AcceptOwnership() (*types.Transaction, error) {
+ return _MockLBTCTokenPool.Contract.AcceptOwnership(&_MockLBTCTokenPool.TransactOpts)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolTransactor) AddRemotePool(opts *bind.TransactOpts, remoteChainSelector uint64, remotePoolAddress []byte) (*types.Transaction, error) {
+ return _MockLBTCTokenPool.contract.Transact(opts, "addRemotePool", remoteChainSelector, remotePoolAddress)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolSession) AddRemotePool(remoteChainSelector uint64, remotePoolAddress []byte) (*types.Transaction, error) {
+ return _MockLBTCTokenPool.Contract.AddRemotePool(&_MockLBTCTokenPool.TransactOpts, remoteChainSelector, remotePoolAddress)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolTransactorSession) AddRemotePool(remoteChainSelector uint64, remotePoolAddress []byte) (*types.Transaction, error) {
+ return _MockLBTCTokenPool.Contract.AddRemotePool(&_MockLBTCTokenPool.TransactOpts, remoteChainSelector, remotePoolAddress)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolTransactor) ApplyAllowListUpdates(opts *bind.TransactOpts, removes []common.Address, adds []common.Address) (*types.Transaction, error) {
+ return _MockLBTCTokenPool.contract.Transact(opts, "applyAllowListUpdates", removes, adds)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolSession) ApplyAllowListUpdates(removes []common.Address, adds []common.Address) (*types.Transaction, error) {
+ return _MockLBTCTokenPool.Contract.ApplyAllowListUpdates(&_MockLBTCTokenPool.TransactOpts, removes, adds)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolTransactorSession) ApplyAllowListUpdates(removes []common.Address, adds []common.Address) (*types.Transaction, error) {
+ return _MockLBTCTokenPool.Contract.ApplyAllowListUpdates(&_MockLBTCTokenPool.TransactOpts, removes, adds)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolTransactor) ApplyChainUpdates(opts *bind.TransactOpts, remoteChainSelectorsToRemove []uint64, chainsToAdd []TokenPoolChainUpdate) (*types.Transaction, error) {
+ return _MockLBTCTokenPool.contract.Transact(opts, "applyChainUpdates", remoteChainSelectorsToRemove, chainsToAdd)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolSession) ApplyChainUpdates(remoteChainSelectorsToRemove []uint64, chainsToAdd []TokenPoolChainUpdate) (*types.Transaction, error) {
+ return _MockLBTCTokenPool.Contract.ApplyChainUpdates(&_MockLBTCTokenPool.TransactOpts, remoteChainSelectorsToRemove, chainsToAdd)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolTransactorSession) ApplyChainUpdates(remoteChainSelectorsToRemove []uint64, chainsToAdd []TokenPoolChainUpdate) (*types.Transaction, error) {
+ return _MockLBTCTokenPool.Contract.ApplyChainUpdates(&_MockLBTCTokenPool.TransactOpts, remoteChainSelectorsToRemove, chainsToAdd)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolTransactor) LockOrBurn(opts *bind.TransactOpts, lockOrBurnIn PoolLockOrBurnInV1) (*types.Transaction, error) {
+ return _MockLBTCTokenPool.contract.Transact(opts, "lockOrBurn", lockOrBurnIn)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolSession) LockOrBurn(lockOrBurnIn PoolLockOrBurnInV1) (*types.Transaction, error) {
+ return _MockLBTCTokenPool.Contract.LockOrBurn(&_MockLBTCTokenPool.TransactOpts, lockOrBurnIn)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolTransactorSession) LockOrBurn(lockOrBurnIn PoolLockOrBurnInV1) (*types.Transaction, error) {
+ return _MockLBTCTokenPool.Contract.LockOrBurn(&_MockLBTCTokenPool.TransactOpts, lockOrBurnIn)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolTransactor) ReleaseOrMint(opts *bind.TransactOpts, releaseOrMintIn PoolReleaseOrMintInV1) (*types.Transaction, error) {
+ return _MockLBTCTokenPool.contract.Transact(opts, "releaseOrMint", releaseOrMintIn)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolSession) ReleaseOrMint(releaseOrMintIn PoolReleaseOrMintInV1) (*types.Transaction, error) {
+ return _MockLBTCTokenPool.Contract.ReleaseOrMint(&_MockLBTCTokenPool.TransactOpts, releaseOrMintIn)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolTransactorSession) ReleaseOrMint(releaseOrMintIn PoolReleaseOrMintInV1) (*types.Transaction, error) {
+ return _MockLBTCTokenPool.Contract.ReleaseOrMint(&_MockLBTCTokenPool.TransactOpts, releaseOrMintIn)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolTransactor) RemoveRemotePool(opts *bind.TransactOpts, remoteChainSelector uint64, remotePoolAddress []byte) (*types.Transaction, error) {
+ return _MockLBTCTokenPool.contract.Transact(opts, "removeRemotePool", remoteChainSelector, remotePoolAddress)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolSession) RemoveRemotePool(remoteChainSelector uint64, remotePoolAddress []byte) (*types.Transaction, error) {
+ return _MockLBTCTokenPool.Contract.RemoveRemotePool(&_MockLBTCTokenPool.TransactOpts, remoteChainSelector, remotePoolAddress)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolTransactorSession) RemoveRemotePool(remoteChainSelector uint64, remotePoolAddress []byte) (*types.Transaction, error) {
+ return _MockLBTCTokenPool.Contract.RemoveRemotePool(&_MockLBTCTokenPool.TransactOpts, remoteChainSelector, remotePoolAddress)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolTransactor) SetChainRateLimiterConfig(opts *bind.TransactOpts, remoteChainSelector uint64, outboundConfig RateLimiterConfig, inboundConfig RateLimiterConfig) (*types.Transaction, error) {
+ return _MockLBTCTokenPool.contract.Transact(opts, "setChainRateLimiterConfig", remoteChainSelector, outboundConfig, inboundConfig)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolSession) SetChainRateLimiterConfig(remoteChainSelector uint64, outboundConfig RateLimiterConfig, inboundConfig RateLimiterConfig) (*types.Transaction, error) {
+ return _MockLBTCTokenPool.Contract.SetChainRateLimiterConfig(&_MockLBTCTokenPool.TransactOpts, remoteChainSelector, outboundConfig, inboundConfig)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolTransactorSession) SetChainRateLimiterConfig(remoteChainSelector uint64, outboundConfig RateLimiterConfig, inboundConfig RateLimiterConfig) (*types.Transaction, error) {
+ return _MockLBTCTokenPool.Contract.SetChainRateLimiterConfig(&_MockLBTCTokenPool.TransactOpts, remoteChainSelector, outboundConfig, inboundConfig)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolTransactor) SetRateLimitAdmin(opts *bind.TransactOpts, rateLimitAdmin common.Address) (*types.Transaction, error) {
+ return _MockLBTCTokenPool.contract.Transact(opts, "setRateLimitAdmin", rateLimitAdmin)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolSession) SetRateLimitAdmin(rateLimitAdmin common.Address) (*types.Transaction, error) {
+ return _MockLBTCTokenPool.Contract.SetRateLimitAdmin(&_MockLBTCTokenPool.TransactOpts, rateLimitAdmin)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolTransactorSession) SetRateLimitAdmin(rateLimitAdmin common.Address) (*types.Transaction, error) {
+ return _MockLBTCTokenPool.Contract.SetRateLimitAdmin(&_MockLBTCTokenPool.TransactOpts, rateLimitAdmin)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolTransactor) SetRouter(opts *bind.TransactOpts, newRouter common.Address) (*types.Transaction, error) {
+ return _MockLBTCTokenPool.contract.Transact(opts, "setRouter", newRouter)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolSession) SetRouter(newRouter common.Address) (*types.Transaction, error) {
+ return _MockLBTCTokenPool.Contract.SetRouter(&_MockLBTCTokenPool.TransactOpts, newRouter)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolTransactorSession) SetRouter(newRouter common.Address) (*types.Transaction, error) {
+ return _MockLBTCTokenPool.Contract.SetRouter(&_MockLBTCTokenPool.TransactOpts, newRouter)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolTransactor) TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) {
+ return _MockLBTCTokenPool.contract.Transact(opts, "transferOwnership", to)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolSession) TransferOwnership(to common.Address) (*types.Transaction, error) {
+ return _MockLBTCTokenPool.Contract.TransferOwnership(&_MockLBTCTokenPool.TransactOpts, to)
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolTransactorSession) TransferOwnership(to common.Address) (*types.Transaction, error) {
+ return _MockLBTCTokenPool.Contract.TransferOwnership(&_MockLBTCTokenPool.TransactOpts, to)
+}
+
+type MockLBTCTokenPoolAllowListAddIterator struct {
+ Event *MockLBTCTokenPoolAllowListAdd
+
+ contract *bind.BoundContract
+ event string
+
+ logs chan types.Log
+ sub ethereum.Subscription
+ done bool
+ fail error
+}
+
+func (it *MockLBTCTokenPoolAllowListAddIterator) Next() bool {
+
+ if it.fail != nil {
+ return false
+ }
+
+ if it.done {
+ select {
+ case log := <-it.logs:
+ it.Event = new(MockLBTCTokenPoolAllowListAdd)
+ if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
+ it.fail = err
+ return false
+ }
+ it.Event.Raw = log
+ return true
+
+ default:
+ return false
+ }
+ }
+
+ select {
+ case log := <-it.logs:
+ it.Event = new(MockLBTCTokenPoolAllowListAdd)
+ if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
+ it.fail = err
+ return false
+ }
+ it.Event.Raw = log
+ return true
+
+ case err := <-it.sub.Err():
+ it.done = true
+ it.fail = err
+ return it.Next()
+ }
+}
+
+func (it *MockLBTCTokenPoolAllowListAddIterator) Error() error {
+ return it.fail
+}
+
+func (it *MockLBTCTokenPoolAllowListAddIterator) Close() error {
+ it.sub.Unsubscribe()
+ return nil
+}
+
+type MockLBTCTokenPoolAllowListAdd struct {
+ Sender common.Address
+ Raw types.Log
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolFilterer) FilterAllowListAdd(opts *bind.FilterOpts) (*MockLBTCTokenPoolAllowListAddIterator, error) {
+
+ logs, sub, err := _MockLBTCTokenPool.contract.FilterLogs(opts, "AllowListAdd")
+ if err != nil {
+ return nil, err
+ }
+ return &MockLBTCTokenPoolAllowListAddIterator{contract: _MockLBTCTokenPool.contract, event: "AllowListAdd", logs: logs, sub: sub}, nil
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolFilterer) WatchAllowListAdd(opts *bind.WatchOpts, sink chan<- *MockLBTCTokenPoolAllowListAdd) (event.Subscription, error) {
+
+ logs, sub, err := _MockLBTCTokenPool.contract.WatchLogs(opts, "AllowListAdd")
+ if err != nil {
+ return nil, err
+ }
+ return event.NewSubscription(func(quit <-chan struct{}) error {
+ defer sub.Unsubscribe()
+ for {
+ select {
+ case log := <-logs:
+
+ event := new(MockLBTCTokenPoolAllowListAdd)
+ if err := _MockLBTCTokenPool.contract.UnpackLog(event, "AllowListAdd", log); err != nil {
+ return err
+ }
+ event.Raw = log
+
+ select {
+ case sink <- event:
+ case err := <-sub.Err():
+ return err
+ case <-quit:
+ return nil
+ }
+ case err := <-sub.Err():
+ return err
+ case <-quit:
+ return nil
+ }
+ }
+ }), nil
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolFilterer) ParseAllowListAdd(log types.Log) (*MockLBTCTokenPoolAllowListAdd, error) {
+ event := new(MockLBTCTokenPoolAllowListAdd)
+ if err := _MockLBTCTokenPool.contract.UnpackLog(event, "AllowListAdd", log); err != nil {
+ return nil, err
+ }
+ event.Raw = log
+ return event, nil
+}
+
+type MockLBTCTokenPoolAllowListRemoveIterator struct {
+ Event *MockLBTCTokenPoolAllowListRemove
+
+ contract *bind.BoundContract
+ event string
+
+ logs chan types.Log
+ sub ethereum.Subscription
+ done bool
+ fail error
+}
+
+func (it *MockLBTCTokenPoolAllowListRemoveIterator) Next() bool {
+
+ if it.fail != nil {
+ return false
+ }
+
+ if it.done {
+ select {
+ case log := <-it.logs:
+ it.Event = new(MockLBTCTokenPoolAllowListRemove)
+ if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
+ it.fail = err
+ return false
+ }
+ it.Event.Raw = log
+ return true
+
+ default:
+ return false
+ }
+ }
+
+ select {
+ case log := <-it.logs:
+ it.Event = new(MockLBTCTokenPoolAllowListRemove)
+ if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
+ it.fail = err
+ return false
+ }
+ it.Event.Raw = log
+ return true
+
+ case err := <-it.sub.Err():
+ it.done = true
+ it.fail = err
+ return it.Next()
+ }
+}
+
+func (it *MockLBTCTokenPoolAllowListRemoveIterator) Error() error {
+ return it.fail
+}
+
+func (it *MockLBTCTokenPoolAllowListRemoveIterator) Close() error {
+ it.sub.Unsubscribe()
+ return nil
+}
+
+type MockLBTCTokenPoolAllowListRemove struct {
+ Sender common.Address
+ Raw types.Log
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolFilterer) FilterAllowListRemove(opts *bind.FilterOpts) (*MockLBTCTokenPoolAllowListRemoveIterator, error) {
+
+ logs, sub, err := _MockLBTCTokenPool.contract.FilterLogs(opts, "AllowListRemove")
+ if err != nil {
+ return nil, err
+ }
+ return &MockLBTCTokenPoolAllowListRemoveIterator{contract: _MockLBTCTokenPool.contract, event: "AllowListRemove", logs: logs, sub: sub}, nil
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolFilterer) WatchAllowListRemove(opts *bind.WatchOpts, sink chan<- *MockLBTCTokenPoolAllowListRemove) (event.Subscription, error) {
+
+ logs, sub, err := _MockLBTCTokenPool.contract.WatchLogs(opts, "AllowListRemove")
+ if err != nil {
+ return nil, err
+ }
+ return event.NewSubscription(func(quit <-chan struct{}) error {
+ defer sub.Unsubscribe()
+ for {
+ select {
+ case log := <-logs:
+
+ event := new(MockLBTCTokenPoolAllowListRemove)
+ if err := _MockLBTCTokenPool.contract.UnpackLog(event, "AllowListRemove", log); err != nil {
+ return err
+ }
+ event.Raw = log
+
+ select {
+ case sink <- event:
+ case err := <-sub.Err():
+ return err
+ case <-quit:
+ return nil
+ }
+ case err := <-sub.Err():
+ return err
+ case <-quit:
+ return nil
+ }
+ }
+ }), nil
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolFilterer) ParseAllowListRemove(log types.Log) (*MockLBTCTokenPoolAllowListRemove, error) {
+ event := new(MockLBTCTokenPoolAllowListRemove)
+ if err := _MockLBTCTokenPool.contract.UnpackLog(event, "AllowListRemove", log); err != nil {
+ return nil, err
+ }
+ event.Raw = log
+ return event, nil
+}
+
+type MockLBTCTokenPoolBurnedIterator struct {
+ Event *MockLBTCTokenPoolBurned
+
+ contract *bind.BoundContract
+ event string
+
+ logs chan types.Log
+ sub ethereum.Subscription
+ done bool
+ fail error
+}
+
+func (it *MockLBTCTokenPoolBurnedIterator) Next() bool {
+
+ if it.fail != nil {
+ return false
+ }
+
+ if it.done {
+ select {
+ case log := <-it.logs:
+ it.Event = new(MockLBTCTokenPoolBurned)
+ if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
+ it.fail = err
+ return false
+ }
+ it.Event.Raw = log
+ return true
+
+ default:
+ return false
+ }
+ }
+
+ select {
+ case log := <-it.logs:
+ it.Event = new(MockLBTCTokenPoolBurned)
+ if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
+ it.fail = err
+ return false
+ }
+ it.Event.Raw = log
+ return true
+
+ case err := <-it.sub.Err():
+ it.done = true
+ it.fail = err
+ return it.Next()
+ }
+}
+
+func (it *MockLBTCTokenPoolBurnedIterator) Error() error {
+ return it.fail
+}
+
+func (it *MockLBTCTokenPoolBurnedIterator) Close() error {
+ it.sub.Unsubscribe()
+ return nil
+}
+
+type MockLBTCTokenPoolBurned struct {
+ Sender common.Address
+ Amount *big.Int
+ Raw types.Log
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolFilterer) FilterBurned(opts *bind.FilterOpts, sender []common.Address) (*MockLBTCTokenPoolBurnedIterator, error) {
+
+ var senderRule []interface{}
+ for _, senderItem := range sender {
+ senderRule = append(senderRule, senderItem)
+ }
+
+ logs, sub, err := _MockLBTCTokenPool.contract.FilterLogs(opts, "Burned", senderRule)
+ if err != nil {
+ return nil, err
+ }
+ return &MockLBTCTokenPoolBurnedIterator{contract: _MockLBTCTokenPool.contract, event: "Burned", logs: logs, sub: sub}, nil
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolFilterer) WatchBurned(opts *bind.WatchOpts, sink chan<- *MockLBTCTokenPoolBurned, sender []common.Address) (event.Subscription, error) {
+
+ var senderRule []interface{}
+ for _, senderItem := range sender {
+ senderRule = append(senderRule, senderItem)
+ }
+
+ logs, sub, err := _MockLBTCTokenPool.contract.WatchLogs(opts, "Burned", 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(MockLBTCTokenPoolBurned)
+ if err := _MockLBTCTokenPool.contract.UnpackLog(event, "Burned", log); err != nil {
+ return err
+ }
+ event.Raw = log
+
+ select {
+ case sink <- event:
+ case err := <-sub.Err():
+ return err
+ case <-quit:
+ return nil
+ }
+ case err := <-sub.Err():
+ return err
+ case <-quit:
+ return nil
+ }
+ }
+ }), nil
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolFilterer) ParseBurned(log types.Log) (*MockLBTCTokenPoolBurned, error) {
+ event := new(MockLBTCTokenPoolBurned)
+ if err := _MockLBTCTokenPool.contract.UnpackLog(event, "Burned", log); err != nil {
+ return nil, err
+ }
+ event.Raw = log
+ return event, nil
+}
+
+type MockLBTCTokenPoolChainAddedIterator struct {
+ Event *MockLBTCTokenPoolChainAdded
+
+ contract *bind.BoundContract
+ event string
+
+ logs chan types.Log
+ sub ethereum.Subscription
+ done bool
+ fail error
+}
+
+func (it *MockLBTCTokenPoolChainAddedIterator) Next() bool {
+
+ if it.fail != nil {
+ return false
+ }
+
+ if it.done {
+ select {
+ case log := <-it.logs:
+ it.Event = new(MockLBTCTokenPoolChainAdded)
+ if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
+ it.fail = err
+ return false
+ }
+ it.Event.Raw = log
+ return true
+
+ default:
+ return false
+ }
+ }
+
+ select {
+ case log := <-it.logs:
+ it.Event = new(MockLBTCTokenPoolChainAdded)
+ if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
+ it.fail = err
+ return false
+ }
+ it.Event.Raw = log
+ return true
+
+ case err := <-it.sub.Err():
+ it.done = true
+ it.fail = err
+ return it.Next()
+ }
+}
+
+func (it *MockLBTCTokenPoolChainAddedIterator) Error() error {
+ return it.fail
+}
+
+func (it *MockLBTCTokenPoolChainAddedIterator) Close() error {
+ it.sub.Unsubscribe()
+ return nil
+}
+
+type MockLBTCTokenPoolChainAdded struct {
+ RemoteChainSelector uint64
+ RemoteToken []byte
+ OutboundRateLimiterConfig RateLimiterConfig
+ InboundRateLimiterConfig RateLimiterConfig
+ Raw types.Log
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolFilterer) FilterChainAdded(opts *bind.FilterOpts) (*MockLBTCTokenPoolChainAddedIterator, error) {
+
+ logs, sub, err := _MockLBTCTokenPool.contract.FilterLogs(opts, "ChainAdded")
+ if err != nil {
+ return nil, err
+ }
+ return &MockLBTCTokenPoolChainAddedIterator{contract: _MockLBTCTokenPool.contract, event: "ChainAdded", logs: logs, sub: sub}, nil
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolFilterer) WatchChainAdded(opts *bind.WatchOpts, sink chan<- *MockLBTCTokenPoolChainAdded) (event.Subscription, error) {
+
+ logs, sub, err := _MockLBTCTokenPool.contract.WatchLogs(opts, "ChainAdded")
+ if err != nil {
+ return nil, err
+ }
+ return event.NewSubscription(func(quit <-chan struct{}) error {
+ defer sub.Unsubscribe()
+ for {
+ select {
+ case log := <-logs:
+
+ event := new(MockLBTCTokenPoolChainAdded)
+ if err := _MockLBTCTokenPool.contract.UnpackLog(event, "ChainAdded", log); err != nil {
+ return err
+ }
+ event.Raw = log
+
+ select {
+ case sink <- event:
+ case err := <-sub.Err():
+ return err
+ case <-quit:
+ return nil
+ }
+ case err := <-sub.Err():
+ return err
+ case <-quit:
+ return nil
+ }
+ }
+ }), nil
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolFilterer) ParseChainAdded(log types.Log) (*MockLBTCTokenPoolChainAdded, error) {
+ event := new(MockLBTCTokenPoolChainAdded)
+ if err := _MockLBTCTokenPool.contract.UnpackLog(event, "ChainAdded", log); err != nil {
+ return nil, err
+ }
+ event.Raw = log
+ return event, nil
+}
+
+type MockLBTCTokenPoolChainConfiguredIterator struct {
+ Event *MockLBTCTokenPoolChainConfigured
+
+ contract *bind.BoundContract
+ event string
+
+ logs chan types.Log
+ sub ethereum.Subscription
+ done bool
+ fail error
+}
+
+func (it *MockLBTCTokenPoolChainConfiguredIterator) Next() bool {
+
+ if it.fail != nil {
+ return false
+ }
+
+ if it.done {
+ select {
+ case log := <-it.logs:
+ it.Event = new(MockLBTCTokenPoolChainConfigured)
+ if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
+ it.fail = err
+ return false
+ }
+ it.Event.Raw = log
+ return true
+
+ default:
+ return false
+ }
+ }
+
+ select {
+ case log := <-it.logs:
+ it.Event = new(MockLBTCTokenPoolChainConfigured)
+ if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
+ it.fail = err
+ return false
+ }
+ it.Event.Raw = log
+ return true
+
+ case err := <-it.sub.Err():
+ it.done = true
+ it.fail = err
+ return it.Next()
+ }
+}
+
+func (it *MockLBTCTokenPoolChainConfiguredIterator) Error() error {
+ return it.fail
+}
+
+func (it *MockLBTCTokenPoolChainConfiguredIterator) Close() error {
+ it.sub.Unsubscribe()
+ return nil
+}
+
+type MockLBTCTokenPoolChainConfigured struct {
+ RemoteChainSelector uint64
+ OutboundRateLimiterConfig RateLimiterConfig
+ InboundRateLimiterConfig RateLimiterConfig
+ Raw types.Log
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolFilterer) FilterChainConfigured(opts *bind.FilterOpts) (*MockLBTCTokenPoolChainConfiguredIterator, error) {
+
+ logs, sub, err := _MockLBTCTokenPool.contract.FilterLogs(opts, "ChainConfigured")
+ if err != nil {
+ return nil, err
+ }
+ return &MockLBTCTokenPoolChainConfiguredIterator{contract: _MockLBTCTokenPool.contract, event: "ChainConfigured", logs: logs, sub: sub}, nil
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolFilterer) WatchChainConfigured(opts *bind.WatchOpts, sink chan<- *MockLBTCTokenPoolChainConfigured) (event.Subscription, error) {
+
+ logs, sub, err := _MockLBTCTokenPool.contract.WatchLogs(opts, "ChainConfigured")
+ if err != nil {
+ return nil, err
+ }
+ return event.NewSubscription(func(quit <-chan struct{}) error {
+ defer sub.Unsubscribe()
+ for {
+ select {
+ case log := <-logs:
+
+ event := new(MockLBTCTokenPoolChainConfigured)
+ if err := _MockLBTCTokenPool.contract.UnpackLog(event, "ChainConfigured", log); err != nil {
+ return err
+ }
+ event.Raw = log
+
+ select {
+ case sink <- event:
+ case err := <-sub.Err():
+ return err
+ case <-quit:
+ return nil
+ }
+ case err := <-sub.Err():
+ return err
+ case <-quit:
+ return nil
+ }
+ }
+ }), nil
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolFilterer) ParseChainConfigured(log types.Log) (*MockLBTCTokenPoolChainConfigured, error) {
+ event := new(MockLBTCTokenPoolChainConfigured)
+ if err := _MockLBTCTokenPool.contract.UnpackLog(event, "ChainConfigured", log); err != nil {
+ return nil, err
+ }
+ event.Raw = log
+ return event, nil
+}
+
+type MockLBTCTokenPoolChainRemovedIterator struct {
+ Event *MockLBTCTokenPoolChainRemoved
+
+ contract *bind.BoundContract
+ event string
+
+ logs chan types.Log
+ sub ethereum.Subscription
+ done bool
+ fail error
+}
+
+func (it *MockLBTCTokenPoolChainRemovedIterator) Next() bool {
+
+ if it.fail != nil {
+ return false
+ }
+
+ if it.done {
+ select {
+ case log := <-it.logs:
+ it.Event = new(MockLBTCTokenPoolChainRemoved)
+ if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
+ it.fail = err
+ return false
+ }
+ it.Event.Raw = log
+ return true
+
+ default:
+ return false
+ }
+ }
+
+ select {
+ case log := <-it.logs:
+ it.Event = new(MockLBTCTokenPoolChainRemoved)
+ if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
+ it.fail = err
+ return false
+ }
+ it.Event.Raw = log
+ return true
+
+ case err := <-it.sub.Err():
+ it.done = true
+ it.fail = err
+ return it.Next()
+ }
+}
+
+func (it *MockLBTCTokenPoolChainRemovedIterator) Error() error {
+ return it.fail
+}
+
+func (it *MockLBTCTokenPoolChainRemovedIterator) Close() error {
+ it.sub.Unsubscribe()
+ return nil
+}
+
+type MockLBTCTokenPoolChainRemoved struct {
+ RemoteChainSelector uint64
+ Raw types.Log
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolFilterer) FilterChainRemoved(opts *bind.FilterOpts) (*MockLBTCTokenPoolChainRemovedIterator, error) {
+
+ logs, sub, err := _MockLBTCTokenPool.contract.FilterLogs(opts, "ChainRemoved")
+ if err != nil {
+ return nil, err
+ }
+ return &MockLBTCTokenPoolChainRemovedIterator{contract: _MockLBTCTokenPool.contract, event: "ChainRemoved", logs: logs, sub: sub}, nil
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolFilterer) WatchChainRemoved(opts *bind.WatchOpts, sink chan<- *MockLBTCTokenPoolChainRemoved) (event.Subscription, error) {
+
+ logs, sub, err := _MockLBTCTokenPool.contract.WatchLogs(opts, "ChainRemoved")
+ if err != nil {
+ return nil, err
+ }
+ return event.NewSubscription(func(quit <-chan struct{}) error {
+ defer sub.Unsubscribe()
+ for {
+ select {
+ case log := <-logs:
+
+ event := new(MockLBTCTokenPoolChainRemoved)
+ if err := _MockLBTCTokenPool.contract.UnpackLog(event, "ChainRemoved", log); err != nil {
+ return err
+ }
+ event.Raw = log
+
+ select {
+ case sink <- event:
+ case err := <-sub.Err():
+ return err
+ case <-quit:
+ return nil
+ }
+ case err := <-sub.Err():
+ return err
+ case <-quit:
+ return nil
+ }
+ }
+ }), nil
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolFilterer) ParseChainRemoved(log types.Log) (*MockLBTCTokenPoolChainRemoved, error) {
+ event := new(MockLBTCTokenPoolChainRemoved)
+ if err := _MockLBTCTokenPool.contract.UnpackLog(event, "ChainRemoved", log); err != nil {
+ return nil, err
+ }
+ event.Raw = log
+ return event, nil
+}
+
+type MockLBTCTokenPoolConfigChangedIterator struct {
+ Event *MockLBTCTokenPoolConfigChanged
+
+ contract *bind.BoundContract
+ event string
+
+ logs chan types.Log
+ sub ethereum.Subscription
+ done bool
+ fail error
+}
+
+func (it *MockLBTCTokenPoolConfigChangedIterator) Next() bool {
+
+ if it.fail != nil {
+ return false
+ }
+
+ if it.done {
+ select {
+ case log := <-it.logs:
+ it.Event = new(MockLBTCTokenPoolConfigChanged)
+ if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
+ it.fail = err
+ return false
+ }
+ it.Event.Raw = log
+ return true
+
+ default:
+ return false
+ }
+ }
+
+ select {
+ case log := <-it.logs:
+ it.Event = new(MockLBTCTokenPoolConfigChanged)
+ if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
+ it.fail = err
+ return false
+ }
+ it.Event.Raw = log
+ return true
+
+ case err := <-it.sub.Err():
+ it.done = true
+ it.fail = err
+ return it.Next()
+ }
+}
+
+func (it *MockLBTCTokenPoolConfigChangedIterator) Error() error {
+ return it.fail
+}
+
+func (it *MockLBTCTokenPoolConfigChangedIterator) Close() error {
+ it.sub.Unsubscribe()
+ return nil
+}
+
+type MockLBTCTokenPoolConfigChanged struct {
+ Config RateLimiterConfig
+ Raw types.Log
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolFilterer) FilterConfigChanged(opts *bind.FilterOpts) (*MockLBTCTokenPoolConfigChangedIterator, error) {
+
+ logs, sub, err := _MockLBTCTokenPool.contract.FilterLogs(opts, "ConfigChanged")
+ if err != nil {
+ return nil, err
+ }
+ return &MockLBTCTokenPoolConfigChangedIterator{contract: _MockLBTCTokenPool.contract, event: "ConfigChanged", logs: logs, sub: sub}, nil
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolFilterer) WatchConfigChanged(opts *bind.WatchOpts, sink chan<- *MockLBTCTokenPoolConfigChanged) (event.Subscription, error) {
+
+ logs, sub, err := _MockLBTCTokenPool.contract.WatchLogs(opts, "ConfigChanged")
+ if err != nil {
+ return nil, err
+ }
+ return event.NewSubscription(func(quit <-chan struct{}) error {
+ defer sub.Unsubscribe()
+ for {
+ select {
+ case log := <-logs:
+
+ event := new(MockLBTCTokenPoolConfigChanged)
+ if err := _MockLBTCTokenPool.contract.UnpackLog(event, "ConfigChanged", log); err != nil {
+ return err
+ }
+ event.Raw = log
+
+ select {
+ case sink <- event:
+ case err := <-sub.Err():
+ return err
+ case <-quit:
+ return nil
+ }
+ case err := <-sub.Err():
+ return err
+ case <-quit:
+ return nil
+ }
+ }
+ }), nil
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolFilterer) ParseConfigChanged(log types.Log) (*MockLBTCTokenPoolConfigChanged, error) {
+ event := new(MockLBTCTokenPoolConfigChanged)
+ if err := _MockLBTCTokenPool.contract.UnpackLog(event, "ConfigChanged", log); err != nil {
+ return nil, err
+ }
+ event.Raw = log
+ return event, nil
+}
+
+type MockLBTCTokenPoolLockedIterator struct {
+ Event *MockLBTCTokenPoolLocked
+
+ contract *bind.BoundContract
+ event string
+
+ logs chan types.Log
+ sub ethereum.Subscription
+ done bool
+ fail error
+}
+
+func (it *MockLBTCTokenPoolLockedIterator) Next() bool {
+
+ if it.fail != nil {
+ return false
+ }
+
+ if it.done {
+ select {
+ case log := <-it.logs:
+ it.Event = new(MockLBTCTokenPoolLocked)
+ if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
+ it.fail = err
+ return false
+ }
+ it.Event.Raw = log
+ return true
+
+ default:
+ return false
+ }
+ }
+
+ select {
+ case log := <-it.logs:
+ it.Event = new(MockLBTCTokenPoolLocked)
+ if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
+ it.fail = err
+ return false
+ }
+ it.Event.Raw = log
+ return true
+
+ case err := <-it.sub.Err():
+ it.done = true
+ it.fail = err
+ return it.Next()
+ }
+}
+
+func (it *MockLBTCTokenPoolLockedIterator) Error() error {
+ return it.fail
+}
+
+func (it *MockLBTCTokenPoolLockedIterator) Close() error {
+ it.sub.Unsubscribe()
+ return nil
+}
+
+type MockLBTCTokenPoolLocked struct {
+ Sender common.Address
+ Amount *big.Int
+ Raw types.Log
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolFilterer) FilterLocked(opts *bind.FilterOpts, sender []common.Address) (*MockLBTCTokenPoolLockedIterator, error) {
+
+ var senderRule []interface{}
+ for _, senderItem := range sender {
+ senderRule = append(senderRule, senderItem)
+ }
+
+ logs, sub, err := _MockLBTCTokenPool.contract.FilterLogs(opts, "Locked", senderRule)
+ if err != nil {
+ return nil, err
+ }
+ return &MockLBTCTokenPoolLockedIterator{contract: _MockLBTCTokenPool.contract, event: "Locked", logs: logs, sub: sub}, nil
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolFilterer) WatchLocked(opts *bind.WatchOpts, sink chan<- *MockLBTCTokenPoolLocked, sender []common.Address) (event.Subscription, error) {
+
+ var senderRule []interface{}
+ for _, senderItem := range sender {
+ senderRule = append(senderRule, senderItem)
+ }
+
+ logs, sub, err := _MockLBTCTokenPool.contract.WatchLogs(opts, "Locked", 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(MockLBTCTokenPoolLocked)
+ if err := _MockLBTCTokenPool.contract.UnpackLog(event, "Locked", log); err != nil {
+ return err
+ }
+ event.Raw = log
+
+ select {
+ case sink <- event:
+ case err := <-sub.Err():
+ return err
+ case <-quit:
+ return nil
+ }
+ case err := <-sub.Err():
+ return err
+ case <-quit:
+ return nil
+ }
+ }
+ }), nil
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolFilterer) ParseLocked(log types.Log) (*MockLBTCTokenPoolLocked, error) {
+ event := new(MockLBTCTokenPoolLocked)
+ if err := _MockLBTCTokenPool.contract.UnpackLog(event, "Locked", log); err != nil {
+ return nil, err
+ }
+ event.Raw = log
+ return event, nil
+}
+
+type MockLBTCTokenPoolMintedIterator struct {
+ Event *MockLBTCTokenPoolMinted
+
+ contract *bind.BoundContract
+ event string
+
+ logs chan types.Log
+ sub ethereum.Subscription
+ done bool
+ fail error
+}
+
+func (it *MockLBTCTokenPoolMintedIterator) Next() bool {
+
+ if it.fail != nil {
+ return false
+ }
+
+ if it.done {
+ select {
+ case log := <-it.logs:
+ it.Event = new(MockLBTCTokenPoolMinted)
+ if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
+ it.fail = err
+ return false
+ }
+ it.Event.Raw = log
+ return true
+
+ default:
+ return false
+ }
+ }
+
+ select {
+ case log := <-it.logs:
+ it.Event = new(MockLBTCTokenPoolMinted)
+ if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
+ it.fail = err
+ return false
+ }
+ it.Event.Raw = log
+ return true
+
+ case err := <-it.sub.Err():
+ it.done = true
+ it.fail = err
+ return it.Next()
+ }
+}
+
+func (it *MockLBTCTokenPoolMintedIterator) Error() error {
+ return it.fail
+}
+
+func (it *MockLBTCTokenPoolMintedIterator) Close() error {
+ it.sub.Unsubscribe()
+ return nil
+}
+
+type MockLBTCTokenPoolMinted struct {
+ Sender common.Address
+ Recipient common.Address
+ Amount *big.Int
+ Raw types.Log
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolFilterer) FilterMinted(opts *bind.FilterOpts, sender []common.Address, recipient []common.Address) (*MockLBTCTokenPoolMintedIterator, error) {
+
+ var senderRule []interface{}
+ for _, senderItem := range sender {
+ senderRule = append(senderRule, senderItem)
+ }
+ var recipientRule []interface{}
+ for _, recipientItem := range recipient {
+ recipientRule = append(recipientRule, recipientItem)
+ }
+
+ logs, sub, err := _MockLBTCTokenPool.contract.FilterLogs(opts, "Minted", senderRule, recipientRule)
+ if err != nil {
+ return nil, err
+ }
+ return &MockLBTCTokenPoolMintedIterator{contract: _MockLBTCTokenPool.contract, event: "Minted", logs: logs, sub: sub}, nil
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolFilterer) WatchMinted(opts *bind.WatchOpts, sink chan<- *MockLBTCTokenPoolMinted, sender []common.Address, recipient []common.Address) (event.Subscription, error) {
+
+ var senderRule []interface{}
+ for _, senderItem := range sender {
+ senderRule = append(senderRule, senderItem)
+ }
+ var recipientRule []interface{}
+ for _, recipientItem := range recipient {
+ recipientRule = append(recipientRule, recipientItem)
+ }
+
+ logs, sub, err := _MockLBTCTokenPool.contract.WatchLogs(opts, "Minted", senderRule, 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(MockLBTCTokenPoolMinted)
+ if err := _MockLBTCTokenPool.contract.UnpackLog(event, "Minted", log); err != nil {
+ return err
+ }
+ event.Raw = log
+
+ select {
+ case sink <- event:
+ case err := <-sub.Err():
+ return err
+ case <-quit:
+ return nil
+ }
+ case err := <-sub.Err():
+ return err
+ case <-quit:
+ return nil
+ }
+ }
+ }), nil
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolFilterer) ParseMinted(log types.Log) (*MockLBTCTokenPoolMinted, error) {
+ event := new(MockLBTCTokenPoolMinted)
+ if err := _MockLBTCTokenPool.contract.UnpackLog(event, "Minted", log); err != nil {
+ return nil, err
+ }
+ event.Raw = log
+ return event, nil
+}
+
+type MockLBTCTokenPoolOwnershipTransferRequestedIterator struct {
+ Event *MockLBTCTokenPoolOwnershipTransferRequested
+
+ contract *bind.BoundContract
+ event string
+
+ logs chan types.Log
+ sub ethereum.Subscription
+ done bool
+ fail error
+}
+
+func (it *MockLBTCTokenPoolOwnershipTransferRequestedIterator) Next() bool {
+
+ if it.fail != nil {
+ return false
+ }
+
+ if it.done {
+ select {
+ case log := <-it.logs:
+ it.Event = new(MockLBTCTokenPoolOwnershipTransferRequested)
+ if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
+ it.fail = err
+ return false
+ }
+ it.Event.Raw = log
+ return true
+
+ default:
+ return false
+ }
+ }
+
+ select {
+ case log := <-it.logs:
+ it.Event = new(MockLBTCTokenPoolOwnershipTransferRequested)
+ if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
+ it.fail = err
+ return false
+ }
+ it.Event.Raw = log
+ return true
+
+ case err := <-it.sub.Err():
+ it.done = true
+ it.fail = err
+ return it.Next()
+ }
+}
+
+func (it *MockLBTCTokenPoolOwnershipTransferRequestedIterator) Error() error {
+ return it.fail
+}
+
+func (it *MockLBTCTokenPoolOwnershipTransferRequestedIterator) Close() error {
+ it.sub.Unsubscribe()
+ return nil
+}
+
+type MockLBTCTokenPoolOwnershipTransferRequested struct {
+ From common.Address
+ To common.Address
+ Raw types.Log
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolFilterer) FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*MockLBTCTokenPoolOwnershipTransferRequestedIterator, 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 := _MockLBTCTokenPool.contract.FilterLogs(opts, "OwnershipTransferRequested", fromRule, toRule)
+ if err != nil {
+ return nil, err
+ }
+ return &MockLBTCTokenPoolOwnershipTransferRequestedIterator{contract: _MockLBTCTokenPool.contract, event: "OwnershipTransferRequested", logs: logs, sub: sub}, nil
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolFilterer) WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *MockLBTCTokenPoolOwnershipTransferRequested, 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 := _MockLBTCTokenPool.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(MockLBTCTokenPoolOwnershipTransferRequested)
+ if err := _MockLBTCTokenPool.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 (_MockLBTCTokenPool *MockLBTCTokenPoolFilterer) ParseOwnershipTransferRequested(log types.Log) (*MockLBTCTokenPoolOwnershipTransferRequested, error) {
+ event := new(MockLBTCTokenPoolOwnershipTransferRequested)
+ if err := _MockLBTCTokenPool.contract.UnpackLog(event, "OwnershipTransferRequested", log); err != nil {
+ return nil, err
+ }
+ event.Raw = log
+ return event, nil
+}
+
+type MockLBTCTokenPoolOwnershipTransferredIterator struct {
+ Event *MockLBTCTokenPoolOwnershipTransferred
+
+ contract *bind.BoundContract
+ event string
+
+ logs chan types.Log
+ sub ethereum.Subscription
+ done bool
+ fail error
+}
+
+func (it *MockLBTCTokenPoolOwnershipTransferredIterator) Next() bool {
+
+ if it.fail != nil {
+ return false
+ }
+
+ if it.done {
+ select {
+ case log := <-it.logs:
+ it.Event = new(MockLBTCTokenPoolOwnershipTransferred)
+ if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
+ it.fail = err
+ return false
+ }
+ it.Event.Raw = log
+ return true
+
+ default:
+ return false
+ }
+ }
+
+ select {
+ case log := <-it.logs:
+ it.Event = new(MockLBTCTokenPoolOwnershipTransferred)
+ if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
+ it.fail = err
+ return false
+ }
+ it.Event.Raw = log
+ return true
+
+ case err := <-it.sub.Err():
+ it.done = true
+ it.fail = err
+ return it.Next()
+ }
+}
+
+func (it *MockLBTCTokenPoolOwnershipTransferredIterator) Error() error {
+ return it.fail
+}
+
+func (it *MockLBTCTokenPoolOwnershipTransferredIterator) Close() error {
+ it.sub.Unsubscribe()
+ return nil
+}
+
+type MockLBTCTokenPoolOwnershipTransferred struct {
+ From common.Address
+ To common.Address
+ Raw types.Log
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolFilterer) FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*MockLBTCTokenPoolOwnershipTransferredIterator, 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 := _MockLBTCTokenPool.contract.FilterLogs(opts, "OwnershipTransferred", fromRule, toRule)
+ if err != nil {
+ return nil, err
+ }
+ return &MockLBTCTokenPoolOwnershipTransferredIterator{contract: _MockLBTCTokenPool.contract, event: "OwnershipTransferred", logs: logs, sub: sub}, nil
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolFilterer) WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *MockLBTCTokenPoolOwnershipTransferred, 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 := _MockLBTCTokenPool.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(MockLBTCTokenPoolOwnershipTransferred)
+ if err := _MockLBTCTokenPool.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 (_MockLBTCTokenPool *MockLBTCTokenPoolFilterer) ParseOwnershipTransferred(log types.Log) (*MockLBTCTokenPoolOwnershipTransferred, error) {
+ event := new(MockLBTCTokenPoolOwnershipTransferred)
+ if err := _MockLBTCTokenPool.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil {
+ return nil, err
+ }
+ event.Raw = log
+ return event, nil
+}
+
+type MockLBTCTokenPoolRateLimitAdminSetIterator struct {
+ Event *MockLBTCTokenPoolRateLimitAdminSet
+
+ contract *bind.BoundContract
+ event string
+
+ logs chan types.Log
+ sub ethereum.Subscription
+ done bool
+ fail error
+}
+
+func (it *MockLBTCTokenPoolRateLimitAdminSetIterator) Next() bool {
+
+ if it.fail != nil {
+ return false
+ }
+
+ if it.done {
+ select {
+ case log := <-it.logs:
+ it.Event = new(MockLBTCTokenPoolRateLimitAdminSet)
+ if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
+ it.fail = err
+ return false
+ }
+ it.Event.Raw = log
+ return true
+
+ default:
+ return false
+ }
+ }
+
+ select {
+ case log := <-it.logs:
+ it.Event = new(MockLBTCTokenPoolRateLimitAdminSet)
+ if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
+ it.fail = err
+ return false
+ }
+ it.Event.Raw = log
+ return true
+
+ case err := <-it.sub.Err():
+ it.done = true
+ it.fail = err
+ return it.Next()
+ }
+}
+
+func (it *MockLBTCTokenPoolRateLimitAdminSetIterator) Error() error {
+ return it.fail
+}
+
+func (it *MockLBTCTokenPoolRateLimitAdminSetIterator) Close() error {
+ it.sub.Unsubscribe()
+ return nil
+}
+
+type MockLBTCTokenPoolRateLimitAdminSet struct {
+ RateLimitAdmin common.Address
+ Raw types.Log
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolFilterer) FilterRateLimitAdminSet(opts *bind.FilterOpts) (*MockLBTCTokenPoolRateLimitAdminSetIterator, error) {
+
+ logs, sub, err := _MockLBTCTokenPool.contract.FilterLogs(opts, "RateLimitAdminSet")
+ if err != nil {
+ return nil, err
+ }
+ return &MockLBTCTokenPoolRateLimitAdminSetIterator{contract: _MockLBTCTokenPool.contract, event: "RateLimitAdminSet", logs: logs, sub: sub}, nil
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolFilterer) WatchRateLimitAdminSet(opts *bind.WatchOpts, sink chan<- *MockLBTCTokenPoolRateLimitAdminSet) (event.Subscription, error) {
+
+ logs, sub, err := _MockLBTCTokenPool.contract.WatchLogs(opts, "RateLimitAdminSet")
+ if err != nil {
+ return nil, err
+ }
+ return event.NewSubscription(func(quit <-chan struct{}) error {
+ defer sub.Unsubscribe()
+ for {
+ select {
+ case log := <-logs:
+
+ event := new(MockLBTCTokenPoolRateLimitAdminSet)
+ if err := _MockLBTCTokenPool.contract.UnpackLog(event, "RateLimitAdminSet", log); err != nil {
+ return err
+ }
+ event.Raw = log
+
+ select {
+ case sink <- event:
+ case err := <-sub.Err():
+ return err
+ case <-quit:
+ return nil
+ }
+ case err := <-sub.Err():
+ return err
+ case <-quit:
+ return nil
+ }
+ }
+ }), nil
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolFilterer) ParseRateLimitAdminSet(log types.Log) (*MockLBTCTokenPoolRateLimitAdminSet, error) {
+ event := new(MockLBTCTokenPoolRateLimitAdminSet)
+ if err := _MockLBTCTokenPool.contract.UnpackLog(event, "RateLimitAdminSet", log); err != nil {
+ return nil, err
+ }
+ event.Raw = log
+ return event, nil
+}
+
+type MockLBTCTokenPoolReleasedIterator struct {
+ Event *MockLBTCTokenPoolReleased
+
+ contract *bind.BoundContract
+ event string
+
+ logs chan types.Log
+ sub ethereum.Subscription
+ done bool
+ fail error
+}
+
+func (it *MockLBTCTokenPoolReleasedIterator) Next() bool {
+
+ if it.fail != nil {
+ return false
+ }
+
+ if it.done {
+ select {
+ case log := <-it.logs:
+ it.Event = new(MockLBTCTokenPoolReleased)
+ if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
+ it.fail = err
+ return false
+ }
+ it.Event.Raw = log
+ return true
+
+ default:
+ return false
+ }
+ }
+
+ select {
+ case log := <-it.logs:
+ it.Event = new(MockLBTCTokenPoolReleased)
+ if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
+ it.fail = err
+ return false
+ }
+ it.Event.Raw = log
+ return true
+
+ case err := <-it.sub.Err():
+ it.done = true
+ it.fail = err
+ return it.Next()
+ }
+}
+
+func (it *MockLBTCTokenPoolReleasedIterator) Error() error {
+ return it.fail
+}
+
+func (it *MockLBTCTokenPoolReleasedIterator) Close() error {
+ it.sub.Unsubscribe()
+ return nil
+}
+
+type MockLBTCTokenPoolReleased struct {
+ Sender common.Address
+ Recipient common.Address
+ Amount *big.Int
+ Raw types.Log
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolFilterer) FilterReleased(opts *bind.FilterOpts, sender []common.Address, recipient []common.Address) (*MockLBTCTokenPoolReleasedIterator, error) {
+
+ var senderRule []interface{}
+ for _, senderItem := range sender {
+ senderRule = append(senderRule, senderItem)
+ }
+ var recipientRule []interface{}
+ for _, recipientItem := range recipient {
+ recipientRule = append(recipientRule, recipientItem)
+ }
+
+ logs, sub, err := _MockLBTCTokenPool.contract.FilterLogs(opts, "Released", senderRule, recipientRule)
+ if err != nil {
+ return nil, err
+ }
+ return &MockLBTCTokenPoolReleasedIterator{contract: _MockLBTCTokenPool.contract, event: "Released", logs: logs, sub: sub}, nil
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolFilterer) WatchReleased(opts *bind.WatchOpts, sink chan<- *MockLBTCTokenPoolReleased, sender []common.Address, recipient []common.Address) (event.Subscription, error) {
+
+ var senderRule []interface{}
+ for _, senderItem := range sender {
+ senderRule = append(senderRule, senderItem)
+ }
+ var recipientRule []interface{}
+ for _, recipientItem := range recipient {
+ recipientRule = append(recipientRule, recipientItem)
+ }
+
+ logs, sub, err := _MockLBTCTokenPool.contract.WatchLogs(opts, "Released", senderRule, 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(MockLBTCTokenPoolReleased)
+ if err := _MockLBTCTokenPool.contract.UnpackLog(event, "Released", log); err != nil {
+ return err
+ }
+ event.Raw = log
+
+ select {
+ case sink <- event:
+ case err := <-sub.Err():
+ return err
+ case <-quit:
+ return nil
+ }
+ case err := <-sub.Err():
+ return err
+ case <-quit:
+ return nil
+ }
+ }
+ }), nil
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolFilterer) ParseReleased(log types.Log) (*MockLBTCTokenPoolReleased, error) {
+ event := new(MockLBTCTokenPoolReleased)
+ if err := _MockLBTCTokenPool.contract.UnpackLog(event, "Released", log); err != nil {
+ return nil, err
+ }
+ event.Raw = log
+ return event, nil
+}
+
+type MockLBTCTokenPoolRemotePoolAddedIterator struct {
+ Event *MockLBTCTokenPoolRemotePoolAdded
+
+ contract *bind.BoundContract
+ event string
+
+ logs chan types.Log
+ sub ethereum.Subscription
+ done bool
+ fail error
+}
+
+func (it *MockLBTCTokenPoolRemotePoolAddedIterator) Next() bool {
+
+ if it.fail != nil {
+ return false
+ }
+
+ if it.done {
+ select {
+ case log := <-it.logs:
+ it.Event = new(MockLBTCTokenPoolRemotePoolAdded)
+ if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
+ it.fail = err
+ return false
+ }
+ it.Event.Raw = log
+ return true
+
+ default:
+ return false
+ }
+ }
+
+ select {
+ case log := <-it.logs:
+ it.Event = new(MockLBTCTokenPoolRemotePoolAdded)
+ if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
+ it.fail = err
+ return false
+ }
+ it.Event.Raw = log
+ return true
+
+ case err := <-it.sub.Err():
+ it.done = true
+ it.fail = err
+ return it.Next()
+ }
+}
+
+func (it *MockLBTCTokenPoolRemotePoolAddedIterator) Error() error {
+ return it.fail
+}
+
+func (it *MockLBTCTokenPoolRemotePoolAddedIterator) Close() error {
+ it.sub.Unsubscribe()
+ return nil
+}
+
+type MockLBTCTokenPoolRemotePoolAdded struct {
+ RemoteChainSelector uint64
+ RemotePoolAddress []byte
+ Raw types.Log
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolFilterer) FilterRemotePoolAdded(opts *bind.FilterOpts, remoteChainSelector []uint64) (*MockLBTCTokenPoolRemotePoolAddedIterator, error) {
+
+ var remoteChainSelectorRule []interface{}
+ for _, remoteChainSelectorItem := range remoteChainSelector {
+ remoteChainSelectorRule = append(remoteChainSelectorRule, remoteChainSelectorItem)
+ }
+
+ logs, sub, err := _MockLBTCTokenPool.contract.FilterLogs(opts, "RemotePoolAdded", remoteChainSelectorRule)
+ if err != nil {
+ return nil, err
+ }
+ return &MockLBTCTokenPoolRemotePoolAddedIterator{contract: _MockLBTCTokenPool.contract, event: "RemotePoolAdded", logs: logs, sub: sub}, nil
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolFilterer) WatchRemotePoolAdded(opts *bind.WatchOpts, sink chan<- *MockLBTCTokenPoolRemotePoolAdded, remoteChainSelector []uint64) (event.Subscription, error) {
+
+ var remoteChainSelectorRule []interface{}
+ for _, remoteChainSelectorItem := range remoteChainSelector {
+ remoteChainSelectorRule = append(remoteChainSelectorRule, remoteChainSelectorItem)
+ }
+
+ logs, sub, err := _MockLBTCTokenPool.contract.WatchLogs(opts, "RemotePoolAdded", remoteChainSelectorRule)
+ if err != nil {
+ return nil, err
+ }
+ return event.NewSubscription(func(quit <-chan struct{}) error {
+ defer sub.Unsubscribe()
+ for {
+ select {
+ case log := <-logs:
+
+ event := new(MockLBTCTokenPoolRemotePoolAdded)
+ if err := _MockLBTCTokenPool.contract.UnpackLog(event, "RemotePoolAdded", log); err != nil {
+ return err
+ }
+ event.Raw = log
+
+ select {
+ case sink <- event:
+ case err := <-sub.Err():
+ return err
+ case <-quit:
+ return nil
+ }
+ case err := <-sub.Err():
+ return err
+ case <-quit:
+ return nil
+ }
+ }
+ }), nil
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolFilterer) ParseRemotePoolAdded(log types.Log) (*MockLBTCTokenPoolRemotePoolAdded, error) {
+ event := new(MockLBTCTokenPoolRemotePoolAdded)
+ if err := _MockLBTCTokenPool.contract.UnpackLog(event, "RemotePoolAdded", log); err != nil {
+ return nil, err
+ }
+ event.Raw = log
+ return event, nil
+}
+
+type MockLBTCTokenPoolRemotePoolRemovedIterator struct {
+ Event *MockLBTCTokenPoolRemotePoolRemoved
+
+ contract *bind.BoundContract
+ event string
+
+ logs chan types.Log
+ sub ethereum.Subscription
+ done bool
+ fail error
+}
+
+func (it *MockLBTCTokenPoolRemotePoolRemovedIterator) Next() bool {
+
+ if it.fail != nil {
+ return false
+ }
+
+ if it.done {
+ select {
+ case log := <-it.logs:
+ it.Event = new(MockLBTCTokenPoolRemotePoolRemoved)
+ if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
+ it.fail = err
+ return false
+ }
+ it.Event.Raw = log
+ return true
+
+ default:
+ return false
+ }
+ }
+
+ select {
+ case log := <-it.logs:
+ it.Event = new(MockLBTCTokenPoolRemotePoolRemoved)
+ if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
+ it.fail = err
+ return false
+ }
+ it.Event.Raw = log
+ return true
+
+ case err := <-it.sub.Err():
+ it.done = true
+ it.fail = err
+ return it.Next()
+ }
+}
+
+func (it *MockLBTCTokenPoolRemotePoolRemovedIterator) Error() error {
+ return it.fail
+}
+
+func (it *MockLBTCTokenPoolRemotePoolRemovedIterator) Close() error {
+ it.sub.Unsubscribe()
+ return nil
+}
+
+type MockLBTCTokenPoolRemotePoolRemoved struct {
+ RemoteChainSelector uint64
+ RemotePoolAddress []byte
+ Raw types.Log
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolFilterer) FilterRemotePoolRemoved(opts *bind.FilterOpts, remoteChainSelector []uint64) (*MockLBTCTokenPoolRemotePoolRemovedIterator, error) {
+
+ var remoteChainSelectorRule []interface{}
+ for _, remoteChainSelectorItem := range remoteChainSelector {
+ remoteChainSelectorRule = append(remoteChainSelectorRule, remoteChainSelectorItem)
+ }
+
+ logs, sub, err := _MockLBTCTokenPool.contract.FilterLogs(opts, "RemotePoolRemoved", remoteChainSelectorRule)
+ if err != nil {
+ return nil, err
+ }
+ return &MockLBTCTokenPoolRemotePoolRemovedIterator{contract: _MockLBTCTokenPool.contract, event: "RemotePoolRemoved", logs: logs, sub: sub}, nil
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolFilterer) WatchRemotePoolRemoved(opts *bind.WatchOpts, sink chan<- *MockLBTCTokenPoolRemotePoolRemoved, remoteChainSelector []uint64) (event.Subscription, error) {
+
+ var remoteChainSelectorRule []interface{}
+ for _, remoteChainSelectorItem := range remoteChainSelector {
+ remoteChainSelectorRule = append(remoteChainSelectorRule, remoteChainSelectorItem)
+ }
+
+ logs, sub, err := _MockLBTCTokenPool.contract.WatchLogs(opts, "RemotePoolRemoved", remoteChainSelectorRule)
+ if err != nil {
+ return nil, err
+ }
+ return event.NewSubscription(func(quit <-chan struct{}) error {
+ defer sub.Unsubscribe()
+ for {
+ select {
+ case log := <-logs:
+
+ event := new(MockLBTCTokenPoolRemotePoolRemoved)
+ if err := _MockLBTCTokenPool.contract.UnpackLog(event, "RemotePoolRemoved", log); err != nil {
+ return err
+ }
+ event.Raw = log
+
+ select {
+ case sink <- event:
+ case err := <-sub.Err():
+ return err
+ case <-quit:
+ return nil
+ }
+ case err := <-sub.Err():
+ return err
+ case <-quit:
+ return nil
+ }
+ }
+ }), nil
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolFilterer) ParseRemotePoolRemoved(log types.Log) (*MockLBTCTokenPoolRemotePoolRemoved, error) {
+ event := new(MockLBTCTokenPoolRemotePoolRemoved)
+ if err := _MockLBTCTokenPool.contract.UnpackLog(event, "RemotePoolRemoved", log); err != nil {
+ return nil, err
+ }
+ event.Raw = log
+ return event, nil
+}
+
+type MockLBTCTokenPoolRouterUpdatedIterator struct {
+ Event *MockLBTCTokenPoolRouterUpdated
+
+ contract *bind.BoundContract
+ event string
+
+ logs chan types.Log
+ sub ethereum.Subscription
+ done bool
+ fail error
+}
+
+func (it *MockLBTCTokenPoolRouterUpdatedIterator) Next() bool {
+
+ if it.fail != nil {
+ return false
+ }
+
+ if it.done {
+ select {
+ case log := <-it.logs:
+ it.Event = new(MockLBTCTokenPoolRouterUpdated)
+ if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
+ it.fail = err
+ return false
+ }
+ it.Event.Raw = log
+ return true
+
+ default:
+ return false
+ }
+ }
+
+ select {
+ case log := <-it.logs:
+ it.Event = new(MockLBTCTokenPoolRouterUpdated)
+ if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
+ it.fail = err
+ return false
+ }
+ it.Event.Raw = log
+ return true
+
+ case err := <-it.sub.Err():
+ it.done = true
+ it.fail = err
+ return it.Next()
+ }
+}
+
+func (it *MockLBTCTokenPoolRouterUpdatedIterator) Error() error {
+ return it.fail
+}
+
+func (it *MockLBTCTokenPoolRouterUpdatedIterator) Close() error {
+ it.sub.Unsubscribe()
+ return nil
+}
+
+type MockLBTCTokenPoolRouterUpdated struct {
+ OldRouter common.Address
+ NewRouter common.Address
+ Raw types.Log
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolFilterer) FilterRouterUpdated(opts *bind.FilterOpts) (*MockLBTCTokenPoolRouterUpdatedIterator, error) {
+
+ logs, sub, err := _MockLBTCTokenPool.contract.FilterLogs(opts, "RouterUpdated")
+ if err != nil {
+ return nil, err
+ }
+ return &MockLBTCTokenPoolRouterUpdatedIterator{contract: _MockLBTCTokenPool.contract, event: "RouterUpdated", logs: logs, sub: sub}, nil
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolFilterer) WatchRouterUpdated(opts *bind.WatchOpts, sink chan<- *MockLBTCTokenPoolRouterUpdated) (event.Subscription, error) {
+
+ logs, sub, err := _MockLBTCTokenPool.contract.WatchLogs(opts, "RouterUpdated")
+ if err != nil {
+ return nil, err
+ }
+ return event.NewSubscription(func(quit <-chan struct{}) error {
+ defer sub.Unsubscribe()
+ for {
+ select {
+ case log := <-logs:
+
+ event := new(MockLBTCTokenPoolRouterUpdated)
+ if err := _MockLBTCTokenPool.contract.UnpackLog(event, "RouterUpdated", log); err != nil {
+ return err
+ }
+ event.Raw = log
+
+ select {
+ case sink <- event:
+ case err := <-sub.Err():
+ return err
+ case <-quit:
+ return nil
+ }
+ case err := <-sub.Err():
+ return err
+ case <-quit:
+ return nil
+ }
+ }
+ }), nil
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPoolFilterer) ParseRouterUpdated(log types.Log) (*MockLBTCTokenPoolRouterUpdated, error) {
+ event := new(MockLBTCTokenPoolRouterUpdated)
+ if err := _MockLBTCTokenPool.contract.UnpackLog(event, "RouterUpdated", log); err != nil {
+ return nil, err
+ }
+ event.Raw = log
+ return event, nil
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPool) ParseLog(log types.Log) (generated.AbigenLog, error) {
+ switch log.Topics[0] {
+ case _MockLBTCTokenPool.abi.Events["AllowListAdd"].ID:
+ return _MockLBTCTokenPool.ParseAllowListAdd(log)
+ case _MockLBTCTokenPool.abi.Events["AllowListRemove"].ID:
+ return _MockLBTCTokenPool.ParseAllowListRemove(log)
+ case _MockLBTCTokenPool.abi.Events["Burned"].ID:
+ return _MockLBTCTokenPool.ParseBurned(log)
+ case _MockLBTCTokenPool.abi.Events["ChainAdded"].ID:
+ return _MockLBTCTokenPool.ParseChainAdded(log)
+ case _MockLBTCTokenPool.abi.Events["ChainConfigured"].ID:
+ return _MockLBTCTokenPool.ParseChainConfigured(log)
+ case _MockLBTCTokenPool.abi.Events["ChainRemoved"].ID:
+ return _MockLBTCTokenPool.ParseChainRemoved(log)
+ case _MockLBTCTokenPool.abi.Events["ConfigChanged"].ID:
+ return _MockLBTCTokenPool.ParseConfigChanged(log)
+ case _MockLBTCTokenPool.abi.Events["Locked"].ID:
+ return _MockLBTCTokenPool.ParseLocked(log)
+ case _MockLBTCTokenPool.abi.Events["Minted"].ID:
+ return _MockLBTCTokenPool.ParseMinted(log)
+ case _MockLBTCTokenPool.abi.Events["OwnershipTransferRequested"].ID:
+ return _MockLBTCTokenPool.ParseOwnershipTransferRequested(log)
+ case _MockLBTCTokenPool.abi.Events["OwnershipTransferred"].ID:
+ return _MockLBTCTokenPool.ParseOwnershipTransferred(log)
+ case _MockLBTCTokenPool.abi.Events["RateLimitAdminSet"].ID:
+ return _MockLBTCTokenPool.ParseRateLimitAdminSet(log)
+ case _MockLBTCTokenPool.abi.Events["Released"].ID:
+ return _MockLBTCTokenPool.ParseReleased(log)
+ case _MockLBTCTokenPool.abi.Events["RemotePoolAdded"].ID:
+ return _MockLBTCTokenPool.ParseRemotePoolAdded(log)
+ case _MockLBTCTokenPool.abi.Events["RemotePoolRemoved"].ID:
+ return _MockLBTCTokenPool.ParseRemotePoolRemoved(log)
+ case _MockLBTCTokenPool.abi.Events["RouterUpdated"].ID:
+ return _MockLBTCTokenPool.ParseRouterUpdated(log)
+
+ default:
+ return nil, fmt.Errorf("abigen wrapper received unknown log topic: %v", log.Topics[0])
+ }
+}
+
+func (MockLBTCTokenPoolAllowListAdd) Topic() common.Hash {
+ return common.HexToHash("0x2640d4d76caf8bf478aabfa982fa4e1c4eb71a37f93cd15e80dbc657911546d8")
+}
+
+func (MockLBTCTokenPoolAllowListRemove) Topic() common.Hash {
+ return common.HexToHash("0x800671136ab6cfee9fbe5ed1fb7ca417811aca3cf864800d127b927adedf7566")
+}
+
+func (MockLBTCTokenPoolBurned) Topic() common.Hash {
+ return common.HexToHash("0x696de425f79f4a40bc6d2122ca50507f0efbeabbff86a84871b7196ab8ea8df7")
+}
+
+func (MockLBTCTokenPoolChainAdded) Topic() common.Hash {
+ return common.HexToHash("0x8d340f17e19058004c20453540862a9c62778504476f6756755cb33bcd6c38c2")
+}
+
+func (MockLBTCTokenPoolChainConfigured) Topic() common.Hash {
+ return common.HexToHash("0x0350d63aa5f270e01729d00d627eeb8f3429772b1818c016c66a588a864f912b")
+}
+
+func (MockLBTCTokenPoolChainRemoved) Topic() common.Hash {
+ return common.HexToHash("0x5204aec90a3c794d8e90fded8b46ae9c7c552803e7e832e0c1d358396d859916")
+}
+
+func (MockLBTCTokenPoolConfigChanged) Topic() common.Hash {
+ return common.HexToHash("0x9ea3374b67bf275e6bb9c8ae68f9cae023e1c528b4b27e092f0bb209d3531c19")
+}
+
+func (MockLBTCTokenPoolLocked) Topic() common.Hash {
+ return common.HexToHash("0x9f1ec8c880f76798e7b793325d625e9b60e4082a553c98f42b6cda368dd60008")
+}
+
+func (MockLBTCTokenPoolMinted) Topic() common.Hash {
+ return common.HexToHash("0x9d228d69b5fdb8d273a2336f8fb8612d039631024ea9bf09c424a9503aa078f0")
+}
+
+func (MockLBTCTokenPoolOwnershipTransferRequested) Topic() common.Hash {
+ return common.HexToHash("0xed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae1278")
+}
+
+func (MockLBTCTokenPoolOwnershipTransferred) Topic() common.Hash {
+ return common.HexToHash("0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0")
+}
+
+func (MockLBTCTokenPoolRateLimitAdminSet) Topic() common.Hash {
+ return common.HexToHash("0x44676b5284b809a22248eba0da87391d79098be38bb03154be88a58bf4d09174")
+}
+
+func (MockLBTCTokenPoolReleased) Topic() common.Hash {
+ return common.HexToHash("0x2d87480f50083e2b2759522a8fdda59802650a8055e609a7772cf70c07748f52")
+}
+
+func (MockLBTCTokenPoolRemotePoolAdded) Topic() common.Hash {
+ return common.HexToHash("0x7d628c9a1796743d365ab521a8b2a4686e419b3269919dc9145ea2ce853b54ea")
+}
+
+func (MockLBTCTokenPoolRemotePoolRemoved) Topic() common.Hash {
+ return common.HexToHash("0x52d00ee4d9bd51b40168f2afc5848837288ce258784ad914278791464b3f4d76")
+}
+
+func (MockLBTCTokenPoolRouterUpdated) Topic() common.Hash {
+ return common.HexToHash("0x02dc5c233404867c793b749c6d644beb2277536d18a7e7974d3f238e4c6f1684")
+}
+
+func (_MockLBTCTokenPool *MockLBTCTokenPool) Address() common.Address {
+ return _MockLBTCTokenPool.address
+}
+
+type MockLBTCTokenPoolInterface interface {
+ GetAllowList(opts *bind.CallOpts) ([]common.Address, error)
+
+ GetAllowListEnabled(opts *bind.CallOpts) (bool, error)
+
+ GetCurrentInboundRateLimiterState(opts *bind.CallOpts, remoteChainSelector uint64) (RateLimiterTokenBucket, error)
+
+ GetCurrentOutboundRateLimiterState(opts *bind.CallOpts, remoteChainSelector uint64) (RateLimiterTokenBucket, error)
+
+ GetRateLimitAdmin(opts *bind.CallOpts) (common.Address, error)
+
+ GetRemotePools(opts *bind.CallOpts, remoteChainSelector uint64) ([][]byte, error)
+
+ GetRemoteToken(opts *bind.CallOpts, remoteChainSelector uint64) ([]byte, error)
+
+ GetRmnProxy(opts *bind.CallOpts) (common.Address, error)
+
+ GetRouter(opts *bind.CallOpts) (common.Address, error)
+
+ GetSupportedChains(opts *bind.CallOpts) ([]uint64, error)
+
+ GetToken(opts *bind.CallOpts) (common.Address, error)
+
+ GetTokenDecimals(opts *bind.CallOpts) (uint8, error)
+
+ IDestPoolData(opts *bind.CallOpts) ([]byte, error)
+
+ IsRemotePool(opts *bind.CallOpts, remoteChainSelector uint64, remotePoolAddress []byte) (bool, error)
+
+ IsSupportedChain(opts *bind.CallOpts, remoteChainSelector uint64) (bool, error)
+
+ IsSupportedToken(opts *bind.CallOpts, token common.Address) (bool, error)
+
+ Owner(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)
+
+ AddRemotePool(opts *bind.TransactOpts, remoteChainSelector uint64, remotePoolAddress []byte) (*types.Transaction, error)
+
+ ApplyAllowListUpdates(opts *bind.TransactOpts, removes []common.Address, adds []common.Address) (*types.Transaction, error)
+
+ ApplyChainUpdates(opts *bind.TransactOpts, remoteChainSelectorsToRemove []uint64, chainsToAdd []TokenPoolChainUpdate) (*types.Transaction, error)
+
+ LockOrBurn(opts *bind.TransactOpts, lockOrBurnIn PoolLockOrBurnInV1) (*types.Transaction, error)
+
+ ReleaseOrMint(opts *bind.TransactOpts, releaseOrMintIn PoolReleaseOrMintInV1) (*types.Transaction, error)
+
+ RemoveRemotePool(opts *bind.TransactOpts, remoteChainSelector uint64, remotePoolAddress []byte) (*types.Transaction, error)
+
+ SetChainRateLimiterConfig(opts *bind.TransactOpts, remoteChainSelector uint64, outboundConfig RateLimiterConfig, inboundConfig RateLimiterConfig) (*types.Transaction, error)
+
+ SetRateLimitAdmin(opts *bind.TransactOpts, rateLimitAdmin common.Address) (*types.Transaction, error)
+
+ SetRouter(opts *bind.TransactOpts, newRouter common.Address) (*types.Transaction, error)
+
+ TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error)
+
+ FilterAllowListAdd(opts *bind.FilterOpts) (*MockLBTCTokenPoolAllowListAddIterator, error)
+
+ WatchAllowListAdd(opts *bind.WatchOpts, sink chan<- *MockLBTCTokenPoolAllowListAdd) (event.Subscription, error)
+
+ ParseAllowListAdd(log types.Log) (*MockLBTCTokenPoolAllowListAdd, error)
+
+ FilterAllowListRemove(opts *bind.FilterOpts) (*MockLBTCTokenPoolAllowListRemoveIterator, error)
+
+ WatchAllowListRemove(opts *bind.WatchOpts, sink chan<- *MockLBTCTokenPoolAllowListRemove) (event.Subscription, error)
+
+ ParseAllowListRemove(log types.Log) (*MockLBTCTokenPoolAllowListRemove, error)
+
+ FilterBurned(opts *bind.FilterOpts, sender []common.Address) (*MockLBTCTokenPoolBurnedIterator, error)
+
+ WatchBurned(opts *bind.WatchOpts, sink chan<- *MockLBTCTokenPoolBurned, sender []common.Address) (event.Subscription, error)
+
+ ParseBurned(log types.Log) (*MockLBTCTokenPoolBurned, error)
+
+ FilterChainAdded(opts *bind.FilterOpts) (*MockLBTCTokenPoolChainAddedIterator, error)
+
+ WatchChainAdded(opts *bind.WatchOpts, sink chan<- *MockLBTCTokenPoolChainAdded) (event.Subscription, error)
+
+ ParseChainAdded(log types.Log) (*MockLBTCTokenPoolChainAdded, error)
+
+ FilterChainConfigured(opts *bind.FilterOpts) (*MockLBTCTokenPoolChainConfiguredIterator, error)
+
+ WatchChainConfigured(opts *bind.WatchOpts, sink chan<- *MockLBTCTokenPoolChainConfigured) (event.Subscription, error)
+
+ ParseChainConfigured(log types.Log) (*MockLBTCTokenPoolChainConfigured, error)
+
+ FilterChainRemoved(opts *bind.FilterOpts) (*MockLBTCTokenPoolChainRemovedIterator, error)
+
+ WatchChainRemoved(opts *bind.WatchOpts, sink chan<- *MockLBTCTokenPoolChainRemoved) (event.Subscription, error)
+
+ ParseChainRemoved(log types.Log) (*MockLBTCTokenPoolChainRemoved, error)
+
+ FilterConfigChanged(opts *bind.FilterOpts) (*MockLBTCTokenPoolConfigChangedIterator, error)
+
+ WatchConfigChanged(opts *bind.WatchOpts, sink chan<- *MockLBTCTokenPoolConfigChanged) (event.Subscription, error)
+
+ ParseConfigChanged(log types.Log) (*MockLBTCTokenPoolConfigChanged, error)
+
+ FilterLocked(opts *bind.FilterOpts, sender []common.Address) (*MockLBTCTokenPoolLockedIterator, error)
+
+ WatchLocked(opts *bind.WatchOpts, sink chan<- *MockLBTCTokenPoolLocked, sender []common.Address) (event.Subscription, error)
+
+ ParseLocked(log types.Log) (*MockLBTCTokenPoolLocked, error)
+
+ FilterMinted(opts *bind.FilterOpts, sender []common.Address, recipient []common.Address) (*MockLBTCTokenPoolMintedIterator, error)
+
+ WatchMinted(opts *bind.WatchOpts, sink chan<- *MockLBTCTokenPoolMinted, sender []common.Address, recipient []common.Address) (event.Subscription, error)
+
+ ParseMinted(log types.Log) (*MockLBTCTokenPoolMinted, error)
+
+ FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*MockLBTCTokenPoolOwnershipTransferRequestedIterator, error)
+
+ WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *MockLBTCTokenPoolOwnershipTransferRequested, from []common.Address, to []common.Address) (event.Subscription, error)
+
+ ParseOwnershipTransferRequested(log types.Log) (*MockLBTCTokenPoolOwnershipTransferRequested, error)
+
+ FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*MockLBTCTokenPoolOwnershipTransferredIterator, error)
+
+ WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *MockLBTCTokenPoolOwnershipTransferred, from []common.Address, to []common.Address) (event.Subscription, error)
+
+ ParseOwnershipTransferred(log types.Log) (*MockLBTCTokenPoolOwnershipTransferred, error)
+
+ FilterRateLimitAdminSet(opts *bind.FilterOpts) (*MockLBTCTokenPoolRateLimitAdminSetIterator, error)
+
+ WatchRateLimitAdminSet(opts *bind.WatchOpts, sink chan<- *MockLBTCTokenPoolRateLimitAdminSet) (event.Subscription, error)
+
+ ParseRateLimitAdminSet(log types.Log) (*MockLBTCTokenPoolRateLimitAdminSet, error)
+
+ FilterReleased(opts *bind.FilterOpts, sender []common.Address, recipient []common.Address) (*MockLBTCTokenPoolReleasedIterator, error)
+
+ WatchReleased(opts *bind.WatchOpts, sink chan<- *MockLBTCTokenPoolReleased, sender []common.Address, recipient []common.Address) (event.Subscription, error)
+
+ ParseReleased(log types.Log) (*MockLBTCTokenPoolReleased, error)
+
+ FilterRemotePoolAdded(opts *bind.FilterOpts, remoteChainSelector []uint64) (*MockLBTCTokenPoolRemotePoolAddedIterator, error)
+
+ WatchRemotePoolAdded(opts *bind.WatchOpts, sink chan<- *MockLBTCTokenPoolRemotePoolAdded, remoteChainSelector []uint64) (event.Subscription, error)
+
+ ParseRemotePoolAdded(log types.Log) (*MockLBTCTokenPoolRemotePoolAdded, error)
+
+ FilterRemotePoolRemoved(opts *bind.FilterOpts, remoteChainSelector []uint64) (*MockLBTCTokenPoolRemotePoolRemovedIterator, error)
+
+ WatchRemotePoolRemoved(opts *bind.WatchOpts, sink chan<- *MockLBTCTokenPoolRemotePoolRemoved, remoteChainSelector []uint64) (event.Subscription, error)
+
+ ParseRemotePoolRemoved(log types.Log) (*MockLBTCTokenPoolRemotePoolRemoved, error)
+
+ FilterRouterUpdated(opts *bind.FilterOpts) (*MockLBTCTokenPoolRouterUpdatedIterator, error)
+
+ WatchRouterUpdated(opts *bind.WatchOpts, sink chan<- *MockLBTCTokenPoolRouterUpdated) (event.Subscription, error)
+
+ ParseRouterUpdated(log types.Log) (*MockLBTCTokenPoolRouterUpdated, error)
+
+ ParseLog(log types.Log) (generated.AbigenLog, error)
+
+ Address() common.Address
+}
diff --git a/core/services/ocr2/delegate.go b/core/services/ocr2/delegate.go
index 13094fefb1a..f6b08cf46a3 100644
--- a/core/services/ocr2/delegate.go
+++ b/core/services/ocr2/delegate.go
@@ -7,6 +7,7 @@ import (
"fmt"
"log"
"strconv"
+ "strings"
"time"
"gopkg.in/guregu/null.v4"
@@ -14,6 +15,7 @@ import (
cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip"
"github.com/smartcontractkit/chainlink-common/pkg/types/core"
+ "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/config"
"github.com/ethereum/go-ethereum/common"
@@ -41,7 +43,6 @@ import (
llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo"
"github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox"
datastreamsllo "github.com/smartcontractkit/chainlink-data-streams/llo"
-
"github.com/smartcontractkit/chainlink/v2/core/bridges"
"github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm"
coreconfig "github.com/smartcontractkit/chainlink/v2/core/config"
@@ -264,12 +265,12 @@ func (d *Delegate) JobType() job.Type {
return job.OffchainReporting2
}
-func (d *Delegate) BeforeJobCreated(spec job.Job) {
+func (d *Delegate) BeforeJobCreated(_ job.Job) {
// This is only called first time the job is created
d.isNewlyCreatedJob = true
}
-func (d *Delegate) AfterJobCreated(spec job.Job) {}
-func (d *Delegate) BeforeJobDeleted(spec job.Job) {}
+func (d *Delegate) AfterJobCreated(_ job.Job) {}
+func (d *Delegate) BeforeJobDeleted(_ job.Job) {}
func (d *Delegate) OnDeleteJob(ctx context.Context, jb job.Job) error {
// If the job spec is malformed in any way, we report the error but return nil so that
// the job deletion itself isn't blocked.
@@ -1650,7 +1651,85 @@ func (d *Delegate) newServicesCCIPCommit(ctx context.Context, lggr logger.Sugare
MetricsRegisterer: prometheus.WrapRegistererWith(map[string]string{"job_name": jb.Name.ValueOrZero()}, prometheus.DefaultRegisterer),
}
- return ccipcommit.NewCommitServices(ctx, d.ds, srcProvider, dstProvider, d.legacyChains, jb, lggr, d.pipelineRunner, oracleArgsNoPlugin, d.isNewlyCreatedJob, int64(srcChainID), dstChainID, logError)
+ priceGetter, err := d.ccipCommitPriceGetter(ctx, lggr, pluginJobSpecConfig, jb)
+ if err != nil {
+ return nil, fmt.Errorf("failed to create price getter: %w", err)
+ }
+ //nolint:gosec // safe to cast
+ return ccipcommit.NewCommitServices(ctx, d.ds, srcProvider, dstProvider, priceGetter, jb, lggr, d.pipelineRunner, oracleArgsNoPlugin, d.isNewlyCreatedJob, int64(srcChainID), dstChainID, logError)
+}
+
+func (d *Delegate) ccipCommitPriceGetter(ctx context.Context, lggr logger.SugaredLogger, pluginJobSpecConfig ccipconfig.CommitPluginJobSpecConfig, jb job.Job) (priceGetter ccip.AllTokensPriceGetter, err error) {
+ spec := jb.OCR2OracleSpec
+ withPipeline := strings.Trim(pluginJobSpecConfig.TokenPricesUSDPipeline, "\n\t ") != ""
+ if withPipeline {
+ priceGetter, err = ccip.NewPipelineGetter(pluginJobSpecConfig.TokenPricesUSDPipeline, d.pipelineRunner, jb.ID, jb.ExternalJobID, jb.Name.ValueOrZero(), lggr)
+ if err != nil {
+ return nil, fmt.Errorf("creating pipeline price getter: %w", err)
+ }
+ } else {
+ // Use dynamic price getter.
+ if pluginJobSpecConfig.PriceGetterConfig == nil {
+ return nil, errors.New("priceGetterConfig is nil")
+ }
+
+ // Configure contract readers for all chains specified in the aggregator configurations.
+ // Some lanes (e.g. Wemix/Kroma) requires other clients than source and destination, since they use feeds from other chains.
+ aggregatorChainsToContracts := make(map[uint64][]common.Address)
+ for _, aggCfg := range pluginJobSpecConfig.PriceGetterConfig.AggregatorPrices {
+ if _, ok := aggregatorChainsToContracts[aggCfg.ChainID]; !ok {
+ aggregatorChainsToContracts[aggCfg.ChainID] = make([]common.Address, 0)
+ }
+
+ aggregatorChainsToContracts[aggCfg.ChainID] = append(aggregatorChainsToContracts[aggCfg.ChainID], aggCfg.AggregatorContractAddress)
+ }
+
+ contractReaders := map[uint64]types.ContractReader{}
+
+ for chainID, aggregatorContracts := range aggregatorChainsToContracts {
+ relayID := types.RelayID{Network: spec.Relay, ChainID: strconv.FormatUint(chainID, 10)}
+ relay, rerr := d.RelayGetter.Get(relayID)
+ if rerr != nil {
+ return nil, fmt.Errorf("get relay by id=%v: %w", relayID, err)
+ }
+
+ contractsConfig := make(map[string]evmrelaytypes.ChainContractReader, len(aggregatorContracts))
+ for i := range aggregatorContracts {
+ contractsConfig[fmt.Sprintf("%v_%v", ccip.OffchainAggregator, i)] = evmrelaytypes.ChainContractReader{
+ ContractABI: ccip.OffChainAggregatorABI,
+ Configs: map[string]*evmrelaytypes.ChainReaderDefinition{
+ "decimals": { // CR consumers choose an alias
+ ChainSpecificName: "decimals",
+ },
+ "latestRoundData": {
+ ChainSpecificName: "latestRoundData",
+ },
+ },
+ }
+ }
+ contractReaderConfig := evmrelaytypes.ChainReaderConfig{
+ Contracts: contractsConfig,
+ }
+
+ contractReaderConfigJSONBytes, jerr := json.Marshal(contractReaderConfig)
+ if jerr != nil {
+ return nil, fmt.Errorf("marshal contract reader config: %w", jerr)
+ }
+
+ contractReader, cerr := relay.NewContractReader(ctx, contractReaderConfigJSONBytes)
+ if cerr != nil {
+ return nil, fmt.Errorf("new ccip commit contract reader %w", cerr)
+ }
+
+ contractReaders[chainID] = contractReader
+ }
+
+ priceGetter, err = ccip.NewDynamicPriceGetter(*pluginJobSpecConfig.PriceGetterConfig, contractReaders)
+ if err != nil {
+ return nil, fmt.Errorf("creating dynamic price getter: %w", err)
+ }
+ }
+ return priceGetter, nil
}
func newCCIPCommitPluginBytes(isSourceProvider bool, sourceStartBlock uint64, destStartBlock uint64) config.CommitPluginConfig {
@@ -1834,7 +1913,7 @@ func (d *Delegate) ccipExecGetDstProvider(ctx context.Context, jb job.Job, plugi
// PROVIDER BASED ARG CONSTRUCTION
// Write PluginConfig bytes to send source/dest relayer provider + info outside of top level rargs/pargs over the wire
- dstConfigBytes, err := newExecPluginConfig(false, pluginJobSpecConfig.SourceStartBlock, pluginJobSpecConfig.DestStartBlock, pluginJobSpecConfig.USDCConfig, string(jb.ID)).Encode()
+ dstConfigBytes, err := newExecPluginConfig(false, pluginJobSpecConfig.SourceStartBlock, pluginJobSpecConfig.DestStartBlock, pluginJobSpecConfig.USDCConfig, pluginJobSpecConfig.LBTCConfig, string(jb.ID)).Encode()
if err != nil {
return nil, err
}
@@ -1867,7 +1946,7 @@ func (d *Delegate) ccipExecGetDstProvider(ctx context.Context, jb job.Job, plugi
func (d *Delegate) ccipExecGetSrcProvider(ctx context.Context, jb job.Job, pluginJobSpecConfig ccipconfig.ExecPluginJobSpecConfig, transmitterID string, dstProvider types.CCIPExecProvider) (srcProvider types.CCIPExecProvider, srcChainID uint64, err error) {
spec := jb.OCR2OracleSpec
- srcConfigBytes, err := newExecPluginConfig(true, pluginJobSpecConfig.SourceStartBlock, pluginJobSpecConfig.DestStartBlock, pluginJobSpecConfig.USDCConfig, string(jb.ID)).Encode()
+ srcConfigBytes, err := newExecPluginConfig(true, pluginJobSpecConfig.SourceStartBlock, pluginJobSpecConfig.DestStartBlock, pluginJobSpecConfig.USDCConfig, pluginJobSpecConfig.LBTCConfig, string(jb.ID)).Encode()
if err != nil {
return nil, 0, err
}
@@ -1916,12 +1995,13 @@ func (d *Delegate) ccipExecGetSrcProvider(ctx context.Context, jb job.Job, plugi
return
}
-func newExecPluginConfig(isSourceProvider bool, srcStartBlock uint64, dstStartBlock uint64, usdcConfig ccipconfig.USDCConfig, jobID string) config.ExecPluginConfig {
+func newExecPluginConfig(isSourceProvider bool, srcStartBlock uint64, dstStartBlock uint64, usdcConfig ccipconfig.USDCConfig, lbtcConfig ccipconfig.LBTCConfig, jobID string) config.ExecPluginConfig {
return config.ExecPluginConfig{
IsSourceProvider: isSourceProvider,
SourceStartBlock: srcStartBlock,
DestStartBlock: dstStartBlock,
USDCConfig: usdcConfig,
+ LBTCConfig: lbtcConfig,
JobID: jobID,
}
}
diff --git a/core/services/ocr2/plugins/ccip/ccipcommit/factory.go b/core/services/ocr2/plugins/ccip/ccipcommit/factory.go
index c7a18567d26..784da3e7eab 100644
--- a/core/services/ocr2/plugins/ccip/ccipcommit/factory.go
+++ b/core/services/ocr2/plugins/ccip/ccipcommit/factory.go
@@ -74,8 +74,11 @@ type reportingPluginAndInfo struct {
func (rf *CommitReportingPluginFactory) NewReportingPlugin(ctx context.Context, config types.ReportingPluginConfig) (types.ReportingPlugin, types.ReportingPluginInfo, error) {
initialRetryDelay := rf.config.newReportingPluginRetryConfig.InitialDelay
maxDelay := rf.config.newReportingPluginRetryConfig.MaxDelay
+ maxRetries := rf.config.newReportingPluginRetryConfig.MaxRetries
- pluginAndInfo, err := ccipcommon.RetryUntilSuccess(rf.NewReportingPluginFn(ctx, config), initialRetryDelay, maxDelay)
+ pluginAndInfo, err := ccipcommon.RetryUntilSuccess(
+ rf.NewReportingPluginFn(ctx, config), initialRetryDelay, maxDelay, maxRetries,
+ )
if err != nil {
return nil, types.ReportingPluginInfo{}, err
}
@@ -86,33 +89,33 @@ func (rf *CommitReportingPluginFactory) NewReportingPlugin(ctx context.Context,
// retried via RetryUntilSuccess. NewReportingPlugin must return successfully in order for the Commit plugin to
// function, hence why we can only keep retrying it until it succeeds.
func (rf *CommitReportingPluginFactory) NewReportingPluginFn(ctx context.Context, config types.ReportingPluginConfig) func() (reportingPluginAndInfo, error) {
- return func() (reportingPluginAndInfo, error) {
+ newReportingPluginFn := func() (reportingPluginAndInfo, error) {
destPriceReg, err := rf.config.commitStore.ChangeConfig(ctx, config.OnchainConfig, config.OffchainConfig)
if err != nil {
- return reportingPluginAndInfo{}, err
+ return reportingPluginAndInfo{}, fmt.Errorf("commitStore.ChangeConfig error: %w", err)
}
priceRegEvmAddr, err := ccipcalc.GenericAddrToEvm(destPriceReg)
if err != nil {
- return reportingPluginAndInfo{}, err
+ return reportingPluginAndInfo{}, fmt.Errorf("GenericAddrToEvm error: %w", err)
}
if err = rf.UpdateDynamicReaders(ctx, priceRegEvmAddr); err != nil {
- return reportingPluginAndInfo{}, err
+ return reportingPluginAndInfo{}, fmt.Errorf("UpdateDynamicReaders error: %w", err)
}
pluginOffChainConfig, err := rf.config.commitStore.OffchainConfig(ctx)
if err != nil {
- return reportingPluginAndInfo{}, err
+ return reportingPluginAndInfo{}, fmt.Errorf("commitStore.OffchainConfig error: %w", err)
}
gasPriceEstimator, err := rf.config.commitStore.GasPriceEstimator(ctx)
if err != nil {
- return reportingPluginAndInfo{}, err
+ return reportingPluginAndInfo{}, fmt.Errorf("commitStore.GasPriceEstimator error: %w", err)
}
err = rf.config.priceService.UpdateDynamicConfig(ctx, gasPriceEstimator, rf.destPriceRegReader)
if err != nil {
- return reportingPluginAndInfo{}, err
+ return reportingPluginAndInfo{}, fmt.Errorf("priceService.UpdateDynamicConfig error: %w", err)
}
lggr := rf.config.lggr.Named("CommitReportingPlugin")
@@ -145,4 +148,14 @@ func (rf *CommitReportingPluginFactory) NewReportingPluginFn(ctx context.Context
return reportingPluginAndInfo{plugin, pluginInfo}, nil
}
+
+ return func() (reportingPluginAndInfo, error) {
+ result, err := newReportingPluginFn()
+ if err != nil {
+ rf.config.lggr.Errorw("NewReportingPlugin failed", "err", err)
+ rf.config.metricsCollector.NewReportingPluginError()
+ }
+
+ return result, err
+ }
}
diff --git a/core/services/ocr2/plugins/ccip/ccipcommit/factory_test.go b/core/services/ocr2/plugins/ccip/ccipcommit/factory_test.go
index 44c63ec87e6..d3332435b06 100644
--- a/core/services/ocr2/plugins/ccip/ccipcommit/factory_test.go
+++ b/core/services/ocr2/plugins/ccip/ccipcommit/factory_test.go
@@ -14,6 +14,7 @@ import (
"github.com/smartcontractkit/chainlink-common/pkg/utils/tests"
"github.com/smartcontractkit/chainlink/v2/core/logger"
+ ccip2 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata"
ccipdataprovidermocks "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata/ccipdataprovider/mocks"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata/mocks"
@@ -28,6 +29,8 @@ import (
func TestNewReportingPluginRetriesUntilSuccess(t *testing.T) {
ctx := tests.Context(t)
commitConfig := CommitPluginStaticConfig{}
+ commitConfig.lggr = logger.TestLogger(t)
+ commitConfig.metricsCollector = ccip2.NoopMetricsCollector
// For this unit test, ensure that there is no delay between retries
commitConfig.newReportingPluginRetryConfig = ccipdata.RetryConfig{
diff --git a/core/services/ocr2/plugins/ccip/ccipcommit/initializers.go b/core/services/ocr2/plugins/ccip/ccipcommit/initializers.go
index 771fdd322f3..3a6148d1b8a 100644
--- a/core/services/ocr2/plugins/ccip/ccipcommit/initializers.go
+++ b/core/services/ocr2/plugins/ccip/ccipcommit/initializers.go
@@ -3,14 +3,9 @@ package ccipcommit
import (
"context"
"encoding/json"
- "fmt"
"math/big"
- "strings"
"time"
- "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/pricegetter"
- "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/rpclib"
-
"github.com/Masterminds/semver/v3"
"github.com/ethereum/go-ethereum/common"
libocr2 "github.com/smartcontractkit/libocr/offchainreporting2plus"
@@ -27,7 +22,6 @@ import (
db "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdb"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr"
- "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm"
"github.com/smartcontractkit/chainlink/v2/core/logger"
"github.com/smartcontractkit/chainlink/v2/core/services/job"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip"
@@ -40,9 +34,29 @@ import (
"github.com/smartcontractkit/chainlink/v2/core/services/pipeline"
)
-var defaultNewReportingPluginRetryConfig = ccipdata.RetryConfig{InitialDelay: time.Second, MaxDelay: 5 * time.Minute}
+var defaultNewReportingPluginRetryConfig = ccipdata.RetryConfig{
+ InitialDelay: time.Second,
+ MaxDelay: 10 * time.Minute,
+ // Retry for approximately 4hrs (MaxDelay of 10m = 6 times per hour, times 4 hours, plus 10 because the first
+ // 10 retries only take 20 minutes due to an initial retry of 1s and exponential backoff)
+ MaxRetries: (6 * 4) + 10,
+}
-func NewCommitServices(ctx context.Context, ds sqlutil.DataSource, srcProvider commontypes.CCIPCommitProvider, dstProvider commontypes.CCIPCommitProvider, chainSet legacyevm.LegacyChainContainer, jb job.Job, lggr logger.Logger, pr pipeline.Runner, argsNoPlugin libocr2.OCR2OracleArgs, new bool, sourceChainID int64, destChainID int64, logError func(string)) ([]job.ServiceCtx, error) {
+func NewCommitServices(
+ ctx context.Context,
+ ds sqlutil.DataSource,
+ srcProvider commontypes.CCIPCommitProvider,
+ dstProvider commontypes.CCIPCommitProvider,
+ priceGetter ccip.AllTokensPriceGetter,
+ jb job.Job,
+ lggr logger.Logger,
+ pr pipeline.Runner,
+ argsNoPlugin libocr2.OCR2OracleArgs,
+ newInstance bool,
+ sourceChainID int64,
+ destChainID int64,
+ logError func(string),
+) ([]job.ServiceCtx, error) {
spec := jb.OCR2OracleSpec
var pluginConfig ccipconfig.CommitPluginJobSpecConfig
@@ -69,45 +83,6 @@ func NewCommitServices(ctx context.Context, ds sqlutil.DataSource, srcProvider c
commitStoreReader = ccip.NewProviderProxyCommitStoreReader(srcCommitStore, dstCommitStore)
commitLggr := lggr.Named("CCIPCommit").With("sourceChain", sourceChainID, "destChain", destChainID)
- var priceGetter pricegetter.AllTokensPriceGetter
- withPipeline := strings.Trim(pluginConfig.TokenPricesUSDPipeline, "\n\t ") != ""
- if withPipeline {
- priceGetter, err = pricegetter.NewPipelineGetter(pluginConfig.TokenPricesUSDPipeline, pr, jb.ID, jb.ExternalJobID, jb.Name.ValueOrZero(), lggr)
- if err != nil {
- return nil, fmt.Errorf("creating pipeline price getter: %w", err)
- }
- } else {
- // Use dynamic price getter.
- if pluginConfig.PriceGetterConfig == nil {
- return nil, fmt.Errorf("priceGetterConfig is nil")
- }
-
- // Build price getter clients for all chains specified in the aggregator configurations.
- // Some lanes (e.g. Wemix/Kroma) requires other clients than source and destination, since they use feeds from other chains.
- priceGetterClients := map[uint64]pricegetter.DynamicPriceGetterClient{}
- for _, aggCfg := range pluginConfig.PriceGetterConfig.AggregatorPrices {
- chainID := aggCfg.ChainID
- // Retrieve the chain.
- chain, _, err2 := ccipconfig.GetChainByChainID(chainSet, chainID)
- if err2 != nil {
- return nil, fmt.Errorf("retrieving chain for chainID %d: %w", chainID, err2)
- }
- caller := rpclib.NewDynamicLimitedBatchCaller(
- lggr,
- chain.Client(),
- rpclib.DefaultRpcBatchSizeLimit,
- rpclib.DefaultRpcBatchBackOffMultiplier,
- rpclib.DefaultMaxParallelRpcCalls,
- )
- priceGetterClients[chainID] = pricegetter.NewDynamicPriceGetterClient(caller)
- }
-
- priceGetter, err = pricegetter.NewDynamicPriceGetter(*pluginConfig.PriceGetterConfig, priceGetterClients)
- if err != nil {
- return nil, fmt.Errorf("creating dynamic price getter: %w", err)
- }
- }
-
offRampReader, err := dstProvider.NewOffRampReader(ctx, pluginConfig.OffRamp)
if err != nil {
return nil, err
@@ -156,7 +131,7 @@ func NewCommitServices(ctx context.Context, ds sqlutil.DataSource, srcProvider c
onRampAddress,
)
- orm, err := cciporm.NewObservedORM(ds, lggr)
+ orm, err := cciporm.NewORM(ds, lggr)
if err != nil {
return nil, err
}
@@ -193,7 +168,7 @@ func NewCommitServices(ctx context.Context, ds sqlutil.DataSource, srcProvider c
return nil, err
}
// If this is a brand-new job, then we make use of the start blocks. If not then we're rebooting and log poller will pick up where we left off.
- if new {
+ if newInstance {
return []job.ServiceCtx{
oraclelib.NewChainAgnosticBackFilledOracle(
lggr,
diff --git a/core/services/ocr2/plugins/ccip/ccipcommit/ocr2.go b/core/services/ocr2/plugins/ccip/ccipcommit/ocr2.go
index 55caf88f1c7..97b982b0e2e 100644
--- a/core/services/ocr2/plugins/ccip/ccipcommit/ocr2.go
+++ b/core/services/ocr2/plugins/ccip/ccipcommit/ocr2.go
@@ -461,24 +461,42 @@ func (r *CommitReportingPlugin) selectPriceUpdates(ctx context.Context, now time
// The returned latestGasPrice and latestTokenPrices should not contain nil values.
func (r *CommitReportingPlugin) calculatePriceUpdates(ctx context.Context, gasPriceObs map[uint64][]*big.Int, tokenPriceObs map[cciptypes.Address][]*big.Int, latestGasPrice map[uint64]update, latestTokenPrices map[cciptypes.Address]update) ([]cciptypes.GasPrice, []cciptypes.TokenPrice, error) {
var tokenPriceUpdates []cciptypes.TokenPrice
+ // Token prices are mostly heartbeat driven. To maximize heartbeat batching, the price inclusion rule is as follows:
+ // If any token requires heartbeat update, include all token prices in the report.
+ // Otherwise, only include token prices that exceed deviation threshold.
+ needTokenHeartbeat := false
+ for token := range tokenPriceObs {
+ latestTokenPrice, exists := latestTokenPrices[token]
+ if !exists || time.Since(latestTokenPrice.timestamp) >= r.offchainConfig.TokenPriceHeartBeat {
+ r.lggr.Infow("Token requires heartbeat update", "token", token)
+ needTokenHeartbeat = true
+ break
+ }
+ }
+
for token, tokenPriceObservations := range tokenPriceObs {
medianPrice := ccipcalc.BigIntSortedMiddle(tokenPriceObservations)
+ if needTokenHeartbeat {
+ r.lggr.Debugw("Token price update included due to heartbeat", "token", token, "newPrice", medianPrice)
+ tokenPriceUpdates = append(tokenPriceUpdates, cciptypes.TokenPrice{
+ Token: token,
+ Value: medianPrice,
+ })
+ continue
+ }
+
latestTokenPrice, exists := latestTokenPrices[token]
if exists {
- tokenPriceUpdatedRecently := time.Since(latestTokenPrice.timestamp) < r.offchainConfig.TokenPriceHeartBeat
- tokenPriceNotChanged := !ccipcalc.Deviates(medianPrice, latestTokenPrice.value, int64(r.offchainConfig.TokenPriceDeviationPPB))
- if tokenPriceUpdatedRecently && tokenPriceNotChanged {
- r.lggr.Debugw("token price was updated recently, skipping the update",
+ if ccipcalc.Deviates(medianPrice, latestTokenPrice.value, int64(r.offchainConfig.TokenPriceDeviationPPB)) {
+ r.lggr.Debugw("Token price update included due to deviation",
"token", token, "newPrice", medianPrice, "existingPrice", latestTokenPrice.value)
- continue // skip the update if we recently had a price update close to the new value
+ tokenPriceUpdates = append(tokenPriceUpdates, cciptypes.TokenPrice{
+ Token: token,
+ Value: medianPrice,
+ })
}
}
-
- tokenPriceUpdates = append(tokenPriceUpdates, cciptypes.TokenPrice{
- Token: token,
- Value: medianPrice,
- })
}
// Determinism required.
@@ -487,31 +505,49 @@ func (r *CommitReportingPlugin) calculatePriceUpdates(ctx context.Context, gasPr
})
var gasPriceUpdate []cciptypes.GasPrice
+ // Gas prices are mostly heartbeat driven. To maximize heartbeat batching, the price inclusion rule is as follows:
+ // If any source chain gas price requires heartbeat update, include all gas prices in the report.
+ // Otherwise, only include gas prices that exceed deviation threshold.
+ needGasHeartbeat := false
+ for chainSelector := range gasPriceObs {
+ latestGasPrice, exists := latestGasPrice[chainSelector]
+ if !exists || latestGasPrice.value == nil || time.Since(latestGasPrice.timestamp) >= r.offchainConfig.GasPriceHeartBeat {
+ r.lggr.Infow("Chain gas price requires heartbeat update", "chainSelector", chainSelector)
+ needGasHeartbeat = true
+ break
+ }
+ }
+
for chainSelector, gasPriceObservations := range gasPriceObs {
newGasPrice, err := r.gasPriceEstimator.Median(ctx, gasPriceObservations) // Compute the median price
if err != nil {
return nil, nil, fmt.Errorf("failed to calculate median gas price for chain selector %d: %w", chainSelector, err)
}
- // Default to updating so that we update if there are no prior updates.
+ if needGasHeartbeat {
+ r.lggr.Debugw("Gas price update included due to heartbeat", "chainSelector", chainSelector)
+ gasPriceUpdate = append(gasPriceUpdate, cciptypes.GasPrice{
+ DestChainSelector: chainSelector,
+ Value: newGasPrice,
+ })
+ continue
+ }
+
latestGasPrice, exists := latestGasPrice[chainSelector]
if exists && latestGasPrice.value != nil {
- gasPriceUpdatedRecently := time.Since(latestGasPrice.timestamp) < r.offchainConfig.GasPriceHeartBeat
gasPriceDeviated, err := r.gasPriceEstimator.Deviates(ctx, newGasPrice, latestGasPrice.value)
if err != nil {
return nil, nil, err
}
- if gasPriceUpdatedRecently && !gasPriceDeviated {
- r.lggr.Debugw("gas price was updated recently and not deviated sufficiently, skipping the update",
+ if gasPriceDeviated {
+ r.lggr.Debugw("Gas price update included due to deviation",
"chainSelector", chainSelector, "newPrice", newGasPrice, "existingPrice", latestGasPrice.value)
- continue
+ gasPriceUpdate = append(gasPriceUpdate, cciptypes.GasPrice{
+ DestChainSelector: chainSelector,
+ Value: newGasPrice,
+ })
}
}
-
- gasPriceUpdate = append(gasPriceUpdate, cciptypes.GasPrice{
- DestChainSelector: chainSelector,
- Value: newGasPrice,
- })
}
sort.Slice(gasPriceUpdate, func(i, j int) bool {
diff --git a/core/services/ocr2/plugins/ccip/ccipcommit/ocr2_test.go b/core/services/ocr2/plugins/ccip/ccipcommit/ocr2_test.go
index 93d8de9f892..ecc0c56b982 100644
--- a/core/services/ocr2/plugins/ccip/ccipcommit/ocr2_test.go
+++ b/core/services/ocr2/plugins/ccip/ccipcommit/ocr2_test.go
@@ -20,15 +20,13 @@ import (
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
- "github.com/smartcontractkit/chainlink-common/pkg/utils/tests"
-
"github.com/smartcontractkit/libocr/offchainreporting2plus/types"
"github.com/smartcontractkit/chainlink-common/pkg/config"
"github.com/smartcontractkit/chainlink-common/pkg/hashutil"
"github.com/smartcontractkit/chainlink-common/pkg/merklemulti"
cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip"
-
+ "github.com/smartcontractkit/chainlink-common/pkg/utils/tests"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas/mocks"
mocks2 "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils"
@@ -44,7 +42,6 @@ import (
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata/factory"
ccipdatamocks "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata/mocks"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0"
-
ccipdbmocks "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdb/mocks"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/prices"
)
@@ -409,7 +406,9 @@ func TestCommitReportingPlugin_Report(t *testing.T) {
evmEstimator := mocks.NewEvmFeeEstimator(t)
evmEstimator.On("L1Oracle").Return(nil)
- gasPriceEstimator := prices.NewDAGasPriceEstimator(evmEstimator, nil, 2e9, 2e9) // 200% deviation
+
+ feeEstimatorConfig := ccipdatamocks.NewFeeEstimatorConfigReader(t)
+ gasPriceEstimator := prices.NewDAGasPriceEstimator(evmEstimator, nil, 2e9, 2e9, feeEstimatorConfig) // 200% deviation
var destTokens []cciptypes.Address
for tk := range tc.tokenDecimals {
@@ -433,7 +432,7 @@ func TestCommitReportingPlugin_Report(t *testing.T) {
})).Return(destDecimals, nil).Maybe()
lp := mocks2.NewLogPoller(t)
- commitStoreReader, err := v1_2_0.NewCommitStore(logger.TestLogger(t), utils.RandomAddress(), nil, lp)
+ commitStoreReader, err := v1_2_0.NewCommitStore(logger.TestLogger(t), utils.RandomAddress(), nil, lp, feeEstimatorConfig)
assert.NoError(t, err)
healthCheck := ccipcachemocks.NewChainHealthcheck(t)
@@ -1131,7 +1130,7 @@ func TestCommitReportingPlugin_calculatePriceUpdates(t *testing.T) {
expGasUpdates: []cciptypes.GasPrice{{DestChainSelector: defaultSourceChainSelector, Value: val1e18(20)}},
},
{
- name: "multichain gas prices",
+ name: "multi-chain gas price updates due to heartbeat",
commitObservations: []ccip.CommitObservation{
{SourceGasPriceUSDPerChain: map[uint64]*big.Int{defaultSourceChainSelector: val1e18(1)}},
{SourceGasPriceUSDPerChain: map[uint64]*big.Int{defaultSourceChainSelector + 1: val1e18(11)}},
@@ -1161,9 +1160,47 @@ func TestCommitReportingPlugin_calculatePriceUpdates(t *testing.T) {
f: 1,
expGasUpdates: []cciptypes.GasPrice{
{DestChainSelector: defaultSourceChainSelector, Value: val1e18(2)},
+ {DestChainSelector: defaultSourceChainSelector + 1, Value: val1e18(22)},
{DestChainSelector: defaultSourceChainSelector + 2, Value: val1e18(222)},
},
},
+ {
+ name: "multi-chain gas prices but only one updates due to deviation",
+ commitObservations: []ccip.CommitObservation{
+ {SourceGasPriceUSDPerChain: map[uint64]*big.Int{defaultSourceChainSelector: val1e18(1)}},
+ {SourceGasPriceUSDPerChain: map[uint64]*big.Int{defaultSourceChainSelector + 1: val1e18(11)}},
+ {SourceGasPriceUSDPerChain: map[uint64]*big.Int{defaultSourceChainSelector + 2: val1e18(111)}},
+ {SourceGasPriceUSDPerChain: map[uint64]*big.Int{defaultSourceChainSelector: val1e18(2)}},
+ {SourceGasPriceUSDPerChain: map[uint64]*big.Int{defaultSourceChainSelector + 1: val1e18(22)}},
+ {SourceGasPriceUSDPerChain: map[uint64]*big.Int{defaultSourceChainSelector + 2: val1e18(222)}},
+ {SourceGasPriceUSDPerChain: map[uint64]*big.Int{defaultSourceChainSelector: val1e18(3)}},
+ {SourceGasPriceUSDPerChain: map[uint64]*big.Int{defaultSourceChainSelector + 1: val1e18(33)}},
+ {SourceGasPriceUSDPerChain: map[uint64]*big.Int{defaultSourceChainSelector + 2: val1e18(333)}},
+ },
+ gasPriceHeartBeat: *config.MustNewDuration(time.Hour),
+ daGasPriceDeviationPPB: 20e7,
+ execGasPriceDeviationPPB: 20e7,
+ tokenPriceHeartBeat: *config.MustNewDuration(time.Hour),
+ tokenPriceDeviationPPB: 20e7,
+ latestGasPrice: map[uint64]update{
+ defaultSourceChainSelector: {
+ timestamp: time.Now().Add(-30 * time.Minute), // recent
+ value: val1e18(9), // median deviates
+ },
+ defaultSourceChainSelector + 1: {
+ timestamp: time.Now().Add(-30 * time.Minute), // recent
+ value: val1e18(20), // median does not deviate
+ },
+ defaultSourceChainSelector + 2: {
+ timestamp: time.Now().Add(-30 * time.Minute), // recent
+ value: val1e18(220), // median does not deviate
+ },
+ },
+ f: 1,
+ expGasUpdates: []cciptypes.GasPrice{
+ {DestChainSelector: defaultSourceChainSelector, Value: val1e18(2)},
+ },
+ },
{
name: "median one token",
commitObservations: []ccip.CommitObservation{
@@ -1204,14 +1241,14 @@ func TestCommitReportingPlugin_calculatePriceUpdates(t *testing.T) {
expGasUpdates: []cciptypes.GasPrice{{DestChainSelector: defaultSourceChainSelector, Value: big.NewInt(0)}},
},
{
- name: "token price update skipped because it is close to the latest",
+ name: "token price update skipped because it does not deviate and are recent",
commitObservations: []ccip.CommitObservation{
{
- TokenPricesUSD: map[cciptypes.Address]*big.Int{feeToken1: val1e18(11)},
+ TokenPricesUSD: map[cciptypes.Address]*big.Int{feeToken1: val1e18(11), feeToken2: val1e18(11)},
SourceGasPriceUSDPerChain: map[uint64]*big.Int{defaultSourceChainSelector: val1e18(0)},
},
{
- TokenPricesUSD: map[cciptypes.Address]*big.Int{feeToken1: val1e18(12)},
+ TokenPricesUSD: map[cciptypes.Address]*big.Int{feeToken1: val1e18(12), feeToken2: val1e18(12)},
SourceGasPriceUSDPerChain: map[uint64]*big.Int{defaultSourceChainSelector: val1e18(0)},
},
},
@@ -1226,10 +1263,81 @@ func TestCommitReportingPlugin_calculatePriceUpdates(t *testing.T) {
timestamp: time.Now().Add(-30 * time.Minute),
value: val1e18(10),
},
+ feeToken2: {
+ timestamp: time.Now().Add(-30 * time.Minute),
+ value: val1e18(10),
+ },
},
// We expect a gas update because no latest
expGasUpdates: []cciptypes.GasPrice{{DestChainSelector: defaultSourceChainSelector, Value: big.NewInt(0)}},
},
+ {
+ name: "multiple token price update due to staleness",
+ commitObservations: []ccip.CommitObservation{
+ {
+ TokenPricesUSD: map[cciptypes.Address]*big.Int{feeToken1: val1e18(11), feeToken2: val1e18(11)},
+ SourceGasPriceUSDPerChain: map[uint64]*big.Int{defaultSourceChainSelector: val1e18(0)},
+ },
+ {
+ TokenPricesUSD: map[cciptypes.Address]*big.Int{feeToken1: val1e18(12), feeToken2: val1e18(12)},
+ SourceGasPriceUSDPerChain: map[uint64]*big.Int{defaultSourceChainSelector: val1e18(0)},
+ },
+ },
+ f: 1,
+ gasPriceHeartBeat: *config.MustNewDuration(time.Hour),
+ daGasPriceDeviationPPB: 20e7,
+ execGasPriceDeviationPPB: 20e7,
+ tokenPriceHeartBeat: *config.MustNewDuration(time.Hour),
+ tokenPriceDeviationPPB: 20e7,
+ latestTokenPrices: map[cciptypes.Address]update{
+ feeToken1: {
+ timestamp: time.Now().Add(-90 * time.Minute),
+ value: val1e18(10),
+ },
+ feeToken2: {
+ timestamp: time.Now().Add(-30 * time.Minute),
+ value: val1e18(10),
+ },
+ },
+ expTokenUpdates: []cciptypes.TokenPrice{
+ {Token: feeToken1, Value: val1e18(12)},
+ {Token: feeToken2, Value: val1e18(12)},
+ },
+ expGasUpdates: []cciptypes.GasPrice{{DestChainSelector: defaultSourceChainSelector, Value: big.NewInt(0)}},
+ },
+ {
+ name: "multiple token exist but only one updates due to deviation",
+ commitObservations: []ccip.CommitObservation{
+ {
+ TokenPricesUSD: map[cciptypes.Address]*big.Int{feeToken1: val1e18(11), feeToken2: val1e18(13)},
+ SourceGasPriceUSDPerChain: map[uint64]*big.Int{defaultSourceChainSelector: val1e18(0)},
+ },
+ {
+ TokenPricesUSD: map[cciptypes.Address]*big.Int{feeToken1: val1e18(12), feeToken2: val1e18(14)},
+ SourceGasPriceUSDPerChain: map[uint64]*big.Int{defaultSourceChainSelector: val1e18(0)},
+ },
+ },
+ f: 1,
+ gasPriceHeartBeat: *config.MustNewDuration(time.Hour),
+ daGasPriceDeviationPPB: 20e7,
+ execGasPriceDeviationPPB: 20e7,
+ tokenPriceHeartBeat: *config.MustNewDuration(time.Hour),
+ tokenPriceDeviationPPB: 20e7,
+ latestTokenPrices: map[cciptypes.Address]update{
+ feeToken1: {
+ timestamp: time.Now().Add(-30 * time.Minute),
+ value: val1e18(10),
+ },
+ feeToken2: {
+ timestamp: time.Now().Add(-30 * time.Minute),
+ value: val1e18(10),
+ },
+ },
+ expTokenUpdates: []cciptypes.TokenPrice{
+ {Token: feeToken2, Value: val1e18(14)},
+ },
+ expGasUpdates: []cciptypes.GasPrice{{DestChainSelector: defaultSourceChainSelector, Value: big.NewInt(0)}},
+ },
{
name: "gas price and token price both included because they are not close to the latest",
commitObservations: []ccip.CommitObservation{
@@ -1330,12 +1438,18 @@ func TestCommitReportingPlugin_calculatePriceUpdates(t *testing.T) {
name: "gas price included because it deviates from latest and token price skipped because it does not deviate",
commitObservations: []ccip.CommitObservation{
{
- TokenPricesUSD: map[cciptypes.Address]*big.Int{feeToken1: val1e18(20)},
- SourceGasPriceUSDPerChain: map[uint64]*big.Int{defaultSourceChainSelector: val1e18(10)},
+ TokenPricesUSD: map[cciptypes.Address]*big.Int{feeToken1: val1e18(20)},
+ SourceGasPriceUSDPerChain: map[uint64]*big.Int{
+ defaultSourceChainSelector: val1e18(10),
+ defaultSourceChainSelector + 1: val1e18(20),
+ },
},
{
- TokenPricesUSD: map[cciptypes.Address]*big.Int{feeToken1: val1e18(21)},
- SourceGasPriceUSDPerChain: map[uint64]*big.Int{defaultSourceChainSelector: val1e18(11)},
+ TokenPricesUSD: map[cciptypes.Address]*big.Int{feeToken1: val1e18(21)},
+ SourceGasPriceUSDPerChain: map[uint64]*big.Int{
+ defaultSourceChainSelector: val1e18(11),
+ defaultSourceChainSelector + 1: val1e18(21),
+ },
},
},
f: 1,
@@ -1346,8 +1460,12 @@ func TestCommitReportingPlugin_calculatePriceUpdates(t *testing.T) {
tokenPriceDeviationPPB: 200e7,
latestGasPrice: map[uint64]update{
defaultSourceChainSelector: {
- timestamp: time.Now().Add(-90 * time.Minute),
- value: val1e18(9),
+ timestamp: time.Now().Add(-30 * time.Minute),
+ value: val1e18(8),
+ },
+ defaultSourceChainSelector + 1: {
+ timestamp: time.Now().Add(-30 * time.Minute),
+ value: val1e18(21),
},
},
latestTokenPrices: map[cciptypes.Address]update{
@@ -1362,11 +1480,11 @@ func TestCommitReportingPlugin_calculatePriceUpdates(t *testing.T) {
name: "gas price skipped because it does not deviate and token price included because it has not been updated recently",
commitObservations: []ccip.CommitObservation{
{
- TokenPricesUSD: map[cciptypes.Address]*big.Int{feeToken1: val1e18(20)},
+ TokenPricesUSD: map[cciptypes.Address]*big.Int{feeToken1: val1e18(10), feeToken2: val1e18(20)},
SourceGasPriceUSDPerChain: map[uint64]*big.Int{defaultSourceChainSelector: val1e18(10)},
},
{
- TokenPricesUSD: map[cciptypes.Address]*big.Int{feeToken1: val1e18(21)},
+ TokenPricesUSD: map[cciptypes.Address]*big.Int{feeToken1: val1e18(11), feeToken2: val1e18(21)},
SourceGasPriceUSDPerChain: map[uint64]*big.Int{defaultSourceChainSelector: val1e18(11)},
},
},
@@ -1385,11 +1503,16 @@ func TestCommitReportingPlugin_calculatePriceUpdates(t *testing.T) {
latestTokenPrices: map[cciptypes.Address]update{
feeToken1: {
timestamp: time.Now().Add(-4 * time.Hour),
+ value: val1e18(11),
+ },
+ feeToken2: {
+ timestamp: time.Now().Add(-1 * time.Hour),
value: val1e18(21),
},
},
expTokenUpdates: []cciptypes.TokenPrice{
- {Token: feeToken1, Value: val1e18(21)},
+ {Token: feeToken1, Value: val1e18(11)},
+ {Token: feeToken2, Value: val1e18(21)},
},
expGasUpdates: nil,
},
@@ -1408,6 +1531,7 @@ func TestCommitReportingPlugin_calculatePriceUpdates(t *testing.T) {
nil,
tc.daGasPriceDeviationPPB,
tc.execGasPriceDeviationPPB,
+ ccipdatamocks.NewFeeEstimatorConfigReader(t),
)
r := &CommitReportingPlugin{
diff --git a/core/services/ocr2/plugins/ccip/ccipexec/batching.go b/core/services/ocr2/plugins/ccip/ccipexec/batching.go
index 866a20c4cde..f1beb035d32 100644
--- a/core/services/ocr2/plugins/ccip/ccipexec/batching.go
+++ b/core/services/ocr2/plugins/ccip/ccipexec/batching.go
@@ -24,6 +24,12 @@ import (
"github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/statuschecker"
)
+// Batching strategies
+const (
+ BestEffortBatchingStrategyID = uint32(0)
+ ZKOverflowBatchingStrategyID = uint32(1)
+)
+
type BatchContext struct {
report commitReportWithSendRequests
inflight []InflightInternalExecutionReport
@@ -47,6 +53,7 @@ type BatchContext struct {
type BatchingStrategy interface {
BuildBatch(ctx context.Context, batchCtx *BatchContext) ([]ccip.ObservedMessage, []messageExecStatus)
+ GetBatchingStrategyID() uint32
}
type BestEffortBatchingStrategy struct{}
@@ -58,9 +65,9 @@ type ZKOverflowBatchingStrategy struct {
func NewBatchingStrategy(batchingStrategyID uint32, statusChecker statuschecker.CCIPTransactionStatusChecker) (BatchingStrategy, error) {
var batchingStrategy BatchingStrategy
switch batchingStrategyID {
- case 0:
+ case BestEffortBatchingStrategyID:
batchingStrategy = &BestEffortBatchingStrategy{}
- case 1:
+ case ZKOverflowBatchingStrategyID:
batchingStrategy = &ZKOverflowBatchingStrategy{
statuschecker: statusChecker,
}
@@ -70,6 +77,10 @@ func NewBatchingStrategy(batchingStrategyID uint32, statusChecker statuschecker.
return batchingStrategy, nil
}
+func (s *BestEffortBatchingStrategy) GetBatchingStrategyID() uint32 {
+ return BestEffortBatchingStrategyID
+}
+
// BestEffortBatchingStrategy is a batching strategy that tries to batch as many messages as possible (up to certain limits).
func (s *BestEffortBatchingStrategy) BuildBatch(
ctx context.Context,
@@ -95,6 +106,10 @@ func (s *BestEffortBatchingStrategy) BuildBatch(
return batchBuilder.batch, batchBuilder.statuses
}
+func (bs *ZKOverflowBatchingStrategy) GetBatchingStrategyID() uint32 {
+ return ZKOverflowBatchingStrategyID
+}
+
// ZKOverflowBatchingStrategy is a batching strategy for ZK chains overflowing under certain conditions.
// It is a simple batching strategy that only allows one message to be added to the batch.
// TXM is used to perform the ZK check: if the message failed the check, it will be skipped.
diff --git a/core/services/ocr2/plugins/ccip/ccipexec/batching_test.go b/core/services/ocr2/plugins/ccip/ccipexec/batching_test.go
index 0fd23496974..e57bc3aa86e 100644
--- a/core/services/ocr2/plugins/ccip/ccipexec/batching_test.go
+++ b/core/services/ocr2/plugins/ccip/ccipexec/batching_test.go
@@ -850,13 +850,13 @@ func runBatchingStrategyTests(t *testing.T, strategy BatchingStrategy, available
seqNrs, execStates := strategy.BuildBatch(context.Background(), batchContext)
- runAssertions(t, tc, seqNrs, execStates)
+ runAssertions(t, tc, seqNrs, execStates, strategy)
})
}
}
// Utility function to run common assertions
-func runAssertions(t *testing.T, tc testCase, seqNrs []ccip.ObservedMessage, execStates []messageExecStatus) {
+func runAssertions(t *testing.T, tc testCase, seqNrs []ccip.ObservedMessage, execStates []messageExecStatus, strategy BatchingStrategy) {
if tc.expectedSeqNrs == nil {
assert.Len(t, seqNrs, 0)
} else {
@@ -868,6 +868,13 @@ func runAssertions(t *testing.T, tc testCase, seqNrs []ccip.ObservedMessage, exe
} else {
assert.Equal(t, tc.expectedStates, execStates)
}
+
+ batchingStratID := strategy.GetBatchingStrategyID()
+ if strategyType := reflect.TypeOf(strategy); strategyType == reflect.TypeOf(&BestEffortBatchingStrategy{}) {
+ assert.Equal(t, uint32(0), batchingStratID)
+ } else {
+ assert.Equal(t, uint32(1), batchingStratID)
+ }
}
func createTestMessage(seqNr uint64, sender cciptypes.Address, nonce uint64, feeToken cciptypes.Address, feeAmount *big.Int, executed bool, data []byte) cciptypes.EVM2EVMOnRampCCIPSendRequestedWithMeta {
diff --git a/core/services/ocr2/plugins/ccip/ccipexec/factory.go b/core/services/ocr2/plugins/ccip/ccipexec/factory.go
index 646121bdba8..20fe65b81a9 100644
--- a/core/services/ocr2/plugins/ccip/ccipexec/factory.go
+++ b/core/services/ocr2/plugins/ccip/ccipexec/factory.go
@@ -71,8 +71,11 @@ type reportingPluginAndInfo struct {
func (rf *ExecutionReportingPluginFactory) NewReportingPlugin(ctx context.Context, config types.ReportingPluginConfig) (types.ReportingPlugin, types.ReportingPluginInfo, error) {
initialRetryDelay := rf.config.newReportingPluginRetryConfig.InitialDelay
maxDelay := rf.config.newReportingPluginRetryConfig.MaxDelay
+ maxRetries := rf.config.newReportingPluginRetryConfig.MaxRetries
- pluginAndInfo, err := ccipcommon.RetryUntilSuccess(rf.NewReportingPluginFn(ctx, config), initialRetryDelay, maxDelay)
+ pluginAndInfo, err := ccipcommon.RetryUntilSuccess(
+ rf.NewReportingPluginFn(ctx, config), initialRetryDelay, maxDelay, maxRetries,
+ )
if err != nil {
return nil, types.ReportingPluginInfo{}, err
}
@@ -83,7 +86,7 @@ func (rf *ExecutionReportingPluginFactory) NewReportingPlugin(ctx context.Contex
// retried via RetryUntilSuccess. NewReportingPlugin must return successfully in order for the Exec plugin to function,
// hence why we can only keep retrying it until it succeeds.
func (rf *ExecutionReportingPluginFactory) NewReportingPluginFn(ctx context.Context, config types.ReportingPluginConfig) func() (reportingPluginAndInfo, error) {
- return func() (reportingPluginAndInfo, error) {
+ newReportingPluginFn := func() (reportingPluginAndInfo, error) {
destPriceRegistry, destWrappedNative, err := rf.config.offRampReader.ChangeConfig(ctx, config.OnchainConfig, config.OffchainConfig)
if err != nil {
return reportingPluginAndInfo{}, err
@@ -159,4 +162,14 @@ func (rf *ExecutionReportingPluginFactory) NewReportingPluginFn(ctx context.Cont
return reportingPluginAndInfo{plugin, pluginInfo}, nil
}
+
+ return func() (reportingPluginAndInfo, error) {
+ result, err := newReportingPluginFn()
+ if err != nil {
+ rf.config.lggr.Errorw("NewReportingPlugin failed", "err", err)
+ rf.config.metricsCollector.NewReportingPluginError()
+ }
+
+ return result, err
+ }
}
diff --git a/core/services/ocr2/plugins/ccip/ccipexec/factory_test.go b/core/services/ocr2/plugins/ccip/ccipexec/factory_test.go
index fe5e0ab1e23..a5df4c356ce 100644
--- a/core/services/ocr2/plugins/ccip/ccipexec/factory_test.go
+++ b/core/services/ocr2/plugins/ccip/ccipexec/factory_test.go
@@ -12,6 +12,7 @@ import (
"github.com/smartcontractkit/chainlink-common/pkg/types/ccip"
"github.com/smartcontractkit/chainlink-common/pkg/utils/tests"
"github.com/smartcontractkit/chainlink/v2/core/logger"
+ ccip2 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata"
ccipdataprovidermocks "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata/ccipdataprovider/mocks"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata/mocks"
@@ -25,6 +26,8 @@ import (
func TestNewReportingPluginRetriesUntilSuccess(t *testing.T) {
ctx := tests.Context(t)
execConfig := ExecutionPluginStaticConfig{}
+ execConfig.lggr = logger.TestLogger(t)
+ execConfig.metricsCollector = ccip2.NoopMetricsCollector
// For this unit test, ensure that there is no delay between retries
execConfig.newReportingPluginRetryConfig = ccipdata.RetryConfig{
diff --git a/core/services/ocr2/plugins/ccip/ccipexec/initializers.go b/core/services/ocr2/plugins/ccip/ccipexec/initializers.go
index 7826f6058fe..3ad9c94dc74 100644
--- a/core/services/ocr2/plugins/ccip/ccipexec/initializers.go
+++ b/core/services/ocr2/plugins/ccip/ccipexec/initializers.go
@@ -18,8 +18,6 @@ import (
cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip"
- "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/cache"
- "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipcalc"
"github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/statuschecker"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr"
@@ -27,6 +25,8 @@ import (
"github.com/smartcontractkit/chainlink/v2/core/services/job"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip"
ccipconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/config"
+ "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/cache"
+ "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/factory"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/observability"
@@ -43,10 +43,18 @@ var (
// 5s for token data worker timeout is a reasonable default.
tokenDataWorkerTimeout = 5 * time.Second
// tokenDataWorkerNumWorkers is the number of workers that will be processing token data in parallel.
- tokenDataWorkerNumWorkers = 5
+ tokenDataWorkerNumWorkers = 10
+ // expirationDur is the duration for which the token data will be cached.
+ expirationDurTokenData = 10 * time.Minute
)
-var defaultNewReportingPluginRetryConfig = ccipdata.RetryConfig{InitialDelay: time.Second, MaxDelay: 5 * time.Minute}
+var defaultNewReportingPluginRetryConfig = ccipdata.RetryConfig{
+ InitialDelay: time.Second,
+ MaxDelay: 10 * time.Minute,
+ // Retry for approximately 4hrs (MaxDelay of 10m = 6 times per hour, times 4 hours, plus 10 because the first
+ // 10 retries only take 20 minutes due to an initial retry of 1s and exponential backoff)
+ MaxRetries: (6 * 4) + 10,
+}
func NewExecServices(ctx context.Context, lggr logger.Logger, jb job.Job, srcProvider types.CCIPExecProvider, dstProvider types.CCIPExecProvider, srcChainID int64, dstChainID int64, new bool, argsNoPlugin libocr2.OCR2OracleArgs, logError func(string)) ([]job.ServiceCtx, error) {
if jb.OCR2OracleSpec == nil {
@@ -115,6 +123,20 @@ func NewExecServices(ctx context.Context, lggr logger.Logger, jb job.Job, srcPro
}
tokenDataProviders[cciptypes.Address(pluginConfig.USDCConfig.SourceTokenAddress.String())] = usdcReader
}
+ // init lbtc token data provider
+ if pluginConfig.LBTCConfig.AttestationAPI != "" {
+ lggr.Infof("LBTC token data provider enabled")
+ err2 := pluginConfig.LBTCConfig.ValidateLBTCConfig()
+ if err2 != nil {
+ return nil, err2
+ }
+
+ lbtcReader, err2 := srcProvider.NewTokenDataReader(ctx, ccip.EvmAddrToGeneric(pluginConfig.LBTCConfig.SourceTokenAddress))
+ if err2 != nil {
+ return nil, fmt.Errorf("new lbtc reader: %w", err2)
+ }
+ tokenDataProviders[cciptypes.Address(pluginConfig.LBTCConfig.SourceTokenAddress.String())] = lbtcReader
+ }
// Prom wrappers
onRampReader = observability.NewObservedOnRampReader(onRampReader, srcChainID, ccip.ExecPluginLabel)
@@ -149,7 +171,7 @@ func NewExecServices(ctx context.Context, lggr logger.Logger, jb job.Job, srcPro
tokenDataProviders,
tokenDataWorkerNumWorkers,
tokenDataWorkerTimeout,
- 2*tokenDataWorkerTimeout,
+ expirationDurTokenData,
)
wrappedPluginFactory := NewExecutionReportingPluginFactory(ExecutionPluginStaticConfig{
diff --git a/core/services/ocr2/plugins/ccip/ccipexec/ocr2.go b/core/services/ocr2/plugins/ccip/ccipexec/ocr2.go
index 4a09cf37b45..2c70cac4978 100644
--- a/core/services/ocr2/plugins/ccip/ccipexec/ocr2.go
+++ b/core/services/ocr2/plugins/ccip/ccipexec/ocr2.go
@@ -468,6 +468,19 @@ func (r *ExecutionReportingPlugin) buildReport(ctx context.Context, lggr logger.
return encodedReport, nil
}
+// Returns required number of observations to reach consensus
+func (r *ExecutionReportingPlugin) getConsensusThreshold() int {
+ // Default consensus threshold is F+1
+ consensusThreshold := r.F + 1
+ if r.batchingStrategy.GetBatchingStrategyID() == ZKOverflowBatchingStrategyID {
+ // For batching strategy 1, consensus threshold is 2F+1
+ // This is because chains that can overflow need to reach consensus during the inflight cache period
+ // to avoid 2 transmissions round of an overflown message.
+ consensusThreshold = 2*r.F + 1
+ }
+ return consensusThreshold
+}
+
func (r *ExecutionReportingPlugin) Report(ctx context.Context, timestamp types.ReportTimestamp, query types.Query, observations []types.AttributedObservation) (bool, types.Report, error) {
lggr := r.lggr.Named("ExecutionReport")
if healthy, err := r.chainHealthcheck.IsHealthy(ctx); err != nil {
@@ -475,14 +488,16 @@ func (r *ExecutionReportingPlugin) Report(ctx context.Context, timestamp types.R
} else if !healthy {
return false, nil, ccip.ErrChainIsNotHealthy
}
+ consensusThreshold := r.getConsensusThreshold()
+ lggr.Infof("Consensus threshold set to: %d", consensusThreshold)
+
parsableObservations := ccip.GetParsableObservations[ccip.ExecutionObservation](lggr, observations)
- // Need at least F+1 observations
- if len(parsableObservations) <= r.F {
- lggr.Warn("Non-empty observations <= F, need at least F+1 to continue")
+ if len(parsableObservations) < consensusThreshold {
+ lggr.Warnf("Insufficient observations: only %d received, but need more than %d to proceed", len(parsableObservations), consensusThreshold)
return false, nil, nil
}
- observedMessages, err := calculateObservedMessagesConsensus(parsableObservations, r.F)
+ observedMessages, err := calculateObservedMessagesConsensus(parsableObservations, consensusThreshold)
if err != nil {
return false, nil, err
}
diff --git a/core/services/ocr2/plugins/ccip/ccipexec/ocr2_test.go b/core/services/ocr2/plugins/ccip/ccipexec/ocr2_test.go
index ff6371fa2ce..73a1f431399 100644
--- a/core/services/ocr2/plugins/ccip/ccipexec/ocr2_test.go
+++ b/core/services/ocr2/plugins/ccip/ccipexec/ocr2_test.go
@@ -16,17 +16,18 @@ import (
mapset "github.com/deckarep/golang-set/v2"
"github.com/ethereum/go-ethereum/common"
"github.com/pkg/errors"
- "github.com/smartcontractkit/libocr/commontypes"
- "github.com/smartcontractkit/libocr/offchainreporting2/types"
- ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
- cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip"
+ "github.com/smartcontractkit/libocr/commontypes"
+ "github.com/smartcontractkit/libocr/offchainreporting2/types"
+ ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types"
+ cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip"
lpMocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils"
+ "github.com/smartcontractkit/chainlink/v2/core/internal/testutils"
"github.com/smartcontractkit/chainlink/v2/core/logger"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/cache"
@@ -40,8 +41,7 @@ import (
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/prices"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/testhelpers"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/tokendata"
-
- "github.com/smartcontractkit/chainlink/v2/core/internal/testutils"
+ "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/statuschecker"
)
func TestExecutionReportingPlugin_Observation(t *testing.T) {
@@ -232,19 +232,21 @@ func TestExecutionReportingPlugin_Observation(t *testing.T) {
func TestExecutionReportingPlugin_Report(t *testing.T) {
testCases := []struct {
- name string
- f int
- committedSeqNum uint64
- observations []ccip.ExecutionObservation
+ name string
+ f int
+ batchingStrategyID uint32
+ committedSeqNum uint64
+ observations []ccip.ExecutionObservation
expectingSomeReport bool
expectedReport cciptypes.ExecReport
expectingSomeErr bool
}{
{
- name: "not enough observations to form consensus",
- f: 5,
- committedSeqNum: 5,
+ name: "not enough observations to form consensus - best effort batching",
+ f: 5,
+ batchingStrategyID: 0,
+ committedSeqNum: 5,
observations: []ccip.ExecutionObservation{
{Messages: map[uint64]ccip.MsgData{3: {}, 4: {}}},
{Messages: map[uint64]ccip.MsgData{3: {}, 4: {}}},
@@ -252,6 +254,21 @@ func TestExecutionReportingPlugin_Report(t *testing.T) {
expectingSomeErr: false,
expectingSomeReport: false,
},
+ {
+ name: "not enough observaitons to form consensus - zk batching",
+ f: 5,
+ batchingStrategyID: 1,
+ committedSeqNum: 5,
+ observations: []ccip.ExecutionObservation{
+ {Messages: map[uint64]ccip.MsgData{3: {}, 4: {}}},
+ {Messages: map[uint64]ccip.MsgData{3: {}, 4: {}}},
+ {Messages: map[uint64]ccip.MsgData{3: {}, 4: {}}},
+ {Messages: map[uint64]ccip.MsgData{3: {}, 4: {}}},
+ {Messages: map[uint64]ccip.MsgData{3: {}, 4: {}}},
+ {Messages: map[uint64]ccip.MsgData{3: {}, 4: {}}},
+ {Messages: map[uint64]ccip.MsgData{3: {}, 4: {}}},
+ },
+ },
{
name: "zero observations",
f: 0,
@@ -268,6 +285,9 @@ func TestExecutionReportingPlugin_Report(t *testing.T) {
p := ExecutionReportingPlugin{}
p.lggr = logger.TestLogger(t)
p.F = tc.f
+ bs, err := NewBatchingStrategy(tc.batchingStrategyID, &statuschecker.TxmStatusChecker{})
+ require.NoError(t, err)
+ p.batchingStrategy = bs
p.commitStoreReader = ccipdatamocks.NewCommitStoreReader(t)
chainHealthcheck := ccipcachemocks.NewChainHealthcheck(t)
@@ -281,12 +301,12 @@ func TestExecutionReportingPlugin_Report(t *testing.T) {
observations[i] = types.AttributedObservation{Observation: b, Observer: commontypes.OracleID(i + 1)}
}
- _, _, err := p.Report(ctx, types.ReportTimestamp{}, types.Query{}, observations)
+ _, _, err2 := p.Report(ctx, types.ReportTimestamp{}, types.Query{}, observations)
if tc.expectingSomeErr {
- assert.Error(t, err)
+ assert.Error(t, err2)
return
}
- assert.NoError(t, err)
+ assert.NoError(t, err2)
})
}
}
@@ -427,8 +447,10 @@ func TestExecutionReportingPlugin_buildReport(t *testing.T) {
p.metricsCollector = ccip.NoopMetricsCollector
p.commitStoreReader = commitStore
+ feeEstimatorConfig := ccipdatamocks.NewFeeEstimatorConfigReader(t)
+
lp := lpMocks.NewLogPoller(t)
- offRampReader, err := v1_2_0.NewOffRamp(logger.TestLogger(t), utils.RandomAddress(), nil, lp, nil, nil)
+ offRampReader, err := v1_2_0.NewOffRamp(logger.TestLogger(t), utils.RandomAddress(), nil, lp, nil, nil, feeEstimatorConfig)
assert.NoError(t, err)
p.offRampReader = offRampReader
@@ -1375,7 +1397,9 @@ func Test_prepareTokenExecData(t *testing.T) {
}
func encodeExecutionReport(t *testing.T, report cciptypes.ExecReport) []byte {
- reader, err := v1_2_0.NewOffRamp(logger.TestLogger(t), utils.RandomAddress(), nil, nil, nil, nil)
+ feeEstimatorConfig := ccipdatamocks.NewFeeEstimatorConfigReader(t)
+
+ reader, err := v1_2_0.NewOffRamp(logger.TestLogger(t), utils.RandomAddress(), nil, nil, nil, nil, feeEstimatorConfig)
require.NoError(t, err)
ctx := testutils.Context(t)
encodedReport, err := reader.EncodeExecutionReport(ctx, report)
@@ -1418,3 +1442,36 @@ func TestExecutionReportingPlugin_ensurePriceRegistrySynchronization(t *testing.
require.NoError(t, err)
require.Equal(t, mockPriceRegistryReader2, p.sourcePriceRegistry)
}
+
+func TestExecutionReportingPlugin_getConsensusThreshold(t *testing.T) {
+ tests := []struct {
+ name string
+ batchingStrategyID uint32
+ F int
+ expectedConsensusThreshold int
+ }{
+ {
+ name: "zk batching strategy",
+ batchingStrategyID: uint32(1),
+ F: 5,
+ expectedConsensusThreshold: 11,
+ },
+ {
+ name: "default batching strategy",
+ batchingStrategyID: uint32(0),
+ F: 5,
+ expectedConsensusThreshold: 6,
+ },
+ }
+
+ for _, tc := range tests {
+ t.Run(tc.name, func(t *testing.T) {
+ p := &ExecutionReportingPlugin{}
+ p.F = tc.F
+ bs, err := NewBatchingStrategy(tc.batchingStrategyID, &statuschecker.TxmStatusChecker{})
+ require.NoError(t, err)
+ p.batchingStrategy = bs
+ require.Equal(t, tc.expectedConsensusThreshold, p.getConsensusThreshold())
+ })
+ }
+}
diff --git a/core/services/ocr2/plugins/ccip/config/config.go b/core/services/ocr2/plugins/ccip/config/config.go
index a24a6edfd13..fbf8d590cfe 100644
--- a/core/services/ocr2/plugins/ccip/config/config.go
+++ b/core/services/ocr2/plugins/ccip/config/config.go
@@ -108,6 +108,7 @@ func (c *DynamicPriceGetterConfig) Validate() error {
type ExecPluginJobSpecConfig struct {
SourceStartBlock, DestStartBlock uint64 // Only for first time job add.
USDCConfig USDCConfig
+ LBTCConfig LBTCConfig
}
type USDCConfig struct {
@@ -119,10 +120,19 @@ type USDCConfig struct {
AttestationAPIIntervalMilliseconds int
}
+type LBTCConfig struct {
+ SourceTokenAddress common.Address
+ AttestationAPI string
+ AttestationAPITimeoutSeconds uint
+ // AttestationAPIIntervalMilliseconds can be set to -1 to disable or 0 to use a default interval.
+ AttestationAPIIntervalMilliseconds int
+}
+
type ExecPluginConfig struct {
SourceStartBlock, DestStartBlock uint64 // Only for first time job add.
IsSourceProvider bool
USDCConfig USDCConfig
+ LBTCConfig LBTCConfig
JobID string
}
@@ -136,17 +146,30 @@ func (e ExecPluginConfig) Encode() ([]byte, error) {
func (uc *USDCConfig) ValidateUSDCConfig() error {
if uc.AttestationAPI == "" {
- return errors.New("AttestationAPI is required")
+ return errors.New("USDCConfig: AttestationAPI is required")
}
if uc.AttestationAPIIntervalMilliseconds < -1 {
- return errors.New("AttestationAPIIntervalMilliseconds must be -1 to disable, 0 for default or greater to define the exact interval")
+ return errors.New("USDCConfig: AttestationAPIIntervalMilliseconds must be -1 to disable, 0 for default or greater to define the exact interval")
}
if uc.SourceTokenAddress == utils.ZeroAddress {
- return errors.New("SourceTokenAddress is required")
+ return errors.New("USDCConfig: SourceTokenAddress is required")
}
if uc.SourceMessageTransmitterAddress == utils.ZeroAddress {
- return errors.New("SourceMessageTransmitterAddress is required")
+ return errors.New("USDCConfig: SourceMessageTransmitterAddress is required")
}
return nil
}
+
+func (lc *LBTCConfig) ValidateLBTCConfig() error {
+ if lc.AttestationAPI == "" {
+ return errors.New("LBTCConfig: AttestationAPI is required")
+ }
+ if lc.AttestationAPIIntervalMilliseconds < -1 {
+ return errors.New("LBTCConfig: AttestationAPIIntervalMilliseconds must be -1 to disable, 0 for default or greater to define the exact interval")
+ }
+ if lc.SourceTokenAddress == utils.ZeroAddress {
+ return errors.New("LBTCConfig: SourceTokenAddress is required")
+ }
+ return nil
+}
diff --git a/core/services/ocr2/plugins/ccip/config/type_and_version.go b/core/services/ocr2/plugins/ccip/config/type_and_version.go
index 9d5e1629c11..ecfe8d0c5a7 100644
--- a/core/services/ocr2/plugins/ccip/config/type_and_version.go
+++ b/core/services/ocr2/plugins/ccip/config/type_and_version.go
@@ -19,6 +19,7 @@ var (
EVM2EVMOffRamp ContractType = "EVM2EVMOffRamp"
CommitStore ContractType = "CommitStore"
PriceRegistry ContractType = "PriceRegistry"
+ Unknown ContractType = "Unknown" // 1.0.0 Contracts which have no TypeAndVersion
ContractTypes = mapset.NewSet[ContractType](
EVM2EVMOffRamp,
EVM2EVMOnRamp,
@@ -63,7 +64,13 @@ func TypeAndVersion(addr common.Address, client bind.ContractBackend) (ContractT
return ContractType(contractType), *v, nil
}
+// default version to use when TypeAndVersion is missing.
+const defaultVersion = "1.0.0"
+
func ParseTypeAndVersion(tvStr string) (string, string, error) {
+ if tvStr == "" {
+ tvStr = string(Unknown) + " " + defaultVersion
+ }
typeAndVersionValues := strings.Split(tvStr, " ")
if len(typeAndVersionValues) < 2 {
diff --git a/core/services/ocr2/plugins/ccip/config/type_and_version_test.go b/core/services/ocr2/plugins/ccip/config/type_and_version_test.go
new file mode 100644
index 00000000000..807bb59f3be
--- /dev/null
+++ b/core/services/ocr2/plugins/ccip/config/type_and_version_test.go
@@ -0,0 +1,50 @@
+package config
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestParseTypeAndVersion(t *testing.T) {
+ tests := []struct {
+ name string
+ input string
+ expectedType string
+ expectedVersion string
+ expectedError string
+ }{
+ {
+ name: "Valid input",
+ input: string(EVM2EVMOnRamp) + " 1.2.0",
+ expectedType: string(EVM2EVMOnRamp),
+ expectedVersion: "1.2.0",
+ },
+ {
+ name: "Empty input",
+ input: "",
+ expectedType: string(Unknown),
+ expectedVersion: defaultVersion,
+ },
+ {
+ name: "Invalid input",
+ input: "InvalidInput",
+ expectedError: "invalid type and version InvalidInput",
+ },
+ }
+
+ for _, tc := range tests {
+ t.Run(tc.name, func(t *testing.T) {
+ actualType, actualVersion, err := ParseTypeAndVersion(tc.input)
+
+ if tc.expectedError != "" {
+ require.EqualError(t, err, tc.expectedError)
+ } else {
+ require.NoError(t, err)
+ assert.Equal(t, tc.expectedType, actualType)
+ assert.Equal(t, tc.expectedVersion, actualVersion)
+ }
+ })
+ }
+}
diff --git a/core/services/ocr2/plugins/ccip/estimatorconfig/config.go b/core/services/ocr2/plugins/ccip/estimatorconfig/config.go
new file mode 100644
index 00000000000..4737bd33e89
--- /dev/null
+++ b/core/services/ocr2/plugins/ccip/estimatorconfig/config.go
@@ -0,0 +1,83 @@
+package estimatorconfig
+
+import (
+ "context"
+ "math/big"
+
+ "github.com/smartcontractkit/chainlink-common/pkg/types/ccip"
+)
+
+// FeeEstimatorConfigProvider implements abstract storage for the DataAvailability settings in onRamp dynamic Config.
+// It's implemented to transfer DA config from different entities offRamp, onRamp, commitStore without injecting the
+// strong dependency between modules. ConfigProvider fetch ccip.OnRampReader object reads and returns only relevant
+// fields for the daGasEstimator from the encapsulated onRampReader.
+type FeeEstimatorConfigProvider interface {
+ SetOnRampReader(reader ccip.OnRampReader)
+ AddGasPriceInterceptor(GasPriceInterceptor)
+ ModifyGasPriceComponents(ctx context.Context, execGasPrice, daGasPrice *big.Int) (modExecGasPrice, modDAGasPrice *big.Int, err error)
+ GetDataAvailabilityConfig(ctx context.Context) (destDataAvailabilityOverheadGas, destGasPerDataAvailabilityByte, destDataAvailabilityMultiplierBps int64, err error)
+}
+
+type GasPriceInterceptor interface {
+ ModifyGasPriceComponents(ctx context.Context, execGasPrice, daGasPrice *big.Int) (modExecGasPrice, modDAGasPrice *big.Int, err error)
+}
+
+type FeeEstimatorConfigService struct {
+ onRampReader ccip.OnRampReader
+ gasPriceInterceptors []GasPriceInterceptor
+}
+
+func NewFeeEstimatorConfigService() *FeeEstimatorConfigService {
+ return &FeeEstimatorConfigService{}
+}
+
+// SetOnRampReader Sets the onRamp reader instance.
+// must be called once for each instance.
+func (c *FeeEstimatorConfigService) SetOnRampReader(reader ccip.OnRampReader) {
+ c.onRampReader = reader
+}
+
+// GetDataAvailabilityConfig Returns dynamic config data availability parameters.
+// GetDynamicConfig should be cached in the onRamp reader to avoid unnecessary on-chain calls
+func (c *FeeEstimatorConfigService) GetDataAvailabilityConfig(ctx context.Context) (destDataAvailabilityOverheadGas, destGasPerDataAvailabilityByte, destDataAvailabilityMultiplierBps int64, err error) {
+ if c.onRampReader == nil {
+ return 0, 0, 0, nil
+ }
+
+ cfg, err := c.onRampReader.GetDynamicConfig(ctx)
+ if err != nil {
+ return 0, 0, 0, err
+ }
+
+ return int64(cfg.DestDataAvailabilityOverheadGas),
+ int64(cfg.DestGasPerDataAvailabilityByte),
+ int64(cfg.DestDataAvailabilityMultiplierBps),
+ err
+}
+
+// AddGasPriceInterceptor adds price interceptors that can modify gas price.
+func (c *FeeEstimatorConfigService) AddGasPriceInterceptor(gpi GasPriceInterceptor) {
+ if gpi != nil {
+ c.gasPriceInterceptors = append(c.gasPriceInterceptors, gpi)
+ }
+}
+
+// ModifyGasPriceComponents applies gasPrice interceptors and returns modified gasPrice.
+func (c *FeeEstimatorConfigService) ModifyGasPriceComponents(ctx context.Context, gasPrice, daGasPrice *big.Int) (*big.Int, *big.Int, error) {
+ if len(c.gasPriceInterceptors) == 0 {
+ return gasPrice, daGasPrice, nil
+ }
+
+ // values are mutable, it is necessary to copy the values to protect the arguments from modification.
+ cpGasPrice := new(big.Int).Set(gasPrice)
+ cpDAGasPrice := new(big.Int).Set(daGasPrice)
+
+ var err error
+ for _, interceptor := range c.gasPriceInterceptors {
+ if cpGasPrice, cpDAGasPrice, err = interceptor.ModifyGasPriceComponents(ctx, cpGasPrice, cpDAGasPrice); err != nil {
+ return nil, nil, err
+ }
+ }
+
+ return cpGasPrice, cpDAGasPrice, nil
+}
diff --git a/core/services/ocr2/plugins/ccip/estimatorconfig/config_test.go b/core/services/ocr2/plugins/ccip/estimatorconfig/config_test.go
new file mode 100644
index 00000000000..3ecd88ae3bd
--- /dev/null
+++ b/core/services/ocr2/plugins/ccip/estimatorconfig/config_test.go
@@ -0,0 +1,112 @@
+package estimatorconfig_test
+
+import (
+ "context"
+ "errors"
+ "math/big"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+
+ mocks2 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/estimatorconfig/mocks"
+
+ "github.com/smartcontractkit/chainlink-common/pkg/types/ccip"
+ "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/estimatorconfig"
+ "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata/mocks"
+)
+
+func TestFeeEstimatorConfigService(t *testing.T) {
+ svc := estimatorconfig.NewFeeEstimatorConfigService()
+ ctx := context.Background()
+
+ var expectedDestDataAvailabilityOverheadGas int64 = 1
+ var expectedDestGasPerDataAvailabilityByte int64 = 2
+ var expectedDestDataAvailabilityMultiplierBps int64 = 3
+
+ onRampReader := mocks.NewOnRampReader(t)
+ destDataAvailabilityOverheadGas, destGasPerDataAvailabilityByte, destDataAvailabilityMultiplierBps, err := svc.GetDataAvailabilityConfig(ctx)
+ require.NoError(t, err) // if onRampReader not set, return nil error and 0 values
+ require.EqualValues(t, 0, destDataAvailabilityOverheadGas)
+ require.EqualValues(t, 0, destGasPerDataAvailabilityByte)
+ require.EqualValues(t, 0, destDataAvailabilityMultiplierBps)
+ svc.SetOnRampReader(onRampReader)
+
+ onRampReader.On("GetDynamicConfig", ctx).
+ Return(ccip.OnRampDynamicConfig{
+ DestDataAvailabilityOverheadGas: uint32(expectedDestDataAvailabilityOverheadGas),
+ DestGasPerDataAvailabilityByte: uint16(expectedDestGasPerDataAvailabilityByte),
+ DestDataAvailabilityMultiplierBps: uint16(expectedDestDataAvailabilityMultiplierBps),
+ }, nil).Once()
+
+ destDataAvailabilityOverheadGas, destGasPerDataAvailabilityByte, destDataAvailabilityMultiplierBps, err = svc.GetDataAvailabilityConfig(ctx)
+ require.NoError(t, err)
+ require.Equal(t, expectedDestDataAvailabilityOverheadGas, destDataAvailabilityOverheadGas)
+ require.Equal(t, expectedDestGasPerDataAvailabilityByte, destGasPerDataAvailabilityByte)
+ require.Equal(t, expectedDestDataAvailabilityMultiplierBps, destDataAvailabilityMultiplierBps)
+
+ onRampReader.On("GetDynamicConfig", ctx).
+ Return(ccip.OnRampDynamicConfig{}, errors.New("test")).Once()
+ _, _, _, err = svc.GetDataAvailabilityConfig(ctx)
+ require.Error(t, err)
+}
+
+func TestModifyGasPriceComponents(t *testing.T) {
+ t.Run("success modification", func(t *testing.T) {
+ svc := estimatorconfig.NewFeeEstimatorConfigService()
+ ctx := context.Background()
+
+ initialExecGasPrice, initialDaGasPrice := big.NewInt(10), big.NewInt(1)
+
+ gpi1 := mocks2.NewGasPriceInterceptor(t)
+ svc.AddGasPriceInterceptor(gpi1)
+
+ // change in first interceptor
+ firstModExecGasPrice, firstModDaGasPrice := big.NewInt(5), big.NewInt(2)
+ gpi1.On("ModifyGasPriceComponents", ctx, initialExecGasPrice, initialDaGasPrice).
+ Return(firstModExecGasPrice, firstModDaGasPrice, nil)
+
+ gpi2 := mocks2.NewGasPriceInterceptor(t)
+ svc.AddGasPriceInterceptor(gpi2)
+
+ // change in second iterceptor
+ secondModExecGasPrice, secondModDaGasPrice := big.NewInt(50), big.NewInt(20)
+ gpi2.On("ModifyGasPriceComponents", ctx, firstModExecGasPrice, firstModDaGasPrice).
+ Return(secondModExecGasPrice, secondModDaGasPrice, nil)
+
+ // has to return second interceptor values
+ resGasPrice, resDAGasPrice, err := svc.ModifyGasPriceComponents(ctx, initialExecGasPrice, initialDaGasPrice)
+ require.NoError(t, err)
+ require.Equal(t, secondModExecGasPrice.Int64(), resGasPrice.Int64())
+ require.Equal(t, secondModDaGasPrice.Int64(), resDAGasPrice.Int64())
+ })
+
+ t.Run("error modification", func(t *testing.T) {
+ svc := estimatorconfig.NewFeeEstimatorConfigService()
+ ctx := context.Background()
+
+ initialExecGasPrice, initialDaGasPrice := big.NewInt(10), big.NewInt(1)
+ gpi1 := mocks2.NewGasPriceInterceptor(t)
+ svc.AddGasPriceInterceptor(gpi1)
+ gpi1.On("ModifyGasPriceComponents", ctx, initialExecGasPrice, initialDaGasPrice).
+ Return(nil, nil, errors.New("test"))
+
+ // has to return second interceptor values
+ _, _, err := svc.ModifyGasPriceComponents(ctx, initialExecGasPrice, initialDaGasPrice)
+ require.Error(t, err)
+ })
+
+ t.Run("without interceptors", func(t *testing.T) {
+ svc := estimatorconfig.NewFeeEstimatorConfigService()
+ ctx := context.Background()
+
+ initialExecGasPrice, initialDaGasPrice := big.NewInt(10), big.NewInt(1)
+
+ // has to return second interceptor values
+ resGasPrice, resDAGasPrice, err := svc.ModifyGasPriceComponents(ctx, initialExecGasPrice, initialDaGasPrice)
+ require.NoError(t, err)
+
+ // values should not be modified
+ require.Equal(t, initialExecGasPrice, resGasPrice)
+ require.Equal(t, initialDaGasPrice, resDAGasPrice)
+ })
+}
diff --git a/core/services/ocr2/plugins/ccip/estimatorconfig/interceptors/mantle/interceptor.go b/core/services/ocr2/plugins/ccip/estimatorconfig/interceptors/mantle/interceptor.go
new file mode 100644
index 00000000000..359f32e13a5
--- /dev/null
+++ b/core/services/ocr2/plugins/ccip/estimatorconfig/interceptors/mantle/interceptor.go
@@ -0,0 +1,82 @@
+package mantle
+
+import (
+ "context"
+ "fmt"
+ "math/big"
+ "strings"
+ "time"
+
+ "github.com/ethereum/go-ethereum"
+ "github.com/ethereum/go-ethereum/accounts/abi"
+ "github.com/ethereum/go-ethereum/common"
+
+ evmClient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client"
+)
+
+const (
+ // tokenRatio is not volatile and can be requested not often.
+ tokenRatioUpdateInterval = 60 * time.Minute
+ // tokenRatio fetches the tokenRatio used for Mantle's gas price calculation
+ tokenRatioMethod = "tokenRatio"
+ mantleTokenRatioAbiString = `[{"inputs":[],"name":"tokenRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]`
+)
+
+type Interceptor struct {
+ client evmClient.Client
+ tokenRatioCallData []byte
+ tokenRatio *big.Int
+ tokenRatioLastUpdate time.Time
+}
+
+func NewInterceptor(_ context.Context, client evmClient.Client) (*Interceptor, error) {
+ // 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 Mantle; %w", tokenRatioMethod, err)
+ }
+ tokenRatioCallData, err := tokenRatioMethodAbi.Pack(tokenRatioMethod)
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse GasPriceOracle %s() calldata for Mantle; %w", tokenRatioMethod, err)
+ }
+
+ return &Interceptor{
+ client: client,
+ tokenRatioCallData: tokenRatioCallData,
+ }, nil
+}
+
+// ModifyGasPriceComponents returns modified gasPrice.
+func (i *Interceptor) ModifyGasPriceComponents(ctx context.Context, execGasPrice, daGasPrice *big.Int) (*big.Int, *big.Int, error) {
+ if time.Since(i.tokenRatioLastUpdate) > tokenRatioUpdateInterval {
+ mantleTokenRatio, err := i.getMantleTokenRatio(ctx)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ i.tokenRatio, i.tokenRatioLastUpdate = mantleTokenRatio, time.Now()
+ }
+
+ // multiply daGasPrice and execGas price by tokenRatio
+ newExecGasPrice := new(big.Int).Mul(execGasPrice, i.tokenRatio)
+ newDAGasPrice := new(big.Int).Mul(daGasPrice, i.tokenRatio)
+ return newExecGasPrice, newDAGasPrice, nil
+}
+
+// getMantleTokenRatio Requests and returns a token ratio value for the Mantle chain.
+func (i *Interceptor) getMantleTokenRatio(ctx context.Context) (*big.Int, error) {
+ // FIXME it's removed from chainlink repo
+ // precompile := common.HexToAddress(rollups.OPGasOracleAddress)
+ precompile := common.Address{}
+
+ tokenRatio, err := i.client.CallContract(ctx, ethereum.CallMsg{
+ To: &precompile,
+ Data: i.tokenRatioCallData,
+ }, nil)
+
+ if err != nil {
+ return nil, fmt.Errorf("getMantleTokenRatio call failed: %w", err)
+ }
+
+ return new(big.Int).SetBytes(tokenRatio), nil
+}
diff --git a/core/services/ocr2/plugins/ccip/estimatorconfig/interceptors/mantle/interceptor_test.go b/core/services/ocr2/plugins/ccip/estimatorconfig/interceptors/mantle/interceptor_test.go
new file mode 100644
index 00000000000..9134d996c27
--- /dev/null
+++ b/core/services/ocr2/plugins/ccip/estimatorconfig/interceptors/mantle/interceptor_test.go
@@ -0,0 +1,96 @@
+package mantle
+
+import (
+ "context"
+ "math/big"
+ "testing"
+
+ "github.com/ethereum/go-ethereum"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/stretchr/testify/mock"
+ "github.com/stretchr/testify/require"
+
+ "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks"
+)
+
+func TestInterceptor(t *testing.T) {
+ ethClient := mocks.NewClient(t)
+ ctx := context.Background()
+
+ tokenRatio := big.NewInt(10)
+ interceptor, err := NewInterceptor(ctx, ethClient)
+ require.NoError(t, err)
+
+ // request token ratio
+ ethClient.On("CallContract", ctx, mock.IsType(ethereum.CallMsg{}), mock.IsType(&big.Int{})).
+ Return(common.BigToHash(tokenRatio).Bytes(), nil).Once()
+
+ modExecGasPrice, modDAGasPrice, err := interceptor.ModifyGasPriceComponents(ctx, big.NewInt(1), big.NewInt(1))
+ require.NoError(t, err)
+ require.Equal(t, int64(10), modExecGasPrice.Int64())
+ require.Equal(t, int64(10), modDAGasPrice.Int64())
+
+ // second call won't invoke eth client
+ modExecGasPrice, modDAGasPrice, err = interceptor.ModifyGasPriceComponents(ctx, big.NewInt(2), big.NewInt(1))
+ require.NoError(t, err)
+ require.Equal(t, int64(20), modExecGasPrice.Int64())
+ require.Equal(t, int64(10), modDAGasPrice.Int64())
+}
+
+func TestModifyGasPriceComponents(t *testing.T) {
+ testCases := map[string]struct {
+ execGasPrice *big.Int
+ daGasPrice *big.Int
+ tokenRatio *big.Int
+ resultExecGasPrice *big.Int
+ resultDAGasPrice *big.Int
+ }{
+ "regular": {
+ execGasPrice: big.NewInt(1000),
+ daGasPrice: big.NewInt(100),
+ resultExecGasPrice: big.NewInt(2000),
+ resultDAGasPrice: big.NewInt(200),
+ tokenRatio: big.NewInt(2),
+ },
+ "zero DAGasPrice": {
+ execGasPrice: big.NewInt(1000),
+ daGasPrice: big.NewInt(0),
+ resultExecGasPrice: big.NewInt(5000),
+ resultDAGasPrice: big.NewInt(0),
+ tokenRatio: big.NewInt(5),
+ },
+ "zero ExecGasPrice": {
+ execGasPrice: big.NewInt(0),
+ daGasPrice: big.NewInt(10),
+ resultExecGasPrice: big.NewInt(0),
+ resultDAGasPrice: big.NewInt(50),
+ tokenRatio: big.NewInt(5),
+ },
+ "zero token ratio": {
+ execGasPrice: big.NewInt(15),
+ daGasPrice: big.NewInt(10),
+ resultExecGasPrice: big.NewInt(0),
+ resultDAGasPrice: big.NewInt(0),
+ tokenRatio: big.NewInt(0),
+ },
+ }
+
+ for tcName, tc := range testCases {
+ t.Run(tcName, func(t *testing.T) {
+ ethClient := mocks.NewClient(t)
+ ctx := context.Background()
+
+ interceptor, err := NewInterceptor(ctx, ethClient)
+ require.NoError(t, err)
+
+ // request token ratio
+ ethClient.On("CallContract", ctx, mock.IsType(ethereum.CallMsg{}), mock.IsType(&big.Int{})).
+ Return(common.BigToHash(tc.tokenRatio).Bytes(), nil).Once()
+
+ modExecGasPrice, modDAGasPrice, err := interceptor.ModifyGasPriceComponents(ctx, tc.execGasPrice, tc.daGasPrice)
+ require.NoError(t, err)
+ require.Equal(t, tc.resultExecGasPrice.Int64(), modExecGasPrice.Int64())
+ require.Equal(t, tc.resultDAGasPrice.Int64(), modDAGasPrice.Int64())
+ })
+ }
+}
diff --git a/core/services/ocr2/plugins/ccip/estimatorconfig/mocks/gas_price_interceptor_mock.go b/core/services/ocr2/plugins/ccip/estimatorconfig/mocks/gas_price_interceptor_mock.go
new file mode 100644
index 00000000000..78d7fa7bb35
--- /dev/null
+++ b/core/services/ocr2/plugins/ccip/estimatorconfig/mocks/gas_price_interceptor_mock.go
@@ -0,0 +1,106 @@
+// Code generated by mockery v2.50.0. DO NOT EDIT.
+
+package mocks
+
+import (
+ context "context"
+ big "math/big"
+
+ mock "github.com/stretchr/testify/mock"
+)
+
+// GasPriceInterceptor is an autogenerated mock type for the GasPriceInterceptor type
+type GasPriceInterceptor struct {
+ mock.Mock
+}
+
+type GasPriceInterceptor_Expecter struct {
+ mock *mock.Mock
+}
+
+func (_m *GasPriceInterceptor) EXPECT() *GasPriceInterceptor_Expecter {
+ return &GasPriceInterceptor_Expecter{mock: &_m.Mock}
+}
+
+// ModifyGasPriceComponents provides a mock function with given fields: ctx, execGasPrice, daGasPrice
+func (_m *GasPriceInterceptor) ModifyGasPriceComponents(ctx context.Context, execGasPrice *big.Int, daGasPrice *big.Int) (*big.Int, *big.Int, error) {
+ ret := _m.Called(ctx, execGasPrice, daGasPrice)
+
+ if len(ret) == 0 {
+ panic("no return value specified for ModifyGasPriceComponents")
+ }
+
+ var r0 *big.Int
+ var r1 *big.Int
+ var r2 error
+ if rf, ok := ret.Get(0).(func(context.Context, *big.Int, *big.Int) (*big.Int, *big.Int, error)); ok {
+ return rf(ctx, execGasPrice, daGasPrice)
+ }
+ if rf, ok := ret.Get(0).(func(context.Context, *big.Int, *big.Int) *big.Int); ok {
+ r0 = rf(ctx, execGasPrice, daGasPrice)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(*big.Int)
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func(context.Context, *big.Int, *big.Int) *big.Int); ok {
+ r1 = rf(ctx, execGasPrice, daGasPrice)
+ } else {
+ if ret.Get(1) != nil {
+ r1 = ret.Get(1).(*big.Int)
+ }
+ }
+
+ if rf, ok := ret.Get(2).(func(context.Context, *big.Int, *big.Int) error); ok {
+ r2 = rf(ctx, execGasPrice, daGasPrice)
+ } else {
+ r2 = ret.Error(2)
+ }
+
+ return r0, r1, r2
+}
+
+// GasPriceInterceptor_ModifyGasPriceComponents_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ModifyGasPriceComponents'
+type GasPriceInterceptor_ModifyGasPriceComponents_Call struct {
+ *mock.Call
+}
+
+// ModifyGasPriceComponents is a helper method to define mock.On call
+// - ctx context.Context
+// - execGasPrice *big.Int
+// - daGasPrice *big.Int
+func (_e *GasPriceInterceptor_Expecter) ModifyGasPriceComponents(ctx interface{}, execGasPrice interface{}, daGasPrice interface{}) *GasPriceInterceptor_ModifyGasPriceComponents_Call {
+ return &GasPriceInterceptor_ModifyGasPriceComponents_Call{Call: _e.mock.On("ModifyGasPriceComponents", ctx, execGasPrice, daGasPrice)}
+}
+
+func (_c *GasPriceInterceptor_ModifyGasPriceComponents_Call) Run(run func(ctx context.Context, execGasPrice *big.Int, daGasPrice *big.Int)) *GasPriceInterceptor_ModifyGasPriceComponents_Call {
+ _c.Call.Run(func(args mock.Arguments) {
+ run(args[0].(context.Context), args[1].(*big.Int), args[2].(*big.Int))
+ })
+ return _c
+}
+
+func (_c *GasPriceInterceptor_ModifyGasPriceComponents_Call) Return(modExecGasPrice *big.Int, modDAGasPrice *big.Int, err error) *GasPriceInterceptor_ModifyGasPriceComponents_Call {
+ _c.Call.Return(modExecGasPrice, modDAGasPrice, err)
+ return _c
+}
+
+func (_c *GasPriceInterceptor_ModifyGasPriceComponents_Call) RunAndReturn(run func(context.Context, *big.Int, *big.Int) (*big.Int, *big.Int, error)) *GasPriceInterceptor_ModifyGasPriceComponents_Call {
+ _c.Call.Return(run)
+ return _c
+}
+
+// NewGasPriceInterceptor creates a new instance of GasPriceInterceptor. 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 NewGasPriceInterceptor(t interface {
+ mock.TestingT
+ Cleanup(func())
+}) *GasPriceInterceptor {
+ mock := &GasPriceInterceptor{}
+ mock.Mock.Test(t)
+
+ t.Cleanup(func() { mock.AssertExpectations(t) })
+
+ return mock
+}
diff --git a/core/services/ocr2/plugins/ccip/exportinternal.go b/core/services/ocr2/plugins/ccip/exportinternal.go
index 6b24cba4857..10b802eeabc 100644
--- a/core/services/ocr2/plugins/ccip/exportinternal.go
+++ b/core/services/ocr2/plugins/ccip/exportinternal.go
@@ -5,14 +5,17 @@ import (
"math/big"
"time"
+ "github.com/smartcontractkit/chainlink-common/pkg/types"
+
"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/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"
@@ -22,8 +25,13 @@ import (
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/pricegetter"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/rpclib"
+ "github.com/smartcontractkit/chainlink/v2/core/services/pipeline"
)
+const OffchainAggregator = "OffchainAggregator"
+const DecimalsMethodName = "decimals"
+const LatestRoundDataMethodName = "latestRoundData"
+
func GenericAddrToEvm(addr ccip.Address) (common.Address, error) {
return ccipcalc.GenericAddrToEvm(addr)
}
@@ -38,20 +46,20 @@ func NewEvmPriceRegistry(lp logpoller.LogPoller, ec client.Client, lggr logger.L
type VersionFinder = factory.VersionFinder
-func NewCommitStoreReader(ctx context.Context, lggr logger.Logger, versionFinder VersionFinder, address ccip.Address, ec client.Client, lp logpoller.LogPoller) (ccipdata.CommitStoreReader, error) {
- return factory.NewCommitStoreReader(ctx, lggr, versionFinder, address, ec, lp)
+func NewCommitStoreReader(ctx context.Context, lggr logger.Logger, versionFinder VersionFinder, address ccip.Address, ec client.Client, lp logpoller.LogPoller, feeEstimatorConfig ccipdata.FeeEstimatorConfigReader) (ccipdata.CommitStoreReader, error) {
+ return factory.NewCommitStoreReader(ctx, lggr, versionFinder, address, ec, lp, feeEstimatorConfig)
}
-func CloseCommitStoreReader(ctx context.Context, lggr logger.Logger, versionFinder VersionFinder, address ccip.Address, ec client.Client, lp logpoller.LogPoller) error {
- return factory.CloseCommitStoreReader(ctx, lggr, versionFinder, address, ec, lp)
+func CloseCommitStoreReader(ctx context.Context, lggr logger.Logger, versionFinder VersionFinder, address ccip.Address, ec client.Client, lp logpoller.LogPoller, feeEstimatorConfig ccipdata.FeeEstimatorConfigReader) error {
+ return factory.CloseCommitStoreReader(ctx, lggr, versionFinder, address, ec, lp, feeEstimatorConfig)
}
-func NewOffRampReader(ctx context.Context, lggr logger.Logger, versionFinder VersionFinder, addr ccip.Address, destClient client.Client, lp logpoller.LogPoller, estimator gas.EvmFeeEstimator, destMaxGasPrice *big.Int, registerFilters bool) (ccipdata.OffRampReader, error) {
- return factory.NewOffRampReader(ctx, lggr, versionFinder, addr, destClient, lp, estimator, destMaxGasPrice, registerFilters)
+func NewOffRampReader(ctx context.Context, lggr logger.Logger, versionFinder VersionFinder, addr ccip.Address, destClient client.Client, lp logpoller.LogPoller, estimator gas.EvmFeeEstimator, destMaxGasPrice *big.Int, registerFilters bool, feeEstimatorConfig ccipdata.FeeEstimatorConfigReader) (ccipdata.OffRampReader, error) {
+ return factory.NewOffRampReader(ctx, lggr, versionFinder, addr, destClient, lp, estimator, destMaxGasPrice, registerFilters, feeEstimatorConfig)
}
-func CloseOffRampReader(ctx context.Context, lggr logger.Logger, versionFinder VersionFinder, addr ccip.Address, destClient client.Client, lp logpoller.LogPoller, estimator gas.EvmFeeEstimator, destMaxGasPrice *big.Int) error {
- return factory.CloseOffRampReader(ctx, lggr, versionFinder, addr, destClient, lp, estimator, destMaxGasPrice)
+func CloseOffRampReader(ctx context.Context, lggr logger.Logger, versionFinder VersionFinder, addr ccip.Address, destClient client.Client, lp logpoller.LogPoller, estimator gas.EvmFeeEstimator, destMaxGasPrice *big.Int, feeEstimatorConfig ccipdata.FeeEstimatorConfigReader) error {
+ return factory.CloseOffRampReader(ctx, lggr, versionFinder, addr, destClient, lp, estimator, destMaxGasPrice, feeEstimatorConfig)
}
func NewEvmVersionFinder() factory.EvmVersionFinder {
@@ -72,12 +80,18 @@ type DynamicPriceGetterClient = pricegetter.DynamicPriceGetterClient
type DynamicPriceGetter = pricegetter.DynamicPriceGetter
+type AllTokensPriceGetter = pricegetter.AllTokensPriceGetter
+
+func NewPipelineGetter(source string, runner pipeline.Runner, jobID int32, externalJobID uuid.UUID, name string, lggr logger.Logger) (*pricegetter.PipelineGetter, error) {
+ return pricegetter.NewPipelineGetter(source, runner, jobID, externalJobID, name, lggr)
+}
+
func NewDynamicPriceGetterClient(batchCaller rpclib.EvmBatchCaller) DynamicPriceGetterClient {
return pricegetter.NewDynamicPriceGetterClient(batchCaller)
}
-func NewDynamicPriceGetter(cfg config.DynamicPriceGetterConfig, evmClients map[uint64]DynamicPriceGetterClient) (*DynamicPriceGetter, error) {
- return pricegetter.NewDynamicPriceGetter(cfg, evmClients)
+func NewDynamicPriceGetter(cfg config.DynamicPriceGetterConfig, contractReaders map[uint64]types.ContractReader) (*DynamicPriceGetter, error) {
+ return pricegetter.NewDynamicPriceGetter(cfg, contractReaders)
}
func NewDynamicLimitedBatchCaller(
@@ -134,3 +148,5 @@ func NewCommitOffchainConfig(
) ccip.CommitOffchainConfig {
return ccipdata.NewCommitOffchainConfig(gasPriceDeviationPPB, gasPriceHeartBeat, tokenPriceDeviationPPB, tokenPriceHeartBeat, inflightCacheExpiry, priceReportingDisabled)
}
+
+const OffChainAggregatorABI = offchainaggregator.OffchainAggregatorABI
diff --git a/core/services/ocr2/plugins/ccip/integration_test.go b/core/services/ocr2/plugins/ccip/integration_test.go
index b116a470363..6a1aef98d0c 100644
--- a/core/services/ocr2/plugins/ccip/integration_test.go
+++ b/core/services/ocr2/plugins/ccip/integration_test.go
@@ -108,7 +108,6 @@ func TestIntegration_CCIP(t *testing.T) {
require.NoError(t, err)
priceGetterConfigJson = string(priceGetterConfigBytes)
}
-
jobParams := ccipTH.SetUpNodesAndJobs(t, tokenPricesUSDPipeline, priceGetterConfigJson, "")
// track sequence number and nonce separately since nonce doesn't bump for messages with allowOutOfOrderExecution == true,
@@ -696,8 +695,9 @@ func TestReorg(t *testing.T) {
require.NoError(t, ccipTH.Dest.Chain.Fork(forkBlock.Hash()),
"Error while forking the chain")
// Make sure that fork is longer than the canonical chain to enforce switch
- noOfBlocks := uint(currentBlock.NumberU64() - forkBlock.NumberU64())
- for i := uint(0); i < noOfBlocks+1; i++ {
+ //nolint:gosec // not a problem in tests
+ noOfBlocks := int(currentBlock.NumberU64() - forkBlock.NumberU64())
+ for i := 0; i < noOfBlocks+1; i++ {
ccipTH.Dest.Chain.Commit()
}
diff --git a/core/services/ocr2/plugins/ccip/internal/cache/commit_roots_test.go b/core/services/ocr2/plugins/ccip/internal/cache/commit_roots_test.go
index c49a0448f96..5f12ae97b8a 100644
--- a/core/services/ocr2/plugins/ccip/internal/cache/commit_roots_test.go
+++ b/core/services/ocr2/plugins/ccip/internal/cache/commit_roots_test.go
@@ -9,7 +9,6 @@ import (
"github.com/stretchr/testify/require"
cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip"
-
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils"
ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big"
@@ -18,6 +17,7 @@ import (
"github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest"
"github.com/smartcontractkit/chainlink/v2/core/logger"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/cache"
+ ccipdatamocks "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata/mocks"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0"
)
@@ -56,7 +56,9 @@ func Test_RootsEligibleForExecution(t *testing.T) {
BlockHash: utils.RandomBytes32(), BlockNumber: 2, BlockTimestamp: time.Now(), FinalizedBlockNumber: 1,
}))
- commitStore, err := v1_2_0.NewCommitStore(logger.TestLogger(t), commitStoreAddr, nil, lp)
+ feeEstimatorConfig := ccipdatamocks.NewFeeEstimatorConfigReader(t)
+
+ commitStore, err := v1_2_0.NewCommitStore(logger.TestLogger(t), commitStoreAddr, nil, lp, feeEstimatorConfig)
require.NoError(t, err)
rootsCache := cache.NewCommitRootsCache(logger.TestLogger(t), commitStore, 10*time.Hour, time.Second)
@@ -170,7 +172,9 @@ func Test_RootsEligibleForExecutionWithReorgs(t *testing.T) {
BlockHash: utils.RandomBytes32(), BlockNumber: 3, BlockTimestamp: time.Now(), FinalizedBlockNumber: 1,
}))
- commitStore, err := v1_2_0.NewCommitStore(logger.TestLogger(t), commitStoreAddr, nil, lp)
+ feeEstimatorConfig := ccipdatamocks.NewFeeEstimatorConfigReader(t)
+
+ commitStore, err := v1_2_0.NewCommitStore(logger.TestLogger(t), commitStoreAddr, nil, lp, feeEstimatorConfig)
require.NoError(t, err)
rootsCache := cache.NewCommitRootsCache(logger.TestLogger(t), commitStore, 10*time.Hour, time.Second)
@@ -233,7 +237,9 @@ func Test_BlocksWithTheSameTimestamps(t *testing.T) {
BlockHash: utils.RandomBytes32(), BlockNumber: 2, BlockTimestamp: time.Now(), FinalizedBlockNumber: 2,
}))
- commitStore, err := v1_2_0.NewCommitStore(logger.TestLogger(t), commitStoreAddr, nil, lp)
+ feeEstimatorConfig := ccipdatamocks.NewFeeEstimatorConfigReader(t)
+
+ commitStore, err := v1_2_0.NewCommitStore(logger.TestLogger(t), commitStoreAddr, nil, lp, feeEstimatorConfig)
require.NoError(t, err)
rootsCache := cache.NewCommitRootsCache(logger.TestLogger(t), commitStore, 10*time.Hour, time.Second)
diff --git a/core/services/ocr2/plugins/ccip/internal/ccipcommon/shortcuts.go b/core/services/ocr2/plugins/ccip/internal/ccipcommon/shortcuts.go
index 8372ae47486..138d674f93f 100644
--- a/core/services/ocr2/plugins/ccip/internal/ccipcommon/shortcuts.go
+++ b/core/services/ocr2/plugins/ccip/internal/ccipcommon/shortcuts.go
@@ -110,14 +110,19 @@ func SelectorToBytes(chainSelector uint64) [16]byte {
return b
}
-// RetryUntilSuccess repeatedly calls fn until it returns a nil error. After each failed call there is an exponential
-// backoff applied, between initialDelay and maxDelay.
-func RetryUntilSuccess[T any](fn func() (T, error), initialDelay time.Duration, maxDelay time.Duration) (T, error) {
+// RetryUntilSuccess repeatedly calls fn until it returns a nil error or retries have been exhausted. After each failed
+// call there is an exponential backoff applied, between initialDelay and maxDelay.
+func RetryUntilSuccess[T any](
+ fn func() (T, error),
+ initialDelay time.Duration,
+ maxDelay time.Duration,
+ maxRetries uint,
+) (T, error) {
return retry.DoWithData(
fn,
retry.Delay(initialDelay),
retry.MaxDelay(maxDelay),
retry.DelayType(retry.BackOffDelay),
- retry.UntilSucceeded(),
+ retry.Attempts(maxRetries),
)
}
diff --git a/core/services/ocr2/plugins/ccip/internal/ccipcommon/shortcuts_test.go b/core/services/ocr2/plugins/ccip/internal/ccipcommon/shortcuts_test.go
index 6f1cdb4a6af..d298eecbbf3 100644
--- a/core/services/ocr2/plugins/ccip/internal/ccipcommon/shortcuts_test.go
+++ b/core/services/ocr2/plugins/ccip/internal/ccipcommon/shortcuts_test.go
@@ -9,6 +9,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip"
)
@@ -145,14 +146,20 @@ func TestRetryUntilSuccess(t *testing.T) {
}
// Assert that RetryUntilSuccess returns the expected value when fn returns success on the 5th attempt
- numCalls, err := RetryUntilSuccess(fn, initialDelay, maxDelay)
- assert.Nil(t, err)
+ numCalls, err := RetryUntilSuccess(fn, initialDelay, maxDelay, 10)
+ require.NoError(t, err)
assert.Equal(t, 5, numCalls)
// Assert that RetryUntilSuccess returns the expected value when fn returns success on the 8th attempt
numAttempts = 8
numCalls = 0
- numCalls, err = RetryUntilSuccess(fn, initialDelay, maxDelay)
- assert.Nil(t, err)
+ numCalls, err = RetryUntilSuccess(fn, initialDelay, maxDelay, 10)
+ require.NoError(t, err)
assert.Equal(t, 8, numCalls)
+
+ // Assert that RetryUntilSuccess exhausts retries
+ numAttempts = 8
+ numCalls = 0
+ numCalls, err = RetryUntilSuccess(fn, initialDelay, maxDelay, 2)
+ require.Error(t, err)
}
diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/batchreader/token_pool_batch_reader_test.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/batchreader/token_pool_batch_reader_test.go
index ceafdf22721..c67c3c15276 100644
--- a/core/services/ocr2/plugins/ccip/internal/ccipdata/batchreader/token_pool_batch_reader_test.go
+++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/batchreader/token_pool_batch_reader_test.go
@@ -13,15 +13,15 @@ import (
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
- "github.com/smartcontractkit/chainlink-common/pkg/logger"
cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip"
"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/internal/ccipcalc"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata"
)
func TestTokenPoolFactory(t *testing.T) {
- lggr := logger.Test(t)
+ lggr := logger.TestLogger(t)
offRamp := utils.RandomAddress()
ctx := context.Background()
remoteChainSelector := uint64(2000)
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 971b507e828..42a7369c7e5 100644
--- a/core/services/ocr2/plugins/ccip/internal/ccipdata/ccipdataprovider/provider.go
+++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/ccipdataprovider/provider.go
@@ -5,7 +5,6 @@ import (
"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/services/ocr2/plugins/ccip/internal/ccipdata/factory"
diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/commit_store_reader_test.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/commit_store_reader_test.go
index 0f234bab8a6..30e74d89e36 100644
--- a/core/services/ocr2/plugins/ccip/internal/ccipdata/commit_store_reader_test.go
+++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/commit_store_reader_test.go
@@ -1,6 +1,7 @@
package ccipdata_test
import (
+ "context"
"math/big"
"reflect"
"testing"
@@ -35,6 +36,7 @@ import (
"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/factory"
+ ccipdatamocks "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata/mocks"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0"
)
@@ -179,8 +181,17 @@ func TestCommitStoreReaders(t *testing.T) {
lm := new(rollupMocks.L1Oracle)
ge.On("L1Oracle").Return(lm)
+ feeEstimatorConfig := ccipdatamocks.NewFeeEstimatorConfigReader(t)
+ feeEstimatorConfig.On(
+ "ModifyGasPriceComponents",
+ mock.Anything,
+ mock.AnythingOfType("*big.Int"),
+ mock.AnythingOfType("*big.Int"),
+ ).Return(func(ctx context.Context, x, y *big.Int) (*big.Int, *big.Int, error) {
+ return x, y, nil
+ })
maxGasPrice := big.NewInt(1e8)
- c12r, err := factory.NewCommitStoreReader(ctx, lggr, factory.NewEvmVersionFinder(), ccipcalc.EvmAddrToGeneric(addr2), ec, lp)
+ c12r, err := factory.NewCommitStoreReader(ctx, lggr, factory.NewEvmVersionFinder(), ccipcalc.EvmAddrToGeneric(addr2), ec, lp, feeEstimatorConfig)
require.NoError(t, err)
err = c12r.SetGasEstimator(ctx, ge)
require.NoError(t, err)
@@ -327,8 +338,10 @@ func TestCommitStoreReaders(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, commonOffchain, c2)
// We should be able to query for gas prices now.
+
gpe, err := cr.GasPriceEstimator(ctx)
require.NoError(t, err)
+
gp, err := gpe.GetGasPrice(ctx)
require.NoError(t, err)
assert.True(t, gp.Cmp(big.NewInt(0)) > 0)
@@ -370,7 +383,10 @@ func TestNewCommitStoreReader(t *testing.T) {
if tc.expectedErr == "" {
lp.On("RegisterFilter", mock.Anything, mock.Anything).Return(nil)
}
- _, err = factory.NewCommitStoreReader(ctx, logger.Test(t), factory.NewEvmVersionFinder(), addr, c, lp)
+
+ feeEstimatorConfig := ccipdatamocks.NewFeeEstimatorConfigReader(t)
+
+ _, err = factory.NewCommitStoreReader(ctx, logger.Test(t), factory.NewEvmVersionFinder(), addr, c, lp, feeEstimatorConfig)
if tc.expectedErr != "" {
require.EqualError(t, err, tc.expectedErr)
} else {
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 d9cd523d75e..2d40a526112 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
@@ -21,16 +21,16 @@ import (
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_5_0"
)
-func NewCommitStoreReader(ctx context.Context, lggr logger.Logger, versionFinder VersionFinder, address cciptypes.Address, ec client.Client, lp logpoller.LogPoller) (ccipdata.CommitStoreReader, error) {
- return initOrCloseCommitStoreReader(ctx, lggr, versionFinder, address, ec, lp, false)
+func NewCommitStoreReader(ctx context.Context, lggr logger.Logger, versionFinder VersionFinder, address cciptypes.Address, ec client.Client, lp logpoller.LogPoller, feeEstimatorConfig ccipdata.FeeEstimatorConfigReader) (ccipdata.CommitStoreReader, error) {
+ return initOrCloseCommitStoreReader(ctx, lggr, versionFinder, address, ec, lp, feeEstimatorConfig, false)
}
-func CloseCommitStoreReader(ctx context.Context, lggr logger.Logger, versionFinder VersionFinder, address cciptypes.Address, ec client.Client, lp logpoller.LogPoller) error {
- _, err := initOrCloseCommitStoreReader(ctx, lggr, versionFinder, address, ec, lp, true)
+func CloseCommitStoreReader(ctx context.Context, lggr logger.Logger, versionFinder VersionFinder, address cciptypes.Address, ec client.Client, lp logpoller.LogPoller, feeEstimatorConfig ccipdata.FeeEstimatorConfigReader) error {
+ _, err := initOrCloseCommitStoreReader(ctx, lggr, versionFinder, address, ec, lp, feeEstimatorConfig, true)
return err
}
-func initOrCloseCommitStoreReader(ctx context.Context, lggr logger.Logger, versionFinder VersionFinder, address cciptypes.Address, ec client.Client, lp logpoller.LogPoller, closeReader bool) (ccipdata.CommitStoreReader, error) {
+func initOrCloseCommitStoreReader(ctx context.Context, lggr logger.Logger, versionFinder VersionFinder, address cciptypes.Address, ec client.Client, lp logpoller.LogPoller, feeEstimatorConfig ccipdata.FeeEstimatorConfigReader, closeReader bool) (ccipdata.CommitStoreReader, error) {
contractType, version, err := versionFinder.TypeAndVersion(address, ec)
if err != nil {
return nil, errors.Wrapf(err, "unable to read type and version")
@@ -48,7 +48,7 @@ func initOrCloseCommitStoreReader(ctx context.Context, lggr logger.Logger, versi
switch version.String() {
case ccipdata.V1_2_0:
- cs, err := v1_2_0.NewCommitStore(lggr, evmAddr, ec, lp)
+ cs, err := v1_2_0.NewCommitStore(lggr, evmAddr, ec, lp, feeEstimatorConfig)
if err != nil {
return nil, err
}
@@ -57,7 +57,7 @@ func initOrCloseCommitStoreReader(ctx context.Context, lggr logger.Logger, versi
}
return cs, cs.RegisterFilters(ctx)
case ccipdata.V1_5_0:
- cs, err := v1_5_0.NewCommitStore(lggr, evmAddr, ec, lp)
+ cs, err := v1_5_0.NewCommitStore(lggr, evmAddr, ec, lp, feeEstimatorConfig)
if err != nil {
return nil, err
}
diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/commit_store_test.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/commit_store_test.go
index cd81a0633ce..d4234bb1a33 100644
--- a/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/commit_store_test.go
+++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/commit_store_test.go
@@ -17,6 +17,7 @@ import (
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils"
ccipconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/config"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata"
+ ccipdatamocks "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata/mocks"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0"
)
@@ -27,14 +28,16 @@ func TestCommitStore(t *testing.T) {
addr := cciptypes.Address(utils.RandomAddress().String())
lp := mocks2.NewLogPoller(t)
+ feeEstimatorConfig := ccipdatamocks.NewFeeEstimatorConfigReader(t)
+
lp.On("RegisterFilter", mock.Anything, mock.Anything).Return(nil)
versionFinder := newMockVersionFinder(ccipconfig.CommitStore, *semver.MustParse(versionStr), nil)
- _, err := NewCommitStoreReader(ctx, lggr, versionFinder, addr, nil, lp)
+ _, err := NewCommitStoreReader(ctx, lggr, versionFinder, addr, nil, lp, feeEstimatorConfig)
assert.NoError(t, err)
expFilterName := logpoller.FilterName(v1_2_0.ExecReportAccepts, addr)
lp.On("UnregisterFilter", mock.Anything, expFilterName).Return(nil)
- err = CloseCommitStoreReader(ctx, lggr, versionFinder, addr, nil, lp)
+ err = CloseCommitStoreReader(ctx, lggr, versionFinder, addr, nil, lp, feeEstimatorConfig)
assert.NoError(t, err)
}
}
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 136079b5b3e..7a1180a5590 100644
--- a/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/offramp.go
+++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/offramp.go
@@ -24,16 +24,16 @@ import (
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_5_0"
)
-func NewOffRampReader(ctx context.Context, lggr logger.Logger, versionFinder VersionFinder, addr cciptypes.Address, destClient client.Client, lp logpoller.LogPoller, estimator gas.EvmFeeEstimator, destMaxGasPrice *big.Int, registerFilters bool) (ccipdata.OffRampReader, error) {
- return initOrCloseOffRampReader(ctx, lggr, versionFinder, addr, destClient, lp, estimator, destMaxGasPrice, false, registerFilters)
+func NewOffRampReader(ctx context.Context, lggr logger.Logger, versionFinder VersionFinder, addr cciptypes.Address, destClient client.Client, lp logpoller.LogPoller, estimator gas.EvmFeeEstimator, destMaxGasPrice *big.Int, registerFilters bool, feeEstimatorConfig ccipdata.FeeEstimatorConfigReader) (ccipdata.OffRampReader, error) {
+ return initOrCloseOffRampReader(ctx, lggr, versionFinder, addr, destClient, lp, estimator, destMaxGasPrice, false, registerFilters, feeEstimatorConfig)
}
-func CloseOffRampReader(ctx context.Context, lggr logger.Logger, versionFinder VersionFinder, addr cciptypes.Address, destClient client.Client, lp logpoller.LogPoller, estimator gas.EvmFeeEstimator, destMaxGasPrice *big.Int) error {
- _, err := initOrCloseOffRampReader(ctx, lggr, versionFinder, addr, destClient, lp, estimator, destMaxGasPrice, true, false)
+func CloseOffRampReader(ctx context.Context, lggr logger.Logger, versionFinder VersionFinder, addr cciptypes.Address, destClient client.Client, lp logpoller.LogPoller, estimator gas.EvmFeeEstimator, destMaxGasPrice *big.Int, feeEstimatorConfig ccipdata.FeeEstimatorConfigReader) error {
+ _, err := initOrCloseOffRampReader(ctx, lggr, versionFinder, addr, destClient, lp, estimator, destMaxGasPrice, true, false, feeEstimatorConfig)
return err
}
-func initOrCloseOffRampReader(ctx context.Context, lggr logger.Logger, versionFinder VersionFinder, addr cciptypes.Address, destClient client.Client, lp logpoller.LogPoller, estimator gas.EvmFeeEstimator, destMaxGasPrice *big.Int, closeReader bool, registerFilters bool) (ccipdata.OffRampReader, error) {
+func initOrCloseOffRampReader(ctx context.Context, lggr logger.Logger, versionFinder VersionFinder, addr cciptypes.Address, destClient client.Client, lp logpoller.LogPoller, estimator gas.EvmFeeEstimator, destMaxGasPrice *big.Int, closeReader bool, registerFilters bool, feeEstimatorConfig ccipdata.FeeEstimatorConfigReader) (ccipdata.OffRampReader, error) {
contractType, version, err := versionFinder.TypeAndVersion(addr, destClient)
if err != nil {
return nil, errors.Wrapf(err, "unable to read type and version")
@@ -51,7 +51,7 @@ func initOrCloseOffRampReader(ctx context.Context, lggr logger.Logger, versionFi
switch version.String() {
case ccipdata.V1_2_0:
- offRamp, err := v1_2_0.NewOffRamp(lggr, evmAddr, destClient, lp, estimator, destMaxGasPrice)
+ offRamp, err := v1_2_0.NewOffRamp(lggr, evmAddr, destClient, lp, estimator, destMaxGasPrice, feeEstimatorConfig)
if err != nil {
return nil, err
}
@@ -60,7 +60,7 @@ func initOrCloseOffRampReader(ctx context.Context, lggr logger.Logger, versionFi
}
return offRamp, offRamp.RegisterFilters(ctx)
case ccipdata.V1_5_0:
- offRamp, err := v1_5_0.NewOffRamp(lggr, evmAddr, destClient, lp, estimator, destMaxGasPrice)
+ offRamp, err := v1_5_0.NewOffRamp(lggr, evmAddr, destClient, lp, estimator, destMaxGasPrice, feeEstimatorConfig)
if err != nil {
return nil, err
}
diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/offramp_test.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/offramp_test.go
index bfb8da5e32c..7640261786f 100644
--- a/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/offramp_test.go
+++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/offramp_test.go
@@ -16,6 +16,7 @@ import (
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils"
ccipconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/config"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata"
+ ccipdatamocks "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata/mocks"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0"
)
@@ -26,6 +27,8 @@ func TestOffRamp(t *testing.T) {
addr := cciptypes.Address(utils.RandomAddress().String())
lp := mocks2.NewLogPoller(t)
+ feeEstimatorConfig := ccipdatamocks.NewFeeEstimatorConfigReader(t)
+
expFilterNames := []string{
logpoller.FilterName(v1_2_0.ExecExecutionStateChanges, addr),
logpoller.FilterName(v1_2_0.ExecTokenPoolAdded, addr),
@@ -34,13 +37,13 @@ func TestOffRamp(t *testing.T) {
versionFinder := newMockVersionFinder(ccipconfig.EVM2EVMOffRamp, *semver.MustParse(versionStr), nil)
lp.On("RegisterFilter", mock.Anything, mock.Anything).Return(nil).Times(len(expFilterNames))
- _, err := NewOffRampReader(ctx, lggr, versionFinder, addr, nil, lp, nil, nil, true)
+ _, err := NewOffRampReader(ctx, lggr, versionFinder, addr, nil, lp, nil, nil, true, feeEstimatorConfig)
assert.NoError(t, err)
for _, f := range expFilterNames {
lp.On("UnregisterFilter", mock.Anything, f).Return(nil)
}
- err = CloseOffRampReader(ctx, lggr, versionFinder, addr, nil, lp, nil, nil)
+ err = CloseOffRampReader(ctx, lggr, versionFinder, addr, nil, lp, nil, nil, feeEstimatorConfig)
assert.NoError(t, err)
}
}
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 57bf6e2eeb3..85eee70e296 100644
--- a/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/onramp.go
+++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/onramp.go
@@ -10,7 +10,6 @@ import (
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/client"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller"
-
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/fee_estimator_config.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/fee_estimator_config.go
new file mode 100644
index 00000000000..0b4d1ce75ab
--- /dev/null
+++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/fee_estimator_config.go
@@ -0,0 +1,11 @@
+package ccipdata
+
+import (
+ "context"
+ "math/big"
+)
+
+type FeeEstimatorConfigReader interface {
+ GetDataAvailabilityConfig(ctx context.Context) (destDAOverheadGas, destGasPerDAByte, destDAMultiplierBps int64, err error)
+ ModifyGasPriceComponents(ctx context.Context, execGasPrice, daGasPrice *big.Int) (modExecGasPrice, modDAGasPrice *big.Int, err error)
+}
diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/mocks/fee_estimator_config_mock.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/mocks/fee_estimator_config_mock.go
new file mode 100644
index 00000000000..9ce400406d4
--- /dev/null
+++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/mocks/fee_estimator_config_mock.go
@@ -0,0 +1,177 @@
+// Code generated by mockery v2.50.0. DO NOT EDIT.
+
+package mocks
+
+import (
+ big "math/big"
+
+ context "context"
+
+ mock "github.com/stretchr/testify/mock"
+)
+
+// FeeEstimatorConfigReader is an autogenerated mock type for the FeeEstimatorConfigReader type
+type FeeEstimatorConfigReader struct {
+ mock.Mock
+}
+
+type FeeEstimatorConfigReader_Expecter struct {
+ mock *mock.Mock
+}
+
+func (_m *FeeEstimatorConfigReader) EXPECT() *FeeEstimatorConfigReader_Expecter {
+ return &FeeEstimatorConfigReader_Expecter{mock: &_m.Mock}
+}
+
+// GetDataAvailabilityConfig provides a mock function with given fields: ctx
+func (_m *FeeEstimatorConfigReader) GetDataAvailabilityConfig(ctx context.Context) (int64, int64, int64, error) {
+ ret := _m.Called(ctx)
+
+ if len(ret) == 0 {
+ panic("no return value specified for GetDataAvailabilityConfig")
+ }
+
+ var r0 int64
+ var r1 int64
+ var r2 int64
+ var r3 error
+ if rf, ok := ret.Get(0).(func(context.Context) (int64, int64, int64, error)); ok {
+ return rf(ctx)
+ }
+ if rf, ok := ret.Get(0).(func(context.Context) int64); ok {
+ r0 = rf(ctx)
+ } else {
+ r0 = ret.Get(0).(int64)
+ }
+
+ if rf, ok := ret.Get(1).(func(context.Context) int64); ok {
+ r1 = rf(ctx)
+ } else {
+ r1 = ret.Get(1).(int64)
+ }
+
+ if rf, ok := ret.Get(2).(func(context.Context) int64); ok {
+ r2 = rf(ctx)
+ } else {
+ r2 = ret.Get(2).(int64)
+ }
+
+ if rf, ok := ret.Get(3).(func(context.Context) error); ok {
+ r3 = rf(ctx)
+ } else {
+ r3 = ret.Error(3)
+ }
+
+ return r0, r1, r2, r3
+}
+
+// FeeEstimatorConfigReader_GetDataAvailabilityConfig_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetDataAvailabilityConfig'
+type FeeEstimatorConfigReader_GetDataAvailabilityConfig_Call struct {
+ *mock.Call
+}
+
+// GetDataAvailabilityConfig is a helper method to define mock.On call
+// - ctx context.Context
+func (_e *FeeEstimatorConfigReader_Expecter) GetDataAvailabilityConfig(ctx interface{}) *FeeEstimatorConfigReader_GetDataAvailabilityConfig_Call {
+ return &FeeEstimatorConfigReader_GetDataAvailabilityConfig_Call{Call: _e.mock.On("GetDataAvailabilityConfig", ctx)}
+}
+
+func (_c *FeeEstimatorConfigReader_GetDataAvailabilityConfig_Call) Run(run func(ctx context.Context)) *FeeEstimatorConfigReader_GetDataAvailabilityConfig_Call {
+ _c.Call.Run(func(args mock.Arguments) {
+ run(args[0].(context.Context))
+ })
+ return _c
+}
+
+func (_c *FeeEstimatorConfigReader_GetDataAvailabilityConfig_Call) Return(destDAOverheadGas int64, destGasPerDAByte int64, destDAMultiplierBps int64, err error) *FeeEstimatorConfigReader_GetDataAvailabilityConfig_Call {
+ _c.Call.Return(destDAOverheadGas, destGasPerDAByte, destDAMultiplierBps, err)
+ return _c
+}
+
+func (_c *FeeEstimatorConfigReader_GetDataAvailabilityConfig_Call) RunAndReturn(run func(context.Context) (int64, int64, int64, error)) *FeeEstimatorConfigReader_GetDataAvailabilityConfig_Call {
+ _c.Call.Return(run)
+ return _c
+}
+
+// ModifyGasPriceComponents provides a mock function with given fields: ctx, execGasPrice, daGasPrice
+func (_m *FeeEstimatorConfigReader) ModifyGasPriceComponents(ctx context.Context, execGasPrice *big.Int, daGasPrice *big.Int) (*big.Int, *big.Int, error) {
+ ret := _m.Called(ctx, execGasPrice, daGasPrice)
+
+ if len(ret) == 0 {
+ panic("no return value specified for ModifyGasPriceComponents")
+ }
+
+ var r0 *big.Int
+ var r1 *big.Int
+ var r2 error
+ if rf, ok := ret.Get(0).(func(context.Context, *big.Int, *big.Int) (*big.Int, *big.Int, error)); ok {
+ return rf(ctx, execGasPrice, daGasPrice)
+ }
+ if rf, ok := ret.Get(0).(func(context.Context, *big.Int, *big.Int) *big.Int); ok {
+ r0 = rf(ctx, execGasPrice, daGasPrice)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(*big.Int)
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func(context.Context, *big.Int, *big.Int) *big.Int); ok {
+ r1 = rf(ctx, execGasPrice, daGasPrice)
+ } else {
+ if ret.Get(1) != nil {
+ r1 = ret.Get(1).(*big.Int)
+ }
+ }
+
+ if rf, ok := ret.Get(2).(func(context.Context, *big.Int, *big.Int) error); ok {
+ r2 = rf(ctx, execGasPrice, daGasPrice)
+ } else {
+ r2 = ret.Error(2)
+ }
+
+ return r0, r1, r2
+}
+
+// FeeEstimatorConfigReader_ModifyGasPriceComponents_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ModifyGasPriceComponents'
+type FeeEstimatorConfigReader_ModifyGasPriceComponents_Call struct {
+ *mock.Call
+}
+
+// ModifyGasPriceComponents is a helper method to define mock.On call
+// - ctx context.Context
+// - execGasPrice *big.Int
+// - daGasPrice *big.Int
+func (_e *FeeEstimatorConfigReader_Expecter) ModifyGasPriceComponents(ctx interface{}, execGasPrice interface{}, daGasPrice interface{}) *FeeEstimatorConfigReader_ModifyGasPriceComponents_Call {
+ return &FeeEstimatorConfigReader_ModifyGasPriceComponents_Call{Call: _e.mock.On("ModifyGasPriceComponents", ctx, execGasPrice, daGasPrice)}
+}
+
+func (_c *FeeEstimatorConfigReader_ModifyGasPriceComponents_Call) Run(run func(ctx context.Context, execGasPrice *big.Int, daGasPrice *big.Int)) *FeeEstimatorConfigReader_ModifyGasPriceComponents_Call {
+ _c.Call.Run(func(args mock.Arguments) {
+ run(args[0].(context.Context), args[1].(*big.Int), args[2].(*big.Int))
+ })
+ return _c
+}
+
+func (_c *FeeEstimatorConfigReader_ModifyGasPriceComponents_Call) Return(modExecGasPrice *big.Int, modDAGasPrice *big.Int, err error) *FeeEstimatorConfigReader_ModifyGasPriceComponents_Call {
+ _c.Call.Return(modExecGasPrice, modDAGasPrice, err)
+ return _c
+}
+
+func (_c *FeeEstimatorConfigReader_ModifyGasPriceComponents_Call) RunAndReturn(run func(context.Context, *big.Int, *big.Int) (*big.Int, *big.Int, error)) *FeeEstimatorConfigReader_ModifyGasPriceComponents_Call {
+ _c.Call.Return(run)
+ return _c
+}
+
+// NewFeeEstimatorConfigReader creates a new instance of FeeEstimatorConfigReader. 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 NewFeeEstimatorConfigReader(t interface {
+ mock.TestingT
+ Cleanup(func())
+}) *FeeEstimatorConfigReader {
+ mock := &FeeEstimatorConfigReader{}
+ mock.Mock.Test(t)
+
+ t.Cleanup(func() { mock.AssertExpectations(t) })
+
+ return mock
+}
diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/offramp_reader_test.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/offramp_reader_test.go
index 17f9bcfb370..420cd5bf64c 100644
--- a/core/services/ocr2/plugins/ccip/internal/ccipdata/offramp_reader_test.go
+++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/offramp_reader_test.go
@@ -12,7 +12,6 @@ import (
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
- "github.com/smartcontractkit/chainlink-common/pkg/logger"
cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip"
"github.com/smartcontractkit/chainlink-common/pkg/utils/tests"
@@ -28,10 +27,12 @@ import (
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/mock_rmn_contract"
"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/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"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata/factory"
+ ccipdatamocks "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata/mocks"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0"
)
@@ -108,7 +109,7 @@ func TestOffRampReaderInit(t *testing.T) {
func setupOffRampReaderTH(t *testing.T, version string) offRampReaderTH {
ctx := testutils.Context(t)
user, bc := ccipdata.NewSimulation(t)
- log := logger.Test(t)
+ log := logger.TestLogger(t)
orm := logpoller.NewORM(testutils.SimulatedChainID, pgtest.NewSqlxDB(t), log)
lpOpts := logpoller.Opts{
PollPeriod: 100 * time.Millisecond,
@@ -139,8 +140,10 @@ func setupOffRampReaderTH(t *testing.T, version string) offRampReaderTH {
require.Fail(t, "Unknown version: ", version)
}
+ feeEstimatorConfig := ccipdatamocks.NewFeeEstimatorConfigReader(t)
+
// Create the version-specific reader.
- reader, err := factory.NewOffRampReader(ctx, log, factory.NewEvmVersionFinder(), ccipcalc.EvmAddrToGeneric(offRampAddress), bc, lp, nil, nil, true)
+ reader, err := factory.NewOffRampReader(ctx, log, factory.NewEvmVersionFinder(), ccipcalc.EvmAddrToGeneric(offRampAddress), bc, lp, nil, nil, true, feeEstimatorConfig)
require.NoError(t, err)
addr, err := reader.Address(ctx)
require.NoError(t, err)
@@ -311,11 +314,14 @@ func TestNewOffRampReader(t *testing.T) {
b, err := utils.ABIEncode(`[{"type":"string"}]`, tc.typeAndVersion)
require.NoError(t, err)
c := evmclientmocks.NewClient(t)
+
+ feeEstimatorConfig := ccipdatamocks.NewFeeEstimatorConfigReader(t)
+
c.On("CallContract", mock.Anything, mock.Anything, mock.Anything).Return(b, nil)
addr := ccipcalc.EvmAddrToGeneric(utils.RandomAddress())
lp := lpmocks.NewLogPoller(t)
lp.On("RegisterFilter", mock.Anything, mock.Anything).Return(nil).Maybe()
- _, err = factory.NewOffRampReader(ctx, logger.Test(t), factory.NewEvmVersionFinder(), addr, c, lp, nil, nil, true)
+ _, err = factory.NewOffRampReader(ctx, logger.TestLogger(t), factory.NewEvmVersionFinder(), addr, c, lp, nil, nil, true, feeEstimatorConfig)
if tc.expectedErr != "" {
assert.EqualError(t, err, tc.expectedErr)
} else {
diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/reader.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/reader.go
index 3c82948b892..8655bc98acc 100644
--- a/core/services/ocr2/plugins/ccip/internal/ccipdata/reader.go
+++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/reader.go
@@ -8,7 +8,6 @@ import (
"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"
)
diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/reader_test.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/reader_test.go
index 0df76873915..06766be81ee 100644
--- a/core/services/ocr2/plugins/ccip/internal/ccipdata/reader_test.go
+++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/reader_test.go
@@ -10,9 +10,8 @@ import (
"github.com/stretchr/testify/require"
"go.uber.org/zap/zapcore"
- "github.com/smartcontractkit/chainlink-common/pkg/logger"
-
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller"
+ "github.com/smartcontractkit/chainlink/v2/core/logger"
)
func Test_parseLogs(t *testing.T) {
@@ -28,7 +27,7 @@ func Test_parseLogs(t *testing.T) {
return &log.Index, nil
}
- parsedEvents, err := ParseLogs[uint](logs, logger.Test(t), parseFn)
+ parsedEvents, err := ParseLogs[uint](logs, logger.TestLogger(t), parseFn)
require.NoError(t, err)
assert.Len(t, parsedEvents, 100)
@@ -56,7 +55,7 @@ func Test_parseLogs_withErrors(t *testing.T) {
return &log.Index, nil
}
- log, observed := logger.TestObserved(t, zapcore.DebugLevel)
+ log, observed := logger.TestLoggerObserved(t, zapcore.DebugLevel)
parsedEvents, err := ParseLogs[uint](logs, log, parseFn)
assert.ErrorContains(t, err, fmt.Sprintf("%d logs were not parsed", len(logs)/2))
assert.Nil(t, parsedEvents, "No events are returned if there was an error.")
diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/retry_config.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/retry_config.go
index 41161ee9388..80c5364e18f 100644
--- a/core/services/ocr2/plugins/ccip/internal/ccipdata/retry_config.go
+++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/retry_config.go
@@ -2,8 +2,10 @@ package ccipdata
import "time"
-// RetryConfig configures an initial delay between retries and a max delay between retries
+// RetryConfig configures an initial delay between retries, a max delay between retries, and a maximum number of
+// times to retry
type RetryConfig struct {
InitialDelay time.Duration
MaxDelay time.Duration
+ MaxRetries uint
}
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 2d772e3bd0a..78f60653668 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
@@ -53,9 +53,10 @@ type CommitStore struct {
commitReportArgs abi.Arguments
// Dynamic config
- configMu sync.RWMutex
- gasPriceEstimator *prices.DAGasPriceEstimator
- offchainConfig cciptypes.CommitOffchainConfig
+ configMu sync.RWMutex
+ gasPriceEstimator *prices.DAGasPriceEstimator
+ offchainConfig cciptypes.CommitOffchainConfig
+ feeEstimatorConfig ccipdata.FeeEstimatorConfigReader
}
func (c *CommitStore) GetCommitStoreStaticConfig(ctx context.Context) (cciptypes.CommitStoreStaticConfig, error) {
@@ -259,6 +260,7 @@ func (c *CommitStore) ChangeConfig(_ context.Context, onchainConfig []byte, offc
c.sourceMaxGasPrice,
int64(offchainConfigParsed.ExecGasPriceDeviationPPB),
int64(offchainConfigParsed.DAGasPriceDeviationPPB),
+ c.feeEstimatorConfig,
)
c.offchainConfig = ccipdata.NewCommitOffchainConfig(
offchainConfigParsed.ExecGasPriceDeviationPPB,
@@ -353,7 +355,8 @@ func (c *CommitStore) GetAcceptedCommitReportsGteTimestamp(ctx context.Context,
return nil, err
}
- reportsQuery, err := logpoller.Where(
+ reportsQuery, err := query.Where(
+ c.address.String(),
logpoller.NewAddressFilter(c.address),
logpoller.NewEventSigFilter(c.reportAcceptedSig),
query.Timestamp(uint64(ts.Unix()), primitives.Gte),
@@ -365,7 +368,7 @@ func (c *CommitStore) GetAcceptedCommitReportsGteTimestamp(ctx context.Context,
logs, err := c.lp.FilteredLogs(
ctx,
- reportsQuery,
+ reportsQuery.Expressions,
query.NewLimitAndSort(query.Limit{}, query.NewSortBySequence(query.Asc)),
"GetAcceptedCommitReportsGteTimestamp",
)
@@ -433,7 +436,7 @@ func (c *CommitStore) RegisterFilters(ctx context.Context) error {
return logpollerutil.RegisterLpFilters(ctx, c.lp, c.filters)
}
-func NewCommitStore(lggr logger.Logger, addr common.Address, ec client.Client, lp logpoller.LogPoller) (*CommitStore, error) {
+func NewCommitStore(lggr logger.Logger, addr common.Address, ec client.Client, lp logpoller.LogPoller, feeEstimatorConfig ccipdata.FeeEstimatorConfigReader) (*CommitStore, error) {
commitStore, err := commit_store_1_2_0.NewCommitStore(addr, ec)
if err != nil {
return nil, err
@@ -466,7 +469,8 @@ func NewCommitStore(lggr logger.Logger, addr common.Address, ec client.Client, l
configMu: sync.RWMutex{},
// The fields below are initially empty and set on ChangeConfig method
- offchainConfig: cciptypes.CommitOffchainConfig{},
- gasPriceEstimator: nil,
+ offchainConfig: cciptypes.CommitOffchainConfig{},
+ gasPriceEstimator: nil,
+ feeEstimatorConfig: feeEstimatorConfig,
}, nil
}
diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0/commit_store_test.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0/commit_store_test.go
index e0771f33cb9..4307be0353c 100644
--- a/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0/commit_store_test.go
+++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0/commit_store_test.go
@@ -10,13 +10,14 @@ import (
"github.com/stretchr/testify/require"
"github.com/smartcontractkit/chainlink-common/pkg/config"
- "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/mocks"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils"
"github.com/smartcontractkit/chainlink/v2/core/internal/testutils"
+ "github.com/smartcontractkit/chainlink/v2/core/logger"
ccipconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/config"
+ ccipdatamocks "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata/mocks"
)
func TestCommitReportEncoding(t *testing.T) {
@@ -47,7 +48,9 @@ func TestCommitReportEncoding(t *testing.T) {
Interval: cciptypes.CommitStoreInterval{Min: 1, Max: 10},
}
- c, err := NewCommitStore(logger.Test(t), utils.RandomAddress(), nil, mocks.NewLogPoller(t))
+ feeEstimatorConfig := ccipdatamocks.NewFeeEstimatorConfigReader(t)
+
+ c, err := NewCommitStore(logger.TestLogger(t), utils.RandomAddress(), nil, mocks.NewLogPoller(t), feeEstimatorConfig)
assert.NoError(t, err)
encodedReport, err := c.EncodeCommitReport(ctx, report)
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 e8017016690..881755d3f3c 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
@@ -155,10 +155,11 @@ type OffRamp struct {
// Dynamic config
// configMu guards all the dynamic config fields.
- configMu sync.RWMutex
- gasPriceEstimator prices.GasPriceEstimatorExec
- offchainConfig cciptypes.ExecOffchainConfig
- onchainConfig cciptypes.ExecOnchainConfig
+ configMu sync.RWMutex
+ gasPriceEstimator prices.GasPriceEstimatorExec
+ offchainConfig cciptypes.ExecOffchainConfig
+ onchainConfig cciptypes.ExecOnchainConfig
+ feeEstimatorConfig ccipdata.FeeEstimatorConfigReader
}
func (o *OffRamp) GetStaticConfig(ctx context.Context) (cciptypes.OffRampStaticConfig, error) {
@@ -416,7 +417,7 @@ func (o *OffRamp) ChangeConfig(ctx context.Context, onchainConfigBytes []byte, o
PermissionLessExecutionThresholdSeconds: time.Second * time.Duration(onchainConfigParsed.PermissionLessExecutionThresholdSeconds),
Router: cciptypes.Address(onchainConfigParsed.Router.String()),
}
- priceEstimator := prices.NewDAGasPriceEstimator(o.Estimator, o.DestMaxGasPrice, 0, 0)
+ priceEstimator := prices.NewDAGasPriceEstimator(o.Estimator, o.DestMaxGasPrice, 0, 0, o.feeEstimatorConfig)
o.UpdateDynamicConfig(onchainConfig, offchainConfig, priceEstimator)
@@ -614,7 +615,7 @@ func (o *OffRamp) DecodeExecutionReport(ctx context.Context, report []byte) (cci
return DecodeExecReport(ctx, o.ExecutionReportArgs, report)
}
-func NewOffRamp(lggr logger.Logger, addr common.Address, ec client.Client, lp logpoller.LogPoller, estimator gas.EvmFeeEstimator, destMaxGasPrice *big.Int) (*OffRamp, error) {
+func NewOffRamp(lggr logger.Logger, addr common.Address, ec client.Client, lp logpoller.LogPoller, estimator gas.EvmFeeEstimator, destMaxGasPrice *big.Int, feeEstimatorConfig ccipdata.FeeEstimatorConfigReader) (*OffRamp, error) {
offRamp, err := evm_2_evm_offramp_1_2_0.NewEVM2EVMOffRamp(addr, ec)
if err != nil {
return nil, err
@@ -669,8 +670,9 @@ func NewOffRamp(lggr logger.Logger, addr common.Address, ec client.Client, lp lo
offRamp.Address(),
),
// values set on the fly after ChangeConfig is called
- gasPriceEstimator: prices.ExecGasPriceEstimator{},
- offchainConfig: cciptypes.ExecOffchainConfig{},
- onchainConfig: cciptypes.ExecOnchainConfig{},
+ gasPriceEstimator: prices.ExecGasPriceEstimator{},
+ offchainConfig: cciptypes.ExecOffchainConfig{},
+ onchainConfig: cciptypes.ExecOnchainConfig{},
+ feeEstimatorConfig: feeEstimatorConfig,
}, nil
}
diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0/offramp_reader_test.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0/offramp_reader_test.go
index 630b92f67fc..c2983492618 100644
--- a/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0/offramp_reader_test.go
+++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0/offramp_reader_test.go
@@ -6,12 +6,12 @@ import (
"github.com/stretchr/testify/require"
- "github.com/smartcontractkit/chainlink-common/pkg/logger"
cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip"
-
lpmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils"
"github.com/smartcontractkit/chainlink/v2/core/internal/testutils"
+ "github.com/smartcontractkit/chainlink/v2/core/logger"
+ "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata/mocks"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0"
)
@@ -26,7 +26,9 @@ func TestExecutionReportEncodingV120(t *testing.T) {
ProofFlagBits: big.NewInt(133),
}
- offRamp, err := v1_2_0.NewOffRamp(logger.Test(t), utils.RandomAddress(), nil, lpmocks.NewLogPoller(t), nil, nil)
+ feeEstimatorConfig := mocks.NewFeeEstimatorConfigReader(t)
+
+ offRamp, err := v1_2_0.NewOffRamp(logger.TestLogger(t), utils.RandomAddress(), nil, lpmocks.NewLogPoller(t), nil, nil, feeEstimatorConfig)
require.NoError(t, err)
ctx := testutils.Context(t)
diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0/offramp_reader_unit_test.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0/offramp_reader_unit_test.go
index 15c91235b92..a1e7f9e6346 100644
--- a/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0/offramp_reader_unit_test.go
+++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0/offramp_reader_unit_test.go
@@ -197,7 +197,7 @@ func Test_LogsAreProperlyMarkedAsFinalized(t *testing.T) {
lp.On("IndexedLogsTopicRange", mock.Anything, ExecutionStateChangedEvent, offrampAddress, 1, logpoller.EvmWord(minSeqNr), logpoller.EvmWord(maxSeqNr), evmtypes.Confirmations(0)).
Return(inputLogs, nil)
- offRamp, err := NewOffRamp(logger.Test(t), offrampAddress, evmclimocks.NewClient(t), lp, nil, nil)
+ offRamp, err := NewOffRamp(logger.Test(t), offrampAddress, evmclimocks.NewClient(t), lp, nil, nil, nil)
require.NoError(t, err)
logs, err := offRamp.GetExecutionStateChangesBetweenSeqNums(testutils.Context(t), minSeqNr, maxSeqNr, 0)
require.NoError(t, err)
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 52f241a30a6..4ae4eeb0d3e 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
@@ -2,6 +2,7 @@ package v1_2_0
import (
"context"
+ "errors"
"fmt"
"strings"
@@ -13,7 +14,6 @@ import (
"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"
@@ -50,16 +50,16 @@ var _ ccipdata.OnRampReader = &OnRamp{}
// Significant change in 1.2:
// - CCIPSendRequested event signature has changed
type OnRamp struct {
- onRamp *evm_2_evm_onramp_1_2_0.EVM2EVMOnRamp
- address common.Address
- lggr logger.Logger
- lp logpoller.LogPoller
- leafHasher ccipdata.LeafHasherInterface[[32]byte]
- client client.Client
- sendRequestedEventSig common.Hash
- sendRequestedSeqNumberWord int
- filters []logpoller.Filter
- cachedSourcePriceRegistryAddress cache.AutoSync[cciptypes.Address]
+ onRamp *evm_2_evm_onramp_1_2_0.EVM2EVMOnRamp
+ address common.Address
+ lggr logger.Logger
+ lp logpoller.LogPoller
+ leafHasher ccipdata.LeafHasherInterface[[32]byte]
+ client client.Client
+ sendRequestedEventSig common.Hash
+ sendRequestedSeqNumberWord int
+ filters []logpoller.Filter
+ cachedOnRampDynamicConfig cache.AutoSync[cciptypes.OnRampDynamicConfig]
// Static config can be cached, because it's never expected to change.
// The only way to change that is through the contract's constructor (redeployment)
cachedStaticConfig cache.OnceCtxFunction[evm_2_evm_onramp_1_2_0.EVM2EVMOnRampStaticConfig]
@@ -108,7 +108,7 @@ func NewOnRamp(lggr logger.Logger, sourceSelector, destSelector uint64, onRampAd
address: onRampAddress,
sendRequestedSeqNumberWord: CCIPSendRequestSeqNumIndex,
sendRequestedEventSig: CCIPSendRequestEventSig,
- cachedSourcePriceRegistryAddress: cache.NewLogpollerEventsBased[cciptypes.Address](
+ cachedOnRampDynamicConfig: cache.NewLogpollerEventsBased[cciptypes.OnRampDynamicConfig](
sourceLP,
[]common.Hash{ConfigSetEventSig},
onRampAddress,
@@ -122,38 +122,39 @@ func (o *OnRamp) Address(context.Context) (cciptypes.Address, error) {
return cciptypes.Address(o.onRamp.Address().String()), nil
}
-func (o *OnRamp) GetDynamicConfig(context.Context) (cciptypes.OnRampDynamicConfig, error) {
- if o.onRamp == nil {
- return cciptypes.OnRampDynamicConfig{}, fmt.Errorf("onramp not initialized")
- }
- config, err := o.onRamp.GetDynamicConfig(&bind.CallOpts{})
- if err != nil {
- return cciptypes.OnRampDynamicConfig{}, fmt.Errorf("get dynamic config v1.2: %w", err)
- }
- return cciptypes.OnRampDynamicConfig{
- Router: cciptypes.Address(config.Router.String()),
- MaxNumberOfTokensPerMsg: config.MaxNumberOfTokensPerMsg,
- DestGasOverhead: config.DestGasOverhead,
- DestGasPerPayloadByte: config.DestGasPerPayloadByte,
- DestDataAvailabilityOverheadGas: config.DestDataAvailabilityOverheadGas,
- DestGasPerDataAvailabilityByte: config.DestGasPerDataAvailabilityByte,
- DestDataAvailabilityMultiplierBps: config.DestDataAvailabilityMultiplierBps,
- PriceRegistry: cciptypes.Address(config.PriceRegistry.String()),
- MaxDataBytes: config.MaxDataBytes,
- MaxPerMsgGasLimit: config.MaxPerMsgGasLimit,
- }, nil
-}
-
-func (o *OnRamp) SourcePriceRegistryAddress(ctx context.Context) (cciptypes.Address, error) {
- return o.cachedSourcePriceRegistryAddress.Get(ctx, func(ctx context.Context) (cciptypes.Address, error) {
- c, err := o.GetDynamicConfig(ctx)
+func (o *OnRamp) GetDynamicConfig(ctx context.Context) (cciptypes.OnRampDynamicConfig, error) {
+ return o.cachedOnRampDynamicConfig.Get(ctx, func(ctx context.Context) (cciptypes.OnRampDynamicConfig, error) {
+ if o.onRamp == nil {
+ return cciptypes.OnRampDynamicConfig{}, errors.New("onramp not initialized")
+ }
+ config, err := o.onRamp.GetDynamicConfig(&bind.CallOpts{Context: ctx})
if err != nil {
- return "", err
+ return cciptypes.OnRampDynamicConfig{}, fmt.Errorf("get dynamic config v1.2: %w", err)
}
- return c.PriceRegistry, nil
+
+ return cciptypes.OnRampDynamicConfig{
+ Router: cciptypes.Address(config.Router.String()),
+ MaxNumberOfTokensPerMsg: config.MaxNumberOfTokensPerMsg,
+ DestGasOverhead: config.DestGasOverhead,
+ DestGasPerPayloadByte: config.DestGasPerPayloadByte,
+ DestDataAvailabilityOverheadGas: config.DestDataAvailabilityOverheadGas,
+ DestGasPerDataAvailabilityByte: config.DestGasPerDataAvailabilityByte,
+ DestDataAvailabilityMultiplierBps: config.DestDataAvailabilityMultiplierBps,
+ PriceRegistry: cciptypes.Address(config.PriceRegistry.String()),
+ MaxDataBytes: config.MaxDataBytes,
+ MaxPerMsgGasLimit: config.MaxPerMsgGasLimit,
+ }, nil
})
}
+func (o *OnRamp) SourcePriceRegistryAddress(ctx context.Context) (cciptypes.Address, error) {
+ c, err := o.GetDynamicConfig(ctx)
+ if err != nil {
+ return "", err
+ }
+ return c.PriceRegistry, nil
+}
+
func (o *OnRamp) GetSendRequestsBetweenSeqNums(ctx context.Context, seqNumMin, seqNumMax uint64, finalized bool) ([]cciptypes.EVM2EVMMessageWithTxMeta, error) {
logs, err := o.lp.LogsDataWordRange(
ctx,
diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0/onramp_test.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0/onramp_test.go
index bbdf52e23a4..ec912667ac7 100644
--- a/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0/onramp_test.go
+++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0/onramp_test.go
@@ -8,12 +8,11 @@ import (
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
- "github.com/smartcontractkit/chainlink-common/pkg/logger"
-
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks"
evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils"
+ "github.com/smartcontractkit/chainlink/v2/core/logger"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/abihelpers"
)
@@ -21,7 +20,7 @@ func TestLogPollerClient_GetSendRequestsBetweenSeqNumsV1_2_0(t *testing.T) {
onRampAddr := utils.RandomAddress()
seqNum := uint64(100)
limit := uint64(10)
- lggr := logger.Test(t)
+ lggr := logger.TestLogger(t)
tests := []struct {
name string
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 636b37c9100..5818f095ea0 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
@@ -13,7 +13,6 @@ import (
"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"
evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types"
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 fd768d4235c..a403139a015 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
@@ -3,6 +3,8 @@ package v1_5_0
import (
"context"
+ "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata"
+
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
@@ -41,8 +43,14 @@ func (c *CommitStore) IsDown(ctx context.Context) (bool, error) {
return !unPausedAndNotCursed, nil
}
-func NewCommitStore(lggr logger.Logger, addr common.Address, ec client.Client, lp logpoller.LogPoller) (*CommitStore, error) {
- v120, err := v1_2_0.NewCommitStore(lggr, addr, ec, lp)
+func NewCommitStore(
+ lggr logger.Logger,
+ addr common.Address,
+ ec client.Client,
+ lp logpoller.LogPoller,
+ feeEstimatorConfig ccipdata.FeeEstimatorConfigReader,
+) (*CommitStore, error) {
+ v120, err := v1_2_0.NewCommitStore(lggr, addr, ec, lp, feeEstimatorConfig)
if err != nil {
return nil, err
}
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 0c45f0d6eac..d5e220c27b1 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
@@ -70,6 +70,7 @@ type OffRamp struct {
*v1_2_0.OffRamp
offRampV150 evm_2_evm_offramp.EVM2EVMOffRampInterface
cachedRateLimitTokens cache.AutoSync[cciptypes.OffRampTokens]
+ feeEstimatorConfig ccipdata.FeeEstimatorConfigReader
}
// GetTokens Returns no data as the offRamps no longer have this information.
@@ -155,7 +156,7 @@ func (o *OffRamp) ChangeConfig(ctx context.Context, onchainConfigBytes []byte, o
PermissionLessExecutionThresholdSeconds: time.Second * time.Duration(onchainConfigParsed.PermissionLessExecutionThresholdSeconds),
Router: cciptypes.Address(onchainConfigParsed.Router.String()),
}
- priceEstimator := prices.NewDAGasPriceEstimator(o.Estimator, o.DestMaxGasPrice, 0, 0)
+ priceEstimator := prices.NewDAGasPriceEstimator(o.Estimator, o.DestMaxGasPrice, 0, 0, o.feeEstimatorConfig)
o.UpdateDynamicConfig(onchainConfig, offchainConfig, priceEstimator)
@@ -166,8 +167,16 @@ func (o *OffRamp) ChangeConfig(ctx context.Context, onchainConfigBytes []byte, o
cciptypes.Address(destWrappedNative.String()), nil
}
-func NewOffRamp(lggr logger.Logger, addr common.Address, ec client.Client, lp logpoller.LogPoller, estimator gas.EvmFeeEstimator, destMaxGasPrice *big.Int) (*OffRamp, error) {
- v120, err := v1_2_0.NewOffRamp(lggr, addr, ec, lp, estimator, destMaxGasPrice)
+func NewOffRamp(
+ lggr logger.Logger,
+ addr common.Address,
+ ec client.Client,
+ lp logpoller.LogPoller,
+ estimator gas.EvmFeeEstimator,
+ destMaxGasPrice *big.Int,
+ feeEstimatorConfig ccipdata.FeeEstimatorConfigReader,
+) (*OffRamp, error) {
+ v120, err := v1_2_0.NewOffRamp(lggr, addr, ec, lp, estimator, destMaxGasPrice, feeEstimatorConfig)
if err != nil {
return nil, err
}
@@ -180,8 +189,9 @@ func NewOffRamp(lggr logger.Logger, addr common.Address, ec client.Client, lp lo
v120.ExecutionReportArgs = abihelpers.MustGetMethodInputs("manuallyExecute", abiOffRamp)[:1]
return &OffRamp{
- OffRamp: v120,
- offRampV150: offRamp,
+ feeEstimatorConfig: feeEstimatorConfig,
+ OffRamp: v120,
+ offRampV150: offRamp,
cachedRateLimitTokens: cache.NewLogpollerEventsBased[cciptypes.OffRampTokens](
lp,
[]common.Hash{RateLimitTokenAddedEvent, RateLimitTokenRemovedEvent},
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 da41d116bc8..6329c8af672 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
@@ -2,6 +2,7 @@ package v1_5_0
import (
"context"
+ "errors"
"fmt"
"strings"
@@ -50,17 +51,17 @@ func init() {
var _ ccipdata.OnRampReader = &OnRamp{}
type OnRamp struct {
- onRamp *evm_2_evm_onramp.EVM2EVMOnRamp
- address common.Address
- destChainSelectorBytes [16]byte
- lggr logger.Logger
- lp logpoller.LogPoller
- leafHasher ccipdata.LeafHasherInterface[[32]byte]
- client client.Client
- sendRequestedEventSig common.Hash
- sendRequestedSeqNumberWord int
- filters []logpoller.Filter
- cachedSourcePriceRegistryAddress cache.AutoSync[cciptypes.Address]
+ onRamp *evm_2_evm_onramp.EVM2EVMOnRamp
+ address common.Address
+ destChainSelectorBytes [16]byte
+ lggr logger.Logger
+ lp logpoller.LogPoller
+ leafHasher ccipdata.LeafHasherInterface[[32]byte]
+ client client.Client
+ sendRequestedEventSig common.Hash
+ sendRequestedSeqNumberWord int
+ filters []logpoller.Filter
+ cachedOnRampDynamicConfig cache.AutoSync[cciptypes.OnRampDynamicConfig]
// Static config can be cached, because it's never expected to change.
// The only way to change that is through the contract's constructor (redeployment)
cachedStaticConfig cache.OnceCtxFunction[evm_2_evm_onramp.EVM2EVMOnRampStaticConfig]
@@ -112,7 +113,7 @@ func NewOnRamp(lggr logger.Logger, sourceSelector, destSelector uint64, onRampAd
address: onRampAddress,
sendRequestedSeqNumberWord: CCIPSendRequestSeqNumIndex,
sendRequestedEventSig: CCIPSendRequestEventSig,
- cachedSourcePriceRegistryAddress: cache.NewLogpollerEventsBased[cciptypes.Address](
+ cachedOnRampDynamicConfig: cache.NewLogpollerEventsBased[cciptypes.OnRampDynamicConfig](
sourceLP,
[]common.Hash{ConfigSetEventSig},
onRampAddress,
@@ -126,38 +127,39 @@ func (o *OnRamp) Address(context.Context) (cciptypes.Address, error) {
return ccipcalc.EvmAddrToGeneric(o.onRamp.Address()), nil
}
-func (o *OnRamp) GetDynamicConfig(context.Context) (cciptypes.OnRampDynamicConfig, error) {
- if o.onRamp == nil {
- return cciptypes.OnRampDynamicConfig{}, fmt.Errorf("onramp not initialized")
- }
- config, err := o.onRamp.GetDynamicConfig(&bind.CallOpts{})
- if err != nil {
- return cciptypes.OnRampDynamicConfig{}, fmt.Errorf("get dynamic config v1.5: %w", err)
- }
- return cciptypes.OnRampDynamicConfig{
- Router: ccipcalc.EvmAddrToGeneric(config.Router),
- MaxNumberOfTokensPerMsg: config.MaxNumberOfTokensPerMsg,
- DestGasOverhead: config.DestGasOverhead,
- DestGasPerPayloadByte: config.DestGasPerPayloadByte,
- DestDataAvailabilityOverheadGas: config.DestDataAvailabilityOverheadGas,
- DestGasPerDataAvailabilityByte: config.DestGasPerDataAvailabilityByte,
- DestDataAvailabilityMultiplierBps: config.DestDataAvailabilityMultiplierBps,
- PriceRegistry: ccipcalc.EvmAddrToGeneric(config.PriceRegistry),
- MaxDataBytes: config.MaxDataBytes,
- MaxPerMsgGasLimit: config.MaxPerMsgGasLimit,
- }, nil
-}
-
-func (o *OnRamp) SourcePriceRegistryAddress(ctx context.Context) (cciptypes.Address, error) {
- return o.cachedSourcePriceRegistryAddress.Get(ctx, func(ctx context.Context) (cciptypes.Address, error) {
- c, err := o.GetDynamicConfig(ctx)
+func (o *OnRamp) GetDynamicConfig(ctx context.Context) (cciptypes.OnRampDynamicConfig, error) {
+ return o.cachedOnRampDynamicConfig.Get(ctx, func(ctx context.Context) (cciptypes.OnRampDynamicConfig, error) {
+ if o.onRamp == nil {
+ return cciptypes.OnRampDynamicConfig{}, errors.New("onramp not initialized")
+ }
+ config, err := o.onRamp.GetDynamicConfig(&bind.CallOpts{})
if err != nil {
- return "", err
+ return cciptypes.OnRampDynamicConfig{}, fmt.Errorf("get dynamic config v1.5: %w", err)
}
- return c.PriceRegistry, nil
+
+ return cciptypes.OnRampDynamicConfig{
+ Router: ccipcalc.EvmAddrToGeneric(config.Router),
+ MaxNumberOfTokensPerMsg: config.MaxNumberOfTokensPerMsg,
+ DestGasOverhead: config.DestGasOverhead,
+ DestGasPerPayloadByte: config.DestGasPerPayloadByte,
+ DestDataAvailabilityOverheadGas: config.DestDataAvailabilityOverheadGas,
+ DestGasPerDataAvailabilityByte: config.DestGasPerDataAvailabilityByte,
+ DestDataAvailabilityMultiplierBps: config.DestDataAvailabilityMultiplierBps,
+ PriceRegistry: ccipcalc.EvmAddrToGeneric(config.PriceRegistry),
+ MaxDataBytes: config.MaxDataBytes,
+ MaxPerMsgGasLimit: config.MaxPerMsgGasLimit,
+ }, nil
})
}
+func (o *OnRamp) SourcePriceRegistryAddress(ctx context.Context) (cciptypes.Address, error) {
+ c, err := o.GetDynamicConfig(ctx)
+ if err != nil {
+ return "", err
+ }
+ return c.PriceRegistry, nil
+}
+
func (o *OnRamp) GetSendRequestsBetweenSeqNums(ctx context.Context, seqNumMin, seqNumMax uint64, finalized bool) ([]cciptypes.EVM2EVMMessageWithTxMeta, error) {
logs, err := o.lp.LogsDataWordRange(
ctx,
diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_5_0/onramp_test.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_5_0/onramp_test.go
index 277b8fd9003..65fccb7821c 100644
--- a/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_5_0/onramp_test.go
+++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_5_0/onramp_test.go
@@ -11,7 +11,6 @@ import (
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
- "github.com/smartcontractkit/chainlink-common/pkg/logger"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/client"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks"
@@ -20,6 +19,7 @@ import (
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_onramp"
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/mock_rmn_contract"
"github.com/smartcontractkit/chainlink/v2/core/internal/testutils"
+ "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/ccipcommon"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata"
@@ -29,7 +29,7 @@ func TestLogPollerClient_GetSendRequestsBetweenSeqNums1_4_0(t *testing.T) {
onRampAddr := utils.RandomAddress()
seqNum := uint64(100)
limit := uint64(10)
- lggr := logger.Test(t)
+ lggr := logger.TestLogger(t)
tests := []struct {
name string
@@ -72,7 +72,7 @@ func Test_ProperlyRecognizesPerLaneCurses(t *testing.T) {
sourceChainSelector := uint64(200)
onRampAddress, mockRMN, mockRMNAddress := setupOnRampV1_5_0(t, user, bc)
- onRamp, err := NewOnRamp(logger.Test(t), 1, destChainSelector, onRampAddress, mocks.NewLogPoller(t), bc)
+ onRamp, err := NewOnRamp(logger.TestLogger(t), 1, destChainSelector, onRampAddress, mocks.NewLogPoller(t), bc)
require.NoError(t, err)
onRamp.cachedStaticConfig = func(ctx context.Context) (evm_2_evm_onramp.EVM2EVMOnRampStaticConfig, error) {
@@ -121,7 +121,7 @@ func BenchmarkIsSourceCursedWithCache(b *testing.B) {
destChainSelector := uint64(100)
onRampAddress, _, _ := setupOnRampV1_5_0(b, user, bc)
- onRamp, err := NewOnRamp(logger.Test(b), 1, destChainSelector, onRampAddress, mocks.NewLogPoller(b), bc)
+ onRamp, err := NewOnRamp(logger.TestLogger(b), 1, destChainSelector, onRampAddress, mocks.NewLogPoller(b), bc)
require.NoError(b, err)
for i := 0; i < b.N; i++ {
diff --git a/core/services/ocr2/plugins/ccip/internal/pricegetter/evm.go b/core/services/ocr2/plugins/ccip/internal/pricegetter/evm.go
index ac4002f53fb..732bbb6be4c 100644
--- a/core/services/ocr2/plugins/ccip/internal/pricegetter/evm.go
+++ b/core/services/ocr2/plugins/ccip/internal/pricegetter/evm.go
@@ -7,20 +7,23 @@ import (
"math/big"
"strings"
- "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/rpclib"
-
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
+ "go.uber.org/multierr"
+ "github.com/smartcontractkit/chainlink-common/pkg/types"
cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip"
+ "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/aggregator_v3_interface"
"github.com/smartcontractkit/chainlink/v2/core/internal/gethwrappers2/generated/offchainaggregator"
"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/rpclib"
)
-const decimalsMethodName = "decimals"
-const latestRoundDataMethodName = "latestRoundData"
+const OffchainAggregator = "OffchainAggregator"
+const DecimalsMethodName = "decimals"
+const LatestRoundDataMethodName = "latestRoundData"
func init() {
// Ensure existence of latestRoundData method on the Aggregator contract.
@@ -28,8 +31,8 @@ func init() {
if err != nil {
panic(err)
}
- ensureMethodOnContract(aggregatorABI, decimalsMethodName)
- ensureMethodOnContract(aggregatorABI, latestRoundDataMethodName)
+ ensureMethodOnContract(aggregatorABI, DecimalsMethodName)
+ ensureMethodOnContract(aggregatorABI, LatestRoundDataMethodName)
}
func ensureMethodOnContract(abi abi.ABI, methodName string) {
@@ -49,9 +52,9 @@ func NewDynamicPriceGetterClient(batchCaller rpclib.EvmBatchCaller) DynamicPrice
}
type DynamicPriceGetter struct {
- cfg config.DynamicPriceGetterConfig
- evmClients map[uint64]DynamicPriceGetterClient
- aggregatorAbi abi.ABI
+ cfg config.DynamicPriceGetterConfig
+ contractReaders map[uint64]types.ContractReader
+ aggregatorAbi abi.ABI
}
func NewDynamicPriceGetterConfig(configJson string) (config.DynamicPriceGetterConfig, error) {
@@ -69,7 +72,7 @@ func NewDynamicPriceGetterConfig(configJson string) (config.DynamicPriceGetterCo
// NewDynamicPriceGetter build a DynamicPriceGetter from a configuration and a map of chain ID to batch callers.
// A batch caller should be provided for all retrieved prices.
-func NewDynamicPriceGetter(cfg config.DynamicPriceGetterConfig, evmClients map[uint64]DynamicPriceGetterClient) (*DynamicPriceGetter, error) {
+func NewDynamicPriceGetter(cfg config.DynamicPriceGetterConfig, contractReaders map[uint64]types.ContractReader) (*DynamicPriceGetter, error) {
if err := cfg.Validate(); err != nil {
return nil, fmt.Errorf("validating dynamic price getter config: %w", err)
}
@@ -77,13 +80,13 @@ func NewDynamicPriceGetter(cfg config.DynamicPriceGetterConfig, evmClients map[u
if err != nil {
return nil, fmt.Errorf("parsing offchainaggregator abi: %w", err)
}
- priceGetter := DynamicPriceGetter{cfg, evmClients, aggregatorAbi}
+ priceGetter := DynamicPriceGetter{cfg, contractReaders, aggregatorAbi}
return &priceGetter, nil
}
// FilterConfiguredTokens implements the PriceGetter interface.
// It filters a list of token addresses for only those that have a price resolution rule configured on the PriceGetterConfig
-func (d *DynamicPriceGetter) FilterConfiguredTokens(ctx context.Context, tokens []cciptypes.Address) (configured []cciptypes.Address, unconfigured []cciptypes.Address, err error) {
+func (d *DynamicPriceGetter) FilterConfiguredTokens(_ context.Context, tokens []cciptypes.Address) (configured []cciptypes.Address, unconfigured []cciptypes.Address, err error) {
configured = []cciptypes.Address{}
unconfigured = []cciptypes.Address{}
for _, tk := range tokens {
@@ -103,7 +106,7 @@ func (d *DynamicPriceGetter) FilterConfiguredTokens(ctx context.Context, tokens
return configured, unconfigured, nil
}
-// It returns the prices of all tokens defined in the price getter.
+// GetJobSpecTokenPricesUSD returns the prices of all tokens defined in the price getter.
func (d *DynamicPriceGetter) GetJobSpecTokenPricesUSD(ctx context.Context) (map[cciptypes.Address]*big.Int, error) {
return d.TokenPricesUSD(ctx, d.getAllTokensDefined())
}
@@ -144,60 +147,113 @@ func (d *DynamicPriceGetter) performBatchCalls(ctx context.Context, batchCallsPe
}
// performBatchCall performs a batch call on a given chain to retrieve token prices.
-func (d *DynamicPriceGetter) performBatchCall(ctx context.Context, chainID uint64, batchCalls *batchCallsForChain, prices map[cciptypes.Address]*big.Int) error {
- // Retrieve the EVM caller for the chain.
- client, exists := d.evmClients[chainID]
- if !exists {
- return fmt.Errorf("evm caller for chain %d not found", chainID)
- }
- evmCaller := client.BatchCaller
-
+func (d *DynamicPriceGetter) performBatchCall(ctx context.Context, chainID uint64, batchCalls *batchCallsForChain, prices map[cciptypes.Address]*big.Int) (err error) {
nbDecimalCalls := len(batchCalls.decimalCalls)
nbLatestRoundDataCalls := len(batchCalls.decimalCalls)
+ nbCalls := len(batchCalls.decimalCalls)
- // Perform batched call (all decimals calls followed by latest round data calls).
- calls := make([]rpclib.EvmCall, 0, nbDecimalCalls+nbLatestRoundDataCalls)
- calls = append(calls, batchCalls.decimalCalls...)
- calls = append(calls, batchCalls.latestRoundDataCalls...)
+ // Retrieve contract reader for the chain
+ contractReader := d.contractReaders[chainID]
- results, err := evmCaller.BatchCall(ctx, 0, calls)
+ // Bind contract reader to the contract addresses necessary for the batch calls
+ bindings := make([]types.BoundContract, 0)
+ for i, call := range batchCalls.decimalCalls {
+ bindings = append(bindings, types.BoundContract{
+ Address: string(ccipcalc.EvmAddrToGeneric(call.ContractAddress())),
+ Name: fmt.Sprintf("%v_%v", OffchainAggregator, i),
+ })
+ }
+
+ err = contractReader.Bind(ctx, bindings)
if err != nil {
- return fmt.Errorf("batch call on chain %d failed: %w", chainID, err)
+ return fmt.Errorf("binding contracts failed: %w", err)
}
- // Extract results.
- decimals := make([]uint8, 0, nbDecimalCalls)
- latestRounds := make([]*big.Int, 0, nbLatestRoundDataCalls)
+ // Construct request, adding a decimals and latestRound req per contract name
+ var decimalsReq uint8
+ batchGetLatestValuesRequest := make(types.BatchGetLatestValuesRequest)
+ for i, call := range batchCalls.decimalCalls {
+ boundContract := types.BoundContract{
+ Address: call.ContractAddress().Hex(),
+ Name: fmt.Sprintf("%v_%v", OffchainAggregator, i),
+ }
+ batchGetLatestValuesRequest[boundContract] = append(batchGetLatestValuesRequest[boundContract], types.BatchRead{
+ ReadName: call.MethodName(),
+ ReturnVal: &decimalsReq,
+ })
+ }
- for i, res := range results[0:nbDecimalCalls] {
- v, err1 := rpclib.ParseOutput[uint8](res, 0)
- if err1 != nil {
- callSignature := batchCalls.decimalCalls[i].String()
- return fmt.Errorf("parse contract output while calling %v on chain %d: %w", callSignature, chainID, err1)
+ for i, call := range batchCalls.latestRoundDataCalls {
+ boundContract := types.BoundContract{
+ Address: call.ContractAddress().Hex(),
+ Name: fmt.Sprintf("%v_%v", OffchainAggregator, i),
}
- decimals = append(decimals, v)
+ batchGetLatestValuesRequest[boundContract] = append(batchGetLatestValuesRequest[boundContract], types.BatchRead{
+ ReadName: call.MethodName(),
+ ReturnVal: &aggregator_v3_interface.LatestRoundData{},
+ })
+ }
+
+ // Perform call
+ result, err2 := contractReader.BatchGetLatestValues(ctx, batchGetLatestValuesRequest)
+ if err2 != nil {
+ return fmt.Errorf("BatchGetLatestValues failed %w", err2)
}
- for i, res := range results[nbDecimalCalls : nbDecimalCalls+nbLatestRoundDataCalls] {
- // latestRoundData function has multiple outputs (roundId,answer,startedAt,updatedAt,answeredInRound).
- // we want the second one (answer, at idx=1).
- v, err1 := rpclib.ParseOutput[*big.Int](res, 1)
- if err1 != nil {
- callSignature := batchCalls.latestRoundDataCalls[i].String()
- return fmt.Errorf("parse contract output while calling %v on chain %d: %w", callSignature, chainID, err1)
+ // Extract results
+ // give result the contract name (key ordering not guaranteed to match that of the request)
+ // and then you get slice of responses
+ decimalsCR := make([]uint8, 0, nbDecimalCalls)
+ latestRoundCR := make([]aggregator_v3_interface.LatestRoundData, 0, nbDecimalCalls)
+ var respErr error
+ for j := range nbCalls {
+ boundContract := types.BoundContract{
+ Address: batchCalls.decimalCalls[j].ContractAddress().Hex(),
+ Name: fmt.Sprintf("%v_%v", OffchainAggregator, j),
}
- latestRounds = append(latestRounds, v)
+ offchainAggregatorRespSlice := result[boundContract]
+
+ for _, read := range offchainAggregatorRespSlice {
+ val, readErr := read.GetResult()
+ if readErr != nil {
+ respErr = multierr.Append(respErr, fmt.Errorf("error with contract reader readName %v: %w", read.ReadName, readErr))
+ continue
+ }
+ if read.ReadName == DecimalsMethodName {
+ decimal, ok := val.(*uint8)
+ if !ok {
+ return fmt.Errorf("expected type uint8 for method call %v on contract %v: %w", batchCalls.decimalCalls[j].MethodName(), batchCalls.decimalCalls[j].ContractAddress(), readErr)
+ }
+
+ decimalsCR = append(decimalsCR, *decimal)
+ } else if read.ReadName == LatestRoundDataMethodName {
+ latestRoundDataRes, ok := val.(*aggregator_v3_interface.LatestRoundData)
+ if !ok {
+ return fmt.Errorf("expected type latestRoundDataConfig for method call %v on contract %v: %w", batchCalls.latestRoundDataCalls[j].MethodName(), batchCalls.latestRoundDataCalls[j].ContractAddress(), readErr)
+ }
+
+ latestRoundCR = append(latestRoundCR, *latestRoundDataRes)
+ }
+ }
+ }
+ if respErr != nil {
+ return respErr
+ }
+
+ latestRoundAnswerCR := make([]*big.Int, 0, nbLatestRoundDataCalls)
+ for i := range nbLatestRoundDataCalls {
+ latestRoundAnswerCR = append(latestRoundAnswerCR, latestRoundCR[i].Answer)
}
// Normalize and store prices.
for i := range batchCalls.tokenOrder {
// Normalize to 1e18.
- if decimals[i] < 18 {
- latestRounds[i].Mul(latestRounds[i], big.NewInt(0).Exp(big.NewInt(10), big.NewInt(18-int64(decimals[i])), nil))
- } else if decimals[i] > 18 {
- latestRounds[i].Div(latestRounds[i], big.NewInt(0).Exp(big.NewInt(10), big.NewInt(int64(decimals[i])-18), nil))
+ if decimalsCR[i] < 18 {
+ latestRoundAnswerCR[i].Mul(latestRoundAnswerCR[i], big.NewInt(0).Exp(big.NewInt(10), big.NewInt(18-int64(decimalsCR[i])), nil))
+ } else if decimalsCR[i] > 18 {
+ latestRoundAnswerCR[i].Div(latestRoundAnswerCR[i], big.NewInt(0).Exp(big.NewInt(10), big.NewInt(int64(decimalsCR[i])-18), nil))
}
- prices[ccipcalc.EvmAddrToGeneric(batchCalls.tokenOrder[i])] = latestRounds[i]
+ prices[ccipcalc.EvmAddrToGeneric(batchCalls.tokenOrder[i])] = latestRoundAnswerCR[i]
}
return nil
}
@@ -225,12 +281,12 @@ func (d *DynamicPriceGetter) preparePricesAndBatchCallsPerChain(tokens []cciptyp
chainCalls := batchCallsPerChain[aggCfg.ChainID]
chainCalls.decimalCalls = append(chainCalls.decimalCalls, rpclib.NewEvmCall(
d.aggregatorAbi,
- decimalsMethodName,
+ DecimalsMethodName,
aggCfg.AggregatorContractAddress,
))
chainCalls.latestRoundDataCalls = append(chainCalls.latestRoundDataCalls, rpclib.NewEvmCall(
d.aggregatorAbi,
- latestRoundDataMethodName,
+ LatestRoundDataMethodName,
aggCfg.AggregatorContractAddress,
))
chainCalls.tokenOrder = append(chainCalls.tokenOrder, tk)
diff --git a/core/services/ocr2/plugins/ccip/internal/pricegetter/evm_test.go b/core/services/ocr2/plugins/ccip/internal/pricegetter/evm_test.go
index bff4bf16a10..ada59d68573 100644
--- a/core/services/ocr2/plugins/ccip/internal/pricegetter/evm_test.go
+++ b/core/services/ocr2/plugins/ccip/internal/pricegetter/evm_test.go
@@ -1,12 +1,15 @@
package pricegetter
import (
+ "context"
+ "fmt"
"math/big"
"testing"
+ "github.com/smartcontractkit/chainlink-common/pkg/types"
+
"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip"
@@ -16,13 +19,11 @@ import (
"github.com/smartcontractkit/chainlink/v2/core/internal/testutils"
"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/rpclib"
- "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/rpclib/rpclibmocks"
)
type testParameters struct {
cfg config.DynamicPriceGetterConfig
- evmClients map[uint64]DynamicPriceGetterClient
+ contractReaders map[uint64]types.ContractReader
tokens []common.Address
expectedTokenPrices map[common.Address]big.Int
expectedTokenPricesForAll map[common.Address]big.Int
@@ -92,13 +93,13 @@ func TestDynamicPriceGetterWithEmptyInput(t *testing.T) {
},
{
name: "get_all_tokens_static_only",
- param: testGetAllTokensStaticOnly(),
+ param: testGetAllTokensStaticOnly(t),
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
- pg, err := NewDynamicPriceGetter(test.param.cfg, test.param.evmClients)
+ pg, err := NewDynamicPriceGetter(test.param.cfg, test.param.contractReaders)
if test.param.invalidConfigErrorExpected {
require.Error(t, err)
return
@@ -194,11 +195,11 @@ func testParamAggregatorOnly(t *testing.T) testParameters {
UpdatedAt: big.NewInt(1715753907),
AnsweredInRound: big.NewInt(4000),
}
- evmClients := map[uint64]DynamicPriceGetterClient{
- uint64(101): mockClient(t, []uint8{8}, []aggregator_v3_interface.LatestRoundData{round1}),
- uint64(102): mockClient(t, []uint8{8}, []aggregator_v3_interface.LatestRoundData{round2}),
- uint64(103): mockClient(t, []uint8{18}, []aggregator_v3_interface.LatestRoundData{round3}),
- uint64(104): mockClient(t, []uint8{20}, []aggregator_v3_interface.LatestRoundData{round4}),
+ contractReaders := map[uint64]types.ContractReader{
+ uint64(101): mockCR([]uint8{8}, cfg, []common.Address{TK1}, []aggregator_v3_interface.LatestRoundData{round1}),
+ uint64(102): mockCR([]uint8{8}, cfg, []common.Address{TK2}, []aggregator_v3_interface.LatestRoundData{round2}),
+ uint64(103): mockCR([]uint8{18}, cfg, []common.Address{TK3}, []aggregator_v3_interface.LatestRoundData{round3}),
+ uint64(104): mockCR([]uint8{20}, cfg, []common.Address{TK4}, []aggregator_v3_interface.LatestRoundData{round4}),
}
expectedTokenPrices := map[common.Address]big.Int{
TK1: *multExp(round1.Answer, 10), // expected in 1e18 format.
@@ -208,7 +209,7 @@ func testParamAggregatorOnly(t *testing.T) testParameters {
}
return testParameters{
cfg: cfg,
- evmClients: evmClients,
+ contractReaders: contractReaders,
tokens: []common.Address{TK1, TK2, TK3, TK4},
expectedTokenPrices: expectedTokenPrices,
invalidConfigErrorExpected: false,
@@ -257,9 +258,9 @@ func testParamAggregatorOnlyMulti(t *testing.T) testParameters {
UpdatedAt: big.NewInt(1704897198),
AnsweredInRound: big.NewInt(3000),
}
- evmClients := map[uint64]DynamicPriceGetterClient{
- uint64(101): mockClient(t, []uint8{8}, []aggregator_v3_interface.LatestRoundData{round1}),
- uint64(102): mockClient(t, []uint8{8, 8}, []aggregator_v3_interface.LatestRoundData{round2, round3}),
+ contractReaders := map[uint64]types.ContractReader{
+ uint64(101): mockCR([]uint8{8}, cfg, []common.Address{TK1}, []aggregator_v3_interface.LatestRoundData{round1}),
+ uint64(102): mockCR([]uint8{8, 8}, cfg, []common.Address{TK2, TK3}, []aggregator_v3_interface.LatestRoundData{round2, round3}),
}
expectedTokenPrices := map[common.Address]big.Int{
TK1: *multExp(round1.Answer, 10),
@@ -268,7 +269,7 @@ func testParamAggregatorOnlyMulti(t *testing.T) testParameters {
}
return testParameters{
cfg: cfg,
- evmClients: evmClients,
+ contractReaders: contractReaders,
invalidConfigErrorExpected: false,
tokens: []common.Address{TK1, TK2, TK3},
expectedTokenPrices: expectedTokenPrices,
@@ -294,7 +295,7 @@ func testParamStaticOnly() testParameters {
},
}
// Real LINK/USD example from OP.
- evmClients := map[uint64]DynamicPriceGetterClient{}
+ contractReaders := map[uint64]types.ContractReader{}
expectedTokenPrices := map[common.Address]big.Int{
TK1: *cfg.StaticPrices[TK1].Price,
TK2: *cfg.StaticPrices[TK2].Price,
@@ -302,7 +303,7 @@ func testParamStaticOnly() testParameters {
}
return testParameters{
cfg: cfg,
- evmClients: evmClients,
+ contractReaders: contractReaders,
tokens: []common.Address{TK1, TK2, TK3},
expectedTokenPrices: expectedTokenPrices,
}
@@ -343,9 +344,9 @@ func testParamNoAggregatorForToken(t *testing.T) testParameters {
UpdatedAt: big.NewInt(1704897197),
AnsweredInRound: big.NewInt(2000),
}
- evmClients := map[uint64]DynamicPriceGetterClient{
- uint64(101): mockClient(t, []uint8{8}, []aggregator_v3_interface.LatestRoundData{round1}),
- uint64(102): mockClient(t, []uint8{8}, []aggregator_v3_interface.LatestRoundData{round2}),
+ contractReaders := map[uint64]types.ContractReader{
+ uint64(101): mockCR([]uint8{8}, cfg, []common.Address{TK1}, []aggregator_v3_interface.LatestRoundData{round1}),
+ uint64(102): mockCR([]uint8{8}, cfg, []common.Address{TK2}, []aggregator_v3_interface.LatestRoundData{round2}),
}
expectedTokenPrices := map[common.Address]big.Int{
TK1: *round1.Answer,
@@ -355,7 +356,7 @@ func testParamNoAggregatorForToken(t *testing.T) testParameters {
}
return testParameters{
cfg: cfg,
- evmClients: evmClients,
+ contractReaders: contractReaders,
tokens: []common.Address{TK1, TK2, TK3, TK4},
expectedTokenPrices: expectedTokenPrices,
priceResolutionErrorExpected: true,
@@ -397,9 +398,9 @@ func testParamAggregatorAndStaticValid(t *testing.T) testParameters {
UpdatedAt: big.NewInt(1704897197),
AnsweredInRound: big.NewInt(2000),
}
- evmClients := map[uint64]DynamicPriceGetterClient{
- uint64(101): mockClient(t, []uint8{8}, []aggregator_v3_interface.LatestRoundData{round1}),
- uint64(102): mockClient(t, []uint8{8}, []aggregator_v3_interface.LatestRoundData{round2}),
+ contractReaders := map[uint64]types.ContractReader{
+ uint64(101): mockCR([]uint8{8}, cfg, []common.Address{TK1}, []aggregator_v3_interface.LatestRoundData{round1}),
+ uint64(102): mockCR([]uint8{8}, cfg, []common.Address{TK2}, []aggregator_v3_interface.LatestRoundData{round2}),
}
expectedTokenPrices := map[common.Address]big.Int{
TK1: *multExp(round1.Answer, 10),
@@ -408,7 +409,7 @@ func testParamAggregatorAndStaticValid(t *testing.T) testParameters {
}
return testParameters{
cfg: cfg,
- evmClients: evmClients,
+ contractReaders: contractReaders,
tokens: []common.Address{TK1, TK2, TK3},
expectedTokenPrices: expectedTokenPrices,
}
@@ -460,14 +461,14 @@ func testParamAggregatorAndStaticTokenCollision(t *testing.T) testParameters {
UpdatedAt: big.NewInt(1704897198),
AnsweredInRound: big.NewInt(3000),
}
- evmClients := map[uint64]DynamicPriceGetterClient{
- uint64(101): mockClient(t, []uint8{8}, []aggregator_v3_interface.LatestRoundData{round1}),
- uint64(102): mockClient(t, []uint8{8}, []aggregator_v3_interface.LatestRoundData{round2}),
- uint64(103): mockClient(t, []uint8{8}, []aggregator_v3_interface.LatestRoundData{round3}),
+ contractReaders := map[uint64]types.ContractReader{
+ uint64(101): mockCR([]uint8{8}, cfg, []common.Address{TK1}, []aggregator_v3_interface.LatestRoundData{round1}),
+ uint64(102): mockCR([]uint8{8}, cfg, []common.Address{TK2}, []aggregator_v3_interface.LatestRoundData{round2}),
+ uint64(103): mockCR([]uint8{8}, cfg, []common.Address{TK3}, []aggregator_v3_interface.LatestRoundData{round3}),
}
return testParameters{
cfg: cfg,
- evmClients: evmClients,
+ contractReaders: contractReaders,
tokens: []common.Address{TK1, TK2, TK3},
invalidConfigErrorExpected: true,
}
@@ -500,17 +501,15 @@ func testParamBatchCallReturnsErr(t *testing.T) testParameters {
UpdatedAt: big.NewInt(1704896575),
AnsweredInRound: big.NewInt(1000),
}
- evmClients := map[uint64]DynamicPriceGetterClient{
- uint64(101): mockClient(t, []uint8{8}, []aggregator_v3_interface.LatestRoundData{round1}),
- uint64(102): {
- BatchCaller: mockErrCaller(t),
- },
+ contractReaders := map[uint64]types.ContractReader{
+ uint64(101): mockCR([]uint8{8}, cfg, []common.Address{TK1}, []aggregator_v3_interface.LatestRoundData{round1}),
+ uint64(102): mockErrCR(),
}
return testParameters{
- cfg: cfg,
- evmClients: evmClients,
- tokens: []common.Address{TK1, TK2, TK3},
- evmCallErr: true,
+ cfg: cfg,
+ contractReaders: contractReaders,
+ tokens: []common.Address{TK1, TK2, TK3},
+ evmCallErr: true,
}
}
@@ -561,10 +560,10 @@ func testLessInputsThanDefinedPrices(t *testing.T) testParameters {
UpdatedAt: big.NewInt(1715743907),
AnsweredInRound: big.NewInt(3000),
}
- evmClients := map[uint64]DynamicPriceGetterClient{
- uint64(101): mockClient(t, []uint8{8}, []aggregator_v3_interface.LatestRoundData{round1}),
- uint64(102): mockClient(t, []uint8{8}, []aggregator_v3_interface.LatestRoundData{round2}),
- uint64(103): mockClient(t, []uint8{8}, []aggregator_v3_interface.LatestRoundData{round3}),
+ contractReaders := map[uint64]types.ContractReader{
+ uint64(101): mockCR([]uint8{8}, cfg, []common.Address{TK1}, []aggregator_v3_interface.LatestRoundData{round1}),
+ uint64(102): mockCR([]uint8{8}, cfg, []common.Address{TK2}, []aggregator_v3_interface.LatestRoundData{round2}),
+ uint64(103): mockCR([]uint8{8}, cfg, []common.Address{TK3}, []aggregator_v3_interface.LatestRoundData{round3}),
}
expectedTokenPrices := map[common.Address]big.Int{
TK1: *multExp(round1.Answer, 10),
@@ -573,7 +572,7 @@ func testLessInputsThanDefinedPrices(t *testing.T) testParameters {
}
return testParameters{
cfg: cfg,
- evmClients: evmClients,
+ contractReaders: contractReaders,
tokens: []common.Address{TK1, TK2, TK3},
expectedTokenPrices: expectedTokenPrices,
}
@@ -626,10 +625,11 @@ func testGetAllTokensAggregatorAndStatic(t *testing.T) testParameters {
UpdatedAt: big.NewInt(1715743907),
AnsweredInRound: big.NewInt(3000),
}
- evmClients := map[uint64]DynamicPriceGetterClient{
- uint64(101): mockClient(t, []uint8{8}, []aggregator_v3_interface.LatestRoundData{round1}),
- uint64(102): mockClient(t, []uint8{8}, []aggregator_v3_interface.LatestRoundData{round2}),
- uint64(103): mockClient(t, []uint8{8}, []aggregator_v3_interface.LatestRoundData{round3}),
+
+ contractReaders := map[uint64]types.ContractReader{
+ uint64(101): mockCR([]uint8{8}, cfg, []common.Address{TK1}, []aggregator_v3_interface.LatestRoundData{round1}),
+ uint64(102): mockCR([]uint8{8}, cfg, []common.Address{TK2}, []aggregator_v3_interface.LatestRoundData{round2}),
+ uint64(103): mockCR([]uint8{8}, cfg, []common.Address{TK3}, []aggregator_v3_interface.LatestRoundData{round3}),
}
expectedTokenPricesForAll := map[common.Address]big.Int{
TK1: *multExp(round1.Answer, 10),
@@ -639,8 +639,8 @@ func testGetAllTokensAggregatorAndStatic(t *testing.T) testParameters {
}
return testParameters{
cfg: cfg,
- evmClients: evmClients,
expectedTokenPricesForAll: expectedTokenPricesForAll,
+ contractReaders: contractReaders,
}
}
@@ -686,11 +686,12 @@ func testGetAllTokensAggregatorOnly(t *testing.T) testParameters {
UpdatedAt: big.NewInt(1715743907),
AnsweredInRound: big.NewInt(3000),
}
- evmClients := map[uint64]DynamicPriceGetterClient{
- uint64(101): mockClient(t, []uint8{8}, []aggregator_v3_interface.LatestRoundData{round1}),
- uint64(102): mockClient(t, []uint8{8}, []aggregator_v3_interface.LatestRoundData{round2}),
- uint64(103): mockClient(t, []uint8{8}, []aggregator_v3_interface.LatestRoundData{round3}),
+ contractReaders := map[uint64]types.ContractReader{
+ uint64(101): mockCR([]uint8{8}, cfg, []common.Address{TK1}, []aggregator_v3_interface.LatestRoundData{round1}),
+ uint64(102): mockCR([]uint8{8}, cfg, []common.Address{TK2}, []aggregator_v3_interface.LatestRoundData{round2}),
+ uint64(103): mockCR([]uint8{8}, cfg, []common.Address{TK3}, []aggregator_v3_interface.LatestRoundData{round3}),
}
+
expectedTokenPricesForAll := map[common.Address]big.Int{
TK1: *multExp(round1.Answer, 10),
TK2: *multExp(round2.Answer, 10),
@@ -698,12 +699,12 @@ func testGetAllTokensAggregatorOnly(t *testing.T) testParameters {
}
return testParameters{
cfg: cfg,
- evmClients: evmClients,
expectedTokenPricesForAll: expectedTokenPricesForAll,
+ contractReaders: contractReaders,
}
}
-func testGetAllTokensStaticOnly() testParameters {
+func testGetAllTokensStaticOnly(t *testing.T) testParameters {
cfg := config.DynamicPriceGetterConfig{
AggregatorPrices: map[common.Address]config.AggregatorPriceConfig{},
StaticPrices: map[common.Address]config.StaticPriceConfig{
@@ -722,7 +723,7 @@ func testGetAllTokensStaticOnly() testParameters {
},
}
- evmClients := map[uint64]DynamicPriceGetterClient{}
+ contractReaders := map[uint64]types.ContractReader{}
expectedTokenPricesForAll := map[common.Address]big.Int{
TK1: *cfg.StaticPrices[TK1].Price,
TK2: *cfg.StaticPrices[TK2].Price,
@@ -730,43 +731,73 @@ func testGetAllTokensStaticOnly() testParameters {
}
return testParameters{
cfg: cfg,
- evmClients: evmClients,
+ contractReaders: contractReaders,
expectedTokenPricesForAll: expectedTokenPricesForAll,
}
}
-func mockClient(t *testing.T, decimals []uint8, rounds []aggregator_v3_interface.LatestRoundData) DynamicPriceGetterClient {
- return DynamicPriceGetterClient{
- BatchCaller: mockCaller(t, decimals, rounds),
- }
-}
-
-func mockCaller(t *testing.T, decimals []uint8, rounds []aggregator_v3_interface.LatestRoundData) *rpclibmocks.EvmBatchCaller {
- caller := rpclibmocks.NewEvmBatchCaller(t)
-
+func mockCR(decimals []uint8, cfg config.DynamicPriceGetterConfig, addr []common.Address, rounds []aggregator_v3_interface.LatestRoundData) *mockContractReader {
// Mock batch calls per chain: all decimals calls then all latestRoundData calls.
- dataAndErrs := make([]rpclib.DataAndErr, 0, len(decimals)+len(rounds))
- for _, d := range decimals {
- dataAndErrs = append(dataAndErrs, rpclib.DataAndErr{
- Outputs: []any{d},
- })
+ bGLVR := make(types.BatchGetLatestValuesResult)
+
+ for i := range len(decimals) {
+ boundContract := types.BoundContract{
+ Address: cfg.AggregatorPrices[addr[i]].AggregatorContractAddress.Hex(),
+ Name: fmt.Sprintf("%v_%v", OffchainAggregator, i),
+ }
+ bGLVR[boundContract] = types.ContractBatchResults{}
+ }
+ for i, d := range decimals {
+ contractName := fmt.Sprintf("%v_%v", OffchainAggregator, i)
+ readRes := types.BatchReadResult{
+ ReadName: DecimalsMethodName,
+ }
+ readRes.SetResult(&d, nil)
+ boundContract := types.BoundContract{
+ Address: cfg.AggregatorPrices[addr[i]].AggregatorContractAddress.Hex(),
+ Name: contractName,
+ }
+ bGLVR[boundContract] = append(bGLVR[boundContract], readRes)
}
- for _, round := range rounds {
- dataAndErrs = append(dataAndErrs, rpclib.DataAndErr{
- Outputs: []any{round.RoundId, round.Answer, round.StartedAt, round.UpdatedAt, round.AnsweredInRound},
- })
+
+ for i, r := range rounds {
+ contractName := fmt.Sprintf("%v_%v", OffchainAggregator, i)
+ readRes := types.BatchReadResult{
+ ReadName: LatestRoundDataMethodName,
+ }
+ readRes.SetResult(&r, nil)
+ boundContract := types.BoundContract{
+ Address: cfg.AggregatorPrices[addr[i]].AggregatorContractAddress.Hex(),
+ Name: contractName,
+ }
+ bGLVR[boundContract] = append(bGLVR[boundContract], readRes)
}
- caller.On("BatchCall", mock.Anything, uint64(0), mock.Anything).Return(dataAndErrs, nil).Maybe()
- return caller
+
+ return &mockContractReader{result: bGLVR}
}
-func mockErrCaller(t *testing.T) *rpclibmocks.EvmBatchCaller {
- caller := rpclibmocks.NewEvmBatchCaller(t)
- caller.On("BatchCall", mock.Anything, uint64(0), mock.Anything).Return(nil, assert.AnError).Maybe()
- return caller
+func mockErrCR() *mockContractReader {
+ return &mockContractReader{err: assert.AnError}
}
// multExp returns the result of multiplying x by 10^e.
func multExp(x *big.Int, e int64) *big.Int {
return big.NewInt(0).Mul(x, big.NewInt(0).Exp(big.NewInt(10), big.NewInt(e), nil))
}
+
+type mockContractReader struct {
+ types.UnimplementedContractReader
+ result types.BatchGetLatestValuesResult
+ err error
+}
+
+func (m *mockContractReader) Bind(context.Context, []types.BoundContract) error {
+ return nil
+}
+
+func (m *mockContractReader) BatchGetLatestValues(context.Context, types.BatchGetLatestValuesRequest) (types.BatchGetLatestValuesResult, error) {
+ if m.err != nil {
+ return nil, m.err
+ }
+ return m.result, nil
+}
diff --git a/core/services/ocr2/plugins/ccip/internal/pricegetter/pipeline.go b/core/services/ocr2/plugins/ccip/internal/pricegetter/pipeline.go
index 34977eda9f1..b9effffda15 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 6c4aabb4355..9fac4595464 100644
--- a/core/services/ocr2/plugins/ccip/internal/rpclib/evm.go
+++ b/core/services/ocr2/plugins/ccip/internal/rpclib/evm.go
@@ -280,6 +280,10 @@ func (c EvmCall) String() string {
return fmt.Sprintf("%s: %s(%+v)", c.contractAddress.String(), c.methodName, c.args)
}
+func (c EvmCall) ContractAddress() common.Address {
+ return c.contractAddress
+}
+
func EVMCallsToString(calls []EvmCall) string {
callString := ""
for _, call := range calls {
diff --git a/core/services/ocr2/plugins/ccip/metrics.go b/core/services/ocr2/plugins/ccip/metrics.go
index f481b5d447d..9ec9fde316e 100644
--- a/core/services/ocr2/plugins/ccip/metrics.go
+++ b/core/services/ocr2/plugins/ccip/metrics.go
@@ -20,6 +20,10 @@ var (
Name: "ccip_sequence_number_counter",
Help: "Sequence number of the last message processed by the plugin",
}, []string{"plugin", "source", "dest", "ocrPhase"})
+ newReportingPluginErrorCounter = promauto.NewGaugeVec(prometheus.GaugeOpts{
+ Name: "ccip_new_reporting_plugin_error_counter",
+ Help: "The count of the number of errors when calling NewReportingPlugin",
+ }, []string{"plugin"})
)
type ocrPhase string
@@ -35,6 +39,7 @@ type PluginMetricsCollector interface {
NumberOfMessagesBasedOnInterval(phase ocrPhase, seqNrMin, seqNrMax uint64)
UnexpiredCommitRoots(count int)
SequenceNumber(phase ocrPhase, seqNr uint64)
+ NewReportingPluginError()
}
type pluginMetricsCollector struct {
@@ -79,6 +84,12 @@ func (p *pluginMetricsCollector) SequenceNumber(phase ocrPhase, seqNr uint64) {
Set(float64(seqNr))
}
+func (p *pluginMetricsCollector) NewReportingPluginError() {
+ newReportingPluginErrorCounter.
+ WithLabelValues(p.pluginName).
+ Inc()
+}
+
var (
// NoopMetricsCollector is a no-op implementation of PluginMetricsCollector
NoopMetricsCollector PluginMetricsCollector = noop{}
@@ -97,3 +108,6 @@ func (d noop) UnexpiredCommitRoots(int) {
func (d noop) SequenceNumber(ocrPhase, uint64) {
}
+
+func (d noop) NewReportingPluginError() {
+}
diff --git a/core/services/ocr2/plugins/ccip/observations.go b/core/services/ocr2/plugins/ccip/observations.go
index f79d667a550..29fa85021fe 100644
--- a/core/services/ocr2/plugins/ccip/observations.go
+++ b/core/services/ocr2/plugins/ccip/observations.go
@@ -19,6 +19,10 @@ import (
// Note if a breaking change is introduced to this struct nodes running different versions
// will not be able to unmarshal each other's observations. Do not modify unless you
// know what you are doing.
+//
+// IMPORTANT: Both CommitObservation and ExecutionObservation are streamed and processed by Atlas.
+// Any change to that struct must be reflected in the Atlas codebase.
+// Additionally, you must test if OTI telemetry ingestion works with the new struct on staging environment.
type CommitObservation struct {
Interval cciptypes.CommitStoreInterval `json:"interval"`
TokenPricesUSD map[cciptypes.Address]*big.Int `json:"tokensPerFeeCoin"`
@@ -47,6 +51,10 @@ func (o CommitObservation) Marshal() ([]byte, error) {
// Note if a breaking change is introduced to this struct nodes running different versions
// will not be able to unmarshal each other's observations. Do not modify unless you
// know what you are doing.
+//
+// IMPORTANT: Both CommitObservation and ExecutionObservation are streamed and processed by Atlas.
+// Any change to that struct must be reflected in the Atlas codebase.
+// Additionally, you must test if OTI telemetry ingestion works with the new struct on staging environment.
type ExecutionObservation struct {
Messages map[uint64]MsgData `json:"messages"`
}
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 d0093e5d672..04002002f5c 100644
--- a/core/services/ocr2/plugins/ccip/prices/da_price_estimator.go
+++ b/core/services/ocr2/plugins/ccip/prices/da_price_estimator.go
@@ -5,6 +5,8 @@ import (
"fmt"
"math/big"
+ "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata"
+
cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas/rollups"
@@ -14,11 +16,9 @@ import (
type DAGasPriceEstimator struct {
execEstimator GasPriceEstimator
l1Oracle rollups.L1Oracle
+ feeEstimatorConfig ccipdata.FeeEstimatorConfigReader
priceEncodingLength uint
daDeviationPPB int64
- daOverheadGas int64
- gasPerDAByte int64
- daMultiplier int64
}
func NewDAGasPriceEstimator(
@@ -26,12 +26,14 @@ func NewDAGasPriceEstimator(
maxGasPrice *big.Int,
deviationPPB int64,
daDeviationPPB int64,
+ feeEstimatorConfig ccipdata.FeeEstimatorConfigReader, // DA Config Cache updates in the onRamp reader and shares the state
) *DAGasPriceEstimator {
return &DAGasPriceEstimator{
execEstimator: NewExecGasPriceEstimator(estimator, maxGasPrice, deviationPPB),
l1Oracle: estimator.L1Oracle(),
priceEncodingLength: daGasPriceEncodingLength,
daDeviationPPB: daDeviationPPB,
+ feeEstimatorConfig: feeEstimatorConfig,
}
}
@@ -54,7 +56,14 @@ func (g DAGasPriceEstimator) GetGasPrice(ctx context.Context) (*big.Int, error)
return nil, err
}
- if daGasPrice := daGasPriceWei.ToInt(); daGasPrice.Cmp(big.NewInt(0)) > 0 {
+ daGasPrice := daGasPriceWei.ToInt()
+
+ gasPrice, daGasPrice, err = g.feeEstimatorConfig.ModifyGasPriceComponents(ctx, gasPrice, daGasPrice)
+ if err != nil {
+ return nil, fmt.Errorf("gasPrice modification failed: %w", err)
+ }
+
+ if daGasPrice.Cmp(big.NewInt(0)) > 0 {
if daGasPrice.BitLen() > int(g.priceEncodingLength) {
return nil, fmt.Errorf("data availability gas price exceeded max range %+v", daGasPrice)
}
@@ -141,7 +150,10 @@ func (g DAGasPriceEstimator) EstimateMsgCostUSD(ctx context.Context, p *big.Int,
// If there is data availability price component, then include data availability cost in fee estimation
if daGasPrice.Cmp(big.NewInt(0)) > 0 {
- daGasCostUSD := g.estimateDACostUSD(daGasPrice, wrappedNativePrice, msg)
+ daGasCostUSD, err := g.estimateDACostUSD(daGasPrice, wrappedNativePrice, msg)
+ if err != nil {
+ return nil, err
+ }
execCostUSD = new(big.Int).Add(daGasCostUSD, execCostUSD)
}
return execCostUSD, nil
@@ -160,17 +172,22 @@ func (g DAGasPriceEstimator) parseEncodedGasPrice(p *big.Int) (*big.Int, *big.In
return daGasPrice, execGasPrice, nil
}
-func (g DAGasPriceEstimator) estimateDACostUSD(daGasPrice *big.Int, wrappedNativePrice *big.Int, msg cciptypes.EVM2EVMOnRampCCIPSendRequestedWithMeta) *big.Int {
+func (g DAGasPriceEstimator) estimateDACostUSD(daGasPrice *big.Int, wrappedNativePrice *big.Int, msg cciptypes.EVM2EVMOnRampCCIPSendRequestedWithMeta) (*big.Int, error) {
var sourceTokenDataLen int
for _, tokenData := range msg.SourceTokenData {
sourceTokenDataLen += len(tokenData)
}
+ daOverheadGas, gasPerDAByte, daMultiplier, err := g.feeEstimatorConfig.GetDataAvailabilityConfig(context.Background())
+ if err != nil {
+ return nil, err
+ }
+
dataLen := evmMessageFixedBytes + len(msg.Data) + len(msg.TokenAmounts)*evmMessageBytesPerToken + sourceTokenDataLen
- dataGas := big.NewInt(int64(dataLen)*g.gasPerDAByte + g.daOverheadGas)
+ dataGas := big.NewInt(int64(dataLen)*gasPerDAByte + daOverheadGas)
dataGasEstimate := new(big.Int).Mul(dataGas, daGasPrice)
- dataGasEstimate = new(big.Int).Div(new(big.Int).Mul(dataGasEstimate, big.NewInt(g.daMultiplier)), big.NewInt(daMultiplierBase))
+ dataGasEstimate = new(big.Int).Div(new(big.Int).Mul(dataGasEstimate, big.NewInt(daMultiplier)), big.NewInt(daMultiplierBase))
- return ccipcalc.CalculateUsdPerUnitGas(dataGasEstimate, wrappedNativePrice)
+ return ccipcalc.CalculateUsdPerUnitGas(dataGasEstimate, wrappedNativePrice), nil
}
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 2772042f68d..aadf969eadd 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
@@ -2,16 +2,19 @@ package prices
import (
"context"
+ "errors"
"math/big"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
+ "github.com/stretchr/testify/require"
cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip"
"github.com/smartcontractkit/chainlink-common/pkg/utils/tests"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas/rollups/mocks"
+ ccipdatamocks "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata/mocks"
)
func encodeGasPrice(daPrice, execPrice *big.Int) *big.Int {
@@ -22,11 +25,13 @@ func TestDAPriceEstimator_GetGasPrice(t *testing.T) {
ctx := context.Background()
testCases := []struct {
- name string
- daGasPrice *big.Int
- execGasPrice *big.Int
- expPrice *big.Int
- expErr bool
+ name string
+ daGasPrice *big.Int
+ execGasPrice *big.Int
+ expPrice *big.Int
+ modExecGasPrice *big.Int
+ modDAGasPrice *big.Int
+ expErr bool
}{
{
name: "base",
@@ -56,6 +61,31 @@ func TestDAPriceEstimator_GetGasPrice(t *testing.T) {
expPrice: encodeGasPrice(big.NewInt(1e9), big.NewInt(0)),
expErr: false,
},
+ {
+ name: "execGasPrice Modified",
+ daGasPrice: big.NewInt(1e9),
+ execGasPrice: big.NewInt(0),
+ modExecGasPrice: big.NewInt(1),
+ expPrice: encodeGasPrice(big.NewInt(1e9), big.NewInt(1)),
+ expErr: false,
+ },
+ {
+ name: "daGasPrice Modified",
+ daGasPrice: big.NewInt(1e9),
+ execGasPrice: big.NewInt(0),
+ modDAGasPrice: big.NewInt(1),
+ expPrice: encodeGasPrice(big.NewInt(1), big.NewInt(0)),
+ expErr: false,
+ },
+ {
+ name: "daGasPrice and execGasPrice Modified",
+ daGasPrice: big.NewInt(1e9),
+ execGasPrice: big.NewInt(0),
+ modDAGasPrice: big.NewInt(1),
+ modExecGasPrice: big.NewInt(2),
+ expPrice: encodeGasPrice(big.NewInt(1), big.NewInt(2)),
+ expErr: false,
+ },
{
name: "price out of bounds",
daGasPrice: new(big.Int).Lsh(big.NewInt(1), daGasPriceEncodingLength),
@@ -73,10 +103,25 @@ func TestDAPriceEstimator_GetGasPrice(t *testing.T) {
l1Oracle := mocks.NewL1Oracle(t)
l1Oracle.On("GasPrice", ctx).Return(assets.NewWei(tc.daGasPrice), nil)
+ feeEstimatorConfig := ccipdatamocks.NewFeeEstimatorConfigReader(t)
+
+ modRespExecGasPrice := tc.execGasPrice
+ if tc.modExecGasPrice != nil {
+ modRespExecGasPrice = tc.modExecGasPrice
+ }
+
+ modRespDAGasPrice := tc.daGasPrice
+ if tc.modDAGasPrice != nil {
+ modRespDAGasPrice = tc.modDAGasPrice
+ }
+ feeEstimatorConfig.On("ModifyGasPriceComponents", mock.Anything, tc.execGasPrice, tc.daGasPrice).
+ Return(modRespExecGasPrice, modRespDAGasPrice, nil)
+
g := DAGasPriceEstimator{
execEstimator: execEstimator,
l1Oracle: l1Oracle,
priceEncodingLength: daGasPriceEncodingLength,
+ feeEstimatorConfig: feeEstimatorConfig,
}
gasPrice, err := g.GetGasPrice(ctx)
@@ -329,14 +374,17 @@ func TestDAPriceEstimator_EstimateMsgCostUSD(t *testing.T) {
execCostUSD := big.NewInt(100_000)
testCases := []struct {
- name string
- gasPrice *big.Int
- wrappedNativePrice *big.Int
- msg cciptypes.EVM2EVMOnRampCCIPSendRequestedWithMeta
- daOverheadGas int64
- gasPerDAByte int64
- daMultiplier int64
- expUSD *big.Int
+ name string
+ gasPrice *big.Int
+ wrappedNativePrice *big.Int
+ msg cciptypes.EVM2EVMOnRampCCIPSendRequestedWithMeta
+ daOverheadGas int64
+ gasPerDAByte int64
+ daMultiplier int64
+ expUSD *big.Int
+ onRampConfig cciptypes.OnRampDynamicConfig
+ execEstimatorResponse []any
+ execEstimatorErr error
}{
{
name: "only DA overhead",
@@ -349,10 +397,8 @@ func TestDAPriceEstimator_EstimateMsgCostUSD(t *testing.T) {
SourceTokenData: [][]byte{},
},
},
- daOverheadGas: 100_000,
- gasPerDAByte: 0,
- daMultiplier: 10_000, // 1x multiplier
- expUSD: new(big.Int).Add(execCostUSD, big.NewInt(100_000e9)),
+ expUSD: new(big.Int).Add(execCostUSD, big.NewInt(100_000e9)),
+ execEstimatorResponse: []any{int64(100_000), int64(0), int64(10_000), nil},
},
{
name: "include message data gas",
@@ -367,10 +413,8 @@ func TestDAPriceEstimator_EstimateMsgCostUSD(t *testing.T) {
},
},
},
- daOverheadGas: 100_000,
- gasPerDAByte: 16,
- daMultiplier: 10_000, // 1x multiplier
- expUSD: new(big.Int).Add(execCostUSD, big.NewInt(134_208e9)),
+ expUSD: new(big.Int).Add(execCostUSD, big.NewInt(134_208e9)),
+ execEstimatorResponse: []any{int64(100_000), int64(16), int64(10_000), nil},
},
{
name: "zero DA price",
@@ -383,10 +427,7 @@ func TestDAPriceEstimator_EstimateMsgCostUSD(t *testing.T) {
SourceTokenData: [][]byte{},
},
},
- daOverheadGas: 100_000,
- gasPerDAByte: 16,
- daMultiplier: 10_000, // 1x multiplier
- expUSD: execCostUSD,
+ expUSD: execCostUSD,
},
{
name: "double native price",
@@ -399,10 +440,8 @@ func TestDAPriceEstimator_EstimateMsgCostUSD(t *testing.T) {
SourceTokenData: [][]byte{},
},
},
- daOverheadGas: 100_000,
- gasPerDAByte: 0,
- daMultiplier: 10_000, // 1x multiplier
- expUSD: new(big.Int).Add(execCostUSD, big.NewInt(200_000e9)),
+ expUSD: new(big.Int).Add(execCostUSD, big.NewInt(200_000e9)),
+ execEstimatorResponse: []any{int64(100_000), int64(0), int64(10_000), nil},
},
{
name: "half multiplier",
@@ -415,31 +454,68 @@ func TestDAPriceEstimator_EstimateMsgCostUSD(t *testing.T) {
SourceTokenData: [][]byte{},
},
},
- daOverheadGas: 100_000,
- gasPerDAByte: 0,
- daMultiplier: 5_000, // 0.5x multiplier
- expUSD: new(big.Int).Add(execCostUSD, big.NewInt(50_000e9)),
+ expUSD: new(big.Int).Add(execCostUSD, big.NewInt(50_000e9)),
+ execEstimatorResponse: []any{int64(100_000), int64(0), int64(5_000), nil},
+ },
+ {
+ name: "onRamp reader error",
+ gasPrice: encodeGasPrice(big.NewInt(1e9), big.NewInt(0)), // 1 gwei DA price, 0 exec price
+ wrappedNativePrice: big.NewInt(1e18), // $1
+ msg: cciptypes.EVM2EVMOnRampCCIPSendRequestedWithMeta{
+ EVM2EVMMessage: cciptypes.EVM2EVMMessage{
+ Data: []byte{},
+ TokenAmounts: []cciptypes.TokenAmount{},
+ SourceTokenData: [][]byte{},
+ },
+ },
+ execEstimatorResponse: []any{int64(0), int64(0), int64(0), errors.New("some reader error")},
+ },
+ {
+ name: "execEstimator error",
+ gasPrice: encodeGasPrice(big.NewInt(1e9), big.NewInt(0)), // 1 gwei DA price, 0 exec price
+ wrappedNativePrice: big.NewInt(1e18), // $1
+ msg: cciptypes.EVM2EVMOnRampCCIPSendRequestedWithMeta{
+ EVM2EVMMessage: cciptypes.EVM2EVMMessage{
+ Data: []byte{},
+ TokenAmounts: []cciptypes.TokenAmount{},
+ SourceTokenData: [][]byte{},
+ },
+ },
+ execEstimatorErr: errors.New("some estimator error"),
},
}
for _, tc := range testCases {
- execEstimator := NewMockGasPriceEstimator(t)
- execEstimator.On("EstimateMsgCostUSD", mock.Anything, mock.Anything, tc.wrappedNativePrice, tc.msg).Return(execCostUSD, nil)
-
t.Run(tc.name, func(t *testing.T) {
ctx := tests.Context(t)
+
+ execEstimator := NewMockGasPriceEstimator(t)
+ execEstimator.On("EstimateMsgCostUSD", mock.Anything, mock.Anything, tc.wrappedNativePrice, tc.msg).
+ Return(execCostUSD, tc.execEstimatorErr)
+
+ feeEstimatorConfig := ccipdatamocks.NewFeeEstimatorConfigReader(t)
+ if len(tc.execEstimatorResponse) > 0 {
+ feeEstimatorConfig.On("GetDataAvailabilityConfig", mock.Anything).
+ Return(tc.execEstimatorResponse...)
+ }
+
g := DAGasPriceEstimator{
execEstimator: execEstimator,
l1Oracle: nil,
priceEncodingLength: daGasPriceEncodingLength,
- daOverheadGas: tc.daOverheadGas,
- gasPerDAByte: tc.gasPerDAByte,
- daMultiplier: tc.daMultiplier,
+ feeEstimatorConfig: feeEstimatorConfig,
}
costUSD, err := g.EstimateMsgCostUSD(ctx, tc.gasPrice, tc.wrappedNativePrice, tc.msg)
- assert.NoError(t, err)
- assert.Equal(t, tc.expUSD, costUSD)
+
+ switch {
+ case len(tc.execEstimatorResponse) == 4 && tc.execEstimatorResponse[3] != nil,
+ tc.execEstimatorErr != nil:
+ require.Error(t, err)
+ default:
+ require.NoError(t, err)
+ assert.Equal(t, tc.expUSD, costUSD)
+ }
})
}
}
diff --git a/core/services/ocr2/plugins/ccip/prices/gas_price_estimator.go b/core/services/ocr2/plugins/ccip/prices/gas_price_estimator.go
index 49a6fbcc4ad..4aac664e33e 100644
--- a/core/services/ocr2/plugins/ccip/prices/gas_price_estimator.go
+++ b/core/services/ocr2/plugins/ccip/prices/gas_price_estimator.go
@@ -3,6 +3,8 @@ package prices
import (
"math/big"
+ "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata"
+
"github.com/Masterminds/semver/v3"
"github.com/pkg/errors"
@@ -47,12 +49,13 @@ func NewGasPriceEstimatorForCommitPlugin(
maxExecGasPrice *big.Int,
daDeviationPPB int64,
execDeviationPPB int64,
+ feeEstimatorConfig ccipdata.FeeEstimatorConfigReader,
) (GasPriceEstimatorCommit, error) {
switch commitStoreVersion.String() {
case "1.0.0", "1.1.0":
return NewExecGasPriceEstimator(estimator, maxExecGasPrice, execDeviationPPB), nil
case "1.2.0":
- return NewDAGasPriceEstimator(estimator, maxExecGasPrice, execDeviationPPB, daDeviationPPB), nil
+ return NewDAGasPriceEstimator(estimator, maxExecGasPrice, execDeviationPPB, daDeviationPPB, feeEstimatorConfig), nil
default:
return nil, errors.Errorf("Invalid commitStore version: %s", commitStoreVersion)
}
diff --git a/core/services/ocr2/plugins/ccip/testhelpers/ccip_contracts.go b/core/services/ocr2/plugins/ccip/testhelpers/ccip_contracts.go
index 6875bae1419..9238d453966 100644
--- a/core/services/ocr2/plugins/ccip/testhelpers/ccip_contracts.go
+++ b/core/services/ocr2/plugins/ccip/testhelpers/ccip_contracts.go
@@ -75,6 +75,8 @@ var (
SourceChainSelector = uint64(11787463284727550157)
DestChainID = uint64(1337)
DestChainSelector = uint64(3379446385462418246)
+
+ TokenDecimals = uint8(18)
)
// Backwards compat, in principle these statuses are version dependent
@@ -147,20 +149,20 @@ func (c ExecOffchainConfig) Encode() ([]byte, error) {
}
func NewExecOffchainConfig(
- DestOptimisticConfirmations uint32,
- BatchGasLimit uint32,
- RelativeBoostPerWaitHour float64,
- InflightCacheExpiry config.Duration,
- RootSnoozeTime config.Duration,
- BatchingStrategyID uint32,
+ destOptimisticConfirmations uint32,
+ batchGasLimit uint32,
+ relativeBoostPerWaitHour float64,
+ inflightCacheExpiry config.Duration,
+ rootSnoozeTime config.Duration,
+ batchingStrategyID uint32, // 0 = Standard, 1 = Out of Order
) ExecOffchainConfig {
return ExecOffchainConfig{v1_2_0.JSONExecOffchainConfig{
- DestOptimisticConfirmations: DestOptimisticConfirmations,
- BatchGasLimit: BatchGasLimit,
- RelativeBoostPerWaitHour: RelativeBoostPerWaitHour,
- InflightCacheExpiry: InflightCacheExpiry,
- RootSnoozeTime: RootSnoozeTime,
- BatchingStrategyID: BatchingStrategyID,
+ DestOptimisticConfirmations: destOptimisticConfirmations,
+ BatchGasLimit: batchGasLimit,
+ RelativeBoostPerWaitHour: relativeBoostPerWaitHour,
+ InflightCacheExpiry: inflightCacheExpiry,
+ RootSnoozeTime: rootSnoozeTime,
+ BatchingStrategyID: batchingStrategyID,
}}
}
diff --git a/core/services/ocr2/plugins/ccip/testhelpers/integration/chainlink.go b/core/services/ocr2/plugins/ccip/testhelpers/integration/chainlink.go
index d21c5b12513..b1c3677e497 100644
--- a/core/services/ocr2/plugins/ccip/testhelpers/integration/chainlink.go
+++ b/core/services/ocr2/plugins/ccip/testhelpers/integration/chainlink.go
@@ -38,10 +38,10 @@ import (
"github.com/smartcontractkit/chainlink-common/pkg/utils/tests"
cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip"
- coretypes "github.com/smartcontractkit/chainlink-common/pkg/types/core/mocks"
pb "github.com/smartcontractkit/chainlink-protos/orchestrator/feedsmanager"
+ evmcapabilities "github.com/smartcontractkit/chainlink/v2/core/capabilities"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/client"
v2 "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller"
@@ -383,6 +383,7 @@ func setupNodeCCIP(
fmt.Sprintf("127.0.0.1:%d", port),
}
c.Log.Level = &loglevel
+ c.Feature.CCIP = &trueRef
c.Feature.UICSAKeys = &trueRef
c.Feature.FeedsManager = &trueRef
c.OCR.Enabled = &falseRef
@@ -468,7 +469,7 @@ func setupNodeCCIP(
Logger: lggr,
LoopRegistry: loopRegistry,
GRPCOpts: loop.GRPCOpts{},
- CapabilitiesRegistry: coretypes.NewCapabilitiesRegistry(t),
+ CapabilitiesRegistry: evmcapabilities.NewRegistry(logger.TestLogger(t)),
}
testCtx := testutils.Context(t)
// evm alway enabled for backward compatibility
diff --git a/core/services/ocr2/plugins/ccip/testhelpers/integration/jobspec.go b/core/services/ocr2/plugins/ccip/testhelpers/integration/jobspec.go
index a520580e614..41fbc76cd7f 100644
--- a/core/services/ocr2/plugins/ccip/testhelpers/integration/jobspec.go
+++ b/core/services/ocr2/plugins/ccip/testhelpers/integration/jobspec.go
@@ -180,6 +180,7 @@ type CCIPJobSpecParams struct {
DestStartBlock uint64
USDCAttestationAPI string
USDCConfig *config.USDCConfig
+ LBTCConfig *config.LBTCConfig
P2PV2Bootstrappers pq.StringArray
}
@@ -305,6 +306,11 @@ func (params CCIPJobSpecParams) ExecutionJobSpec() (*OCR2TaskJobSpec, error) {
ocrSpec.PluginConfig["USDCConfig.SourceMessageTransmitterAddress"] = fmt.Sprintf(`"%s"`, params.USDCConfig.SourceMessageTransmitterAddress)
ocrSpec.PluginConfig["USDCConfig.AttestationAPITimeoutSeconds"] = params.USDCConfig.AttestationAPITimeoutSeconds
}
+ if params.LBTCConfig != nil {
+ ocrSpec.PluginConfig["LBTCConfig.AttestationAPI"] = fmt.Sprintf(`"%s"`, params.LBTCConfig.AttestationAPI)
+ ocrSpec.PluginConfig["LBTCConfig.SourceTokenAddress"] = fmt.Sprintf(`"%s"`, params.LBTCConfig.SourceTokenAddress)
+ ocrSpec.PluginConfig["LBTCConfig.AttestationAPITimeoutSeconds"] = params.LBTCConfig.AttestationAPITimeoutSeconds
+ }
return &OCR2TaskJobSpec{
OCR2OracleSpec: ocrSpec,
JobType: "offchainreporting2",
diff --git a/core/services/ocr2/plugins/ccip/testhelpers/testhelpers_1_4_0/ccip_contracts_1_4_0.go b/core/services/ocr2/plugins/ccip/testhelpers/testhelpers_1_4_0/ccip_contracts_1_4_0.go
new file mode 100644
index 00000000000..feb18ee50c1
--- /dev/null
+++ b/core/services/ocr2/plugins/ccip/testhelpers/testhelpers_1_4_0/ccip_contracts_1_4_0.go
@@ -0,0 +1,1601 @@
+//nolint:revive // helpers for specific version
+package testhelpers_1_4_0
+
+import (
+ "context"
+ "fmt"
+ "math"
+ "math/big"
+ "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/types"
+ "github.com/pkg/errors"
+ "github.com/rs/zerolog/log"
+ "github.com/stretchr/testify/require"
+
+ "github.com/smartcontractkit/libocr/offchainreporting2/confighelper"
+ ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2/types"
+ ocr2types "github.com/smartcontractkit/libocr/offchainreporting2plus/types"
+
+ "github.com/smartcontractkit/chainlink-common/pkg/config"
+ "github.com/smartcontractkit/chainlink-common/pkg/hashutil"
+ "github.com/smartcontractkit/chainlink-common/pkg/merklemulti"
+ cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip"
+
+ "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller"
+ "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils"
+ burn_mint_token_pool "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/burn_mint_token_pool_1_4_0"
+ "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/commit_store_1_2_0"
+ evm_2_evm_offramp "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/evm_2_evm_onramp_1_2_0"
+ evm_2_evm_onramp "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/lock_release_token_pool_1_0_0"
+ lock_release_token_pool "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/lock_release_token_pool_1_4_0"
+ "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/maybe_revert_message_receiver"
+ "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/mock_rmn_contract"
+ "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/price_registry_1_2_0"
+ "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/rmn_proxy_contract"
+ "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router"
+ "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/weth9"
+ "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/link_token_interface"
+ "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/burn_mint_erc677"
+ "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/ccipdata"
+ "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0"
+ "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/testhelpers"
+)
+
+var (
+ // Source
+ SourcePool = "source Link pool"
+ SourcePriceRegistry = "source PriceRegistry"
+ OnRamp = "onramp"
+ OnRampNative = "onramp-native"
+ SourceRouter = "source router"
+
+ // Dest
+ OffRamp = "offramp"
+ DestPool = "dest Link pool"
+
+ Receiver = "receiver"
+ Sender = "sender"
+ Link = func(amount int64) *big.Int { return new(big.Int).Mul(big.NewInt(1e18), big.NewInt(amount)) }
+ HundredLink = Link(100)
+ LinkUSDValue = func(amount int64) *big.Int { return new(big.Int).Mul(big.NewInt(1e18), big.NewInt(amount)) }
+ SourceChainID = uint64(1000)
+ SourceChainSelector = uint64(11787463284727550157)
+ DestChainID = uint64(1337)
+ DestChainSelector = uint64(3379446385462418246)
+)
+
+// Backwards compat, in principle these statuses are version dependent
+// TODO: Adjust integration tests to be version agnostic using readers
+var (
+ ExecutionStateSuccess = MessageExecutionState(cciptypes.ExecutionStateSuccess)
+ ExecutionStateFailure = MessageExecutionState(cciptypes.ExecutionStateFailure)
+)
+
+type MessageExecutionState cciptypes.MessageExecutionState
+type CommitOffchainConfig struct {
+ v1_2_0.JSONCommitOffchainConfig
+}
+
+func (c CommitOffchainConfig) Encode() ([]byte, error) {
+ return ccipconfig.EncodeOffchainConfig(c.JSONCommitOffchainConfig)
+}
+
+func NewCommitOffchainConfig(
+ gasPriceHeartBeat config.Duration,
+ daGasPriceDeviationPPB uint32,
+ execGasPriceDeviationPPB uint32,
+ tokenPriceHeartBeat config.Duration,
+ tokenPriceDeviationPPB uint32,
+ inflightCacheExpiry config.Duration,
+ priceReportingDisabled bool,
+) CommitOffchainConfig {
+ return CommitOffchainConfig{v1_2_0.JSONCommitOffchainConfig{
+ GasPriceHeartBeat: gasPriceHeartBeat,
+ DAGasPriceDeviationPPB: daGasPriceDeviationPPB,
+ ExecGasPriceDeviationPPB: execGasPriceDeviationPPB,
+ TokenPriceHeartBeat: tokenPriceHeartBeat,
+ TokenPriceDeviationPPB: tokenPriceDeviationPPB,
+ InflightCacheExpiry: inflightCacheExpiry,
+ PriceReportingDisabled: priceReportingDisabled,
+ }}
+}
+
+type CommitOnchainConfig struct {
+ ccipdata.CommitOnchainConfig
+}
+
+func NewCommitOnchainConfig(
+ priceRegistry common.Address,
+) CommitOnchainConfig {
+ return CommitOnchainConfig{ccipdata.CommitOnchainConfig{
+ PriceRegistry: priceRegistry,
+ }}
+}
+
+type ExecOnchainConfig struct {
+ v1_2_0.ExecOnchainConfig
+}
+
+func NewExecOnchainConfig(
+ permissionLessExecutionThresholdSeconds uint32,
+ router common.Address,
+ priceRegistry common.Address,
+ maxNumberOfTokensPerMsg uint16,
+ maxDataBytes uint32,
+ maxPoolReleaseOrMintGas uint32,
+) ExecOnchainConfig {
+ return ExecOnchainConfig{v1_2_0.ExecOnchainConfig{
+ PermissionLessExecutionThresholdSeconds: permissionLessExecutionThresholdSeconds,
+ Router: router,
+ PriceRegistry: priceRegistry,
+ MaxNumberOfTokensPerMsg: maxNumberOfTokensPerMsg,
+ MaxDataBytes: maxDataBytes,
+ MaxPoolReleaseOrMintGas: maxPoolReleaseOrMintGas,
+ }}
+}
+
+type ExecOffchainConfig struct {
+ v1_2_0.JSONExecOffchainConfig
+}
+
+func (c ExecOffchainConfig) Encode() ([]byte, error) {
+ return ccipconfig.EncodeOffchainConfig(c.JSONExecOffchainConfig)
+}
+
+func NewExecOffchainConfig(
+ destOptimisticConfirmations uint32,
+ batchGasLimit uint32,
+ relativeBoostPerWaitHour float64,
+ inflightCacheExpiry config.Duration,
+ rootSnoozeTime config.Duration,
+ batchingStrategyID uint32,
+) ExecOffchainConfig {
+ return ExecOffchainConfig{v1_2_0.JSONExecOffchainConfig{
+ DestOptimisticConfirmations: destOptimisticConfirmations,
+ BatchGasLimit: batchGasLimit,
+ RelativeBoostPerWaitHour: relativeBoostPerWaitHour,
+ InflightCacheExpiry: inflightCacheExpiry,
+ RootSnoozeTime: rootSnoozeTime,
+ BatchingStrategyID: batchingStrategyID,
+ }}
+}
+
+type MaybeRevertReceiver struct {
+ Receiver *maybe_revert_message_receiver.MaybeRevertMessageReceiver
+ Strict bool
+}
+
+type Common struct {
+ ChainID uint64
+ ChainSelector uint64
+ User *bind.TransactOpts
+ Chain *backends.SimulatedBackend
+ LinkToken *link_token_interface.LinkToken
+ LinkTokenPool *lock_release_token_pool.LockReleaseTokenPool
+ CustomToken *link_token_interface.LinkToken
+ WrappedNative *weth9.WETH9
+ WrappedNativePool *lock_release_token_pool_1_0_0.LockReleaseTokenPool
+ ARM *mock_rmn_contract.MockRMNContract
+ ARMProxy *rmn_proxy_contract.RMNProxy
+ PriceRegistry *price_registry_1_2_0.PriceRegistry
+}
+
+type SourceChain struct {
+ Common
+ Router *router.Router
+ OnRamp *evm_2_evm_onramp.EVM2EVMOnRamp
+}
+
+type DestinationChain struct {
+ Common
+
+ CommitStore *commit_store_1_2_0.CommitStore
+ Router *router.Router
+ OffRamp *evm_2_evm_offramp.EVM2EVMOffRamp
+ Receivers []MaybeRevertReceiver
+}
+
+type OCR2Config struct {
+ Signers []common.Address
+ Transmitters []common.Address
+ F uint8
+ OnchainConfig []byte
+ OffchainConfigVersion uint64
+ OffchainConfig []byte
+}
+
+type BalanceAssertion struct {
+ Name string
+ Address common.Address
+ Expected string
+ Getter func(t *testing.T, addr common.Address) *big.Int
+ Within string
+}
+
+type BalanceReq struct {
+ Name string
+ Addr common.Address
+ Getter func(t *testing.T, addr common.Address) *big.Int
+}
+
+type CCIPContracts struct {
+ Source SourceChain
+ Dest DestinationChain
+ Oracles []confighelper.OracleIdentityExtra
+
+ commitOCRConfig, execOCRConfig *OCR2Config
+}
+
+func (c *CCIPContracts) DeployNewOffRamp(t *testing.T) {
+ prevOffRamp := common.HexToAddress("")
+ if c.Dest.OffRamp != nil {
+ prevOffRamp = c.Dest.OffRamp.Address()
+ }
+ offRampAddress, _, _, err := evm_2_evm_offramp.DeployEVM2EVMOffRamp(
+ c.Dest.User,
+ c.Dest.Chain,
+ evm_2_evm_offramp.EVM2EVMOffRampStaticConfig{
+ CommitStore: c.Dest.CommitStore.Address(),
+ ChainSelector: c.Dest.ChainSelector,
+ SourceChainSelector: c.Source.ChainSelector,
+ OnRamp: c.Source.OnRamp.Address(),
+ PrevOffRamp: prevOffRamp,
+ ArmProxy: c.Dest.ARMProxy.Address(),
+ },
+ []common.Address{c.Source.LinkToken.Address()}, // source tokens
+ []common.Address{c.Dest.LinkTokenPool.Address()}, // pools
+ evm_2_evm_offramp.RateLimiterConfig{
+ IsEnabled: true,
+ Capacity: LinkUSDValue(100),
+ Rate: LinkUSDValue(1),
+ },
+ )
+ require.NoError(t, err)
+ c.Dest.Chain.Commit()
+
+ c.Dest.OffRamp, err = evm_2_evm_offramp.NewEVM2EVMOffRamp(offRampAddress, c.Dest.Chain)
+ require.NoError(t, err)
+
+ c.Dest.Chain.Commit()
+ c.Source.Chain.Commit()
+}
+
+func (c *CCIPContracts) EnableOffRamp(t *testing.T) {
+ _, err := c.Dest.Router.ApplyRampUpdates(c.Dest.User, nil, nil, []router.RouterOffRamp{{SourceChainSelector: SourceChainSelector, OffRamp: c.Dest.OffRamp.Address()}})
+ require.NoError(t, err)
+ c.Dest.Chain.Commit()
+
+ onChainConfig := c.CreateDefaultExecOnchainConfig(t)
+ offChainConfig := c.CreateDefaultExecOffchainConfig(t)
+
+ c.SetupExecOCR2Config(t, onChainConfig, offChainConfig)
+}
+
+func (c *CCIPContracts) EnableCommitStore(t *testing.T) {
+ onChainConfig := c.CreateDefaultCommitOnchainConfig(t)
+ offChainConfig := c.CreateDefaultCommitOffchainConfig(t)
+
+ c.SetupCommitOCR2Config(t, onChainConfig, offChainConfig)
+
+ _, err := c.Dest.PriceRegistry.ApplyPriceUpdatersUpdates(c.Dest.User, []common.Address{c.Dest.CommitStore.Address()}, []common.Address{})
+ require.NoError(t, err)
+ c.Dest.Chain.Commit()
+}
+
+func (c *CCIPContracts) DeployNewOnRamp(t *testing.T) {
+ t.Log("Deploying new onRamp")
+ // find the last onRamp
+ prevOnRamp := common.HexToAddress("")
+ if c.Source.OnRamp != nil {
+ prevOnRamp = c.Source.OnRamp.Address()
+ }
+ onRampAddress, _, _, err := evm_2_evm_onramp.DeployEVM2EVMOnRamp(
+ c.Source.User, // user
+ c.Source.Chain, // client
+ evm_2_evm_onramp.EVM2EVMOnRampStaticConfig{
+ LinkToken: c.Source.LinkToken.Address(),
+ ChainSelector: c.Source.ChainSelector,
+ DestChainSelector: c.Dest.ChainSelector,
+ DefaultTxGasLimit: 200_000,
+ MaxNopFeesJuels: big.NewInt(0).Mul(big.NewInt(100_000_000), big.NewInt(1e18)),
+ PrevOnRamp: prevOnRamp,
+ ArmProxy: c.Source.ARM.Address(), // ARM
+ },
+ evm_2_evm_onramp.EVM2EVMOnRampDynamicConfig{
+ Router: c.Source.Router.Address(),
+ MaxNumberOfTokensPerMsg: 5,
+ DestGasOverhead: 350_000,
+ DestGasPerPayloadByte: 16,
+ DestDataAvailabilityOverheadGas: 33_596,
+ DestGasPerDataAvailabilityByte: 16,
+ DestDataAvailabilityMultiplierBps: 6840, // 0.684
+ PriceRegistry: c.Source.PriceRegistry.Address(),
+ MaxDataBytes: 1e5,
+ MaxPerMsgGasLimit: 4_000_000,
+ },
+ []evm_2_evm_onramp.InternalPoolUpdate{
+ {
+ Token: c.Source.LinkToken.Address(),
+ Pool: c.Source.LinkTokenPool.Address(),
+ },
+ },
+ evm_2_evm_onramp.RateLimiterConfig{
+ IsEnabled: true,
+ Capacity: LinkUSDValue(100),
+ Rate: LinkUSDValue(1),
+ },
+ []evm_2_evm_onramp.EVM2EVMOnRampFeeTokenConfigArgs{
+ {
+ Token: c.Source.LinkToken.Address(),
+ NetworkFeeUSDCents: 1_00,
+ GasMultiplierWeiPerEth: 1e18,
+ PremiumMultiplierWeiPerEth: 9e17,
+ Enabled: true,
+ },
+ {
+ Token: c.Source.WrappedNative.Address(),
+ NetworkFeeUSDCents: 1_00,
+ GasMultiplierWeiPerEth: 1e18,
+ PremiumMultiplierWeiPerEth: 1e18,
+ Enabled: true,
+ },
+ },
+ []evm_2_evm_onramp.EVM2EVMOnRampTokenTransferFeeConfigArgs{
+ {
+ Token: c.Source.LinkToken.Address(),
+ MinFeeUSDCents: 50, // $0.5
+ MaxFeeUSDCents: 1_000_000_00, // $ 1 million
+ DeciBps: 5_0, // 5 bps
+ DestGasOverhead: 34_000,
+ DestBytesOverhead: 32,
+ },
+ },
+ []evm_2_evm_onramp.EVM2EVMOnRampNopAndWeight{},
+ )
+
+ require.NoError(t, err)
+ c.Source.Chain.Commit()
+ c.Dest.Chain.Commit()
+ c.Source.OnRamp, err = evm_2_evm_onramp.NewEVM2EVMOnRamp(onRampAddress, c.Source.Chain)
+ require.NoError(t, err)
+ c.Source.Chain.Commit()
+ c.Dest.Chain.Commit()
+}
+
+func (c *CCIPContracts) EnableOnRamp(t *testing.T) {
+ t.Log("Setting onRamp on source router")
+ _, err := c.Source.Router.ApplyRampUpdates(c.Source.User, []router.RouterOnRamp{{DestChainSelector: c.Dest.ChainSelector, OnRamp: c.Source.OnRamp.Address()}}, nil, nil)
+ require.NoError(t, err)
+ c.Source.Chain.Commit()
+ c.Dest.Chain.Commit()
+}
+
+func (c *CCIPContracts) DeployNewCommitStore(t *testing.T) {
+ commitStoreAddress, _, _, err := commit_store_1_2_0.DeployCommitStore(
+ c.Dest.User, // user
+ c.Dest.Chain, // client
+ commit_store_1_2_0.CommitStoreStaticConfig{
+ ChainSelector: c.Dest.ChainSelector,
+ SourceChainSelector: c.Source.ChainSelector,
+ OnRamp: c.Source.OnRamp.Address(),
+ ArmProxy: c.Dest.ARMProxy.Address(),
+ },
+ )
+ require.NoError(t, err)
+ c.Dest.Chain.Commit()
+ // since CommitStoreHelper derives from CommitStore, it's safe to instantiate both on same address
+ c.Dest.CommitStore, err = commit_store_1_2_0.NewCommitStore(commitStoreAddress, c.Dest.Chain)
+ require.NoError(t, err)
+}
+
+func (c *CCIPContracts) DeployNewPriceRegistry(t *testing.T) {
+ t.Log("Deploying new Price Registry")
+ destPricesAddress, _, _, err := price_registry_1_2_0.DeployPriceRegistry(
+ c.Dest.User,
+ c.Dest.Chain,
+ []common.Address{c.Dest.CommitStore.Address()},
+ []common.Address{c.Dest.LinkToken.Address()},
+ 60*60*24*14, // two weeks
+ )
+ require.NoError(t, err)
+ c.Source.Chain.Commit()
+ c.Dest.Chain.Commit()
+ c.Dest.PriceRegistry, err = price_registry_1_2_0.NewPriceRegistry(destPricesAddress, c.Dest.Chain)
+ require.NoError(t, err)
+
+ priceUpdates := price_registry_1_2_0.InternalPriceUpdates{
+ TokenPriceUpdates: []price_registry_1_2_0.InternalTokenPriceUpdate{
+ {
+ SourceToken: c.Dest.LinkToken.Address(),
+ UsdPerToken: big.NewInt(8e18), // 8usd
+ },
+ {
+ SourceToken: c.Dest.WrappedNative.Address(),
+ UsdPerToken: big.NewInt(1e18), // 1usd
+ },
+ },
+ GasPriceUpdates: []price_registry_1_2_0.InternalGasPriceUpdate{
+ {
+ DestChainSelector: c.Source.ChainSelector,
+ UsdPerUnitGas: big.NewInt(2000e9), // $2000 per eth * 1gwei = 2000e9
+ },
+ },
+ }
+ _, err = c.Dest.PriceRegistry.UpdatePrices(c.Dest.User, priceUpdates)
+ require.NoError(t, err)
+
+ c.Source.Chain.Commit()
+ c.Dest.Chain.Commit()
+
+ t.Logf("New Price Registry deployed at %s", destPricesAddress.String())
+}
+
+func (c *CCIPContracts) SetNopsOnRamp(t *testing.T, nopsAndWeights []evm_2_evm_onramp.EVM2EVMOnRampNopAndWeight) {
+ tx, err := c.Source.OnRamp.SetNops(c.Source.User, nopsAndWeights)
+ require.NoError(t, err)
+ c.Source.Chain.Commit()
+ _, err = bind.WaitMined(context.Background(), c.Source.Chain, tx)
+ require.NoError(t, err)
+}
+
+func (c *CCIPContracts) GetSourceLinkBalance(t *testing.T, addr common.Address) *big.Int {
+ return GetBalance(t, c.Source.Chain, c.Source.LinkToken.Address(), addr)
+}
+
+func (c *CCIPContracts) GetDestLinkBalance(t *testing.T, addr common.Address) *big.Int {
+ return GetBalance(t, c.Dest.Chain, c.Dest.LinkToken.Address(), addr)
+}
+
+func (c *CCIPContracts) GetSourceWrappedTokenBalance(t *testing.T, addr common.Address) *big.Int {
+ return GetBalance(t, c.Source.Chain, c.Source.WrappedNative.Address(), addr)
+}
+
+func (c *CCIPContracts) GetDestWrappedTokenBalance(t *testing.T, addr common.Address) *big.Int {
+ return GetBalance(t, c.Dest.Chain, c.Dest.WrappedNative.Address(), addr)
+}
+
+func (c *CCIPContracts) AssertBalances(t *testing.T, bas []BalanceAssertion) {
+ for _, b := range bas {
+ actual := b.Getter(t, b.Address)
+ t.Log("Checking balance for", b.Name, "at", b.Address.Hex(), "got", actual)
+ require.NotNil(t, actual, "%v getter return nil", b.Name)
+ if b.Within == "" {
+ require.Equal(t, b.Expected, actual.String(), "wrong balance for %s got %s want %s", b.Name, actual, b.Expected)
+ } else {
+ bi, _ := big.NewInt(0).SetString(b.Expected, 10)
+ withinI, _ := big.NewInt(0).SetString(b.Within, 10)
+ high := big.NewInt(0).Add(bi, withinI)
+ low := big.NewInt(0).Sub(bi, withinI)
+ require.Equal(t, -1, actual.Cmp(high), "wrong balance for %s got %s outside expected range [%s, %s]", b.Name, actual, low, high)
+ require.Equal(t, 1, actual.Cmp(low), "wrong balance for %s got %s outside expected range [%s, %s]", b.Name, actual, low, high)
+ }
+ }
+}
+
+func AccountToAddress(accounts []ocr2types.Account) (addresses []common.Address, err error) {
+ for _, signer := range accounts {
+ bytes, err := hexutil.Decode(string(signer))
+ if err != nil {
+ return []common.Address{}, errors.Wrap(err, fmt.Sprintf("given address is not valid %s", signer))
+ }
+ if len(bytes) != 20 {
+ return []common.Address{}, errors.Errorf("address is not 20 bytes %s", signer)
+ }
+ addresses = append(addresses, common.BytesToAddress(bytes))
+ }
+ return addresses, nil
+}
+
+func OnchainPublicKeyToAddress(publicKeys []ocrtypes.OnchainPublicKey) (addresses []common.Address, err error) {
+ for _, signer := range publicKeys {
+ if len(signer) != 20 {
+ return []common.Address{}, errors.Errorf("address is not 20 bytes %s", signer)
+ }
+ addresses = append(addresses, common.BytesToAddress(signer))
+ }
+ return addresses, nil
+}
+
+func (c *CCIPContracts) DeriveOCR2Config(t *testing.T, oracles []confighelper.OracleIdentityExtra, rawOnchainConfig []byte, rawOffchainConfig []byte) *OCR2Config {
+ signers, transmitters, threshold, onchainConfig, offchainConfigVersion, offchainConfig, err := confighelper.ContractSetConfigArgsForTests(
+ 2*time.Second, // deltaProgress
+ 1*time.Second, // deltaResend
+ 1*time.Second, // deltaRound
+ 500*time.Millisecond, // deltaGrace
+ 2*time.Second, // deltaStage
+ 3,
+ []int{1, 1, 1, 1},
+ oracles,
+ rawOffchainConfig,
+ nil,
+ 50*time.Millisecond, // Max duration query
+ 1*time.Second, // Max duration observation
+ 100*time.Millisecond,
+ 100*time.Millisecond,
+ 100*time.Millisecond,
+ 1, // faults
+ rawOnchainConfig,
+ )
+ require.NoError(t, err)
+ lggr := logger.TestLogger(t)
+ lggr.Infow("Setting Config on Oracle Contract",
+ "signers", signers,
+ "transmitters", transmitters,
+ "threshold", threshold,
+ "onchainConfig", onchainConfig,
+ "encodedConfigVersion", offchainConfigVersion,
+ )
+ signerAddresses, err := OnchainPublicKeyToAddress(signers)
+ require.NoError(t, err)
+ transmitterAddresses, err := AccountToAddress(transmitters)
+ require.NoError(t, err)
+
+ return &OCR2Config{
+ Signers: signerAddresses,
+ Transmitters: transmitterAddresses,
+ F: threshold,
+ OnchainConfig: onchainConfig,
+ OffchainConfigVersion: offchainConfigVersion,
+ OffchainConfig: offchainConfig,
+ }
+}
+
+func (c *CCIPContracts) SetupCommitOCR2Config(t *testing.T, commitOnchainConfig, commitOffchainConfig []byte) {
+ c.commitOCRConfig = c.DeriveOCR2Config(t, c.Oracles, commitOnchainConfig, commitOffchainConfig)
+ // Set the DON on the commit store
+ _, err := c.Dest.CommitStore.SetOCR2Config(
+ c.Dest.User,
+ c.commitOCRConfig.Signers,
+ c.commitOCRConfig.Transmitters,
+ c.commitOCRConfig.F,
+ c.commitOCRConfig.OnchainConfig,
+ c.commitOCRConfig.OffchainConfigVersion,
+ c.commitOCRConfig.OffchainConfig,
+ )
+ require.NoError(t, err)
+ c.Dest.Chain.Commit()
+}
+
+func (c *CCIPContracts) SetupExecOCR2Config(t *testing.T, execOnchainConfig, execOffchainConfig []byte) {
+ c.execOCRConfig = c.DeriveOCR2Config(t, c.Oracles, execOnchainConfig, execOffchainConfig)
+ // Same DON on the offramp
+ _, err := c.Dest.OffRamp.SetOCR2Config(
+ c.Dest.User,
+ c.execOCRConfig.Signers,
+ c.execOCRConfig.Transmitters,
+ c.execOCRConfig.F,
+ c.execOCRConfig.OnchainConfig,
+ c.execOCRConfig.OffchainConfigVersion,
+ c.execOCRConfig.OffchainConfig,
+ )
+ require.NoError(t, err)
+ c.Dest.Chain.Commit()
+}
+
+func (c *CCIPContracts) SetupOnchainConfig(t *testing.T, commitOnchainConfig, commitOffchainConfig, execOnchainConfig, execOffchainConfig []byte) int64 {
+ // Note We do NOT set the payees, payment is done in the OCR2Base implementation
+ blockBeforeConfig, err := c.Dest.Chain.BlockByNumber(context.Background(), nil)
+ require.NoError(t, err)
+
+ c.SetupCommitOCR2Config(t, commitOnchainConfig, commitOffchainConfig)
+ c.SetupExecOCR2Config(t, execOnchainConfig, execOffchainConfig)
+
+ return blockBeforeConfig.Number().Int64()
+}
+
+func (c *CCIPContracts) SetupLockAndMintTokenPool(
+ sourceTokenAddress common.Address,
+ wrappedTokenName,
+ wrappedTokenSymbol string) (common.Address, *burn_mint_erc677.BurnMintERC677, error) {
+ // Deploy dest token & pool
+ destTokenAddress, _, _, err := burn_mint_erc677.DeployBurnMintERC677(c.Dest.User, c.Dest.Chain, wrappedTokenName, wrappedTokenSymbol, 18, big.NewInt(0))
+ if err != nil {
+ return [20]byte{}, nil, err
+ }
+ c.Dest.Chain.Commit()
+
+ destToken, err := burn_mint_erc677.NewBurnMintERC677(destTokenAddress, c.Dest.Chain)
+ if err != nil {
+ return [20]byte{}, nil, err
+ }
+
+ destPoolAddress, _, destPool, err := burn_mint_token_pool.DeployBurnMintTokenPool(
+ c.Dest.User,
+ c.Dest.Chain,
+ destTokenAddress,
+ []common.Address{}, // pool originalSender allowList
+ c.Dest.ARMProxy.Address(),
+ c.Dest.Router.Address(),
+ )
+ if err != nil {
+ return [20]byte{}, nil, err
+ }
+ c.Dest.Chain.Commit()
+
+ _, err = destToken.GrantMintAndBurnRoles(c.Dest.User, destPoolAddress)
+ if err != nil {
+ return [20]byte{}, nil, err
+ }
+
+ _, err = destPool.ApplyChainUpdates(c.Dest.User,
+ []burn_mint_token_pool.TokenPoolChainUpdate{
+ {
+ RemoteChainSelector: c.Source.ChainSelector,
+ Allowed: true,
+ OutboundRateLimiterConfig: burn_mint_token_pool.RateLimiterConfig{
+ IsEnabled: true,
+ Capacity: HundredLink,
+ Rate: big.NewInt(1e18),
+ },
+ InboundRateLimiterConfig: burn_mint_token_pool.RateLimiterConfig{
+ IsEnabled: true,
+ Capacity: HundredLink,
+ Rate: big.NewInt(1e18),
+ },
+ },
+ })
+ if err != nil {
+ return [20]byte{}, nil, err
+ }
+ c.Dest.Chain.Commit()
+
+ sourcePoolAddress, _, sourcePool, err := lock_release_token_pool.DeployLockReleaseTokenPool(
+ c.Source.User,
+ c.Source.Chain,
+ sourceTokenAddress,
+ []common.Address{}, // empty allowList at deploy time indicates pool has no original sender restrictions
+ c.Source.ARMProxy.Address(),
+ true,
+ c.Source.Router.Address(),
+ )
+ if err != nil {
+ return [20]byte{}, nil, err
+ }
+ c.Source.Chain.Commit()
+
+ // set onRamp as valid caller for source pool
+ _, err = sourcePool.ApplyChainUpdates(c.Source.User, []lock_release_token_pool.TokenPoolChainUpdate{
+ {
+ RemoteChainSelector: c.Dest.ChainSelector,
+ Allowed: true,
+ OutboundRateLimiterConfig: lock_release_token_pool.RateLimiterConfig{
+ IsEnabled: true,
+ Capacity: HundredLink,
+ Rate: big.NewInt(1e18),
+ },
+ InboundRateLimiterConfig: lock_release_token_pool.RateLimiterConfig{
+ IsEnabled: true,
+ Capacity: HundredLink,
+ Rate: big.NewInt(1e18),
+ },
+ },
+ })
+ if err != nil {
+ return [20]byte{}, nil, err
+ }
+ c.Source.Chain.Commit()
+
+ wrappedNativeAddress, err := c.Source.Router.GetWrappedNative(nil)
+ if err != nil {
+ return [20]byte{}, nil, err
+ }
+
+ // native token is used as fee token
+ _, err = c.Source.PriceRegistry.UpdatePrices(c.Source.User, price_registry_1_2_0.InternalPriceUpdates{
+ TokenPriceUpdates: []price_registry_1_2_0.InternalTokenPriceUpdate{
+ {
+ SourceToken: sourceTokenAddress,
+ UsdPerToken: big.NewInt(5),
+ },
+ },
+ GasPriceUpdates: []price_registry_1_2_0.InternalGasPriceUpdate{},
+ })
+ if err != nil {
+ return [20]byte{}, nil, err
+ }
+ c.Source.Chain.Commit()
+
+ _, err = c.Source.PriceRegistry.ApplyFeeTokensUpdates(c.Source.User, []common.Address{wrappedNativeAddress}, nil)
+ if err != nil {
+ return [20]byte{}, nil, err
+ }
+ c.Source.Chain.Commit()
+
+ // add new token pool created above
+ _, err = c.Source.OnRamp.ApplyPoolUpdates(c.Source.User, nil, []evm_2_evm_onramp.InternalPoolUpdate{
+ {
+ Token: sourceTokenAddress,
+ Pool: sourcePoolAddress,
+ },
+ })
+ if err != nil {
+ return [20]byte{}, nil, err
+ }
+
+ _, err = c.Dest.OffRamp.ApplyPoolUpdates(c.Dest.User, nil, []evm_2_evm_offramp.InternalPoolUpdate{
+ {
+ Token: sourceTokenAddress,
+ Pool: destPoolAddress,
+ },
+ })
+ if err != nil {
+ return [20]byte{}, nil, err
+ }
+ c.Dest.Chain.Commit()
+
+ return sourcePoolAddress, destToken, err
+}
+
+func (c *CCIPContracts) SendMessage(t *testing.T, gasLimit, tokenAmount *big.Int, receiverAddr common.Address) {
+ extraArgs, err := GetEVMExtraArgsV1(gasLimit, false)
+ require.NoError(t, err)
+ msg := router.ClientEVM2AnyMessage{
+ Receiver: MustEncodeAddress(t, receiverAddr),
+ Data: []byte("hello"),
+ TokenAmounts: []router.ClientEVMTokenAmount{
+ {
+ Token: c.Source.LinkToken.Address(),
+ Amount: tokenAmount,
+ },
+ },
+ FeeToken: c.Source.LinkToken.Address(),
+ ExtraArgs: extraArgs,
+ }
+ fee, err := c.Source.Router.GetFee(nil, c.Dest.ChainSelector, msg)
+ require.NoError(t, err)
+ // Currently no overhead and 1gwei dest gas price. So fee is simply gasLimit * gasPrice.
+ // require.Equal(t, new(big.Int).Mul(gasLimit, gasPrice).String(), fee.String())
+ // Approve the fee amount + the token amount
+ _, err = c.Source.LinkToken.Approve(c.Source.User, c.Source.Router.Address(), new(big.Int).Add(fee, tokenAmount))
+ require.NoError(t, err)
+ c.Source.Chain.Commit()
+ c.SendRequest(t, msg)
+}
+
+func GetBalances(t *testing.T, brs []BalanceReq) (map[string]*big.Int, error) {
+ m := make(map[string]*big.Int)
+ for _, br := range brs {
+ m[br.Name] = br.Getter(t, br.Addr)
+ if m[br.Name] == nil {
+ return nil, fmt.Errorf("%v getter return nil", br.Name)
+ }
+ }
+ return m, nil
+}
+
+func MustAddBigInt(a *big.Int, b string) *big.Int {
+ bi, _ := big.NewInt(0).SetString(b, 10)
+ return big.NewInt(0).Add(a, bi)
+}
+
+func MustSubBigInt(a *big.Int, b string) *big.Int {
+ bi, _ := big.NewInt(0).SetString(b, 10)
+ return big.NewInt(0).Sub(a, bi)
+}
+
+func MustEncodeAddress(t *testing.T, address common.Address) []byte {
+ bts, err := utils.ABIEncode(`[{"type":"address"}]`, address)
+ require.NoError(t, err)
+ return bts
+}
+
+func SetupCCIPContracts(t *testing.T, sourceChainID, sourceChainSelector, destChainID, destChainSelector uint64) CCIPContracts {
+ sourceChain, sourceUser := testhelpers.SetupChain(t)
+ destChain, destUser := testhelpers.SetupChain(t)
+
+ armSourceAddress, _, _, err := mock_rmn_contract.DeployMockRMNContract(
+ sourceUser,
+ sourceChain.Client(),
+ )
+ require.NoError(t, err)
+ sourceARM, err := mock_rmn_contract.NewMockRMNContract(armSourceAddress, sourceChain.Client())
+ require.NoError(t, err)
+ armProxySourceAddress, _, _, err := rmn_proxy_contract.DeployRMNProxy(
+ sourceUser,
+ sourceChain.Client(),
+ armSourceAddress,
+ )
+ require.NoError(t, err)
+ sourceARMProxy, err := rmn_proxy_contract.NewRMNProxy(armProxySourceAddress, sourceChain.Client())
+ require.NoError(t, err)
+ sourceChain.Commit()
+
+ armDestAddress, _, _, err := mock_rmn_contract.DeployMockRMNContract(
+ destUser,
+ destChain.Client(),
+ )
+ require.NoError(t, err)
+ armProxyDestAddress, _, _, err := rmn_proxy_contract.DeployRMNProxy(
+ destUser,
+ destChain.Client(),
+ armDestAddress,
+ )
+ require.NoError(t, err)
+ destChain.Commit()
+ destARM, err := mock_rmn_contract.NewMockRMNContract(armDestAddress, destChain.Client())
+ require.NoError(t, err)
+ destARMProxy, err := rmn_proxy_contract.NewRMNProxy(armProxyDestAddress, destChain.Client())
+ require.NoError(t, err)
+
+ // Deploy link token and pool on source chain
+ sourceLinkTokenAddress, _, _, err := link_token_interface.DeployLinkToken(sourceUser, sourceChain.Client())
+ require.NoError(t, err)
+ sourceChain.Commit()
+ sourceLinkToken, err := link_token_interface.NewLinkToken(sourceLinkTokenAddress, sourceChain.Client())
+ require.NoError(t, err)
+
+ // Create router
+ sourceWeth9addr, _, _, err := weth9.DeployWETH9(sourceUser, sourceChain.Client())
+ require.NoError(t, err)
+ sourceWrapped, err := weth9.NewWETH9(sourceWeth9addr, sourceChain.Client())
+ require.NoError(t, err)
+
+ sourceRouterAddress, _, _, err := router.DeployRouter(sourceUser, sourceChain.Client(), sourceWeth9addr, armProxySourceAddress)
+ require.NoError(t, err)
+ sourceRouter, err := router.NewRouter(sourceRouterAddress, sourceChain.Client())
+ require.NoError(t, err)
+ sourceChain.Commit()
+
+ sourceWeth9PoolAddress, _, _, err := lock_release_token_pool_1_0_0.DeployLockReleaseTokenPool(
+ sourceUser,
+ sourceChain.Client(),
+ sourceWeth9addr,
+ []common.Address{},
+ armProxySourceAddress,
+ )
+ require.NoError(t, err)
+ sourceChain.Commit()
+
+ sourceWeth9Pool, err := lock_release_token_pool_1_0_0.NewLockReleaseTokenPool(sourceWeth9PoolAddress, sourceChain.Client())
+ require.NoError(t, err)
+
+ sourcePoolAddress, _, _, err := lock_release_token_pool.DeployLockReleaseTokenPool(
+ sourceUser,
+ sourceChain.Client(),
+ sourceLinkTokenAddress,
+ []common.Address{},
+ armProxySourceAddress,
+ true,
+ sourceRouterAddress,
+ )
+ require.NoError(t, err)
+ sourceChain.Commit()
+ sourcePool, err := lock_release_token_pool.NewLockReleaseTokenPool(sourcePoolAddress, sourceChain.Client())
+ require.NoError(t, err)
+
+ // Deploy custom token pool source
+ sourceCustomTokenAddress, _, _, err := link_token_interface.DeployLinkToken(sourceUser, sourceChain.Client()) // Just re-use this, it's an ERC20.
+ require.NoError(t, err)
+ sourceCustomToken, err := link_token_interface.NewLinkToken(sourceCustomTokenAddress, sourceChain.Client())
+ require.NoError(t, err)
+ destChain.Commit()
+
+ // Deploy custom token pool dest
+ destCustomTokenAddress, _, _, err := link_token_interface.DeployLinkToken(destUser, destChain.Client()) // Just re-use this, it's an ERC20.
+ require.NoError(t, err)
+ destCustomToken, err := link_token_interface.NewLinkToken(destCustomTokenAddress, destChain.Client())
+ require.NoError(t, err)
+ destChain.Commit()
+
+ // Deploy and configure onramp
+ sourcePricesAddress, _, _, err := price_registry_1_2_0.DeployPriceRegistry(
+ sourceUser,
+ sourceChain.Client(),
+ nil,
+ []common.Address{sourceLinkTokenAddress, sourceWeth9addr},
+ 60*60*24*14, // two weeks
+ )
+ require.NoError(t, err)
+
+ srcPriceRegistry, err := price_registry_1_2_0.NewPriceRegistry(sourcePricesAddress, sourceChain.Client())
+ require.NoError(t, err)
+
+ _, err = srcPriceRegistry.UpdatePrices(sourceUser, price_registry_1_2_0.InternalPriceUpdates{
+ TokenPriceUpdates: []price_registry_1_2_0.InternalTokenPriceUpdate{
+ {
+ SourceToken: sourceLinkTokenAddress,
+ UsdPerToken: new(big.Int).Mul(big.NewInt(1e18), big.NewInt(20)),
+ },
+ {
+ SourceToken: sourceWeth9addr,
+ UsdPerToken: new(big.Int).Mul(big.NewInt(1e18), big.NewInt(2000)),
+ },
+ },
+ GasPriceUpdates: []price_registry_1_2_0.InternalGasPriceUpdate{
+ {
+ DestChainSelector: destChainSelector,
+ UsdPerUnitGas: big.NewInt(20000e9),
+ },
+ },
+ })
+ require.NoError(t, err)
+
+ onRampAddress, _, _, err := evm_2_evm_onramp.DeployEVM2EVMOnRamp(
+ sourceUser, // user
+ sourceChain.Client(), // client
+ evm_2_evm_onramp.EVM2EVMOnRampStaticConfig{
+ LinkToken: sourceLinkTokenAddress,
+ ChainSelector: sourceChainSelector,
+ DestChainSelector: destChainSelector,
+ DefaultTxGasLimit: 200_000,
+ MaxNopFeesJuels: big.NewInt(0).Mul(big.NewInt(100_000_000), big.NewInt(1e18)),
+ PrevOnRamp: common.HexToAddress(""),
+ ArmProxy: armProxySourceAddress, // ARM
+ },
+ evm_2_evm_onramp.EVM2EVMOnRampDynamicConfig{
+ Router: sourceRouterAddress,
+ MaxNumberOfTokensPerMsg: 5,
+ DestGasOverhead: 350_000,
+ DestGasPerPayloadByte: 16,
+ DestDataAvailabilityOverheadGas: 33_596,
+ DestGasPerDataAvailabilityByte: 16,
+ DestDataAvailabilityMultiplierBps: 6840, // 0.684
+ PriceRegistry: sourcePricesAddress,
+ MaxDataBytes: 1e5,
+ MaxPerMsgGasLimit: 4_000_000,
+ },
+ []evm_2_evm_onramp.InternalPoolUpdate{
+ {
+ Token: sourceLinkTokenAddress,
+ Pool: sourcePoolAddress,
+ },
+ {
+ Token: sourceWeth9addr,
+ Pool: sourceWeth9PoolAddress,
+ },
+ },
+ evm_2_evm_onramp.RateLimiterConfig{
+ IsEnabled: true,
+ Capacity: LinkUSDValue(100),
+ Rate: LinkUSDValue(1),
+ },
+ []evm_2_evm_onramp.EVM2EVMOnRampFeeTokenConfigArgs{
+ {
+ Token: sourceLinkTokenAddress,
+ NetworkFeeUSDCents: 1_00,
+ GasMultiplierWeiPerEth: 1e18,
+ PremiumMultiplierWeiPerEth: 9e17,
+ Enabled: true,
+ },
+ {
+ Token: sourceWeth9addr,
+ NetworkFeeUSDCents: 1_00,
+ GasMultiplierWeiPerEth: 1e18,
+ PremiumMultiplierWeiPerEth: 1e18,
+ Enabled: true,
+ },
+ },
+ []evm_2_evm_onramp.EVM2EVMOnRampTokenTransferFeeConfigArgs{
+ {
+ Token: sourceLinkTokenAddress,
+ MinFeeUSDCents: 50, // $0.5
+ MaxFeeUSDCents: 1_000_000_00, // $ 1 million
+ DeciBps: 5_0, // 5 bps
+ DestGasOverhead: 34_000,
+ DestBytesOverhead: 32,
+ },
+ },
+ []evm_2_evm_onramp.EVM2EVMOnRampNopAndWeight{},
+ )
+ require.NoError(t, err)
+ onRamp, err := evm_2_evm_onramp.NewEVM2EVMOnRamp(onRampAddress, sourceChain.Client())
+ require.NoError(t, err)
+ _, err = sourcePool.ApplyChainUpdates(
+ sourceUser,
+ []lock_release_token_pool.TokenPoolChainUpdate{{
+ RemoteChainSelector: DestChainSelector,
+ Allowed: true,
+ OutboundRateLimiterConfig: lock_release_token_pool.RateLimiterConfig{
+ IsEnabled: true,
+ Capacity: HundredLink,
+ Rate: big.NewInt(1e18),
+ },
+ InboundRateLimiterConfig: lock_release_token_pool.RateLimiterConfig{
+ IsEnabled: true,
+ Capacity: HundredLink,
+ Rate: big.NewInt(1e18),
+ },
+ }},
+ )
+ require.NoError(t, err)
+ _, err = sourceWeth9Pool.ApplyRampUpdates(sourceUser,
+ []lock_release_token_pool_1_0_0.TokenPoolRampUpdate{{Ramp: onRampAddress, Allowed: true,
+ RateLimiterConfig: lock_release_token_pool_1_0_0.RateLimiterConfig{
+ IsEnabled: true,
+ Capacity: HundredLink,
+ Rate: big.NewInt(1e18),
+ },
+ }},
+ []lock_release_token_pool_1_0_0.TokenPoolRampUpdate{},
+ )
+ require.NoError(t, err)
+ sourceChain.Commit()
+ _, err = sourceRouter.ApplyRampUpdates(sourceUser, []router.RouterOnRamp{{DestChainSelector: destChainSelector, OnRamp: onRampAddress}}, nil, nil)
+ require.NoError(t, err)
+ sourceChain.Commit()
+
+ destWethaddr, _, _, err := weth9.DeployWETH9(destUser, destChain.Client())
+ require.NoError(t, err)
+ destWrapped, err := weth9.NewWETH9(destWethaddr, destChain.Client())
+ require.NoError(t, err)
+
+ // Create dest router
+ destRouterAddress, _, _, err := router.DeployRouter(destUser, destChain.Client(), destWethaddr, armProxyDestAddress)
+ require.NoError(t, err)
+ destChain.Commit()
+ destRouter, err := router.NewRouter(destRouterAddress, destChain.Client())
+ require.NoError(t, err)
+
+ // Deploy link token and pool on destination chain
+ destLinkTokenAddress, _, _, err := link_token_interface.DeployLinkToken(destUser, destChain.Client())
+ require.NoError(t, err)
+ destChain.Commit()
+ destLinkToken, err := link_token_interface.NewLinkToken(destLinkTokenAddress, destChain.Client())
+ require.NoError(t, err)
+ destPoolAddress, _, _, err := lock_release_token_pool.DeployLockReleaseTokenPool(
+ destUser,
+ destChain.Client(),
+ destLinkTokenAddress,
+ []common.Address{},
+ armProxyDestAddress,
+ true,
+ destRouterAddress,
+ )
+ require.NoError(t, err)
+ destChain.Commit()
+ destPool, err := lock_release_token_pool.NewLockReleaseTokenPool(destPoolAddress, destChain.Client())
+ require.NoError(t, err)
+ destChain.Commit()
+
+ // Float the offramp pool
+ o, err := destPool.Owner(nil)
+ require.NoError(t, err)
+ require.Equal(t, destUser.From.String(), o.String())
+ _, err = destPool.SetRebalancer(destUser, destUser.From)
+ require.NoError(t, err)
+ _, err = destLinkToken.Approve(destUser, destPoolAddress, Link(200))
+ require.NoError(t, err)
+ _, err = destPool.ProvideLiquidity(destUser, Link(200))
+ require.NoError(t, err)
+ destChain.Commit()
+
+ destWrappedPoolAddress, _, _, err := lock_release_token_pool_1_0_0.DeployLockReleaseTokenPool(
+ destUser,
+ destChain.Client(),
+ destWethaddr,
+ []common.Address{},
+ armProxyDestAddress,
+ )
+ require.NoError(t, err)
+ destWrappedPool, err := lock_release_token_pool_1_0_0.NewLockReleaseTokenPool(destWrappedPoolAddress, destChain.Client())
+ require.NoError(t, err)
+
+ poolFloatValue := big.NewInt(1e18)
+
+ destUser.Value = poolFloatValue
+ _, err = destWrapped.Deposit(destUser)
+ require.NoError(t, err)
+ destChain.Commit()
+ destUser.Value = nil
+
+ _, err = destWrapped.Transfer(destUser, destWrappedPool.Address(), poolFloatValue)
+ require.NoError(t, err)
+ destChain.Commit()
+
+ // Deploy and configure ge offramp.
+ destPricesAddress, _, _, err := price_registry_1_2_0.DeployPriceRegistry(
+ destUser,
+ destChain.Client(),
+ nil,
+ []common.Address{destLinkTokenAddress},
+ 60*60*24*14, // two weeks
+ )
+ require.NoError(t, err)
+ destPriceRegistry, err := price_registry_1_2_0.NewPriceRegistry(destPricesAddress, destChain.Client())
+ require.NoError(t, err)
+
+ // Deploy commit store.
+ commitStoreAddress, _, _, err := commit_store_1_2_0.DeployCommitStore(
+ destUser, // user
+ destChain.Client(), // client
+ commit_store_1_2_0.CommitStoreStaticConfig{
+ ChainSelector: destChainSelector,
+ SourceChainSelector: sourceChainSelector,
+ OnRamp: onRamp.Address(),
+ ArmProxy: destARMProxy.Address(),
+ },
+ )
+ require.NoError(t, err)
+ destChain.Commit()
+ commitStore, err := commit_store_1_2_0.NewCommitStore(commitStoreAddress, destChain.Client())
+ require.NoError(t, err)
+
+ offRampAddress, _, _, err := evm_2_evm_offramp.DeployEVM2EVMOffRamp(
+ destUser,
+ destChain.Client(),
+ evm_2_evm_offramp.EVM2EVMOffRampStaticConfig{
+ CommitStore: commitStore.Address(),
+ ChainSelector: destChainSelector,
+ SourceChainSelector: sourceChainSelector,
+ OnRamp: onRampAddress,
+ PrevOffRamp: common.HexToAddress(""),
+ ArmProxy: armProxyDestAddress,
+ },
+ []common.Address{sourceLinkTokenAddress, sourceWeth9addr},
+ []common.Address{destPoolAddress, destWrappedPool.Address()},
+ evm_2_evm_offramp.RateLimiterConfig{
+ IsEnabled: true,
+ Capacity: LinkUSDValue(100),
+ Rate: LinkUSDValue(1),
+ },
+ )
+ require.NoError(t, err)
+ offRamp, err := evm_2_evm_offramp.NewEVM2EVMOffRamp(offRampAddress, destChain.Client())
+ require.NoError(t, err)
+ _, err = destPool.ApplyChainUpdates(destUser,
+ []lock_release_token_pool.TokenPoolChainUpdate{{
+ RemoteChainSelector: sourceChainSelector,
+ Allowed: true,
+ OutboundRateLimiterConfig: lock_release_token_pool.RateLimiterConfig{
+ IsEnabled: true,
+ Capacity: HundredLink,
+ Rate: big.NewInt(1e18),
+ },
+ InboundRateLimiterConfig: lock_release_token_pool.RateLimiterConfig{
+ IsEnabled: true,
+ Capacity: HundredLink,
+ Rate: big.NewInt(1e18),
+ },
+ }},
+ )
+ require.NoError(t, err)
+
+ _, err = destWrappedPool.ApplyRampUpdates(destUser,
+ []lock_release_token_pool_1_0_0.TokenPoolRampUpdate{},
+ []lock_release_token_pool_1_0_0.TokenPoolRampUpdate{{
+ Ramp: offRampAddress,
+ Allowed: true,
+ RateLimiterConfig: lock_release_token_pool_1_0_0.RateLimiterConfig{
+ IsEnabled: true,
+ Capacity: HundredLink,
+ Rate: big.NewInt(1e18),
+ },
+ }},
+ )
+ require.NoError(t, err)
+
+ destChain.Commit()
+ _, err = destPriceRegistry.ApplyPriceUpdatersUpdates(destUser, []common.Address{commitStoreAddress}, []common.Address{})
+ require.NoError(t, err)
+ _, err = destRouter.ApplyRampUpdates(destUser, nil,
+ nil, []router.RouterOffRamp{{SourceChainSelector: sourceChainSelector, OffRamp: offRampAddress}})
+ require.NoError(t, err)
+
+ // Deploy 2 revertable (one SS one non-SS)
+ revertingMessageReceiver1Address, _, _, err := maybe_revert_message_receiver.DeployMaybeRevertMessageReceiver(destUser, destChain.Client(), false)
+ require.NoError(t, err)
+ revertingMessageReceiver1, _ := maybe_revert_message_receiver.NewMaybeRevertMessageReceiver(revertingMessageReceiver1Address, destChain.Client())
+ revertingMessageReceiver2Address, _, _, err := maybe_revert_message_receiver.DeployMaybeRevertMessageReceiver(destUser, destChain.Client(), false)
+ require.NoError(t, err)
+ revertingMessageReceiver2, _ := maybe_revert_message_receiver.NewMaybeRevertMessageReceiver(revertingMessageReceiver2Address, destChain.Client())
+ // Need to commit here, or we will hit the block gas limit when deploying the executor
+ sourceChain.Commit()
+ destChain.Commit()
+
+ // Ensure we have at least finality blocks.
+ for i := 0; i < 50; i++ {
+ sourceChain.Commit()
+ destChain.Commit()
+ }
+
+ source := SourceChain{
+ Common: Common{
+ ChainID: sourceChainID,
+ ChainSelector: sourceChainSelector,
+ User: sourceUser,
+ // FIXME
+ //Chain: sourceChain,
+ LinkToken: sourceLinkToken,
+ LinkTokenPool: sourcePool,
+ CustomToken: sourceCustomToken,
+ ARM: sourceARM,
+ ARMProxy: sourceARMProxy,
+ PriceRegistry: srcPriceRegistry,
+ WrappedNative: sourceWrapped,
+ WrappedNativePool: sourceWeth9Pool,
+ },
+ Router: sourceRouter,
+ OnRamp: onRamp,
+ }
+ dest := DestinationChain{
+ Common: Common{
+ ChainID: destChainID,
+ ChainSelector: destChainSelector,
+ User: destUser,
+ // FIXME
+ //Chain: destChain,
+ LinkToken: destLinkToken,
+ LinkTokenPool: destPool,
+ CustomToken: destCustomToken,
+ ARM: destARM,
+ ARMProxy: destARMProxy,
+ PriceRegistry: destPriceRegistry,
+ WrappedNative: destWrapped,
+ WrappedNativePool: destWrappedPool,
+ },
+ CommitStore: commitStore,
+ Router: destRouter,
+ OffRamp: offRamp,
+ Receivers: []MaybeRevertReceiver{{Receiver: revertingMessageReceiver1, Strict: false}, {Receiver: revertingMessageReceiver2, Strict: true}},
+ }
+
+ return CCIPContracts{
+ Source: source,
+ Dest: dest,
+ }
+}
+
+func (c *CCIPContracts) SendRequest(t *testing.T, msg router.ClientEVM2AnyMessage) *types.Transaction {
+ tx, err := c.Source.Router.CcipSend(c.Source.User, c.Dest.ChainSelector, msg)
+ require.NoError(t, err)
+ // FIXME
+ // testhelpers.ConfirmTxs(t, []*types.Transaction{tx}, c.Source.Chain)
+ return tx
+}
+
+func (c *CCIPContracts) AssertExecState(t *testing.T, log logpoller.Log, state MessageExecutionState, offRampOpts ...common.Address) {
+ var offRamp *evm_2_evm_offramp.EVM2EVMOffRamp
+ var err error
+ if len(offRampOpts) > 0 {
+ offRamp, err = evm_2_evm_offramp.NewEVM2EVMOffRamp(offRampOpts[0], c.Dest.Chain)
+ require.NoError(t, err)
+ } else {
+ require.NotNil(t, c.Dest.OffRamp, "no offRamp configured")
+ offRamp = c.Dest.OffRamp
+ }
+ executionStateChanged, err := offRamp.ParseExecutionStateChanged(log.ToGethLog())
+ require.NoError(t, err)
+ if MessageExecutionState(executionStateChanged.State) != state {
+ t.Log("Execution failed", hexutil.Encode(executionStateChanged.ReturnData))
+ t.Fail()
+ }
+}
+
+func GetEVMExtraArgsV1(gasLimit *big.Int, strict bool) ([]byte, error) {
+ EVMV1Tag := []byte{0x97, 0xa6, 0x57, 0xc9}
+
+ encodedArgs, err := utils.ABIEncode(`[{"type":"uint256"},{"type":"bool"}]`, gasLimit, strict)
+ if err != nil {
+ return nil, err
+ }
+
+ return append(EVMV1Tag, encodedArgs...), nil
+}
+
+type ManualExecArgs struct {
+ SourceChainID, DestChainID uint64
+ DestUser *bind.TransactOpts
+ SourceChain, DestChain bind.ContractBackend
+ SourceStartBlock *big.Int // the block in/after which failed ccip-send transaction was triggered
+ DestStartBlock uint64 // the start block for filtering ReportAccepted event (including the failed seq num)
+ // in destination chain. if not provided to be derived by ApproxDestStartBlock method
+ DestLatestBlockNum uint64 // current block number in destination
+ DestDeployedAt uint64 // destination block number for the initial destination contract deployment.
+ // Can be any number before the tx was reverted in destination chain. Preferably this needs to be set up with
+ // a value greater than zero to avoid performance issue in locating approximate destination block
+ SendReqLogIndex uint // log index of the CCIPSendRequested log in source chain
+ SendReqTxHash string // tx hash of the ccip-send transaction for which execution was reverted
+ CommitStore string
+ OnRamp string
+ OffRamp string
+ SeqNr uint64
+ GasLimit *big.Int
+}
+
+// ApproxDestStartBlock attempts to locate a block in destination chain with timestamp closest to the timestamp of the block
+// in source chain in which ccip-send transaction was included
+// it uses binary search to locate the block with the closest timestamp
+// if the block located has a timestamp greater than the timestamp of mentioned source block
+// it just returns the first block found with lesser timestamp of the source block
+// providing a value of args.DestDeployedAt ensures better performance by reducing the range of block numbers to be traversed
+func (args *ManualExecArgs) ApproxDestStartBlock() error {
+ sourceBlockHdr, err := args.SourceChain.HeaderByNumber(context.Background(), args.SourceStartBlock)
+ if err != nil {
+ return err
+ }
+ sendTxTime := sourceBlockHdr.Time
+ maxBlockNum := args.DestLatestBlockNum
+ // setting this to an approx value of 1000 considering destination chain would have at least 1000 blocks before the transaction started
+ minBlockNum := args.DestDeployedAt
+ closestBlockNum := uint64(math.Floor((float64(maxBlockNum) + float64(minBlockNum)) / 2))
+ var closestBlockHdr *types.Header
+ //nolint:gosec // safe to casts in tests
+ closestBlockHdr, err = args.DestChain.HeaderByNumber(context.Background(), big.NewInt(int64(closestBlockNum)))
+ if err != nil {
+ return err
+ }
+ // to reduce the number of RPC calls increase the value of blockOffset
+ blockOffset := uint64(10)
+ for {
+ blockNum := closestBlockHdr.Number.Uint64()
+ if minBlockNum > maxBlockNum {
+ break
+ }
+ timeDiff := math.Abs(float64(closestBlockHdr.Time - sendTxTime))
+ // break if the difference in timestamp is lesser than 1 minute
+ //nolint:gocritic // tests
+ if timeDiff < 60 {
+ break
+ } else if closestBlockHdr.Time > sendTxTime {
+ maxBlockNum = blockNum - 1
+ } else {
+ minBlockNum = blockNum + 1
+ }
+ closestBlockNum = uint64(math.Floor((float64(maxBlockNum) + float64(minBlockNum)) / 2))
+ //nolint:gosec // safe to casts in tests
+ closestBlockHdr, err = args.DestChain.HeaderByNumber(context.Background(), big.NewInt(int64(closestBlockNum)))
+ if err != nil {
+ return err
+ }
+ }
+
+ for closestBlockHdr.Time > sendTxTime {
+ closestBlockNum -= blockOffset
+ if closestBlockNum <= 0 {
+ return errors.New("approx destination blocknumber not found")
+ }
+ //nolint:gosec // safe to casts in tests
+ closestBlockHdr, err = args.DestChain.HeaderByNumber(context.Background(), big.NewInt(int64(closestBlockNum)))
+ if err != nil {
+ return err
+ }
+ }
+ args.DestStartBlock = closestBlockHdr.Number.Uint64()
+ fmt.Println("using approx destination start block number", args.DestStartBlock)
+ return nil
+}
+
+func (args *ManualExecArgs) FindSeqNrFromCCIPSendRequested() (uint64, error) {
+ var seqNr uint64
+ onRampContract, err := evm_2_evm_onramp.NewEVM2EVMOnRamp(common.HexToAddress(args.OnRamp), args.SourceChain)
+ if err != nil {
+ return seqNr, err
+ }
+ iterator, err := onRampContract.FilterCCIPSendRequested(&bind.FilterOpts{
+ Start: args.SourceStartBlock.Uint64(),
+ })
+ if err != nil {
+ return seqNr, err
+ }
+ for iterator.Next() {
+ if iterator.Event.Raw.Index == args.SendReqLogIndex &&
+ iterator.Event.Raw.TxHash.Hex() == args.SendReqTxHash {
+ seqNr = iterator.Event.Message.SequenceNumber
+ break
+ }
+ }
+ if seqNr == 0 {
+ return seqNr,
+ fmt.Errorf("no CCIPSendRequested logs found for logIndex %d starting from block number %d", args.SendReqLogIndex, args.SourceStartBlock)
+ }
+ return seqNr, nil
+}
+
+func (args *ManualExecArgs) ExecuteManually() (*types.Transaction, error) {
+ if args.SourceChainID == 0 ||
+ args.DestChainID == 0 ||
+ args.DestUser == nil {
+ return nil, errors.New("chain ids and owners are mandatory for source and dest chain")
+ }
+ if !common.IsHexAddress(args.CommitStore) ||
+ !common.IsHexAddress(args.OffRamp) ||
+ !common.IsHexAddress(args.OnRamp) {
+ return nil, errors.New("contract addresses must be valid hex address")
+ }
+ if args.SendReqTxHash == "" {
+ return nil, errors.New("tx hash of ccip-send request are required")
+ }
+ if args.SourceStartBlock == nil {
+ return nil, errors.New("must provide the value of source block in/after which ccip-send tx was included")
+ }
+ if args.SeqNr == 0 {
+ if args.SendReqLogIndex == 0 {
+ return nil, errors.New("must provide the value of log index of ccip-send request")
+ }
+ // locate seq nr from CCIPSendRequested log
+ seqNr, err := args.FindSeqNrFromCCIPSendRequested()
+ if err != nil {
+ return nil, err
+ }
+ args.SeqNr = seqNr
+ }
+ commitStore, err := commit_store_1_2_0.NewCommitStore(common.HexToAddress(args.CommitStore), args.DestChain)
+ if err != nil {
+ return nil, err
+ }
+ if args.DestStartBlock < 1 {
+ err = args.ApproxDestStartBlock()
+ if err != nil {
+ return nil, err
+ }
+ }
+ iterator, err := commitStore.FilterReportAccepted(&bind.FilterOpts{Start: args.DestStartBlock})
+ if err != nil {
+ return nil, err
+ }
+
+ var commitReport *commit_store_1_2_0.CommitStoreCommitReport
+ for iterator.Next() {
+ if iterator.Event.Report.Interval.Min <= args.SeqNr && iterator.Event.Report.Interval.Max >= args.SeqNr {
+ commitReport = &iterator.Event.Report
+ fmt.Println("Found root")
+ break
+ }
+ }
+ if commitReport == nil {
+ return nil, fmt.Errorf("unable to find seq num %d in commit report", args.SeqNr)
+ }
+
+ return args.execute(commitReport)
+}
+
+func (args *ManualExecArgs) execute(report *commit_store_1_2_0.CommitStoreCommitReport) (*types.Transaction, error) {
+ log.Info().Msg("Executing request manually")
+ seqNr := args.SeqNr
+ // Build a merkle tree for the report
+ mctx := hashutil.NewKeccak()
+ onRampContract, err := evm_2_evm_onramp_1_2_0.NewEVM2EVMOnRamp(common.HexToAddress(args.OnRamp), args.SourceChain)
+ if err != nil {
+ return nil, err
+ }
+ leafHasher := v1_2_0.NewLeafHasher(args.SourceChainID, args.DestChainID, common.HexToAddress(args.OnRamp), mctx, onRampContract)
+ if leafHasher == nil {
+ return nil, errors.New("unable to create leaf hasher")
+ }
+
+ var leaves [][32]byte
+ var curr, prove int
+ var msgs []evm_2_evm_offramp.InternalEVM2EVMMessage
+ var manualExecGasLimits []*big.Int
+ var tokenData [][][]byte
+ sendRequestedIterator, err := onRampContract.FilterCCIPSendRequested(&bind.FilterOpts{
+ Start: args.SourceStartBlock.Uint64(),
+ })
+ if err != nil {
+ return nil, err
+ }
+ for sendRequestedIterator.Next() {
+ if sendRequestedIterator.Event.Message.SequenceNumber <= report.Interval.Max &&
+ sendRequestedIterator.Event.Message.SequenceNumber >= report.Interval.Min {
+ fmt.Println("Found seq num", sendRequestedIterator.Event.Message.SequenceNumber, report.Interval)
+ hash, err2 := leafHasher.HashLeaf(sendRequestedIterator.Event.Raw)
+ if err2 != nil {
+ return nil, err2
+ }
+ leaves = append(leaves, hash)
+ if sendRequestedIterator.Event.Message.SequenceNumber == seqNr {
+ fmt.Printf("Found proving %d %+v\n", curr, sendRequestedIterator.Event.Message)
+ var tokensAndAmounts []evm_2_evm_offramp.ClientEVMTokenAmount
+ for _, tokenAndAmount := range sendRequestedIterator.Event.Message.TokenAmounts {
+ tokensAndAmounts = append(tokensAndAmounts, evm_2_evm_offramp.ClientEVMTokenAmount{
+ Token: tokenAndAmount.Token,
+ Amount: tokenAndAmount.Amount,
+ })
+ }
+ msg := evm_2_evm_offramp.InternalEVM2EVMMessage{
+ SourceChainSelector: sendRequestedIterator.Event.Message.SourceChainSelector,
+ Sender: sendRequestedIterator.Event.Message.Sender,
+ Receiver: sendRequestedIterator.Event.Message.Receiver,
+ SequenceNumber: sendRequestedIterator.Event.Message.SequenceNumber,
+ GasLimit: sendRequestedIterator.Event.Message.GasLimit,
+ Strict: sendRequestedIterator.Event.Message.Strict,
+ Nonce: sendRequestedIterator.Event.Message.Nonce,
+ FeeToken: sendRequestedIterator.Event.Message.FeeToken,
+ FeeTokenAmount: sendRequestedIterator.Event.Message.FeeTokenAmount,
+ Data: sendRequestedIterator.Event.Message.Data,
+ TokenAmounts: tokensAndAmounts,
+ SourceTokenData: sendRequestedIterator.Event.Message.SourceTokenData,
+ MessageId: sendRequestedIterator.Event.Message.MessageId,
+ }
+ msgs = append(msgs, msg)
+ if args.GasLimit != nil {
+ msg.GasLimit = args.GasLimit
+ }
+ manualExecGasLimits = append(manualExecGasLimits, msg.GasLimit)
+ var msgTokenData [][]byte
+ for range sendRequestedIterator.Event.Message.TokenAmounts {
+ msgTokenData = append(msgTokenData, []byte{})
+ }
+
+ tokenData = append(tokenData, msgTokenData)
+ prove = curr
+ }
+ curr++
+ }
+ }
+ sendRequestedIterator.Close()
+ if msgs == nil {
+ return nil, fmt.Errorf("unable to find msg with seqNr %d", seqNr)
+ }
+ tree, err := merklemulti.NewTree(mctx, leaves)
+ if err != nil {
+ return nil, err
+ }
+ if tree.Root() != report.MerkleRoot {
+ return nil, errors.New("root doesn't match")
+ }
+
+ proof, err := tree.Prove([]int{prove})
+ if err != nil {
+ return nil, err
+ }
+
+ offRampProof := evm_2_evm_offramp.InternalExecutionReport{
+ Messages: msgs,
+ OffchainTokenData: tokenData,
+ Proofs: proof.Hashes,
+ ProofFlagBits: abihelpers.ProofFlagsToBits(proof.SourceFlags),
+ }
+ offRamp, err := evm_2_evm_offramp.NewEVM2EVMOffRamp(common.HexToAddress(args.OffRamp), args.DestChain)
+ if err != nil {
+ return nil, err
+ }
+ // Execute.
+ return offRamp.ManuallyExecute(args.DestUser, offRampProof, manualExecGasLimits)
+}
+
+func (c *CCIPContracts) ExecuteMessage(
+ t *testing.T,
+ req logpoller.Log,
+ txHash common.Hash,
+ destStartBlock uint64,
+) uint64 {
+ t.Log("Executing request manually")
+ sendReqReceipt, err := c.Source.Chain.TransactionReceipt(context.Background(), txHash)
+ require.NoError(t, err)
+ args := ManualExecArgs{
+ SourceChainID: c.Source.ChainID,
+ DestChainID: c.Dest.ChainID,
+ DestUser: c.Dest.User,
+ SourceChain: c.Source.Chain,
+ DestChain: c.Dest.Chain,
+ SourceStartBlock: sendReqReceipt.BlockNumber,
+ DestStartBlock: destStartBlock,
+ // FIXME
+ //DestLatestBlockNum: c.Dest.Chain.Blockchain().CurrentBlock().Number.Uint64(),
+ //nolint:gosec // safe to casts in tests
+ SendReqLogIndex: uint(req.LogIndex),
+ SendReqTxHash: txHash.String(),
+ CommitStore: c.Dest.CommitStore.Address().String(),
+ OnRamp: c.Source.OnRamp.Address().String(),
+ OffRamp: c.Dest.OffRamp.Address().String(),
+ }
+ tx, err := args.ExecuteManually()
+ require.NoError(t, err)
+ c.Dest.Chain.Commit()
+ c.Source.Chain.Commit()
+ rec, err := c.Dest.Chain.TransactionReceipt(context.Background(), tx.Hash())
+ require.NoError(t, err)
+ require.Equal(t, uint64(1), rec.Status, "manual execution failed")
+ t.Logf("Manual Execution completed for seqNum %d", args.SeqNr)
+ return args.SeqNr
+}
+
+func GetBalance(t *testing.T, chain bind.ContractBackend, tokenAddr common.Address, addr common.Address) *big.Int {
+ token, err := link_token_interface.NewLinkToken(tokenAddr, chain)
+ require.NoError(t, err)
+ bal, err := token.BalanceOf(nil, addr)
+ require.NoError(t, err)
+ return bal
+}
diff --git a/core/services/ocr2/plugins/ccip/testhelpers/testhelpers_1_4_0/chainlink.go b/core/services/ocr2/plugins/ccip/testhelpers/testhelpers_1_4_0/chainlink.go
new file mode 100644
index 00000000000..cec58136b5c
--- /dev/null
+++ b/core/services/ocr2/plugins/ccip/testhelpers/testhelpers_1_4_0/chainlink.go
@@ -0,0 +1,1054 @@
+//nolint:revive // helpers for specific version
+package testhelpers_1_4_0
+
+import (
+ "context"
+ "encoding/hex"
+ "fmt"
+ "math/big"
+ "net/http"
+ "net/http/httptest"
+ "strconv"
+ "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"
+ types3 "github.com/ethereum/go-ethereum/core/types"
+ "github.com/google/uuid"
+ "github.com/hashicorp/consul/sdk/freeport"
+ "github.com/jmoiron/sqlx"
+ "github.com/onsi/gomega"
+ "github.com/pkg/errors"
+
+ "github.com/stretchr/testify/mock"
+ "github.com/stretchr/testify/require"
+ "go.uber.org/zap"
+ "k8s.io/utils/pointer" //nolint:staticcheck // tests
+
+ "github.com/smartcontractkit/libocr/commontypes"
+ "github.com/smartcontractkit/libocr/offchainreporting2/confighelper"
+ types4 "github.com/smartcontractkit/libocr/offchainreporting2plus/types"
+
+ "github.com/smartcontractkit/chainlink-common/pkg/config"
+ "github.com/smartcontractkit/chainlink-common/pkg/loop"
+ "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox"
+
+ cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip"
+
+ pb "github.com/smartcontractkit/chainlink-protos/orchestrator/feedsmanager"
+
+ evmcapabilities "github.com/smartcontractkit/chainlink/v2/core/capabilities"
+ "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client"
+ v2 "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml"
+ "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller"
+ "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils"
+ evmUtils "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big"
+ "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm"
+ configv2 "github.com/smartcontractkit/chainlink/v2/core/config/toml"
+ "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/commit_store_1_2_0"
+ "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/evm_2_evm_onramp_1_2_0"
+ "github.com/smartcontractkit/chainlink/v2/core/internal/testutils"
+ "github.com/smartcontractkit/chainlink/v2/core/logger"
+ "github.com/smartcontractkit/chainlink/v2/core/logger/audit"
+ "github.com/smartcontractkit/chainlink/v2/core/services/chainlink"
+ feeds2 "github.com/smartcontractkit/chainlink/v2/core/services/feeds"
+ feedsMocks "github.com/smartcontractkit/chainlink/v2/core/services/feeds/mocks"
+ "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/csakey"
+ "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ocr2key"
+ ksMocks "github.com/smartcontractkit/chainlink/v2/core/services/keystore/mocks"
+ "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/abihelpers"
+ "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_2_0"
+ "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/testhelpers"
+ integrationtesthelpers "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/testhelpers/integration"
+ "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/validate"
+ "github.com/smartcontractkit/chainlink/v2/core/services/ocrbootstrap"
+ evmrelay "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm"
+ clutils "github.com/smartcontractkit/chainlink/v2/core/utils"
+ "github.com/smartcontractkit/chainlink/v2/core/utils/crypto"
+ "github.com/smartcontractkit/chainlink/v2/core/utils/testutils/heavyweight"
+ "github.com/smartcontractkit/chainlink/v2/plugins"
+)
+
+const (
+ execSpecTemplate = `
+ type = "offchainreporting2"
+ schemaVersion = 1
+ name = "ccip-exec-1"
+ externalJobID = "67ffad71-d90f-4fe3-b4e4-494924b707fb"
+ forwardingAllowed = false
+ maxTaskDuration = "0s"
+ contractID = "%s"
+ contractConfigConfirmations = 1
+ contractConfigTrackerPollInterval = "20s"
+ ocrKeyBundleID = "%s"
+ relay = "evm"
+ pluginType = "ccip-execution"
+ transmitterID = "%s"
+
+ [relayConfig]
+ chainID = 1_337
+
+ [pluginConfig]
+ destStartBlock = 50
+
+ [pluginConfig.USDCConfig]
+ AttestationAPI = "http://blah.com"
+ SourceMessageTransmitterAddress = "%s"
+ SourceTokenAddress = "%s"
+ AttestationAPITimeoutSeconds = 10
+ `
+ commitSpecTemplatePipeline = `
+ type = "offchainreporting2"
+ schemaVersion = 1
+ name = "ccip-commit-1"
+ externalJobID = "13c997cf-1a14-4ab7-9068-07ee6d2afa55"
+ forwardingAllowed = false
+ maxTaskDuration = "0s"
+ contractID = "%s"
+ contractConfigConfirmations = 1
+ contractConfigTrackerPollInterval = "20s"
+ ocrKeyBundleID = "%s"
+ relay = "evm"
+ pluginType = "ccip-commit"
+ transmitterID = "%s"
+
+ [relayConfig]
+ chainID = 1_337
+
+ [pluginConfig]
+ destStartBlock = 50
+ offRamp = "%s"
+ tokenPricesUSDPipeline = """
+ %s
+ """
+ `
+ commitSpecTemplateDynamicPriceGetter = `
+ type = "offchainreporting2"
+ schemaVersion = 1
+ name = "ccip-commit-1"
+ externalJobID = "13c997cf-1a14-4ab7-9068-07ee6d2afa55"
+ forwardingAllowed = false
+ maxTaskDuration = "0s"
+ contractID = "%s"
+ contractConfigConfirmations = 1
+ contractConfigTrackerPollInterval = "20s"
+ ocrKeyBundleID = "%s"
+ relay = "evm"
+ pluginType = "ccip-commit"
+ transmitterID = "%s"
+
+ [relayConfig]
+ chainID = 1_337
+
+ [pluginConfig]
+ destStartBlock = 50
+ offRamp = "%s"
+ priceGetterConfig = """
+ %s
+ """
+ `
+)
+
+type Node struct {
+ App chainlink.Application
+ Transmitter common.Address
+ PaymentReceiver common.Address
+ KeyBundle ocr2key.KeyBundle
+}
+
+func (node *Node) FindJobIDForContract(t *testing.T, addr common.Address) int32 {
+ jobs := node.App.JobSpawner().ActiveJobs()
+ for _, j := range jobs {
+ if j.Type == job.OffchainReporting2 && j.OCR2OracleSpec.ContractID == addr.Hex() {
+ return j.ID
+ }
+ }
+ t.Fatalf("Could not find job for contract %s", addr.Hex())
+ return 0
+}
+
+func (node *Node) EventuallyNodeUsesUpdatedPriceRegistry(t *testing.T, ccipContracts CCIPIntegrationTestHarness) logpoller.Log {
+ c, err := node.App.GetRelayers().LegacyEVMChains().Get(strconv.FormatUint(ccipContracts.Dest.ChainID, 10))
+ require.NoError(t, err)
+ var log logpoller.Log
+ gomega.NewGomegaWithT(t).Eventually(func() bool {
+ ccipContracts.Source.Chain.Commit()
+ ccipContracts.Dest.Chain.Commit()
+ log, err := c.LogPoller().LatestLogByEventSigWithConfs(
+ testutils.Context(t),
+ v1_2_0.UsdPerUnitGasUpdated,
+ ccipContracts.Dest.PriceRegistry.Address(),
+ 0,
+ )
+ // err can be transient errors such as sql row set empty
+ if err != nil {
+ return false
+ }
+ return log != nil
+ }, testutils.WaitTimeout(t), 1*time.Second).Should(gomega.BeTrue(), "node is not using updated price registry %s", ccipContracts.Dest.PriceRegistry.Address().Hex())
+ return log
+}
+
+func (node *Node) EventuallyNodeUsesNewCommitConfig(t *testing.T, ccipContracts CCIPIntegrationTestHarness, commitCfg ccipdata.CommitOnchainConfig) logpoller.Log {
+ c, err := node.App.GetRelayers().LegacyEVMChains().Get(strconv.FormatUint(ccipContracts.Dest.ChainID, 10))
+ require.NoError(t, err)
+ var log logpoller.Log
+ gomega.NewGomegaWithT(t).Eventually(func() bool {
+ ccipContracts.Source.Chain.Commit()
+ ccipContracts.Dest.Chain.Commit()
+ log, err := c.LogPoller().LatestLogByEventSigWithConfs(
+ testutils.Context(t),
+ evmrelay.OCR2AggregatorLogDecoder.EventSig(),
+ ccipContracts.Dest.CommitStore.Address(),
+ 0,
+ )
+ require.NoError(t, err)
+ var latestCfg ccipdata.CommitOnchainConfig
+ if log != nil {
+ latestCfg, err = DecodeCommitOnChainConfig(log.Data)
+ require.NoError(t, err)
+ return latestCfg == commitCfg
+ }
+ return false
+ }, testutils.WaitTimeout(t), 1*time.Second).Should(gomega.BeTrue(), "node is using old cfg")
+ return log
+}
+
+func (node *Node) EventuallyNodeUsesNewExecConfig(t *testing.T, ccipContracts CCIPIntegrationTestHarness, execCfg v1_2_0.ExecOnchainConfig) logpoller.Log {
+ c, err := node.App.GetRelayers().LegacyEVMChains().Get(strconv.FormatUint(ccipContracts.Dest.ChainID, 10))
+ require.NoError(t, err)
+ var log logpoller.Log
+ gomega.NewGomegaWithT(t).Eventually(func() bool {
+ ccipContracts.Source.Chain.Commit()
+ ccipContracts.Dest.Chain.Commit()
+ log, err := c.LogPoller().LatestLogByEventSigWithConfs(
+ testutils.Context(t),
+ evmrelay.OCR2AggregatorLogDecoder.EventSig(),
+ ccipContracts.Dest.OffRamp.Address(),
+ 0,
+ )
+ require.NoError(t, err)
+ var latestCfg v1_2_0.ExecOnchainConfig
+ if log != nil {
+ latestCfg, err = DecodeExecOnChainConfig(log.Data)
+ require.NoError(t, err)
+ return latestCfg == execCfg
+ }
+ return false
+ }, testutils.WaitTimeout(t), 1*time.Second).Should(gomega.BeTrue(), "node is using old cfg")
+ return log
+}
+
+//nolint:gosec // safe cast in tests
+func (node *Node) EventuallyHasReqSeqNum(t *testing.T, ccipContracts *CCIPIntegrationTestHarness, onRamp common.Address, seqNum int) logpoller.Log {
+ c, err := node.App.GetRelayers().LegacyEVMChains().Get(strconv.FormatUint(ccipContracts.Source.ChainID, 10))
+ require.NoError(t, err)
+ var log logpoller.Log
+ gomega.NewGomegaWithT(t).Eventually(func() bool {
+ ccipContracts.Source.Chain.Commit()
+ ccipContracts.Dest.Chain.Commit()
+ lgs, err := c.LogPoller().LogsDataWordRange(
+ testutils.Context(t),
+ v1_2_0.CCIPSendRequestEventSig,
+ onRamp,
+ v1_2_0.CCIPSendRequestSeqNumIndex,
+ abihelpers.EvmWord(uint64(seqNum)),
+ abihelpers.EvmWord(uint64(seqNum)),
+ 1,
+ )
+ require.NoError(t, err)
+ t.Log("Send requested", len(lgs))
+ if len(lgs) == 1 {
+ log = lgs[0]
+ return true
+ }
+ return false
+ }, testutils.WaitTimeout(t), 1*time.Second).Should(gomega.BeTrue(), "eventually has seq num")
+ return log
+}
+
+//nolint:gosec // safe cast in tests
+func (node *Node) EventuallyHasExecutedSeqNums(t *testing.T, ccipContracts *CCIPIntegrationTestHarness, offRamp common.Address, minSeqNum int, maxSeqNum int) []logpoller.Log {
+ c, err := node.App.GetRelayers().LegacyEVMChains().Get(strconv.FormatUint(ccipContracts.Dest.ChainID, 10))
+ require.NoError(t, err)
+ var logs []logpoller.Log
+ gomega.NewGomegaWithT(t).Eventually(func() bool {
+ ccipContracts.Source.Chain.Commit()
+ ccipContracts.Dest.Chain.Commit()
+ lgs, err := c.LogPoller().IndexedLogsTopicRange(
+ testutils.Context(t),
+ v1_2_0.ExecutionStateChangedEvent,
+ offRamp,
+ v1_2_0.ExecutionStateChangedSeqNrIndex,
+ abihelpers.EvmWord(uint64(minSeqNum)),
+ abihelpers.EvmWord(uint64(maxSeqNum)),
+ 1,
+ )
+ require.NoError(t, err)
+ t.Logf("Have executed logs %d want %d", len(lgs), maxSeqNum-minSeqNum+1)
+ if len(lgs) == maxSeqNum-minSeqNum+1 {
+ logs = lgs
+ t.Logf("Seq Num %d-%d executed", minSeqNum, maxSeqNum)
+ return true
+ }
+ return false
+ }, testutils.WaitTimeout(t), 1*time.Second).Should(gomega.BeTrue(), "eventually has not executed seq num")
+ return logs
+}
+
+//nolint:gosec // safe to casts in tests
+func (node *Node) ConsistentlySeqNumHasNotBeenExecuted(t *testing.T, ccipContracts *CCIPIntegrationTestHarness, offRamp common.Address, seqNum int) logpoller.Log {
+ c, err := node.App.GetRelayers().LegacyEVMChains().Get(strconv.FormatUint(ccipContracts.Dest.ChainID, 10))
+ require.NoError(t, err)
+ var log logpoller.Log
+ gomega.NewGomegaWithT(t).Consistently(func() bool {
+ ccipContracts.Source.Chain.Commit()
+ ccipContracts.Dest.Chain.Commit()
+ lgs, err := c.LogPoller().IndexedLogsTopicRange(
+ testutils.Context(t),
+ v1_2_0.ExecutionStateChangedEvent,
+ offRamp,
+ v1_2_0.ExecutionStateChangedSeqNrIndex,
+ abihelpers.EvmWord(uint64(seqNum)),
+ abihelpers.EvmWord(uint64(seqNum)),
+ 1,
+ )
+ require.NoError(t, err)
+ t.Log("Executed logs", lgs)
+ if len(lgs) == 1 {
+ log = lgs[0]
+ return true
+ }
+ return false
+ }, 10*time.Second, 1*time.Second).Should(gomega.BeFalse(), "seq number got executed")
+ return log
+}
+
+func (node *Node) AddJob(t *testing.T, spec *integrationtesthelpers.OCR2TaskJobSpec) {
+ specString, err := spec.String()
+ require.NoError(t, err)
+ ccipJob, err := validate.ValidatedOracleSpecToml(
+ testutils.Context(t),
+ node.App.GetConfig().OCR2(),
+ node.App.GetConfig().Insecure(),
+ specString,
+ // FIXME Ani
+ nil,
+ )
+ require.NoError(t, err)
+ err = node.App.AddJobV2(context.Background(), &ccipJob)
+ require.NoError(t, err)
+}
+
+func (node *Node) AddBootstrapJob(t *testing.T, spec *integrationtesthelpers.OCR2TaskJobSpec) {
+ specString, err := spec.String()
+ require.NoError(t, err)
+ ccipJob, err := ocrbootstrap.ValidatedBootstrapSpecToml(specString)
+ require.NoError(t, err)
+ err = node.App.AddJobV2(context.Background(), &ccipJob)
+ require.NoError(t, err)
+}
+
+func (node *Node) AddJobsWithSpec(t *testing.T, jobSpec *integrationtesthelpers.OCR2TaskJobSpec) {
+ // set node specific values
+ jobSpec.OCR2OracleSpec.OCRKeyBundleID.SetValid(node.KeyBundle.ID())
+ jobSpec.OCR2OracleSpec.TransmitterID.SetValid(node.Transmitter.Hex())
+ node.AddJob(t, jobSpec)
+}
+
+func setupNodeCCIP(
+ t *testing.T,
+ owner *bind.TransactOpts,
+ port int64,
+ dbName string,
+ sourceChain *backends.SimulatedBackend, destChain *backends.SimulatedBackend,
+ sourceChainID *big.Int, destChainID *big.Int,
+ bootstrapPeerID string,
+ bootstrapPort int64,
+) (chainlink.Application, string, common.Address, ocr2key.KeyBundle) {
+ trueRef, falseRef := true, false
+
+ // Do not want to load fixtures as they contain a dummy chainID.
+ loglevel := configv2.LogLevel(zap.DebugLevel)
+ config, db := heavyweight.FullTestDBNoFixturesV2(t, func(c *chainlink.Config, _ *chainlink.Secrets) {
+ p2pAddresses := []string{
+ fmt.Sprintf("127.0.0.1:%d", port),
+ }
+ c.Log.Level = &loglevel
+ c.Feature.CCIP = &trueRef
+ c.Feature.UICSAKeys = &trueRef
+ c.Feature.FeedsManager = &trueRef
+ c.OCR.Enabled = &falseRef
+ c.OCR.DefaultTransactionQueueDepth = pointer.Uint32(200)
+ c.OCR2.Enabled = &trueRef
+ c.Feature.LogPoller = &trueRef
+ c.P2P.V2.Enabled = &trueRef
+
+ dur, err := config.NewDuration(500 * time.Millisecond)
+ if err != nil {
+ panic(err)
+ }
+ c.P2P.V2.DeltaDial = &dur
+
+ dur2, err := config.NewDuration(5 * time.Second)
+ if err != nil {
+ panic(err)
+ }
+
+ c.P2P.V2.DeltaReconcile = &dur2
+ c.P2P.V2.ListenAddresses = &p2pAddresses
+ c.P2P.V2.AnnounceAddresses = &p2pAddresses
+
+ c.EVM = []*v2.EVMConfig{createConfigV2Chain(sourceChainID), createConfigV2Chain(destChainID)}
+
+ if bootstrapPeerID != "" {
+ // Supply the bootstrap IP and port as a V2 peer address
+ c.P2P.V2.DefaultBootstrappers = &[]commontypes.BootstrapperLocator{
+ {
+ PeerID: bootstrapPeerID, Addrs: []string{
+ fmt.Sprintf("127.0.0.1:%d", bootstrapPort),
+ },
+ },
+ }
+ }
+ })
+
+ lggr := logger.TestLogger(t)
+
+ // The in-memory geth sim does not let you create a custom ChainID, it will always be 1337.
+ // In particular this means that if you sign an eip155 tx, the chainID used MUST be 1337
+ // and the CHAINID op code will always emit 1337. To work around this to simulate a "multichain"
+ // test, we fake different chainIDs using the wrapped sim cltest.SimulatedBackend so the RPC
+ // appears to operate on different chainIDs and we use an EthKeyStoreSim wrapper which always
+ // signs 1337 see https://github.com/smartcontractkit/chainlink-ccip/blob/a24dd436810250a458d27d8bb3fb78096afeb79c/core/services/ocr2/plugins/ccip/testhelpers/simulated_backend.go#L35
+ sourceClient := client.NewSimulatedBackendClient(t, sourceChain.Backend, sourceChainID)
+ destClient := client.NewSimulatedBackendClient(t, destChain.Backend, destChainID)
+ csaKeyStore := ksMocks.NewCSA(t)
+
+ key, err := csakey.NewV2()
+ require.NoError(t, err)
+ csaKeyStore.On("GetAll").Return([]csakey.KeyV2{key}, nil)
+ keyStore := NewKsa(db, lggr, csaKeyStore)
+
+ simEthKeyStore := testhelpers.EthKeyStoreSim{
+ ETHKS: keyStore.Eth(),
+ CSAKS: keyStore.CSA(),
+ }
+ mailMon := mailbox.NewMonitor("CCIP", lggr.Named("Mailbox"))
+ evmOpts := chainlink.EVMFactoryConfig{
+ ChainOpts: legacyevm.ChainOpts{
+ AppConfig: config,
+ GenEthClient: func(chainID *big.Int) client.Client {
+ if chainID.String() == sourceChainID.String() {
+ return sourceClient
+ } else if chainID.String() == destChainID.String() {
+ return destClient
+ }
+ t.Fatalf("invalid chain ID %v", chainID.String())
+ return nil
+ },
+ MailMon: mailMon,
+ DS: db,
+ },
+ CSAETHKeystore: simEthKeyStore,
+ }
+ loopRegistry := plugins.NewLoopRegistry(lggr.Named("LoopRegistry"), config.Tracing(), config.Telemetry(), nil, "")
+ relayerFactory := chainlink.RelayerFactory{
+ Logger: lggr,
+ LoopRegistry: loopRegistry,
+ GRPCOpts: loop.GRPCOpts{},
+ CapabilitiesRegistry: evmcapabilities.NewRegistry(lggr),
+ }
+ testCtx := testutils.Context(t)
+ // evm alway enabled for backward compatibility
+ initOps := []chainlink.CoreRelayerChainInitFunc{
+ chainlink.InitEVM(testCtx, relayerFactory, evmOpts),
+ }
+
+ relayChainInterops, err := chainlink.NewCoreRelayerChainInteroperators(initOps...)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ app, err := chainlink.NewApplication(chainlink.ApplicationOpts{
+ Config: config,
+ DS: db,
+ KeyStore: keyStore,
+ RelayerChainInteroperators: relayChainInterops,
+ Logger: lggr,
+ ExternalInitiatorManager: nil,
+ CloseLogger: lggr.Sync,
+ UnrestrictedHTTPClient: &http.Client{},
+ RestrictedHTTPClient: &http.Client{},
+ AuditLogger: audit.NoopLogger,
+ MailMon: mailMon,
+ LoopRegistry: plugins.NewLoopRegistry(lggr, config.Tracing(), config.Telemetry(), nil, ""),
+ })
+ ctx := testutils.Context(t)
+ require.NoError(t, err)
+ require.NoError(t, app.GetKeyStore().Unlock(ctx, "password"))
+ _, err = app.GetKeyStore().P2P().Create(ctx)
+ require.NoError(t, err)
+
+ p2pIDs, err := app.GetKeyStore().P2P().GetAll()
+ require.NoError(t, err)
+ require.Len(t, p2pIDs, 1)
+ peerID := p2pIDs[0].PeerID()
+
+ _, err = app.GetKeyStore().Eth().Create(testCtx, destChainID)
+ require.NoError(t, err)
+ sendingKeys, err := app.GetKeyStore().Eth().EnabledKeysForChain(testCtx, destChainID)
+ require.NoError(t, err)
+ require.Len(t, sendingKeys, 1)
+ transmitter := sendingKeys[0].Address
+ s, err := app.GetKeyStore().Eth().GetState(testCtx, sendingKeys[0].ID(), destChainID)
+ require.NoError(t, err)
+ lggr.Debug(fmt.Sprintf("Transmitter address %s chainID %s", transmitter, s.EVMChainID.String()))
+
+ // Fund the commitTransmitter address with some ETH
+ n, err := destChain.NonceAt(context.Background(), owner.From, nil)
+ require.NoError(t, err)
+
+ tx := types3.NewTransaction(n, transmitter, big.NewInt(1000000000000000000), 21000, big.NewInt(1000000000), nil)
+ signedTx, err := owner.Signer(owner.From, tx)
+ require.NoError(t, err)
+ err = destChain.SendTransaction(context.Background(), signedTx)
+ require.NoError(t, err)
+ destChain.Commit()
+
+ kb, err := app.GetKeyStore().OCR2().Create(ctx, chaintype.EVM)
+ require.NoError(t, err)
+ return app, peerID.Raw(), transmitter, kb
+}
+
+func createConfigV2Chain(chainID *big.Int) *v2.EVMConfig {
+ // NOTE: For the executor jobs, the default of 500k is insufficient for a 3 message batch
+ defaultGasLimit := uint64(5000000)
+ tr := true
+
+ sourceC := v2.Defaults((*evmUtils.Big)(chainID))
+ sourceC.GasEstimator.LimitDefault = &defaultGasLimit
+ fixedPrice := "FixedPrice"
+ sourceC.GasEstimator.Mode = &fixedPrice
+ d, _ := config.NewDuration(100 * time.Millisecond)
+ sourceC.LogPollInterval = &d
+ fd := uint32(2)
+ sourceC.FinalityDepth = &fd
+ return &v2.EVMConfig{
+ ChainID: (*evmUtils.Big)(chainID),
+ Enabled: &tr,
+ Chain: sourceC,
+ Nodes: v2.EVMNodes{&v2.Node{}},
+ }
+}
+
+type CCIPIntegrationTestHarness struct {
+ CCIPContracts
+ Nodes []Node
+ Bootstrap Node
+}
+
+func SetupCCIPIntegrationTH(t *testing.T, sourceChainID, sourceChainSelector, destChainId, destChainSelector uint64) CCIPIntegrationTestHarness {
+ return CCIPIntegrationTestHarness{
+ CCIPContracts: SetupCCIPContracts(t, sourceChainID, sourceChainSelector, destChainId, destChainSelector),
+ }
+}
+
+//nolint:testifylint //require is used for assertions in handlers
+func (c *CCIPIntegrationTestHarness) CreatePricesPipeline(t *testing.T) (string, *httptest.Server, *httptest.Server) {
+ linkUSD := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
+ _, err := w.Write([]byte(`{"UsdPerLink": "8000000000000000000"}`))
+ require.NoError(t, err)
+ }))
+ ethUSD := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
+ _, err := w.Write([]byte(`{"UsdPerETH": "1700000000000000000000"}`))
+ require.NoError(t, err)
+ }))
+ sourceWrappedNative, err := c.Source.Router.GetWrappedNative(nil)
+ require.NoError(t, err)
+ destWrappedNative, err := c.Dest.Router.GetWrappedNative(nil)
+ require.NoError(t, err)
+ tokenPricesUSDPipeline := fmt.Sprintf(`
+// Price 1
+link [type=http method=GET url="%s"];
+link_parse [type=jsonparse path="UsdPerLink"];
+link->link_parse;
+eth [type=http method=GET url="%s"];
+eth_parse [type=jsonparse path="UsdPerETH"];
+eth->eth_parse;
+merge [type=merge left="{}" right="{\\\"%s\\\":$(link_parse), \\\"%s\\\":$(eth_parse), \\\"%s\\\":$(eth_parse)}"];`,
+ linkUSD.URL, ethUSD.URL, c.Dest.LinkToken.Address(), sourceWrappedNative, destWrappedNative)
+
+ return tokenPricesUSDPipeline, linkUSD, ethUSD
+}
+
+func (c *CCIPIntegrationTestHarness) AddAllJobs(t *testing.T, jobParams integrationtesthelpers.CCIPJobSpecParams) {
+ jobParams.OffRamp = c.Dest.OffRamp.Address()
+
+ commitSpec, err := jobParams.CommitJobSpec()
+ require.NoError(t, err)
+ geExecutionSpec, err := jobParams.ExecutionJobSpec()
+ require.NoError(t, err)
+ nodes := c.Nodes
+ for _, node := range nodes {
+ node.AddJobsWithSpec(t, commitSpec)
+ node.AddJobsWithSpec(t, geExecutionSpec)
+ }
+}
+
+func (c *CCIPIntegrationTestHarness) jobSpecProposal(t *testing.T, specTemplate string, f func() (*integrationtesthelpers.OCR2TaskJobSpec, error), feedsManagerId int64, version int32, opts ...any) feeds2.ProposeJobArgs {
+ spec, err := f()
+ require.NoError(t, err)
+
+ args := []any{spec.OCR2OracleSpec.ContractID}
+ args = append(args, opts...)
+
+ return feeds2.ProposeJobArgs{
+ FeedsManagerID: feedsManagerId,
+ RemoteUUID: uuid.New(),
+ Multiaddrs: nil,
+ Version: version,
+ Spec: fmt.Sprintf(specTemplate, args...),
+ }
+}
+
+func (c *CCIPIntegrationTestHarness) SetupFeedsManager(t *testing.T) {
+ ctx := testutils.Context(t)
+ for _, node := range c.Nodes {
+ f := node.App.GetFeedsService()
+
+ managers, err := f.ListManagers(ctx)
+ require.NoError(t, err)
+ if len(managers) > 0 {
+ // Use at most one feeds manager, don't register if one already exists
+ continue
+ }
+
+ secret := utils.RandomBytes32()
+ pkey, err := crypto.PublicKeyFromHex(hex.EncodeToString(secret[:]))
+ require.NoError(t, err)
+
+ m := feeds2.RegisterManagerParams{
+ Name: "CCIP",
+ URI: "http://localhost:8080",
+ PublicKey: *pkey,
+ }
+
+ _, err = f.RegisterManager(testutils.Context(t), m)
+ require.NoError(t, err)
+
+ connManager := feedsMocks.NewConnectionsManager(t)
+ connManager.On("GetClient", mock.Anything).Maybe().Return(NoopFeedsClient{}, nil)
+ connManager.On("Close").Maybe().Return()
+ connManager.On("IsConnected", mock.Anything).Maybe().Return(true)
+ f.Unsafe_SetConnectionsManager(connManager)
+ }
+}
+
+func (c *CCIPIntegrationTestHarness) ApproveJobSpecs(t *testing.T, jobParams integrationtesthelpers.CCIPJobSpecParams) {
+ ctx := testutils.Context(t)
+
+ for _, node := range c.Nodes {
+ f := node.App.GetFeedsService()
+ managers, err := f.ListManagers(ctx)
+ require.NoError(t, err)
+ require.Len(t, managers, 1, "expected exactly one feeds manager")
+
+ execSpec := c.jobSpecProposal(
+ t,
+ execSpecTemplate,
+ jobParams.ExecutionJobSpec,
+ managers[0].ID,
+ 1,
+ node.KeyBundle.ID(),
+ node.Transmitter.Hex(),
+ utils.RandomAddress().String(),
+ utils.RandomAddress().String(),
+ )
+ execID, err := f.ProposeJob(ctx, &execSpec)
+ require.NoError(t, err)
+
+ err = f.ApproveSpec(ctx, execID, true)
+ require.NoError(t, err)
+
+ var commitSpec feeds2.ProposeJobArgs
+ if jobParams.TokenPricesUSDPipeline != "" {
+ commitSpec = c.jobSpecProposal(
+ t,
+ commitSpecTemplatePipeline,
+ jobParams.CommitJobSpec,
+ managers[0].ID,
+ 2,
+ node.KeyBundle.ID(),
+ node.Transmitter.Hex(),
+ jobParams.OffRamp.String(),
+ jobParams.TokenPricesUSDPipeline,
+ )
+ } else {
+ commitSpec = c.jobSpecProposal(
+ t,
+ commitSpecTemplateDynamicPriceGetter,
+ jobParams.CommitJobSpec,
+ managers[0].ID,
+ 2,
+ node.KeyBundle.ID(),
+ node.Transmitter.Hex(),
+ jobParams.OffRamp.String(),
+ jobParams.PriceGetterConfig,
+ )
+ }
+
+ commitID, err := f.ProposeJob(ctx, &commitSpec)
+ require.NoError(t, err)
+
+ err = f.ApproveSpec(ctx, commitID, true)
+ require.NoError(t, err)
+ }
+}
+
+func (c *CCIPIntegrationTestHarness) AllNodesHaveReqSeqNum(t *testing.T, seqNum int, onRampOpts ...common.Address) logpoller.Log {
+ var log logpoller.Log
+ nodes := c.Nodes
+ var onRamp common.Address
+ if len(onRampOpts) > 0 {
+ onRamp = onRampOpts[0]
+ } else {
+ require.NotNil(t, c.Source.OnRamp, "no onramp configured")
+ onRamp = c.Source.OnRamp.Address()
+ }
+ for _, node := range nodes {
+ log = node.EventuallyHasReqSeqNum(t, c, onRamp, seqNum)
+ }
+ return log
+}
+
+func (c *CCIPIntegrationTestHarness) AllNodesHaveExecutedSeqNums(t *testing.T, minSeqNum int, maxSeqNum int, offRampOpts ...common.Address) []logpoller.Log {
+ var logs []logpoller.Log
+ nodes := c.Nodes
+ var offRamp common.Address
+
+ if len(offRampOpts) > 0 {
+ offRamp = offRampOpts[0]
+ } else {
+ require.NotNil(t, c.Dest.OffRamp, "no offramp configured")
+ offRamp = c.Dest.OffRamp.Address()
+ }
+ for _, node := range nodes {
+ logs = node.EventuallyHasExecutedSeqNums(t, c, offRamp, minSeqNum, maxSeqNum)
+ }
+ return logs
+}
+
+func (c *CCIPIntegrationTestHarness) NoNodesHaveExecutedSeqNum(t *testing.T, seqNum int, offRampOpts ...common.Address) logpoller.Log {
+ var log logpoller.Log
+ nodes := c.Nodes
+ var offRamp common.Address
+ if len(offRampOpts) > 0 {
+ offRamp = offRampOpts[0]
+ } else {
+ require.NotNil(t, c.Dest.OffRamp, "no offramp configured")
+ offRamp = c.Dest.OffRamp.Address()
+ }
+ for _, node := range nodes {
+ log = node.ConsistentlySeqNumHasNotBeenExecuted(t, c, offRamp, seqNum)
+ }
+ return log
+}
+
+func (c *CCIPIntegrationTestHarness) EventuallyCommitReportAccepted(t *testing.T, currentBlock uint64, commitStoreOpts ...common.Address) commit_store_1_2_0.CommitStoreCommitReport {
+ var commitStore *commit_store_1_2_0.CommitStore
+ var err error
+ if len(commitStoreOpts) > 0 {
+ commitStore, err = commit_store_1_2_0.NewCommitStore(commitStoreOpts[0], c.Dest.Chain)
+ require.NoError(t, err)
+ } else {
+ require.NotNil(t, c.Dest.CommitStore, "no commitStore configured")
+ commitStore = c.Dest.CommitStore
+ }
+ g := gomega.NewGomegaWithT(t)
+ var report commit_store_1_2_0.CommitStoreCommitReport
+ g.Eventually(func() bool {
+ it, err := commitStore.FilterReportAccepted(&bind.FilterOpts{Start: currentBlock})
+ g.Expect(err).NotTo(gomega.HaveOccurred(), "Error filtering ReportAccepted event")
+ g.Expect(it.Next()).To(gomega.BeTrue(), "No ReportAccepted event found")
+ report = it.Event.Report
+ if report.MerkleRoot != [32]byte{} {
+ t.Log("Report Accepted by commitStore")
+ return true
+ }
+ return false
+ }, testutils.WaitTimeout(t), 1*time.Second).Should(gomega.BeTrue(), "report has not been committed")
+ return report
+}
+
+func (c *CCIPIntegrationTestHarness) EventuallyExecutionStateChangedToSuccess(t *testing.T, seqNum []uint64, blockNum uint64, offRampOpts ...common.Address) {
+ var offRamp *evm_2_evm_offramp_1_2_0.EVM2EVMOffRamp
+ var err error
+ if len(offRampOpts) > 0 {
+ offRamp, err = evm_2_evm_offramp_1_2_0.NewEVM2EVMOffRamp(offRampOpts[0], c.Dest.Chain)
+ require.NoError(t, err)
+ } else {
+ require.NotNil(t, c.Dest.OffRamp, "no offRamp configured")
+ offRamp = c.Dest.OffRamp
+ }
+ gomega.NewGomegaWithT(t).Eventually(func() bool {
+ it, err := offRamp.FilterExecutionStateChanged(&bind.FilterOpts{Start: blockNum}, seqNum, [][32]byte{})
+ require.NoError(t, err)
+ for it.Next() {
+ if cciptypes.MessageExecutionState(it.Event.State) == cciptypes.ExecutionStateSuccess {
+ t.Logf("ExecutionStateChanged event found for seqNum %d", it.Event.SequenceNumber)
+ return true
+ }
+ }
+ c.Source.Chain.Commit()
+ c.Dest.Chain.Commit()
+ return false
+ }, testutils.WaitTimeout(t), time.Second).
+ Should(gomega.BeTrue(), "ExecutionStateChanged Event")
+}
+
+//nolint:gosec // safe to casts in tests
+func (c *CCIPIntegrationTestHarness) EventuallyReportCommitted(t *testing.T, maxSeqNr int, commitStoreOpts ...common.Address) uint64 {
+ var commitStore *commit_store_1_2_0.CommitStore
+ var err error
+ var committedSeqNum uint64
+ if len(commitStoreOpts) > 0 {
+ commitStore, err = commit_store_1_2_0.NewCommitStore(commitStoreOpts[0], c.Dest.Chain)
+ require.NoError(t, err)
+ } else {
+ require.NotNil(t, c.Dest.CommitStore, "no commitStore configured")
+ commitStore = c.Dest.CommitStore
+ }
+ gomega.NewGomegaWithT(t).Eventually(func() bool {
+ minSeqNum, err := commitStore.GetExpectedNextSequenceNumber(nil)
+ require.NoError(t, err)
+ c.Source.Chain.Commit()
+ c.Dest.Chain.Commit()
+ t.Log("next expected seq num reported", minSeqNum)
+ committedSeqNum = minSeqNum
+ return minSeqNum > uint64(maxSeqNr)
+ }, testutils.WaitTimeout(t), time.Second).Should(gomega.BeTrue(), "report has not been committed")
+ return committedSeqNum
+}
+
+func (c *CCIPIntegrationTestHarness) EventuallySendRequested(t *testing.T, seqNum uint64, onRampOpts ...common.Address) {
+ var onRamp *evm_2_evm_onramp_1_2_0.EVM2EVMOnRamp
+ var err error
+ if len(onRampOpts) > 0 {
+ onRamp, err = evm_2_evm_onramp_1_2_0.NewEVM2EVMOnRamp(onRampOpts[0], c.Source.Chain)
+ require.NoError(t, err)
+ } else {
+ require.NotNil(t, c.Source.OnRamp, "no onRamp configured")
+ onRamp = c.Source.OnRamp
+ }
+ gomega.NewGomegaWithT(t).Eventually(func() bool {
+ it, err := onRamp.FilterCCIPSendRequested(nil)
+ require.NoError(t, err)
+ for it.Next() {
+ if it.Event.Message.SequenceNumber == seqNum {
+ t.Log("sendRequested generated for", seqNum)
+ return true
+ }
+ }
+ c.Source.Chain.Commit()
+ c.Dest.Chain.Commit()
+ return false
+ }, testutils.WaitTimeout(t), time.Second).Should(gomega.BeTrue(), "sendRequested has not been generated")
+}
+
+//nolint:gosec // safe to cast in tests
+func (c *CCIPIntegrationTestHarness) ConsistentlyReportNotCommitted(t *testing.T, maxSeqNr int, commitStoreOpts ...common.Address) {
+ var commitStore *commit_store_1_2_0.CommitStore
+ var err error
+ if len(commitStoreOpts) > 0 {
+ commitStore, err = commit_store_1_2_0.NewCommitStore(commitStoreOpts[0], c.Dest.Chain)
+ require.NoError(t, err)
+ } else {
+ require.NotNil(t, c.Dest.CommitStore, "no commitStore configured")
+ commitStore = c.Dest.CommitStore
+ }
+ gomega.NewGomegaWithT(t).Consistently(func() bool {
+ minSeqNum, err := commitStore.GetExpectedNextSequenceNumber(nil)
+ require.NoError(t, err)
+ c.Source.Chain.Commit()
+ c.Dest.Chain.Commit()
+ t.Log("min seq num reported", minSeqNum)
+ return minSeqNum > uint64(maxSeqNr)
+ }, testutils.WaitTimeout(t), time.Second).Should(gomega.BeFalse(), "report has been committed")
+}
+
+func (c *CCIPIntegrationTestHarness) SetupAndStartNodes(ctx context.Context, t *testing.T, bootstrapNodePort int64) (Node, []Node, int64) {
+ appBootstrap, bootstrapPeerID, bootstrapTransmitter, bootstrapKb := setupNodeCCIP(t, c.Dest.User, bootstrapNodePort,
+ "bootstrap_ccip", c.Source.Chain, c.Dest.Chain, big.NewInt(0).SetUint64(c.Source.ChainID),
+ big.NewInt(0).SetUint64(c.Dest.ChainID), "", 0)
+ var (
+ oracles []confighelper.OracleIdentityExtra
+ nodes []Node
+ )
+ err := appBootstrap.Start(ctx)
+ require.NoError(t, err)
+ t.Cleanup(func() {
+ require.NoError(t, appBootstrap.Stop())
+ })
+ bootstrapNode := Node{
+ App: appBootstrap,
+ Transmitter: bootstrapTransmitter,
+ KeyBundle: bootstrapKb,
+ }
+ // Set up the minimum 4 oracles all funded with destination ETH
+ for i := int64(0); i < 4; i++ {
+ app, peerID, transmitter, kb := setupNodeCCIP(
+ t,
+ c.Dest.User,
+ int64(freeport.GetOne(t)),
+ fmt.Sprintf("oracle_ccip%d", i),
+ c.Source.Chain,
+ c.Dest.Chain,
+ big.NewInt(0).SetUint64(c.Source.ChainID),
+ big.NewInt(0).SetUint64(c.Dest.ChainID),
+ bootstrapPeerID,
+ bootstrapNodePort,
+ )
+ nodes = append(nodes, Node{
+ App: app,
+ Transmitter: transmitter,
+ KeyBundle: kb,
+ })
+ offchainPublicKey, _ := hex.DecodeString(strings.TrimPrefix(kb.OnChainPublicKey(), "0x"))
+ oracles = append(oracles, confighelper.OracleIdentityExtra{
+ OracleIdentity: confighelper.OracleIdentity{
+ OnchainPublicKey: offchainPublicKey,
+ TransmitAccount: types4.Account(transmitter.String()),
+ OffchainPublicKey: kb.OffchainPublicKey(),
+ PeerID: peerID,
+ },
+ ConfigEncryptionPublicKey: kb.ConfigEncryptionPublicKey(),
+ })
+ err = app.Start(ctx)
+ require.NoError(t, err)
+ t.Cleanup(func() {
+ require.NoError(t, app.Stop())
+ })
+ }
+
+ c.Oracles = oracles
+ commitOnchainConfig := c.CreateDefaultCommitOnchainConfig(t)
+ commitOffchainConfig := c.CreateDefaultCommitOffchainConfig(t)
+ execOnchainConfig := c.CreateDefaultExecOnchainConfig(t)
+ execOffchainConfig := c.CreateDefaultExecOffchainConfig(t)
+
+ configBlock := c.SetupOnchainConfig(t, commitOnchainConfig, commitOffchainConfig, execOnchainConfig, execOffchainConfig)
+ c.Nodes = nodes
+ c.Bootstrap = bootstrapNode
+ return bootstrapNode, nodes, configBlock
+}
+
+func (c *CCIPIntegrationTestHarness) SetUpNodesAndJobs(t *testing.T, pricePipeline string, priceGetterConfig string, usdcAttestationAPI string) integrationtesthelpers.CCIPJobSpecParams {
+ // setup Jobs
+ ctx := context.Background()
+ // Starts nodes and configures them in the OCR contracts.
+ bootstrapNode, _, configBlock := c.SetupAndStartNodes(ctx, t, int64(freeport.GetOne(t)))
+
+ jobParams := c.NewCCIPJobSpecParams(pricePipeline, priceGetterConfig, configBlock, usdcAttestationAPI)
+
+ // Add the bootstrap job
+ c.Bootstrap.AddBootstrapJob(t, jobParams.BootstrapJob(c.Dest.CommitStore.Address().Hex()))
+ c.AddAllJobs(t, jobParams)
+
+ // Replay for bootstrap.
+ bc, err := bootstrapNode.App.GetRelayers().LegacyEVMChains().Get(strconv.FormatUint(c.Dest.ChainID, 10))
+ require.NoError(t, err)
+ require.NoError(t, bc.LogPoller().Replay(context.Background(), configBlock))
+ c.Dest.Chain.Commit()
+
+ return jobParams
+}
+
+//nolint:gosec // safe to cast in tests
+func (c *CCIPIntegrationTestHarness) NewCCIPJobSpecParams(tokenPricesUSDPipeline string, priceGetterConfig string, configBlock int64, usdcAttestationAPI string) integrationtesthelpers.CCIPJobSpecParams {
+ return integrationtesthelpers.CCIPJobSpecParams{
+ CommitStore: c.Dest.CommitStore.Address(),
+ OffRamp: c.Dest.OffRamp.Address(),
+ DestEvmChainId: c.Dest.ChainID,
+ SourceChainName: "SimulatedSource",
+ DestChainName: "SimulatedDest",
+ TokenPricesUSDPipeline: tokenPricesUSDPipeline,
+ PriceGetterConfig: priceGetterConfig,
+ DestStartBlock: uint64(configBlock),
+ USDCAttestationAPI: usdcAttestationAPI,
+ }
+}
+
+func DecodeCommitOnChainConfig(encoded []byte) (ccipdata.CommitOnchainConfig, error) {
+ var onchainConfig ccipdata.CommitOnchainConfig
+ unpacked, err := abihelpers.DecodeOCR2Config(encoded)
+ if err != nil {
+ return onchainConfig, err
+ }
+ onChainCfg := unpacked.OnchainConfig
+ onchainConfig, err = abihelpers.DecodeAbiStruct[ccipdata.CommitOnchainConfig](onChainCfg)
+ if err != nil {
+ return onchainConfig, err
+ }
+ return onchainConfig, nil
+}
+
+func DecodeExecOnChainConfig(encoded []byte) (v1_2_0.ExecOnchainConfig, error) {
+ var onchainConfig v1_2_0.ExecOnchainConfig
+ unpacked, err := abihelpers.DecodeOCR2Config(encoded)
+ if err != nil {
+ return onchainConfig, errors.Wrap(err, "failed to unpack log data")
+ }
+ onChainCfg := unpacked.OnchainConfig
+ onchainConfig, err = abihelpers.DecodeAbiStruct[v1_2_0.ExecOnchainConfig](onChainCfg)
+ if err != nil {
+ return onchainConfig, err
+ }
+ return onchainConfig, nil
+}
+
+type ksa struct {
+ keystore.Master
+ csa keystore.CSA
+}
+
+func (k *ksa) CSA() keystore.CSA {
+ return k.csa
+}
+
+func NewKsa(db *sqlx.DB, lggr logger.Logger, csa keystore.CSA) *ksa {
+ return &ksa{
+ Master: keystore.New(db, clutils.FastScryptParams, lggr),
+ csa: csa,
+ }
+}
+
+type NoopFeedsClient struct{}
+
+func (n NoopFeedsClient) ApprovedJob(context.Context, *pb.ApprovedJobRequest) (*pb.ApprovedJobResponse, error) {
+ return &pb.ApprovedJobResponse{}, nil
+}
+
+func (n NoopFeedsClient) Healthcheck(context.Context, *pb.HealthcheckRequest) (*pb.HealthcheckResponse, error) {
+ return &pb.HealthcheckResponse{}, nil
+}
+
+func (n NoopFeedsClient) UpdateNode(context.Context, *pb.UpdateNodeRequest) (*pb.UpdateNodeResponse, error) {
+ return &pb.UpdateNodeResponse{}, nil
+}
+
+func (n NoopFeedsClient) RejectedJob(context.Context, *pb.RejectedJobRequest) (*pb.RejectedJobResponse, error) {
+ return &pb.RejectedJobResponse{}, nil
+}
+
+func (n NoopFeedsClient) CancelledJob(context.Context, *pb.CancelledJobRequest) (*pb.CancelledJobResponse, error) {
+ return &pb.CancelledJobResponse{}, nil
+}
diff --git a/core/services/ocr2/plugins/ccip/testhelpers/testhelpers_1_4_0/config_1_4_0.go b/core/services/ocr2/plugins/ccip/testhelpers/testhelpers_1_4_0/config_1_4_0.go
new file mode 100644
index 00000000000..adfdfd9283e
--- /dev/null
+++ b/core/services/ocr2/plugins/ccip/testhelpers/testhelpers_1_4_0/config_1_4_0.go
@@ -0,0 +1,76 @@
+// Package testhelpers_1_4_0 pkg with set of configs that should be used only within tests suites
+//
+//nolint:revive // used in tests
+package testhelpers_1_4_0
+
+import (
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/require"
+
+ "github.com/smartcontractkit/chainlink-common/pkg/config"
+
+ "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/abihelpers"
+ "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_2_0"
+ "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/testhelpers"
+)
+
+var PermissionLessExecutionThresholdSeconds = uint32(testhelpers.FirstBlockAge.Seconds())
+
+func (c *CCIPContracts) CreateDefaultCommitOnchainConfig(t *testing.T) []byte {
+ config, err := abihelpers.EncodeAbiStruct(ccipdata.CommitOnchainConfig{
+ PriceRegistry: c.Dest.PriceRegistry.Address(),
+ })
+ require.NoError(t, err)
+ return config
+}
+
+func (c *CCIPContracts) CreateDefaultCommitOffchainConfig(t *testing.T) []byte {
+ return c.createCommitOffchainConfig(t, 10*time.Second, 5*time.Second)
+}
+
+func (c *CCIPContracts) createCommitOffchainConfig(t *testing.T, feeUpdateHearBeat time.Duration, inflightCacheExpiry time.Duration) []byte {
+ config, err := NewCommitOffchainConfig(
+ *config.MustNewDuration(feeUpdateHearBeat),
+ 1,
+ 1,
+ *config.MustNewDuration(feeUpdateHearBeat),
+ 1,
+ *config.MustNewDuration(inflightCacheExpiry),
+ false,
+ ).Encode()
+ require.NoError(t, err)
+ return config
+}
+
+func (c *CCIPContracts) CreateDefaultExecOnchainConfig(t *testing.T) []byte {
+ config, err := abihelpers.EncodeAbiStruct(v1_2_0.ExecOnchainConfig{
+ PermissionLessExecutionThresholdSeconds: PermissionLessExecutionThresholdSeconds,
+ Router: c.Dest.Router.Address(),
+ PriceRegistry: c.Dest.PriceRegistry.Address(),
+ MaxDataBytes: 1e5,
+ MaxNumberOfTokensPerMsg: 5,
+ MaxPoolReleaseOrMintGas: 200_000,
+ })
+ require.NoError(t, err)
+ return config
+}
+
+func (c *CCIPContracts) CreateDefaultExecOffchainConfig(t *testing.T) []byte {
+ return c.createExecOffchainConfig(t, 1*time.Minute, 1*time.Minute)
+}
+
+func (c *CCIPContracts) createExecOffchainConfig(t *testing.T, inflightCacheExpiry time.Duration, rootSnoozeTime time.Duration) []byte {
+ config, err := NewExecOffchainConfig(
+ 1,
+ 5_000_000,
+ 0.07,
+ *config.MustNewDuration(inflightCacheExpiry),
+ *config.MustNewDuration(rootSnoozeTime),
+ uint32(0),
+ ).Encode()
+ require.NoError(t, err)
+ return config
+}
diff --git a/core/services/ocr2/plugins/ccip/tokendata/bgworker.go b/core/services/ocr2/plugins/ccip/tokendata/bgworker.go
index bc5aba557e6..458c2e412cc 100644
--- a/core/services/ocr2/plugins/ccip/tokendata/bgworker.go
+++ b/core/services/ocr2/plugins/ccip/tokendata/bgworker.go
@@ -58,7 +58,7 @@ func NewBackgroundWorker(
return &BackgroundWorker{
tokenDataReaders: tokenDataReaders,
numWorkers: numWorkers,
- jobsChan: make(chan cciptypes.EVM2EVMOnRampCCIPSendRequestedWithMeta, numWorkers*100),
+ jobsChan: make(chan cciptypes.EVM2EVMOnRampCCIPSendRequestedWithMeta, numWorkers*200),
resultsCache: cache.New(expirationDur, expirationDur/2),
timeoutDur: timeoutDur,
stopChan: make(services.StopChan),
diff --git a/core/services/ocr2/plugins/ccip/tokendata/http/http_client.go b/core/services/ocr2/plugins/ccip/tokendata/http/http_client.go
index 79ec21b1b83..dd303822c79 100644
--- a/core/services/ocr2/plugins/ccip/tokendata/http/http_client.go
+++ b/core/services/ocr2/plugins/ccip/tokendata/http/http_client.go
@@ -12,18 +12,21 @@ import (
)
type IHttpClient interface {
- // Get issue a GET request to the given url and return the response body and status code.
+ // Get issues a GET request to the given url and returns the response body and status code.
Get(ctx context.Context, url string, timeout time.Duration) ([]byte, int, http.Header, error)
+
+ // Post issues a POST request to the given url with the given request data and returns the response body and status code.
+ Post(ctx context.Context, url string, requestData io.Reader, timeout time.Duration) ([]byte, int, http.Header, error)
}
type HttpClient struct {
}
-func (s *HttpClient) Get(ctx context.Context, url string, timeout time.Duration) ([]byte, int, http.Header, error) {
+func doRequest(ctx context.Context, url string, requestType string, requestBody io.Reader, timeout time.Duration) ([]byte, int, http.Header, error) {
// Use a timeout to guard against attestation API hanging, causing observation timeout and failing to make any progress.
timeoutCtx, cancel := context.WithTimeoutCause(ctx, timeout, tokendata.ErrTimeout)
defer cancel()
- req, err := http.NewRequestWithContext(timeoutCtx, http.MethodGet, url, nil)
+ req, err := http.NewRequestWithContext(timeoutCtx, requestType, url, requestBody)
if err != nil {
return nil, http.StatusBadRequest, nil, err
}
@@ -46,3 +49,11 @@ func (s *HttpClient) Get(ctx context.Context, url string, timeout time.Duration)
body, err := io.ReadAll(res.Body)
return body, res.StatusCode, res.Header, err
}
+
+func (s *HttpClient) Get(ctx context.Context, url string, timeout time.Duration) ([]byte, int, http.Header, error) {
+ return doRequest(ctx, url, http.MethodGet, nil, timeout)
+}
+
+func (s *HttpClient) Post(ctx context.Context, url string, requestBody io.Reader, timeout time.Duration) ([]byte, int, http.Header, error) {
+ return doRequest(ctx, url, http.MethodPost, requestBody, timeout)
+}
diff --git a/core/services/ocr2/plugins/ccip/tokendata/http/observed_http_client.go b/core/services/ocr2/plugins/ccip/tokendata/http/observed_http_client.go
index d8fb9b1c576..bac36abf7ec 100644
--- a/core/services/ocr2/plugins/ccip/tokendata/http/observed_http_client.go
+++ b/core/services/ocr2/plugins/ccip/tokendata/http/observed_http_client.go
@@ -11,7 +11,7 @@ import (
)
var (
- usdcLatencyBuckets = []float64{
+ latencyBuckets = []float64{
float64(10 * time.Millisecond),
float64(25 * time.Millisecond),
float64(50 * time.Millisecond),
@@ -29,7 +29,12 @@ var (
usdcClientHistogram = promauto.NewHistogramVec(prometheus.HistogramOpts{
Name: "ccip_usdc_client_request_total",
Help: "Latency of calls to the USDC client",
- Buckets: usdcLatencyBuckets,
+ Buckets: latencyBuckets,
+ }, []string{"status", "success"})
+ lbtcClientHistogram = promauto.NewHistogramVec(prometheus.HistogramOpts{
+ Name: "ccip_lbtc_client_request_total",
+ Help: "Latency of calls to the LBTC client",
+ Buckets: latencyBuckets,
}, []string{"status", "success"})
)
@@ -38,11 +43,16 @@ type ObservedIHttpClient struct {
histogram *prometheus.HistogramVec
}
-// NewObservedIHttpClient Create a new ObservedIHttpClient with the USDC client metric.
-func NewObservedIHttpClient(origin IHttpClient) *ObservedIHttpClient {
+// NewObservedUsdcIHttpClient Create a new ObservedIHttpClient with the USDC client metric.
+func NewObservedUsdcIHttpClient(origin IHttpClient) *ObservedIHttpClient {
return NewObservedIHttpClientWithMetric(origin, usdcClientHistogram)
}
+// NewObservedLbtcIHttpClient Create a new ObservedIHttpClient with the LBTC client metric.
+func NewObservedLbtcIHttpClient(origin IHttpClient) *ObservedIHttpClient {
+ return NewObservedIHttpClientWithMetric(origin, lbtcClientHistogram)
+}
+
func NewObservedIHttpClientWithMetric(origin IHttpClient, histogram *prometheus.HistogramVec) *ObservedIHttpClient {
return &ObservedIHttpClient{
IHttpClient: origin,
diff --git a/core/services/ocr2/plugins/ccip/tokendata/lbtc/lbtc.go b/core/services/ocr2/plugins/ccip/tokendata/lbtc/lbtc.go
new file mode 100644
index 00000000000..76245ef24a5
--- /dev/null
+++ b/core/services/ocr2/plugins/ccip/tokendata/lbtc/lbtc.go
@@ -0,0 +1,275 @@
+package lbtc
+
+import (
+ "bytes"
+ "context"
+ "encoding/json"
+ "fmt"
+ "net/url"
+ "sync"
+ "time"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/pkg/errors"
+ "golang.org/x/time/rate"
+
+ "github.com/smartcontractkit/chainlink-common/pkg/logger"
+ cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip"
+ "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/abihelpers"
+ "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/tokendata"
+ "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/tokendata/http"
+)
+
+const (
+ apiVersion = "v1"
+ attestationPath = "deposits/getByHash"
+ defaultAttestationTimeout = 5 * time.Second
+
+ // defaultCoolDownDurationSec defines the default time to wait after getting rate limited.
+ // this value is only used if the 429 response does not contain the Retry-After header
+ defaultCoolDownDuration = 30 * time.Second
+
+ // defaultRequestInterval defines the rate in requests per second that the attestation API can be called.
+ // this is set according to the APIs recommended 5 requests per second rate limit.
+ defaultRequestInterval = 200 * time.Millisecond
+
+ // APIIntervalRateLimitDisabled is a special value to disable the rate limiting.
+ APIIntervalRateLimitDisabled = -1
+ // APIIntervalRateLimitDefault is a special value to select the default rate limit interval.
+ APIIntervalRateLimitDefault = 0
+)
+
+type attestationStatus string
+
+const (
+ attestationStatusUnspecified attestationStatus = "NOTARIZATION_STATUS_UNSPECIFIED"
+ attestationStatusPending attestationStatus = "NOTARIZATION_STATUS_PENDING"
+ attestationStatusSubmitted attestationStatus = "NOTARIZATION_STATUS_SUBMITTED"
+ attestationStatusSessionApproved attestationStatus = "NOTARIZATION_STATUS_SESSION_APPROVED"
+ attestationStatusFailed attestationStatus = "NOTARIZATION_STATUS_FAILED"
+)
+
+var (
+ ErrUnknownResponse = errors.New("unexpected response from attestation API")
+)
+
+type TokenDataReader struct {
+ lggr logger.Logger
+ httpClient http.IHttpClient
+ attestationAPI *url.URL
+ attestationAPITimeout time.Duration
+ lbtcTokenAddress common.Address
+ rate *rate.Limiter
+
+ // coolDownUntil defines whether requests are blocked or not.
+ coolDownUntil time.Time
+ coolDownMu *sync.RWMutex
+}
+
+type messageAttestationResponse struct {
+ MessageHash string `json:"message_hash"`
+ Status attestationStatus `json:"status"`
+ Attestation string `json:"attestation,omitempty"` // Attestation represented by abi.encode(payload, proof)
+}
+
+type attestationRequest struct {
+ PayloadHashes []string `json:"messageHash"`
+}
+
+type attestationResponse struct {
+ Attestations []messageAttestationResponse `json:"attestations"`
+}
+
+type sourceTokenData struct {
+ SourcePoolAddress []byte
+ DestTokenAddress []byte
+ ExtraData []byte
+ DestGasAmount uint32
+}
+
+func (m sourceTokenData) AbiString() string {
+ return `[{
+ "components": [
+ {"name": "sourcePoolAddress", "type": "bytes"},
+ {"name": "destTokenAddress", "type": "bytes"},
+ {"name": "extraData", "type": "bytes"},
+ {"name": "destGasAmount", "type": "uint32"}
+ ],
+ "type": "tuple"
+ }]`
+}
+
+func (m sourceTokenData) Validate() error {
+ if len(m.SourcePoolAddress) == 0 {
+ return errors.New("sourcePoolAddress must be non-empty")
+ }
+ if len(m.DestTokenAddress) == 0 {
+ return errors.New("destTokenAddress must be non-empty")
+ }
+ if len(m.ExtraData) == 0 {
+ return errors.New("extraData must be non-empty")
+ }
+ return nil
+}
+
+var _ tokendata.Reader = &TokenDataReader{}
+
+func NewLBTCTokenDataReader(
+ lggr logger.Logger,
+ lbtcAttestationAPI *url.URL,
+ lbtcAttestationAPITimeoutSeconds int,
+ lbtcTokenAddress common.Address,
+ requestInterval time.Duration,
+) *TokenDataReader {
+ timeout := time.Duration(lbtcAttestationAPITimeoutSeconds) * time.Second
+ if lbtcAttestationAPITimeoutSeconds == 0 {
+ timeout = defaultAttestationTimeout
+ }
+
+ if requestInterval == APIIntervalRateLimitDisabled {
+ requestInterval = 0
+ } else if requestInterval == APIIntervalRateLimitDefault {
+ requestInterval = defaultRequestInterval
+ }
+
+ return &TokenDataReader{
+ lggr: lggr,
+ httpClient: http.NewObservedLbtcIHttpClient(&http.HttpClient{}),
+ attestationAPI: lbtcAttestationAPI,
+ attestationAPITimeout: timeout,
+ lbtcTokenAddress: lbtcTokenAddress,
+ coolDownMu: &sync.RWMutex{},
+ rate: rate.NewLimiter(rate.Every(requestInterval), 1),
+ }
+}
+
+func NewLBTCTokenDataReaderWithHTTPClient(
+ origin TokenDataReader,
+ httpClient http.IHttpClient,
+ lbtcTokenAddress common.Address,
+ requestInterval time.Duration,
+) *TokenDataReader {
+ return &TokenDataReader{
+ lggr: origin.lggr,
+ httpClient: httpClient,
+ attestationAPI: origin.attestationAPI,
+ attestationAPITimeout: origin.attestationAPITimeout,
+ coolDownMu: origin.coolDownMu,
+ lbtcTokenAddress: lbtcTokenAddress,
+ rate: rate.NewLimiter(rate.Every(requestInterval), 1),
+ }
+}
+
+// ReadTokenData queries the LBTC attestation API.
+func (s *TokenDataReader) ReadTokenData(ctx context.Context, msg cciptypes.EVM2EVMOnRampCCIPSendRequestedWithMeta, tokenIndex int) ([]byte, error) {
+ if tokenIndex < 0 || tokenIndex >= len(msg.TokenAmounts) {
+ return nil, errors.New("token index out of bounds")
+ }
+
+ if s.inCoolDownPeriod() {
+ // rate limiting cool-down period, we prevent new requests from being sent
+ return nil, tokendata.ErrRequestsBlocked
+ }
+
+ if s.rate != nil {
+ // Wait blocks until it the attestation API can be called or the
+ // context is Done.
+ if waitErr := s.rate.Wait(ctx); waitErr != nil {
+ return nil, fmt.Errorf("lbtc rate limiting error: %w", waitErr)
+ }
+ }
+
+ decodedSourceTokenData, err := abihelpers.DecodeAbiStruct[sourceTokenData](msg.SourceTokenData[tokenIndex])
+ if err != nil {
+ return []byte{}, err
+ }
+ destTokenData := decodedSourceTokenData.ExtraData
+ // We don't have better way to determine if the extraData is a payload or sha256(payload)
+ // Last parameter of the payload struct is 32-bytes nonce (see Lombard's Bridge._deposit(...) method),
+ // so we can assume that payload always exceeds 32 bytes
+ if len(destTokenData) != 32 {
+ s.lggr.Infow("SourceTokenData.extraData size is not 32. This is deposit payload, not sha256(payload). Attestation is disabled onchain",
+ "destTokenData", hexutil.Encode(destTokenData))
+ return destTokenData, nil
+ }
+ payloadHash := [32]byte(destTokenData)
+
+ msgID := hexutil.Encode(msg.MessageID[:])
+ payloadHashHex := hexutil.Encode(payloadHash[:])
+ s.lggr.Infow("Calling attestation API", "messageBodyHash", payloadHashHex, "messageID", msgID)
+
+ attestationResp, err := s.callAttestationAPI(ctx, payloadHash)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed calling lbtc attestation API")
+ }
+ if len(attestationResp.Attestations) == 0 {
+ return nil, errors.New("attestation response is empty")
+ }
+ if len(attestationResp.Attestations) > 1 {
+ s.lggr.Warnw("Multiple attestations received, expected one", "attestations", attestationResp.Attestations)
+ }
+ var attestation messageAttestationResponse
+ for _, attestationCandidate := range attestationResp.Attestations {
+ if attestationCandidate.MessageHash == payloadHashHex {
+ attestation = attestationCandidate
+ }
+ }
+ if attestation == (messageAttestationResponse{}) {
+ return nil, fmt.Errorf("requested attestation %s not found in response", payloadHashHex)
+ }
+ s.lggr.Infow("Got response from attestation API", "messageID", msgID,
+ "attestationStatus", attestation.Status, "attestation", attestation)
+ switch attestation.Status {
+ case attestationStatusSessionApproved:
+ payloadAndProof, err := hexutil.Decode(attestation.Attestation)
+ if err != nil {
+ return nil, err
+ }
+ return payloadAndProof, nil
+ case attestationStatusPending:
+ return nil, tokendata.ErrNotReady
+ case attestationStatusSubmitted:
+ return nil, tokendata.ErrNotReady
+ default:
+ s.lggr.Errorw("Unexpected response from attestation API", "attestation", attestation)
+ return nil, ErrUnknownResponse
+ }
+}
+
+func (s *TokenDataReader) callAttestationAPI(ctx context.Context, lbtcMessageHash [32]byte) (attestationResponse, error) {
+ attestationURL := fmt.Sprintf("%s/bridge/%s/%s", s.attestationAPI.String(), apiVersion, attestationPath)
+ request := attestationRequest{PayloadHashes: []string{hexutil.Encode(lbtcMessageHash[:])}}
+ encodedRequest, err := json.Marshal(request)
+ requestBuffer := bytes.NewBuffer(encodedRequest)
+ if err != nil {
+ return attestationResponse{}, err
+ }
+ respRaw, _, _, err := s.httpClient.Post(ctx, attestationURL, requestBuffer, s.attestationAPITimeout)
+ switch {
+ case errors.Is(err, tokendata.ErrRateLimit):
+ s.setCoolDownPeriod(defaultCoolDownDuration)
+ return attestationResponse{}, tokendata.ErrRateLimit
+ case err != nil:
+ return attestationResponse{}, err
+ }
+ var attestationResp attestationResponse
+ err = json.Unmarshal(respRaw, &attestationResp)
+ return attestationResp, err
+}
+
+func (s *TokenDataReader) setCoolDownPeriod(d time.Duration) {
+ s.coolDownMu.Lock()
+ s.coolDownUntil = time.Now().Add(d)
+ s.coolDownMu.Unlock()
+}
+
+func (s *TokenDataReader) inCoolDownPeriod() bool {
+ s.coolDownMu.RLock()
+ defer s.coolDownMu.RUnlock()
+ return time.Now().Before(s.coolDownUntil)
+}
+
+func (s *TokenDataReader) Close() error {
+ return nil
+}
diff --git a/core/services/ocr2/plugins/ccip/tokendata/lbtc/lbtc_test.go b/core/services/ocr2/plugins/ccip/tokendata/lbtc/lbtc_test.go
new file mode 100644
index 00000000000..375bb62aaeb
--- /dev/null
+++ b/core/services/ocr2/plugins/ccip/tokendata/lbtc/lbtc_test.go
@@ -0,0 +1,490 @@
+package lbtc
+
+import (
+ "context"
+ "encoding/json"
+ "net/http"
+ "net/http/httptest"
+ "net/url"
+ "strings"
+ "sync"
+ "testing"
+ "time"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/pkg/errors"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ "go.uber.org/zap/zapcore"
+
+ cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip"
+ "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/tokendata"
+)
+
+var (
+ lbtcMessageHash = "0xbc427abf571a5cfcf7c98799d1f0055f4db25f203f657d30026728a19d16f092"
+ lbtcMessageAttestation = "0x0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000000e45c70a5050000000000000000000000000000000000000000000000000000000000aa36a7000000000000000000000000845f8e3c214d8d0e4d83fc094f302aa26a12a0bc0000000000000000000000000000000000000000000000000000000000014a34000000000000000000000000845f8e3c214d8d0e4d83fc094f302aa26a12a0bc00000000000000000000000062f10ce5b727edf787ea45776bd050308a61150800000000000000000000000000000000000000000000000000000000000003e60000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000040277eeafba008d767c2636d9428f2ebb13ab29ac70337f4fc34b0f5606767cae546f9be3f12160de6d142e5b3c1c3ebd0bf4298662b32b597d0cc5970c7742fc10000000000000000000000000000000000000000000000000000000000000040bbcd60ecc9e06f2effe7c94161219498a1eb435b419387adadb86ec9a52dfb066ce027532517df7216404049d193a25b85c35edfa3e7c5aa4757bfe84887a3980000000000000000000000000000000000000000000000000000000000000040da4a6dc619b5ca2349783cabecc4efdbc910090d3e234d7b8d0430165f8fae532f9a965ceb85c18bb92e059adefa7ce5835850a705761ab9e026d2db4a13ef9a"
+ payloadAndProof, _ = hexutil.Decode(lbtcMessageAttestation)
+)
+
+func getMockLBTCEndpoint(t *testing.T, response attestationResponse) *httptest.Server {
+ responseBytes, err := json.Marshal(response)
+ require.NoError(t, err)
+
+ return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ _, err := w.Write(responseBytes)
+ //nolint:testifylint // we need to use require here
+ require.NoError(t, err)
+ }))
+}
+
+func TestLBTCReader_callAttestationApi(t *testing.T) {
+ t.Skipf("Skipping test because it uses the real LBTC attestation API")
+ attestationURI, err := url.ParseRequestURI("https://bridge-manager.staging.lombard.finance")
+ require.NoError(t, err)
+ lggr := logger.TestLogger(t)
+ lbtcService := NewLBTCTokenDataReader(lggr, attestationURI, 0, common.Address{}, APIIntervalRateLimitDisabled)
+
+ attestation, err := lbtcService.callAttestationAPI(context.Background(), [32]byte(common.FromHex(lbtcMessageHash)))
+ require.NoError(t, err)
+
+ require.Equal(t, lbtcMessageHash, attestation.Attestations[0].MessageHash)
+ require.Equal(t, attestationStatusSessionApproved, attestation.Attestations[0].Status)
+ require.Equal(t, lbtcMessageAttestation, attestation.Attestations[0].Attestation)
+}
+
+func TestLBTCReader_callAttestationApiMock(t *testing.T) {
+ response := attestationResponse{
+ Attestations: []messageAttestationResponse{
+ {
+ MessageHash: lbtcMessageHash,
+ Status: attestationStatusSessionApproved,
+ Attestation: lbtcMessageAttestation,
+ },
+ },
+ }
+
+ ts := getMockLBTCEndpoint(t, response)
+ defer ts.Close()
+ attestationURI, err := url.ParseRequestURI(ts.URL)
+ require.NoError(t, err)
+
+ lggr := logger.TestLogger(t)
+ lbtcService := NewLBTCTokenDataReader(lggr, attestationURI, 0, common.Address{}, APIIntervalRateLimitDisabled)
+ attestation, err := lbtcService.callAttestationAPI(context.Background(), [32]byte(common.FromHex(lbtcMessageHash)))
+ require.NoError(t, err)
+
+ require.Equal(t, response.Attestations[0].Status, attestation.Attestations[0].Status)
+ require.Equal(t, response.Attestations[0].Attestation, attestation.Attestations[0].Attestation)
+}
+
+func TestLBTCReader_callAttestationApiMockError(t *testing.T) {
+ t.Parallel()
+
+ sessionApprovedResponse := attestationResponse{
+ Attestations: []messageAttestationResponse{
+ {
+ MessageHash: lbtcMessageHash,
+ Status: attestationStatusSessionApproved,
+ Attestation: lbtcMessageAttestation,
+ },
+ },
+ }
+
+ tests := []struct {
+ name string
+ getTs func() *httptest.Server
+ parentTimeoutSeconds int
+ customTimeoutSeconds int
+ expectedError error
+ }{
+ {
+ name: "server error",
+ getTs: func() *httptest.Server {
+ return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.WriteHeader(http.StatusInternalServerError)
+ }))
+ },
+ parentTimeoutSeconds: 60,
+ expectedError: nil,
+ },
+ {
+ name: "default timeout",
+ getTs: func() *httptest.Server {
+ responseBytes, _ := json.Marshal(sessionApprovedResponse)
+
+ return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ time.Sleep(defaultAttestationTimeout + time.Second)
+ _, err := w.Write(responseBytes)
+ //nolint:testifylint // we need to use require here
+ require.NoError(t, err)
+ }))
+ },
+ parentTimeoutSeconds: 60,
+ expectedError: tokendata.ErrTimeout,
+ },
+ {
+ name: "custom timeout",
+ getTs: func() *httptest.Server {
+ responseBytes, _ := json.Marshal(sessionApprovedResponse)
+
+ return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ time.Sleep(2*time.Second + time.Second)
+ _, err := w.Write(responseBytes)
+ //nolint:testifylint // we need to use require here
+ require.NoError(t, err)
+ }))
+ },
+ parentTimeoutSeconds: 60,
+ customTimeoutSeconds: 2,
+ expectedError: tokendata.ErrTimeout,
+ },
+ {
+ name: "rate limit",
+ getTs: func() *httptest.Server {
+ return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.WriteHeader(http.StatusTooManyRequests)
+ }))
+ },
+ parentTimeoutSeconds: 60,
+ expectedError: tokendata.ErrRateLimit,
+ },
+ {
+ name: "parent context timeout",
+ getTs: func() *httptest.Server {
+ return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ time.Sleep(defaultAttestationTimeout + time.Second)
+ }))
+ },
+ parentTimeoutSeconds: 1,
+ expectedError: nil,
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ ts := test.getTs()
+ defer ts.Close()
+
+ attestationURI, err := url.ParseRequestURI(ts.URL)
+ require.NoError(t, err)
+
+ lggr := logger.TestLogger(t)
+ lbtcService := NewLBTCTokenDataReader(lggr, attestationURI, test.customTimeoutSeconds, common.Address{}, APIIntervalRateLimitDisabled)
+
+ parentCtx, cancel := context.WithTimeout(context.Background(), time.Duration(test.parentTimeoutSeconds)*time.Second)
+ defer cancel()
+
+ _, err = lbtcService.callAttestationAPI(parentCtx, [32]byte(common.FromHex(lbtcMessageHash)))
+ require.Error(t, err)
+
+ if test.expectedError != nil {
+ require.True(t, errors.Is(err, test.expectedError))
+ }
+ })
+ }
+}
+
+func TestLBTCReader_rateLimiting(t *testing.T) {
+ sessionApprovedResponse := attestationResponse{
+ Attestations: []messageAttestationResponse{
+ {
+ MessageHash: lbtcMessageHash,
+ Status: attestationStatusSessionApproved,
+ Attestation: lbtcMessageAttestation,
+ },
+ },
+ }
+
+ testCases := []struct {
+ name string
+ requests uint64
+ rateConfig time.Duration
+ testDuration time.Duration
+ timeout time.Duration
+ err string
+ additionalErr string
+ }{
+ {
+ name: "no rate limit when disabled",
+ requests: 10,
+ rateConfig: APIIntervalRateLimitDisabled,
+ testDuration: 1 * time.Millisecond,
+ },
+ {
+ name: "yes rate limited with default config",
+ requests: 5,
+ rateConfig: APIIntervalRateLimitDefault,
+ testDuration: 4 * defaultRequestInterval,
+ },
+ {
+ name: "yes rate limited with config",
+ requests: 10,
+ rateConfig: 50 * time.Millisecond,
+ testDuration: 9 * 50 * time.Millisecond,
+ },
+ {
+ name: "request timeout",
+ requests: 5,
+ rateConfig: 100 * time.Millisecond,
+ testDuration: 1 * time.Millisecond,
+ timeout: 1 * time.Millisecond,
+ err: "lbtc rate limiting error:",
+ additionalErr: "token data API timed out",
+ },
+ }
+
+ extraData, err := hexutil.Decode(lbtcMessageHash)
+ require.NoError(t, err)
+
+ srcTokenData, err := abihelpers.EncodeAbiStruct[sourceTokenData](sourceTokenData{
+ SourcePoolAddress: utils.RandomAddress().Bytes(),
+ DestTokenAddress: utils.RandomAddress().Bytes(),
+ ExtraData: extraData,
+ })
+ require.NoError(t, err)
+
+ for _, tc := range testCases {
+ tc := tc
+ t.Run(tc.name, func(t *testing.T) {
+ t.Parallel()
+
+ ts := getMockLBTCEndpoint(t, sessionApprovedResponse)
+ defer ts.Close()
+ attestationURI, err := url.ParseRequestURI(ts.URL)
+ require.NoError(t, err)
+
+ lggr := logger.TestLogger(t)
+ lbtcService := NewLBTCTokenDataReader(lggr, attestationURI, 0, utils.RandomAddress(), tc.rateConfig)
+
+ ctx := context.Background()
+ if tc.timeout > 0 {
+ var cf context.CancelFunc
+ ctx, cf = context.WithTimeout(ctx, tc.timeout)
+ defer cf()
+ }
+
+ trigger := make(chan struct{})
+ errorChan := make(chan error, tc.requests)
+ wg := sync.WaitGroup{}
+ for i := uint64(0); i < tc.requests; i++ {
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+
+ <-trigger
+ _, err := lbtcService.ReadTokenData(ctx, cciptypes.EVM2EVMOnRampCCIPSendRequestedWithMeta{
+ EVM2EVMMessage: cciptypes.EVM2EVMMessage{
+ SourceTokenData: [][]byte{srcTokenData},
+ TokenAmounts: []cciptypes.TokenAmount{{Token: ccipcalc.EvmAddrToGeneric(utils.ZeroAddress), Amount: nil}}, // trigger failure due to wrong address
+ },
+ }, 0)
+
+ errorChan <- err
+ }()
+ }
+
+ // Start the test
+ start := time.Now()
+ close(trigger)
+
+ // Wait for requests to complete
+ wg.Wait()
+ finish := time.Now()
+ close(errorChan)
+
+ // Collect errors
+ errorFound := false
+ for err := range errorChan {
+ //nolint:gocritic // easier to read using ifElse instead of switch
+ if tc.err != "" && strings.Contains(err.Error(), tc.err) {
+ errorFound = true
+ } else if tc.additionalErr != "" && strings.Contains(err.Error(), tc.additionalErr) {
+ errorFound = true
+ } else if err != nil {
+ require.Fail(t, "unexpected error", err)
+ }
+ }
+
+ if tc.err != "" {
+ assert.True(t, errorFound)
+ }
+ assert.WithinDuration(t, start.Add(tc.testDuration), finish, 50*time.Millisecond)
+ })
+ }
+}
+
+func TestLBTCReader_skipApiOnFullPayload(t *testing.T) {
+ sessionApprovedResponse := attestationResponse{
+ Attestations: []messageAttestationResponse{
+ {
+ MessageHash: lbtcMessageHash,
+ Status: attestationStatusSessionApproved,
+ Attestation: lbtcMessageAttestation,
+ },
+ },
+ }
+
+ srcTokenData, err := abihelpers.EncodeAbiStruct[sourceTokenData](sourceTokenData{
+ SourcePoolAddress: utils.RandomAddress().Bytes(),
+ DestTokenAddress: utils.RandomAddress().Bytes(),
+ ExtraData: []byte(lbtcMessageHash), // more than 32 bytes
+ })
+ require.NoError(t, err)
+
+ ts := getMockLBTCEndpoint(t, sessionApprovedResponse)
+ defer ts.Close()
+ attestationURI, err := url.ParseRequestURI(ts.URL)
+ require.NoError(t, err)
+
+ lggr, logs := logger.TestLoggerObserved(t, zapcore.InfoLevel)
+ lbtcService := NewLBTCTokenDataReader(lggr, attestationURI, 0, utils.RandomAddress(), APIIntervalRateLimitDefault)
+
+ ctx := context.Background()
+
+ destTokenData, err := lbtcService.ReadTokenData(ctx, cciptypes.EVM2EVMOnRampCCIPSendRequestedWithMeta{
+ EVM2EVMMessage: cciptypes.EVM2EVMMessage{
+ SourceTokenData: [][]byte{srcTokenData},
+ TokenAmounts: []cciptypes.TokenAmount{{Token: ccipcalc.EvmAddrToGeneric(utils.ZeroAddress), Amount: nil}}, // trigger failure due to wrong address
+ },
+ }, 0)
+ require.NoError(t, err)
+ require.EqualValues(t, []byte(lbtcMessageHash), destTokenData)
+
+ require.Equal(t, 1, logs.Len())
+ require.Contains(t, logs.All()[0].Message, "SourceTokenData.extraData size is not 32. This is deposit payload, not sha256(payload). Attestation is disabled onchain")
+}
+
+func TestLBTCReader_expectedOutput(t *testing.T) {
+ t.Parallel()
+
+ tests := []struct {
+ name string
+ response attestationResponse
+ expectedReturn []byte
+ expectedError error
+ }{
+ {
+ name: "expected payloadAndProof when status SESSION_APPROVED",
+ response: attestationResponse{
+ Attestations: []messageAttestationResponse{
+ {
+ MessageHash: lbtcMessageHash,
+ Status: attestationStatusSessionApproved,
+ Attestation: lbtcMessageAttestation,
+ },
+ },
+ },
+ expectedReturn: payloadAndProof,
+ expectedError: nil,
+ },
+ {
+ name: "expected ErrNotReady on status PENDING",
+ response: attestationResponse{
+ Attestations: []messageAttestationResponse{
+ {
+ MessageHash: lbtcMessageHash,
+ Status: attestationStatusPending,
+ Attestation: lbtcMessageAttestation,
+ },
+ },
+ },
+ expectedReturn: nil,
+ expectedError: tokendata.ErrNotReady,
+ },
+ {
+ name: "expected ErrNotReady on status SUBMITTED",
+ response: attestationResponse{
+ Attestations: []messageAttestationResponse{
+ {
+ MessageHash: lbtcMessageHash,
+ Status: attestationStatusSubmitted,
+ Attestation: lbtcMessageAttestation,
+ },
+ },
+ },
+ expectedReturn: nil,
+ expectedError: tokendata.ErrNotReady,
+ },
+ {
+ name: "expected ErrUnknownResponse on status UNSPECIFIED",
+ response: attestationResponse{
+ Attestations: []messageAttestationResponse{
+ {
+ MessageHash: lbtcMessageHash,
+ Status: attestationStatusUnspecified,
+ Attestation: lbtcMessageAttestation,
+ },
+ },
+ },
+ expectedReturn: nil,
+ expectedError: ErrUnknownResponse,
+ },
+ {
+ name: "expected ErrUnknownResponse on status FAILED",
+ response: attestationResponse{
+ Attestations: []messageAttestationResponse{
+ {
+ MessageHash: lbtcMessageHash,
+ Status: attestationStatusFailed,
+ Attestation: lbtcMessageAttestation,
+ },
+ },
+ },
+ expectedReturn: nil,
+ expectedError: ErrUnknownResponse,
+ },
+ }
+
+ extraData, err := hexutil.Decode(lbtcMessageHash)
+ require.NoError(t, err)
+
+ srcTokenData, err := abihelpers.EncodeAbiStruct[sourceTokenData](sourceTokenData{
+ SourcePoolAddress: utils.RandomAddress().Bytes(),
+ DestTokenAddress: utils.RandomAddress().Bytes(),
+ ExtraData: extraData,
+ })
+ require.NoError(t, err)
+
+ for _, tc := range tests {
+ t.Run(tc.name, func(t *testing.T) {
+ ts := getMockLBTCEndpoint(t, tc.response)
+ defer ts.Close()
+ attestationURI, err := url.ParseRequestURI(ts.URL)
+ require.NoError(t, err)
+
+ lggr := logger.TestLogger(t)
+ lbtcService := NewLBTCTokenDataReader(lggr, attestationURI, 0, utils.RandomAddress(), APIIntervalRateLimitDefault)
+
+ ctx := context.Background()
+
+ payloadAndProof, err := lbtcService.ReadTokenData(ctx, cciptypes.EVM2EVMOnRampCCIPSendRequestedWithMeta{
+ EVM2EVMMessage: cciptypes.EVM2EVMMessage{
+ SourceTokenData: [][]byte{srcTokenData},
+ TokenAmounts: []cciptypes.TokenAmount{{Token: ccipcalc.EvmAddrToGeneric(utils.ZeroAddress), Amount: nil}}, // trigger failure due to wrong address
+ },
+ }, 0)
+
+ if tc.expectedReturn != nil {
+ require.EqualValues(t, tc.expectedReturn, payloadAndProof)
+ } else if tc.expectedError != nil {
+ require.Contains(t, err.Error(), tc.expectedError.Error())
+ }
+ })
+ }
+}
+
+func Test_DecodeSourceTokenData(t *testing.T) {
+ input, err := hexutil.Decode("0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000249f00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000267d40f64ecc4d95f3e8b2237df5f37b10812c250000000000000000000000000000000000000000000000000000000000000020000000000000000000000000c47e4b3124597fdf8dd07843d4a7052f2ee80c3000000000000000000000000000000000000000000000000000000000000000e45c70a5050000000000000000000000000000000000000000000000000000000000aa36a7000000000000000000000000845f8e3c214d8d0e4d83fc094f302aa26a12a0bc0000000000000000000000000000000000000000000000000000000000014a34000000000000000000000000845f8e3c214d8d0e4d83fc094f302aa26a12a0bc00000000000000000000000062f10ce5b727edf787ea45776bd050308a61150800000000000000000000000000000000000000000000000000000000000003e6000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000")
+ require.NoError(t, err)
+ decoded, err := abihelpers.DecodeAbiStruct[sourceTokenData](input)
+ require.NoError(t, err)
+ expected, err := hexutil.Decode("0x5c70a5050000000000000000000000000000000000000000000000000000000000aa36a7000000000000000000000000845f8e3c214d8d0e4d83fc094f302aa26a12a0bc0000000000000000000000000000000000000000000000000000000000014a34000000000000000000000000845f8e3c214d8d0e4d83fc094f302aa26a12a0bc00000000000000000000000062f10ce5b727edf787ea45776bd050308a61150800000000000000000000000000000000000000000000000000000000000003e60000000000000000000000000000000000000000000000000000000000000006")
+ require.NoError(t, err)
+ require.Equal(t, expected, decoded.ExtraData)
+}
diff --git a/core/services/ocr2/plugins/ccip/tokendata/usdc/usdc.go b/core/services/ocr2/plugins/ccip/tokendata/usdc/usdc.go
index aaa6086fbc9..9e27ebcc59e 100644
--- a/core/services/ocr2/plugins/ccip/tokendata/usdc/usdc.go
+++ b/core/services/ocr2/plugins/ccip/tokendata/usdc/usdc.go
@@ -133,7 +133,7 @@ func NewUSDCTokenDataReader(
return &TokenDataReader{
lggr: lggr,
usdcReader: usdcReader,
- httpClient: http.NewObservedIHttpClient(&http.HttpClient{}),
+ httpClient: http.NewObservedUsdcIHttpClient(&http.HttpClient{}),
attestationApi: usdcAttestationApi,
attestationApiTimeout: timeout,
usdcTokenAddress: usdcTokenAddress,
diff --git a/core/services/relay/evm/ccip.go b/core/services/relay/evm/ccip.go
index a06f60c6fd4..9fc6ae8b1a0 100644
--- a/core/services/relay/evm/ccip.go
+++ b/core/services/relay/evm/ccip.go
@@ -15,6 +15,7 @@ import (
"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"
+ "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/estimatorconfig"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/prices"
)
@@ -24,16 +25,18 @@ var _ cciptypes.CommitStoreReader = (*IncompleteDestCommitStoreReader)(nil)
// IncompleteSourceCommitStoreReader is an implementation of CommitStoreReader with the only valid methods being
// GasPriceEstimator, ChangeConfig, and OffchainConfig
type IncompleteSourceCommitStoreReader struct {
- estimator gas.EvmFeeEstimator
- gasPriceEstimator *prices.DAGasPriceEstimator
- sourceMaxGasPrice *big.Int
- offchainConfig cciptypes.CommitOffchainConfig
+ estimator gas.EvmFeeEstimator
+ gasPriceEstimator *prices.DAGasPriceEstimator
+ sourceMaxGasPrice *big.Int
+ offchainConfig cciptypes.CommitOffchainConfig
+ feeEstimatorConfig estimatorconfig.FeeEstimatorConfigProvider
}
-func NewIncompleteSourceCommitStoreReader(estimator gas.EvmFeeEstimator, sourceMaxGasPrice *big.Int) *IncompleteSourceCommitStoreReader {
+func NewIncompleteSourceCommitStoreReader(estimator gas.EvmFeeEstimator, sourceMaxGasPrice *big.Int, feeEstimatorConfig estimatorconfig.FeeEstimatorConfigProvider) *IncompleteSourceCommitStoreReader {
return &IncompleteSourceCommitStoreReader{
- estimator: estimator,
- sourceMaxGasPrice: sourceMaxGasPrice,
+ estimator: estimator,
+ sourceMaxGasPrice: sourceMaxGasPrice,
+ feeEstimatorConfig: feeEstimatorConfig,
}
}
@@ -53,6 +56,7 @@ func (i *IncompleteSourceCommitStoreReader) ChangeConfig(ctx context.Context, on
i.sourceMaxGasPrice,
int64(offchainConfigParsed.ExecGasPriceDeviationPPB),
int64(offchainConfigParsed.DAGasPriceDeviationPPB),
+ i.feeEstimatorConfig,
)
i.offchainConfig = ccip.NewCommitOffchainConfig(
offchainConfigParsed.ExecGasPriceDeviationPPB,
@@ -131,8 +135,16 @@ type IncompleteDestCommitStoreReader struct {
cs cciptypes.CommitStoreReader
}
-func NewIncompleteDestCommitStoreReader(ctx context.Context, lggr logger.Logger, versionFinder ccip.VersionFinder, address cciptypes.Address, ec client.Client, lp logpoller.LogPoller) (*IncompleteDestCommitStoreReader, error) {
- cs, err := ccip.NewCommitStoreReader(ctx, lggr, versionFinder, address, ec, lp)
+func NewIncompleteDestCommitStoreReader(
+ ctx context.Context,
+ lggr logger.Logger,
+ versionFinder ccip.VersionFinder,
+ address cciptypes.Address,
+ ec client.Client,
+ lp logpoller.LogPoller,
+ feeEstimatorConfig estimatorconfig.FeeEstimatorConfigProvider,
+) (*IncompleteDestCommitStoreReader, error) {
+ cs, err := ccip.NewCommitStoreReader(ctx, lggr, versionFinder, address, ec, lp, feeEstimatorConfig)
if err != nil {
return nil, err
}
diff --git a/core/services/relay/evm/commit_provider.go b/core/services/relay/evm/commit_provider.go
index 95d7371ab16..6be639674e2 100644
--- a/core/services/relay/evm/commit_provider.go
+++ b/core/services/relay/evm/commit_provider.go
@@ -14,24 +14,25 @@ import (
"github.com/smartcontractkit/chainlink-common/pkg/logger"
commontypes "github.com/smartcontractkit/chainlink-common/pkg/types"
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/router"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip"
+ "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/estimatorconfig"
)
var _ commontypes.CCIPCommitProvider = (*SrcCommitProvider)(nil)
var _ commontypes.CCIPCommitProvider = (*DstCommitProvider)(nil)
type SrcCommitProvider struct {
- lggr logger.Logger
- startBlock uint64
- client client.Client
- lp logpoller.LogPoller
- estimator gas.EvmFeeEstimator
- maxGasPrice *big.Int
+ lggr logger.Logger
+ startBlock uint64
+ client client.Client
+ lp logpoller.LogPoller
+ estimator gas.EvmFeeEstimator
+ maxGasPrice *big.Int
+ feeEstimatorConfig estimatorconfig.FeeEstimatorConfigProvider
// these values will be lazily initialized
seenOnRampAddress *cciptypes.Address
@@ -46,14 +47,16 @@ func NewSrcCommitProvider(
lp logpoller.LogPoller,
srcEstimator gas.EvmFeeEstimator,
maxGasPrice *big.Int,
+ feeEstimatorConfig estimatorconfig.FeeEstimatorConfigProvider,
) commontypes.CCIPCommitProvider {
return &SrcCommitProvider{
- lggr: logger.Named(lggr, "SrcCommitProvider"),
- startBlock: startBlock,
- client: client,
- lp: lp,
- estimator: srcEstimator,
- maxGasPrice: maxGasPrice,
+ lggr: logger.Named(lggr, "SrcCommitProvider"),
+ startBlock: startBlock,
+ client: client,
+ lp: lp,
+ estimator: srcEstimator,
+ maxGasPrice: maxGasPrice,
+ feeEstimatorConfig: feeEstimatorConfig,
}
}
@@ -67,6 +70,7 @@ type DstCommitProvider struct {
configWatcher *configWatcher
gasEstimator gas.EvmFeeEstimator
maxGasPrice big.Int
+ feeEstimatorConfig estimatorconfig.FeeEstimatorConfigProvider
// these values will be lazily initialized
seenCommitStoreAddress *cciptypes.Address
@@ -83,6 +87,7 @@ func NewDstCommitProvider(
maxGasPrice big.Int,
contractTransmitter contractTransmitter,
configWatcher *configWatcher,
+ feeEstimatorConfig estimatorconfig.FeeEstimatorConfigProvider,
) commontypes.CCIPCommitProvider {
return &DstCommitProvider{
lggr: logger.Named(lggr, "DstCommitProvider"),
@@ -94,6 +99,7 @@ func NewDstCommitProvider(
configWatcher: configWatcher,
gasEstimator: gasEstimator,
maxGasPrice: maxGasPrice,
+ feeEstimatorConfig: feeEstimatorConfig,
}
}
@@ -173,13 +179,13 @@ func (p *DstCommitProvider) Close() error {
if p.seenCommitStoreAddress == nil {
return nil
}
- return ccip.CloseCommitStoreReader(ctx, p.lggr, versionFinder, *p.seenCommitStoreAddress, p.client, p.lp)
+ return ccip.CloseCommitStoreReader(ctx, p.lggr, versionFinder, *p.seenCommitStoreAddress, p.client, p.lp, p.feeEstimatorConfig)
})
unregisterFuncs = append(unregisterFuncs, func(ctx context.Context) error {
if p.seenOffRampAddress == nil {
return nil
}
- return ccip.CloseOffRampReader(ctx, p.lggr, versionFinder, *p.seenOffRampAddress, p.client, p.lp, nil, big.NewInt(0))
+ return ccip.CloseOffRampReader(ctx, p.lggr, versionFinder, *p.seenOffRampAddress, p.client, p.lp, nil, big.NewInt(0), p.feeEstimatorConfig)
})
var multiErr error
@@ -250,7 +256,7 @@ func (p *DstCommitProvider) NewPriceGetter(ctx context.Context) (priceGetter cci
}
func (p *SrcCommitProvider) NewCommitStoreReader(ctx context.Context, commitStoreAddress cciptypes.Address) (commitStoreReader cciptypes.CommitStoreReader, err error) {
- commitStoreReader = NewIncompleteSourceCommitStoreReader(p.estimator, p.maxGasPrice)
+ commitStoreReader = NewIncompleteSourceCommitStoreReader(p.estimator, p.maxGasPrice, p.feeEstimatorConfig)
return
}
@@ -258,7 +264,7 @@ func (p *DstCommitProvider) NewCommitStoreReader(ctx context.Context, commitStor
p.seenCommitStoreAddress = &commitStoreAddress
versionFinder := ccip.NewEvmVersionFinder()
- commitStoreReader, err = NewIncompleteDestCommitStoreReader(ctx, p.lggr, versionFinder, commitStoreAddress, p.client, p.lp)
+ commitStoreReader, err = NewIncompleteDestCommitStoreReader(ctx, p.lggr, versionFinder, commitStoreAddress, p.client, p.lp, p.feeEstimatorConfig)
return
}
@@ -268,7 +274,12 @@ func (p *SrcCommitProvider) NewOnRampReader(ctx context.Context, onRampAddress c
p.seenDestChainSelector = &destChainSelector
versionFinder := ccip.NewEvmVersionFinder()
+
onRampReader, err = ccip.NewOnRampReader(ctx, p.lggr, versionFinder, sourceChainSelector, destChainSelector, onRampAddress, p.lp, p.client)
+ if err != nil {
+ return nil, err
+ }
+ p.feeEstimatorConfig.SetOnRampReader(onRampReader)
return
}
@@ -281,7 +292,7 @@ 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(ctx, p.lggr, p.versionFinder, offRampAddr, p.client, p.lp, p.gasEstimator, &p.maxGasPrice, true)
+ offRampReader, err = ccip.NewOffRampReader(ctx, p.lggr, p.versionFinder, offRampAddr, p.client, p.lp, p.gasEstimator, &p.maxGasPrice, true, p.feeEstimatorConfig)
return
}
diff --git a/core/services/relay/evm/evm.go b/core/services/relay/evm/evm.go
index e60dbe1bfdb..cf79d2aea59 100644
--- a/core/services/relay/evm/evm.go
+++ b/core/services/relay/evm/evm.go
@@ -50,6 +50,8 @@ import (
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/ccipcommit"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/ccipexec"
ccipconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/config"
+ "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/estimatorconfig"
+ "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/estimatorconfig/interceptors/mantle"
cciptransmitter "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/transmitter"
lloconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/llo/config"
mercuryconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/mercury/config"
@@ -217,7 +219,7 @@ func NewRelayer(ctx context.Context, lggr logger.Logger, chain legacyevm.Chain,
relayer := &Relayer{
ds: opts.DS,
chain: chain,
- lggr: sugared,
+ lggr: logger.Sugared(sugared),
registerer: opts.Registerer,
ks: opts.CSAETHKeystore,
mercuryPool: opts.MercuryPool,
@@ -505,6 +507,183 @@ func (r *Relayer) NewMercuryProvider(ctx context.Context, rargs commontypes.Rela
return NewMercuryProvider(cp, r.codec, NewMercuryChainReader(r.chain.HeadTracker()), transmitter, reportCodecV1, reportCodecV2, reportCodecV3, reportCodecV4, lggr), nil
}
+func chainToUUID(chainID *big.Int) uuid.UUID {
+ // See https://www.rfc-editor.org/rfc/rfc4122.html#section-4.1.3 for the list of supported versions.
+ const VersionSHA1 = 5
+ var buf bytes.Buffer
+ buf.WriteString("CCIP:")
+ buf.Write(chainID.Bytes())
+ // We use SHA-256 instead of SHA-1 because the former has better collision resistance.
+ // The UUID will contain only the first 16 bytes of the hash.
+ // You can't say which algorithms was used just by looking at the UUID bytes.
+ return uuid.NewHash(sha256.New(), uuid.NameSpaceOID, buf.Bytes(), VersionSHA1)
+}
+
+// NewCCIPCommitProvider constructs a provider of type CCIPCommitProvider. Since this is happening in the Relayer,
+// which lives in a separate process from delegate which is requesting a provider, we need to wire in through pargs
+// which *type* (impl) of CCIPCommitProvider should be created. CCIP is currently a special case where the provider has a
+// subset of implementations of the complete interface as certain contracts in a CCIP lane are only deployed on the src
+// chain or on the dst chain. This results in the two implementations of providers: a src and dst implementation.
+func (r *Relayer) NewCCIPCommitProvider(ctx context.Context, rargs commontypes.RelayArgs, pargs commontypes.PluginArgs) (commontypes.CCIPCommitProvider, error) {
+ versionFinder := ccip.NewEvmVersionFinder()
+
+ var commitPluginConfig ccipconfig.CommitPluginConfig
+ err := json.Unmarshal(pargs.PluginConfig, &commitPluginConfig)
+ if err != nil {
+ return nil, err
+ }
+ sourceStartBlock := commitPluginConfig.SourceStartBlock
+ destStartBlock := commitPluginConfig.DestStartBlock
+
+ feeEstimatorConfig := estimatorconfig.NewFeeEstimatorConfigService()
+
+ // CCIPCommit reads only when source chain is Mantle, then reports to dest chain
+ // to minimize misconfigure risk, might make sense to wire Mantle only when Commit + Mantle + IsSourceProvider
+ if r.chain.Config().EVM().ChainID().Uint64() == 5003 || r.chain.Config().EVM().ChainID().Uint64() == 5000 {
+ if commitPluginConfig.IsSourceProvider {
+ mantleInterceptor, iErr := mantle.NewInterceptor(ctx, r.chain.Client())
+ if iErr != nil {
+ return nil, iErr
+ }
+ feeEstimatorConfig.AddGasPriceInterceptor(mantleInterceptor)
+ }
+ }
+
+ // The src chain implementation of this provider does not need a configWatcher or contractTransmitter;
+ // bail early.
+ if commitPluginConfig.IsSourceProvider {
+ return NewSrcCommitProvider(
+ r.lggr,
+ sourceStartBlock,
+ r.chain.Client(),
+ r.chain.LogPoller(),
+ r.chain.GasEstimator(),
+ r.chain.Config().EVM().GasEstimator().PriceMax().ToInt(),
+ feeEstimatorConfig,
+ ), nil
+ }
+
+ relayOpts := types.NewRelayOpts(rargs)
+ configWatcher, err := newStandardConfigProvider(ctx, r.lggr, r.chain, relayOpts)
+ if err != nil {
+ return nil, err
+ }
+ address := common.HexToAddress(relayOpts.ContractID)
+ typ, ver, err := ccipconfig.TypeAndVersion(address, r.chain.Client())
+ if err != nil {
+ return nil, err
+ }
+ fn, err := ccipcommit.CommitReportToEthTxMeta(typ, ver)
+ if err != nil {
+ return nil, err
+ }
+ subjectID := chainToUUID(configWatcher.chain.ID())
+ contractTransmitter, err := newOnChainContractTransmitter(ctx, r.lggr, rargs, r.ks.Eth(), configWatcher, configTransmitterOpts{
+ subjectID: &subjectID,
+ }, OCR2AggregatorTransmissionContractABI, WithReportToEthMetadata(fn), WithRetention(0))
+ if err != nil {
+ return nil, err
+ }
+
+ return NewDstCommitProvider(
+ r.lggr,
+ versionFinder,
+ destStartBlock,
+ r.chain.Client(),
+ r.chain.LogPoller(),
+ r.chain.GasEstimator(),
+ *r.chain.Config().EVM().GasEstimator().PriceMax().ToInt(),
+ *contractTransmitter,
+ configWatcher,
+ feeEstimatorConfig,
+ ), nil
+}
+
+// NewCCIPExecProvider constructs a provider of type CCIPExecProvider. Since this is happening in the Relayer,
+// which lives in a separate process from delegate which is requesting a provider, we need to wire in through pargs
+// which *type* (impl) of CCIPExecProvider should be created. CCIP is currently a special case where the provider has a
+// subset of implementations of the complete interface as certain contracts in a CCIP lane are only deployed on the src
+// chain or on the dst chain. This results in the two implementations of providers: a src and dst implementation.
+func (r *Relayer) NewCCIPExecProvider(ctx context.Context, rargs commontypes.RelayArgs, pargs commontypes.PluginArgs) (commontypes.CCIPExecProvider, error) {
+ versionFinder := ccip.NewEvmVersionFinder()
+
+ var execPluginConfig ccipconfig.ExecPluginConfig
+ err := json.Unmarshal(pargs.PluginConfig, &execPluginConfig)
+ if err != nil {
+ return nil, err
+ }
+
+ feeEstimatorConfig := estimatorconfig.NewFeeEstimatorConfigService()
+
+ // CCIPExec reads when dest chain is mantle, and uses it to calc boosting in batching
+ // to minimize misconfigure risk, make sense to wire Mantle only when Exec + Mantle + !IsSourceProvider
+ if r.chain.Config().EVM().ChainID().Uint64() == 5003 || r.chain.Config().EVM().ChainID().Uint64() == 5000 {
+ if !execPluginConfig.IsSourceProvider {
+ mantleInterceptor, iErr := mantle.NewInterceptor(ctx, r.chain.Client())
+ if iErr != nil {
+ return nil, iErr
+ }
+ feeEstimatorConfig.AddGasPriceInterceptor(mantleInterceptor)
+ }
+ }
+
+ // The src chain implementation of this provider does not need a configWatcher or contractTransmitter;
+ // bail early.
+ if execPluginConfig.IsSourceProvider {
+ return NewSrcExecProvider(
+ ctx,
+ r.lggr,
+ versionFinder,
+ r.chain.Client(),
+ r.chain.GasEstimator(),
+ r.chain.Config().EVM().GasEstimator().PriceMax().ToInt(),
+ r.chain.LogPoller(),
+ execPluginConfig.SourceStartBlock,
+ execPluginConfig.JobID,
+ execPluginConfig.USDCConfig,
+ execPluginConfig.LBTCConfig,
+ feeEstimatorConfig,
+ )
+ }
+
+ relayOpts := types.NewRelayOpts(rargs)
+ configWatcher, err := newStandardConfigProvider(ctx, r.lggr, r.chain, relayOpts)
+ if err != nil {
+ return nil, err
+ }
+ address := common.HexToAddress(relayOpts.ContractID)
+ typ, ver, err := ccipconfig.TypeAndVersion(address, r.chain.Client())
+ if err != nil {
+ return nil, err
+ }
+ fn, err := ccipexec.ExecReportToEthTxMeta(ctx, typ, ver)
+ if err != nil {
+ return nil, err
+ }
+ subjectID := chainToUUID(configWatcher.chain.ID())
+ contractTransmitter, err := newOnChainContractTransmitter(ctx, r.lggr, rargs, r.ks.Eth(), configWatcher, configTransmitterOpts{
+ subjectID: &subjectID,
+ }, OCR2AggregatorTransmissionContractABI, WithReportToEthMetadata(fn), WithRetention(0), WithExcludeSignatures())
+ if err != nil {
+ return nil, err
+ }
+
+ return NewDstExecProvider(
+ r.lggr,
+ versionFinder,
+ r.chain.Client(),
+ r.chain.LogPoller(),
+ execPluginConfig.DestStartBlock,
+ contractTransmitter,
+ configWatcher,
+ r.chain.GasEstimator(),
+ *r.chain.Config().EVM().GasEstimator().PriceMax().ToInt(),
+ feeEstimatorConfig,
+ r.chain.TxManager(),
+ cciptypes.Address(rargs.ContractID),
+ )
+}
+
func (r *Relayer) NewLLOProvider(ctx context.Context, rargs commontypes.RelayArgs, pargs commontypes.PluginArgs) (commontypes.LLOProvider, error) {
relayOpts := types.NewRelayOpts(rargs)
var relayConfig types.RelayConfig
@@ -949,155 +1128,6 @@ func (r *Relayer) NewAutomationProvider(ctx context.Context, rargs commontypes.R
return ocr2keeperRelayer.NewOCR2KeeperProvider(ctx, rargs, pargs)
}
-func chainToUUID(chainID *big.Int) uuid.UUID {
- // See https://www.rfc-editor.org/rfc/rfc4122.html#section-4.1.3 for the list of supported versions.
- const VersionSHA1 = 5
- var buf bytes.Buffer
- buf.WriteString("CCIP:")
- buf.Write(chainID.Bytes())
- // We use SHA-256 instead of SHA-1 because the former has better collision resistance.
- // The UUID will contain only the first 16 bytes of the hash.
- // You can't say which algorithms was used just by looking at the UUID bytes.
- return uuid.NewHash(sha256.New(), uuid.NameSpaceOID, buf.Bytes(), VersionSHA1)
-}
-
-// NewCCIPCommitProvider constructs a provider of type CCIPCommitProvider. Since this is happening in the Relayer,
-// which lives in a separate process from delegate which is requesting a provider, we need to wire in through pargs
-// which *type* (impl) of CCIPCommitProvider should be created. CCIP is currently a special case where the provider has a
-// subset of implementations of the complete interface as certain contracts in a CCIP lane are only deployed on the src
-// chain or on the dst chain. This results in the two implementations of providers: a src and dst implementation.
-func (r *Relayer) NewCCIPCommitProvider(ctx context.Context, rargs commontypes.RelayArgs, pargs commontypes.PluginArgs) (commontypes.CCIPCommitProvider, error) {
- versionFinder := ccip.NewEvmVersionFinder()
-
- var commitPluginConfig ccipconfig.CommitPluginConfig
- err := json.Unmarshal(pargs.PluginConfig, &commitPluginConfig)
- if err != nil {
- return nil, err
- }
- sourceStartBlock := commitPluginConfig.SourceStartBlock
- destStartBlock := commitPluginConfig.DestStartBlock
-
- // The src chain implementation of this provider does not need a configWatcher or contractTransmitter;
- // bail early.
- if commitPluginConfig.IsSourceProvider {
- return NewSrcCommitProvider(
- r.lggr,
- sourceStartBlock,
- r.chain.Client(),
- r.chain.LogPoller(),
- r.chain.GasEstimator(),
- r.chain.Config().EVM().GasEstimator().PriceMax().ToInt(),
- ), nil
- }
-
- relayOpts := types.NewRelayOpts(rargs)
- configWatcher, err := newStandardConfigProvider(ctx, r.lggr, r.chain, relayOpts)
- if err != nil {
- return nil, err
- }
- address := common.HexToAddress(relayOpts.ContractID)
- typ, ver, err := ccipconfig.TypeAndVersion(address, r.chain.Client())
- if err != nil {
- return nil, err
- }
- fn, err := ccipcommit.CommitReportToEthTxMeta(typ, ver)
- if err != nil {
- return nil, err
- }
- subjectID := chainToUUID(configWatcher.chain.ID())
- contractTransmitter, err := newOnChainContractTransmitter(ctx, r.lggr, rargs, r.ks.Eth(), configWatcher, configTransmitterOpts{
- subjectID: &subjectID,
- }, OCR2AggregatorTransmissionContractABI, WithReportToEthMetadata(fn), WithRetention(0))
- if err != nil {
- return nil, err
- }
-
- return NewDstCommitProvider(
- r.lggr,
- versionFinder,
- destStartBlock,
- r.chain.Client(),
- r.chain.LogPoller(),
- r.chain.GasEstimator(),
- *r.chain.Config().EVM().GasEstimator().PriceMax().ToInt(),
- *contractTransmitter,
- configWatcher,
- ), nil
-}
-
-// NewCCIPExecProvider constructs a provider of type CCIPExecProvider. Since this is happening in the Relayer,
-// which lives in a separate process from delegate which is requesting a provider, we need to wire in through pargs
-// which *type* (impl) of CCIPExecProvider should be created. CCIP is currently a special case where the provider has a
-// subset of implementations of the complete interface as certain contracts in a CCIP lane are only deployed on the src
-// chain or on the dst chain. This results in the two implementations of providers: a src and dst implementation.
-func (r *Relayer) NewCCIPExecProvider(ctx context.Context, rargs commontypes.RelayArgs, pargs commontypes.PluginArgs) (commontypes.CCIPExecProvider, error) {
- versionFinder := ccip.NewEvmVersionFinder()
-
- var execPluginConfig ccipconfig.ExecPluginConfig
- err := json.Unmarshal(pargs.PluginConfig, &execPluginConfig)
- if err != nil {
- return nil, err
- }
-
- usdcConfig := execPluginConfig.USDCConfig
-
- // The src chain implementation of this provider does not need a configWatcher or contractTransmitter;
- // bail early.
- if execPluginConfig.IsSourceProvider {
- return NewSrcExecProvider(
- ctx,
- r.lggr,
- versionFinder,
- r.chain.Client(),
- r.chain.GasEstimator(),
- r.chain.Config().EVM().GasEstimator().PriceMax().ToInt(),
- r.chain.LogPoller(),
- execPluginConfig.SourceStartBlock,
- execPluginConfig.JobID,
- usdcConfig.AttestationAPI,
- int(usdcConfig.AttestationAPITimeoutSeconds),
- usdcConfig.AttestationAPIIntervalMilliseconds,
- usdcConfig.SourceMessageTransmitterAddress,
- )
- }
-
- relayOpts := types.NewRelayOpts(rargs)
- configWatcher, err := newStandardConfigProvider(ctx, r.lggr, r.chain, relayOpts)
- if err != nil {
- return nil, err
- }
- address := common.HexToAddress(relayOpts.ContractID)
- typ, ver, err := ccipconfig.TypeAndVersion(address, r.chain.Client())
- if err != nil {
- return nil, err
- }
- fn, err := ccipexec.ExecReportToEthTxMeta(ctx, typ, ver)
- if err != nil {
- return nil, err
- }
- subjectID := chainToUUID(configWatcher.chain.ID())
- contractTransmitter, err := newOnChainContractTransmitter(ctx, r.lggr, rargs, r.ks.Eth(), configWatcher, configTransmitterOpts{
- subjectID: &subjectID,
- }, OCR2AggregatorTransmissionContractABI, WithReportToEthMetadata(fn), WithRetention(0))
- if err != nil {
- return nil, err
- }
-
- return NewDstExecProvider(
- r.lggr,
- versionFinder,
- r.chain.Client(),
- r.chain.LogPoller(),
- execPluginConfig.DestStartBlock,
- contractTransmitter,
- configWatcher,
- r.chain.GasEstimator(),
- *r.chain.Config().EVM().GasEstimator().PriceMax().ToInt(),
- r.chain.TxManager(),
- cciptypes.Address(rargs.ContractID),
- )
-}
-
var _ commontypes.MedianProvider = (*medianProvider)(nil)
type medianProvider struct {
diff --git a/core/services/relay/evm/exec_provider.go b/core/services/relay/evm/exec_provider.go
index da190d20356..e5b00205caa 100644
--- a/core/services/relay/evm/exec_provider.go
+++ b/core/services/relay/evm/exec_provider.go
@@ -10,13 +10,14 @@ import (
"go.uber.org/multierr"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
- "github.com/ethereum/go-ethereum/common"
ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types"
"github.com/smartcontractkit/chainlink-common/pkg/logger"
"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/v2/core/services/ocr2/plugins/ccip/config"
+ "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/tokendata/lbtc"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/client"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas"
@@ -24,22 +25,25 @@ import (
"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/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"
)
type SrcExecProvider struct {
- lggr logger.Logger
- versionFinder ccip.VersionFinder
- client client.Client
- lp logpoller.LogPoller
- startBlock uint64
- estimator gas.EvmFeeEstimator
- maxGasPrice *big.Int
- usdcReader *ccip.USDCReaderImpl
- usdcAttestationAPI string
- usdcAttestationAPITimeoutSeconds int
- usdcAttestationAPIIntervalMilliseconds int
- usdcSrcMsgTransmitterAddr common.Address
+ lggr logger.Logger
+ versionFinder ccip.VersionFinder
+ client client.Client
+ lp logpoller.LogPoller
+ startBlock uint64
+ estimator gas.EvmFeeEstimator
+ maxGasPrice *big.Int
+ usdcReader *ccip.USDCReaderImpl
+ usdcConfig config.USDCConfig
+ lbtcConfig config.LBTCConfig
+
+ feeEstimatorConfig estimatorconfig.FeeEstimatorConfigProvider
+
+ // TODO: Add lbtc reader & api fields
// these values are nil and are updated for Close()
seenOnRampAddress *cciptypes.Address
@@ -57,33 +61,31 @@ func NewSrcExecProvider(
lp logpoller.LogPoller,
startBlock uint64,
jobID string,
- usdcAttestationAPI string,
- usdcAttestationAPITimeoutSeconds int,
- usdcAttestationAPIIntervalMilliseconds int,
- usdcSrcMsgTransmitterAddr common.Address,
+ usdcConfig config.USDCConfig,
+ lbtcConfig config.LBTCConfig,
+ feeEstimatorConfig estimatorconfig.FeeEstimatorConfigProvider,
) (commontypes.CCIPExecProvider, error) {
var usdcReader *ccip.USDCReaderImpl
var err error
- if usdcAttestationAPI != "" {
- usdcReader, err = ccip.NewUSDCReader(ctx, lggr, jobID, usdcSrcMsgTransmitterAddr, lp, true)
+ if usdcConfig.AttestationAPI != "" {
+ usdcReader, err = ccip.NewUSDCReader(ctx, lggr, jobID, usdcConfig.SourceMessageTransmitterAddress, lp, true)
if err != nil {
return nil, fmt.Errorf("new usdc reader: %w", err)
}
}
return &SrcExecProvider{
- lggr: logger.Named(lggr, "SrcExecProvider"),
- versionFinder: versionFinder,
- client: client,
- estimator: estimator,
- maxGasPrice: maxGasPrice,
- lp: lp,
- startBlock: startBlock,
- usdcReader: usdcReader,
- usdcAttestationAPI: usdcAttestationAPI,
- usdcAttestationAPITimeoutSeconds: usdcAttestationAPITimeoutSeconds,
- usdcAttestationAPIIntervalMilliseconds: usdcAttestationAPIIntervalMilliseconds,
- usdcSrcMsgTransmitterAddr: usdcSrcMsgTransmitterAddr,
+ lggr: logger.Named(lggr, "SrcExecProvider"),
+ versionFinder: versionFinder,
+ client: client,
+ estimator: estimator,
+ maxGasPrice: maxGasPrice,
+ lp: lp,
+ startBlock: startBlock,
+ usdcReader: usdcReader,
+ usdcConfig: usdcConfig,
+ lbtcConfig: lbtcConfig,
+ feeEstimatorConfig: feeEstimatorConfig,
}, nil
}
@@ -113,10 +115,10 @@ func (s *SrcExecProvider) Close() error {
return ccip.CloseOnRampReader(ctx, s.lggr, versionFinder, *s.seenSourceChainSelector, *s.seenDestChainSelector, *s.seenOnRampAddress, s.lp, s.client)
})
unregisterFuncs = append(unregisterFuncs, func(ctx context.Context) error {
- if s.usdcAttestationAPI == "" {
+ if s.usdcConfig.AttestationAPI == "" {
return nil
}
- return ccip.CloseUSDCReader(ctx, s.lggr, s.lggr.Name(), s.usdcSrcMsgTransmitterAddr, s.lp)
+ return ccip.CloseUSDCReader(ctx, s.lggr, s.lggr.Name(), s.usdcConfig.SourceMessageTransmitterAddress, s.lp)
})
var multiErr error
for _, fn := range unregisterFuncs {
@@ -166,7 +168,7 @@ func (s *SrcExecProvider) GetTransactionStatus(ctx context.Context, transactionI
}
func (s *SrcExecProvider) NewCommitStoreReader(ctx context.Context, addr cciptypes.Address) (commitStoreReader cciptypes.CommitStoreReader, err error) {
- commitStoreReader = NewIncompleteSourceCommitStoreReader(s.estimator, s.maxGasPrice)
+ commitStoreReader = NewIncompleteSourceCommitStoreReader(s.estimator, s.maxGasPrice, s.feeEstimatorConfig)
return
}
@@ -179,6 +181,10 @@ func (s *SrcExecProvider) NewOnRampReader(ctx context.Context, onRampAddress cci
versionFinder := ccip.NewEvmVersionFinder()
onRampReader, err = ccip.NewOnRampReader(ctx, s.lggr, versionFinder, sourceChainSelector, destChainSelector, onRampAddress, s.lp, s.client)
+ if err != nil {
+ return nil, err
+ }
+ s.feeEstimatorConfig.SetOnRampReader(onRampReader)
return
}
@@ -188,24 +194,42 @@ func (s *SrcExecProvider) NewPriceRegistryReader(ctx context.Context, addr ccipt
return
}
-func (s *SrcExecProvider) NewTokenDataReader(ctx context.Context, tokenAddress cciptypes.Address) (tokenDataReader cciptypes.TokenDataReader, err error) {
- attestationURI, err2 := url.ParseRequestURI(s.usdcAttestationAPI)
- if err2 != nil {
- return nil, fmt.Errorf("failed to parse USDC attestation API: %w", err2)
+func (s *SrcExecProvider) NewTokenDataReader(ctx context.Context, tokenAddress cciptypes.Address) (cciptypes.TokenDataReader, error) {
+ tokenAddr, err := ccip.GenericAddrToEvm(tokenAddress)
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse token address: %w", err)
}
- tokenAddr, err2 := ccip.GenericAddrToEvm(tokenAddress)
- if err2 != nil {
- return nil, fmt.Errorf("failed to parse token address: %w", err2)
+ switch tokenAddr {
+ case s.usdcConfig.SourceTokenAddress:
+ attestationURI, err := url.ParseRequestURI(s.usdcConfig.AttestationAPI)
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse USDC attestation API: %w", err)
+ }
+ return usdc.NewUSDCTokenDataReader(
+ s.lggr,
+ s.usdcReader,
+ attestationURI,
+ //nolint:gosec // integer overflow
+ int(s.usdcConfig.AttestationAPITimeoutSeconds),
+ tokenAddr,
+ time.Duration(s.usdcConfig.AttestationAPIIntervalMilliseconds)*time.Millisecond,
+ ), nil
+ case s.lbtcConfig.SourceTokenAddress:
+ attestationURI, err := url.ParseRequestURI(s.lbtcConfig.AttestationAPI)
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse USDC attestation API: %w", err)
+ }
+ return lbtc.NewLBTCTokenDataReader(
+ s.lggr,
+ attestationURI,
+ //nolint:gosec // integer overflow
+ int(s.lbtcConfig.AttestationAPITimeoutSeconds),
+ tokenAddr,
+ time.Duration(s.lbtcConfig.AttestationAPIIntervalMilliseconds)*time.Millisecond,
+ ), nil
+ default:
+ return nil, fmt.Errorf("unsupported token address: %s", tokenAddress)
}
- tokenDataReader = usdc.NewUSDCTokenDataReader(
- s.lggr,
- s.usdcReader,
- attestationURI,
- s.usdcAttestationAPITimeoutSeconds,
- tokenAddr,
- time.Duration(s.usdcAttestationAPIIntervalMilliseconds)*time.Millisecond,
- )
- return
}
func (s *SrcExecProvider) NewTokenPoolBatchedReader(ctx context.Context, offRampAddr cciptypes.Address, sourceChainSelector uint64) (cciptypes.TokenPoolBatchedReader, error) {
@@ -239,6 +263,7 @@ type DstExecProvider struct {
configWatcher *configWatcher
gasEstimator gas.EvmFeeEstimator
maxGasPrice big.Int
+ feeEstimatorConfig estimatorconfig.FeeEstimatorConfigProvider
txm txmgr.TxManager
offRampAddress cciptypes.Address
@@ -256,6 +281,7 @@ func NewDstExecProvider(
configWatcher *configWatcher,
gasEstimator gas.EvmFeeEstimator,
maxGasPrice big.Int,
+ feeEstimatorConfig estimatorconfig.FeeEstimatorConfigProvider,
txm txmgr.TxManager,
offRampAddress cciptypes.Address,
) (commontypes.CCIPExecProvider, error) {
@@ -269,6 +295,7 @@ func NewDstExecProvider(
configWatcher: configWatcher,
gasEstimator: gasEstimator,
maxGasPrice: maxGasPrice,
+ feeEstimatorConfig: feeEstimatorConfig,
txm: txm,
offRampAddress: offRampAddress,
}, nil
@@ -299,10 +326,10 @@ func (d *DstExecProvider) Close() error {
if d.seenCommitStoreAddr == nil {
return nil
}
- return ccip.CloseCommitStoreReader(ctx, d.lggr, versionFinder, *d.seenCommitStoreAddr, d.client, d.lp)
+ return ccip.CloseCommitStoreReader(ctx, d.lggr, versionFinder, *d.seenCommitStoreAddr, d.client, d.lp, d.feeEstimatorConfig)
})
unregisterFuncs = append(unregisterFuncs, func(ctx context.Context) error {
- return ccip.CloseOffRampReader(ctx, d.lggr, versionFinder, d.offRampAddress, d.client, d.lp, nil, big.NewInt(0))
+ return ccip.CloseOffRampReader(ctx, d.lggr, versionFinder, d.offRampAddress, d.client, d.lp, nil, big.NewInt(0), d.feeEstimatorConfig)
})
var multiErr error
@@ -350,12 +377,12 @@ func (d *DstExecProvider) NewCommitStoreReader(ctx context.Context, addr cciptyp
d.seenCommitStoreAddr = &addr
versionFinder := ccip.NewEvmVersionFinder()
- commitStoreReader, err = NewIncompleteDestCommitStoreReader(ctx, d.lggr, versionFinder, addr, d.client, d.lp)
+ commitStoreReader, err = NewIncompleteDestCommitStoreReader(ctx, d.lggr, versionFinder, addr, d.client, d.lp, d.feeEstimatorConfig)
return
}
func (d *DstExecProvider) NewOffRampReader(ctx context.Context, offRampAddress cciptypes.Address) (offRampReader cciptypes.OffRampReader, err error) {
- offRampReader, err = ccip.NewOffRampReader(ctx, d.lggr, d.versionFinder, offRampAddress, d.client, d.lp, d.gasEstimator, &d.maxGasPrice, true)
+ offRampReader, err = ccip.NewOffRampReader(ctx, d.lggr, d.versionFinder, offRampAddress, d.client, d.lp, d.gasEstimator, &d.maxGasPrice, true, d.feeEstimatorConfig)
return
}
diff --git a/core/services/relay/evm/interceptors/mantle/interceptor.go b/core/services/relay/evm/interceptors/mantle/interceptor.go
new file mode 100644
index 00000000000..c1520652d67
--- /dev/null
+++ b/core/services/relay/evm/interceptors/mantle/interceptor.go
@@ -0,0 +1,81 @@
+package mantle
+
+import (
+ "context"
+ "fmt"
+ "math/big"
+ "strings"
+ "time"
+
+ "github.com/ethereum/go-ethereum"
+ "github.com/ethereum/go-ethereum/accounts/abi"
+
+ evmClient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client"
+ "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils"
+)
+
+const (
+ // tokenRatio is not volatile and can be requested not often.
+ tokenRatioUpdateInterval = 60 * time.Minute
+ // tokenRatio fetches the tokenRatio used for Mantle's gas price calculation
+ tokenRatioMethod = "tokenRatio"
+ mantleTokenRatioAbiString = `[{"inputs":[],"name":"tokenRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]`
+)
+
+type Interceptor struct {
+ client evmClient.Client
+ tokenRatioCallData []byte
+ tokenRatio *big.Int
+ tokenRatioLastUpdate time.Time
+}
+
+func NewInterceptor(_ context.Context, client evmClient.Client) (*Interceptor, error) {
+ // 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 Mantle; %w", tokenRatioMethod, err)
+ }
+ tokenRatioCallData, err := tokenRatioMethodAbi.Pack(tokenRatioMethod)
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse GasPriceOracle %s() calldata for Mantle; %w", tokenRatioMethod, err)
+ }
+
+ return &Interceptor{
+ client: client,
+ tokenRatioCallData: tokenRatioCallData,
+ }, nil
+}
+
+// ModifyGasPriceComponents returns modified gasPrice.
+func (i *Interceptor) ModifyGasPriceComponents(ctx context.Context, execGasPrice, daGasPrice *big.Int) (*big.Int, *big.Int, error) {
+ if time.Since(i.tokenRatioLastUpdate) > tokenRatioUpdateInterval {
+ mantleTokenRatio, err := i.getMantleTokenRatio(ctx)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ i.tokenRatio, i.tokenRatioLastUpdate = mantleTokenRatio, time.Now()
+ }
+
+ // multiply daGasPrice and execGas price by tokenRatio
+ newExecGasPrice := new(big.Int).Mul(execGasPrice, i.tokenRatio)
+ newDAGasPrice := new(big.Int).Mul(daGasPrice, i.tokenRatio)
+ return newExecGasPrice, newDAGasPrice, nil
+}
+
+// getMantleTokenRatio Requests and returns a token ratio value for the Mantle chain.
+func (i *Interceptor) getMantleTokenRatio(ctx context.Context) (*big.Int, error) {
+ // FIXME it's removed from chainlink repo
+ // precompile := common.HexToAddress(rollups.OPGasOracleAddress)
+ precompile := utils.RandomAddress()
+ tokenRatio, err := i.client.CallContract(ctx, ethereum.CallMsg{
+ To: &precompile,
+ Data: i.tokenRatioCallData,
+ }, nil)
+
+ if err != nil {
+ return nil, fmt.Errorf("getMantleTokenRatio call failed: %w", err)
+ }
+
+ return new(big.Int).SetBytes(tokenRatio), nil
+}
diff --git a/core/services/relay/evm/interceptors/mantle/interceptor_test.go b/core/services/relay/evm/interceptors/mantle/interceptor_test.go
new file mode 100644
index 00000000000..9134d996c27
--- /dev/null
+++ b/core/services/relay/evm/interceptors/mantle/interceptor_test.go
@@ -0,0 +1,96 @@
+package mantle
+
+import (
+ "context"
+ "math/big"
+ "testing"
+
+ "github.com/ethereum/go-ethereum"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/stretchr/testify/mock"
+ "github.com/stretchr/testify/require"
+
+ "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks"
+)
+
+func TestInterceptor(t *testing.T) {
+ ethClient := mocks.NewClient(t)
+ ctx := context.Background()
+
+ tokenRatio := big.NewInt(10)
+ interceptor, err := NewInterceptor(ctx, ethClient)
+ require.NoError(t, err)
+
+ // request token ratio
+ ethClient.On("CallContract", ctx, mock.IsType(ethereum.CallMsg{}), mock.IsType(&big.Int{})).
+ Return(common.BigToHash(tokenRatio).Bytes(), nil).Once()
+
+ modExecGasPrice, modDAGasPrice, err := interceptor.ModifyGasPriceComponents(ctx, big.NewInt(1), big.NewInt(1))
+ require.NoError(t, err)
+ require.Equal(t, int64(10), modExecGasPrice.Int64())
+ require.Equal(t, int64(10), modDAGasPrice.Int64())
+
+ // second call won't invoke eth client
+ modExecGasPrice, modDAGasPrice, err = interceptor.ModifyGasPriceComponents(ctx, big.NewInt(2), big.NewInt(1))
+ require.NoError(t, err)
+ require.Equal(t, int64(20), modExecGasPrice.Int64())
+ require.Equal(t, int64(10), modDAGasPrice.Int64())
+}
+
+func TestModifyGasPriceComponents(t *testing.T) {
+ testCases := map[string]struct {
+ execGasPrice *big.Int
+ daGasPrice *big.Int
+ tokenRatio *big.Int
+ resultExecGasPrice *big.Int
+ resultDAGasPrice *big.Int
+ }{
+ "regular": {
+ execGasPrice: big.NewInt(1000),
+ daGasPrice: big.NewInt(100),
+ resultExecGasPrice: big.NewInt(2000),
+ resultDAGasPrice: big.NewInt(200),
+ tokenRatio: big.NewInt(2),
+ },
+ "zero DAGasPrice": {
+ execGasPrice: big.NewInt(1000),
+ daGasPrice: big.NewInt(0),
+ resultExecGasPrice: big.NewInt(5000),
+ resultDAGasPrice: big.NewInt(0),
+ tokenRatio: big.NewInt(5),
+ },
+ "zero ExecGasPrice": {
+ execGasPrice: big.NewInt(0),
+ daGasPrice: big.NewInt(10),
+ resultExecGasPrice: big.NewInt(0),
+ resultDAGasPrice: big.NewInt(50),
+ tokenRatio: big.NewInt(5),
+ },
+ "zero token ratio": {
+ execGasPrice: big.NewInt(15),
+ daGasPrice: big.NewInt(10),
+ resultExecGasPrice: big.NewInt(0),
+ resultDAGasPrice: big.NewInt(0),
+ tokenRatio: big.NewInt(0),
+ },
+ }
+
+ for tcName, tc := range testCases {
+ t.Run(tcName, func(t *testing.T) {
+ ethClient := mocks.NewClient(t)
+ ctx := context.Background()
+
+ interceptor, err := NewInterceptor(ctx, ethClient)
+ require.NoError(t, err)
+
+ // request token ratio
+ ethClient.On("CallContract", ctx, mock.IsType(ethereum.CallMsg{}), mock.IsType(&big.Int{})).
+ Return(common.BigToHash(tc.tokenRatio).Bytes(), nil).Once()
+
+ modExecGasPrice, modDAGasPrice, err := interceptor.ModifyGasPriceComponents(ctx, tc.execGasPrice, tc.daGasPrice)
+ require.NoError(t, err)
+ require.Equal(t, tc.resultExecGasPrice.Int64(), modExecGasPrice.Int64())
+ require.Equal(t, tc.resultDAGasPrice.Int64(), modDAGasPrice.Int64())
+ })
+ }
+}
From 6a91c2851de12708c4a7e64a5c1178d63fdd1333 Mon Sep 17 00:00:00 2001
From: Anindita Ghosh <88458927+AnieeG@users.noreply.github.com>
Date: Fri, 10 Jan 2025 13:22:20 -0800
Subject: [PATCH 33/91] Ccip-4110 migration test (#15854)
* remove deployCCIPContracts
* deprecate existing add lane
* new migration test
* first version
* more updates
* fix tests
* more fixes
* include in pipeline
* enabling test
* upgrade chainlink ccip fixing router binding issues in migration
* upgrade chainlink ccip fixing router binding issues in migration
* review comments
* review comments
* review comments
* fix
* fix imports
* upgrade ccip
* fix
---------
Co-authored-by: asoliman
---
.github/integration-in-memory-tests.yml | 7 +
core/scripts/go.mod | 2 +-
core/scripts/go.sum | 4 +-
.../ccip/testhelpers/integration/jobspec.go | 4 +-
.../ccip/changeset/accept_ownership_test.go | 2 +-
.../changeset/cs_active_candidate_test.go | 3 +-
deployment/ccip/changeset/cs_add_lane_test.go | 84 +----
.../ccip/changeset/cs_ccip_home_test.go | 8 +-
.../ccip/changeset/cs_chain_contracts.go | 10 +-
.../ccip/changeset/cs_chain_contracts_test.go | 10 +-
.../ccip/changeset/cs_deploy_chain_test.go | 2 +-
.../ccip/changeset/cs_home_chain_test.go | 4 +-
.../changeset/cs_update_rmn_config_test.go | 19 +-
deployment/ccip/changeset/state_test.go | 2 +-
deployment/ccip/changeset/test_environment.go | 165 +++++----
deployment/ccip/changeset/test_params.go | 6 +-
deployment/ccip/changeset/v1_5/e2e_test.go | 4 +-
deployment/ccip/changeset/view_test.go | 2 +-
.../transfer_to_mcms_with_timelock.go | 2 +-
deployment/go.mod | 2 +-
deployment/go.sum | 4 +-
go.mod | 2 +-
go.sum | 4 +-
.../contracts/ccipreader_test.go | 10 +-
integration-tests/go.mod | 2 +-
integration-tests/go.sum | 4 +-
integration-tests/load/go.mod | 2 +-
integration-tests/load/go.sum | 4 +-
.../smoke/ccip/ccip_batching_test.go | 2 +-
.../smoke/ccip/ccip_fee_boosting_test.go | 2 +-
.../smoke/ccip/ccip_fees_test.go | 2 +-
.../smoke/ccip/ccip_gas_price_updates_test.go | 2 +-
.../smoke/ccip/ccip_legacy_test.go | 51 ---
.../ccip/ccip_message_limitations_test.go | 2 +-
.../smoke/ccip/ccip_messaging_test.go | 2 +-
.../ccip/ccip_migration_to_v_1_6_test.go | 317 ++++++++++++++++++
.../smoke/ccip/ccip_ooo_execution_test.go | 2 +-
integration-tests/smoke/ccip/ccip_rmn_test.go | 2 +-
.../ccip/ccip_token_price_updates_test.go | 2 +-
.../smoke/ccip/ccip_token_transfer_test.go | 2 +-
.../smoke/ccip/ccip_usdc_test.go | 2 +-
.../testsetups/ccip/test_helpers.go | 60 ++--
42 files changed, 551 insertions(+), 273 deletions(-)
delete mode 100644 integration-tests/smoke/ccip/ccip_legacy_test.go
create mode 100644 integration-tests/smoke/ccip/ccip_migration_to_v_1_6_test.go
diff --git a/.github/integration-in-memory-tests.yml b/.github/integration-in-memory-tests.yml
index 9550d74f21f..ef0bf95c596 100644
--- a/.github/integration-in-memory-tests.yml
+++ b/.github/integration-in-memory-tests.yml
@@ -7,6 +7,13 @@
#
runner-test-matrix:
# START: CCIPv1.6 tests
+ - id: smoke/ccip/ccip_migration_to_v_1_6_test.go:*
+ path: integration-tests/smoke/ccip/ccip_migration_to_v_1_6_test.go.go
+ test_env_type: in-memory
+ runs_on: ubuntu-latest
+ triggers:
+ - PR Integration CCIP Tests
+ test_cmd: cd integration-tests/smoke/ccip && go test ccip_migration_to_v_1_6_test.go -timeout 12m -test.parallel=1 -count=1 -json
- id: smoke/ccip/ccip_fees_test.go:*
path: integration-tests/smoke/ccip/ccip_fees_test.go
diff --git a/core/scripts/go.mod b/core/scripts/go.mod
index 7d32653538d..b86baf9a203 100644
--- a/core/scripts/go.mod
+++ b/core/scripts/go.mod
@@ -306,7 +306,7 @@ require (
github.com/shirou/gopsutil/v3 v3.24.3 // indirect
github.com/smartcontractkit/ccip-owner-contracts v0.0.0-salt-fix // indirect
github.com/smartcontractkit/chain-selectors v1.0.36 // indirect
- github.com/smartcontractkit/chainlink-ccip v0.0.0-20250109124515-ff9d86b874ba // indirect
+ github.com/smartcontractkit/chainlink-ccip v0.0.0-20250110181647-9dba278f2103 // indirect
github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250103152858-8973fd0c912b // indirect
github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e // indirect
github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241216163550-fa030d178ba3 // indirect
diff --git a/core/scripts/go.sum b/core/scripts/go.sum
index 6494f578fdf..7cb29867b3a 100644
--- a/core/scripts/go.sum
+++ b/core/scripts/go.sum
@@ -1158,8 +1158,8 @@ github.com/smartcontractkit/chain-selectors v1.0.36 h1:KSpO8I+JOiuyN4FuXsV471sPo
github.com/smartcontractkit/chain-selectors v1.0.36/go.mod h1:xsKM0aN3YGcQKTPRPDDtPx2l4mlTN1Djmg0VVXV40b8=
github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgBc2xpDKBco/Q4h4ydl6+UUU=
github.com/smartcontractkit/chainlink-automation v0.8.1/go.mod h1:Iij36PvWZ6blrdC5A/nrQUBuf3MH3JvsBB9sSyc9W08=
-github.com/smartcontractkit/chainlink-ccip v0.0.0-20250109124515-ff9d86b874ba h1:gisAer1YxKKui6LhxDgfuZ3OyrHVjHm/oK/0idusFeI=
-github.com/smartcontractkit/chainlink-ccip v0.0.0-20250109124515-ff9d86b874ba/go.mod h1:ncjd6mPZSRlelEqH/2KeLE1pU3UlqzBSn8RYkEoECzY=
+github.com/smartcontractkit/chainlink-ccip v0.0.0-20250110181647-9dba278f2103 h1:TeWnxdgSO2cYCKcBMwdkRcs/YdhSvXoWqqw7QWz/KeI=
+github.com/smartcontractkit/chainlink-ccip v0.0.0-20250110181647-9dba278f2103/go.mod h1:ncjd6mPZSRlelEqH/2KeLE1pU3UlqzBSn8RYkEoECzY=
github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250103152858-8973fd0c912b h1:UBXi9Yj8YSMHDDaxQLu273x1fWjyEL9xP58nuJsqZfg=
github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250103152858-8973fd0c912b/go.mod h1:Bmwq4lNb5tE47sydN0TKetcLEGbgl+VxHEWp4S0LI60=
github.com/smartcontractkit/chainlink-common v0.4.1-0.20250108194320-2ebd63bbb16e h1:8BStiP1F4W8AvjBRga0TYtjvAtkwN8oHYnHJztAlSF4=
diff --git a/core/services/ocr2/plugins/ccip/testhelpers/integration/jobspec.go b/core/services/ocr2/plugins/ccip/testhelpers/integration/jobspec.go
index 41fbc76cd7f..2a70ea0edd6 100644
--- a/core/services/ocr2/plugins/ccip/testhelpers/integration/jobspec.go
+++ b/core/services/ocr2/plugins/ccip/testhelpers/integration/jobspec.go
@@ -156,7 +156,7 @@ type JobType string
const (
Commit JobType = "commit"
Execution JobType = "exec"
- Boostrap JobType = "bootstrap"
+ Bootstrap JobType = "bootstrap"
)
func JobName(jobType JobType, source string, destination, version string) string {
@@ -329,7 +329,7 @@ func (params CCIPJobSpecParams) BootstrapJob(contractID string) *OCR2TaskJobSpec
},
}
return &OCR2TaskJobSpec{
- Name: fmt.Sprintf("%s-%s", Boostrap, params.DestChainName),
+ Name: fmt.Sprintf("%s-%s-%s", Bootstrap, params.SourceChainName, params.DestChainName),
JobType: "bootstrap",
OCR2OracleSpec: bootstrapSpec,
}
diff --git a/deployment/ccip/changeset/accept_ownership_test.go b/deployment/ccip/changeset/accept_ownership_test.go
index f74556b6600..dc3b0ba33b3 100644
--- a/deployment/ccip/changeset/accept_ownership_test.go
+++ b/deployment/ccip/changeset/accept_ownership_test.go
@@ -14,7 +14,7 @@ import (
func Test_NewAcceptOwnershipChangeset(t *testing.T) {
t.Parallel()
- e := NewMemoryEnvironment(t)
+ e, _ := NewMemoryEnvironment(t)
state, err := LoadOnchainState(e.Env)
require.NoError(t, err)
diff --git a/deployment/ccip/changeset/cs_active_candidate_test.go b/deployment/ccip/changeset/cs_active_candidate_test.go
index 92e3e825620..6016b06884b 100644
--- a/deployment/ccip/changeset/cs_active_candidate_test.go
+++ b/deployment/ccip/changeset/cs_active_candidate_test.go
@@ -9,6 +9,7 @@ import (
"golang.org/x/exp/maps"
"github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext"
+
"github.com/smartcontractkit/chainlink/deployment/ccip/changeset/internal"
commonchangeset "github.com/smartcontractkit/chainlink/deployment/common/changeset"
@@ -25,7 +26,7 @@ func Test_ActiveCandidate(t *testing.T) {
// We want to have the active instance execute a few messages
// and then setup a candidate instance. The candidate instance
// should not be able to transmit anything until we make it active.
- tenv := NewMemoryEnvironment(t,
+ tenv, _ := NewMemoryEnvironment(t,
WithChains(2),
WithNodes(4))
state, err := LoadOnchainState(tenv.Env)
diff --git a/deployment/ccip/changeset/cs_add_lane_test.go b/deployment/ccip/changeset/cs_add_lane_test.go
index e793c1866d9..4b9f1f8641f 100644
--- a/deployment/ccip/changeset/cs_add_lane_test.go
+++ b/deployment/ccip/changeset/cs_add_lane_test.go
@@ -1,7 +1,6 @@
package changeset
import (
- "math/big"
"testing"
"github.com/ethereum/go-ethereum/common"
@@ -9,98 +8,19 @@ import (
"github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext"
- commoncs "github.com/smartcontractkit/chainlink/deployment/common/changeset"
- "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/fee_quoter"
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router"
)
func TestAddLanesWithTestRouter(t *testing.T) {
t.Parallel()
- e := NewMemoryEnvironment(t)
+ e, _ := NewMemoryEnvironment(t)
// Here we have CR + nodes set up, but no CCIP contracts deployed.
state, err := LoadOnchainState(e.Env)
require.NoError(t, err)
selectors := e.Env.AllChainSelectors()
chain1, chain2 := selectors[0], selectors[1]
-
- stateChain1 := state.Chains[chain1]
- e.Env, err = commoncs.ApplyChangesets(t, e.Env, e.TimelockContracts(t), []commoncs.ChangesetApplication{
- {
- Changeset: commoncs.WrapChangeSet(UpdateOnRampsDests),
- Config: UpdateOnRampDestsConfig{
- UpdatesByChain: map[uint64]map[uint64]OnRampDestinationUpdate{
- chain1: {
- chain2: {
- IsEnabled: true,
- TestRouter: true,
- AllowListEnabled: false,
- },
- },
- },
- },
- },
- {
- Changeset: commoncs.WrapChangeSet(UpdateFeeQuoterPricesCS),
- Config: UpdateFeeQuoterPricesConfig{
- PricesByChain: map[uint64]FeeQuoterPriceUpdatePerSource{
- chain1: {
- TokenPrices: map[common.Address]*big.Int{
- stateChain1.LinkToken.Address(): DefaultLinkPrice,
- stateChain1.Weth9.Address(): DefaultWethPrice,
- },
- GasPrices: map[uint64]*big.Int{
- chain2: DefaultGasPrice,
- },
- },
- },
- },
- },
- {
- Changeset: commoncs.WrapChangeSet(UpdateFeeQuoterDests),
- Config: UpdateFeeQuoterDestsConfig{
- UpdatesByChain: map[uint64]map[uint64]fee_quoter.FeeQuoterDestChainConfig{
- chain1: {
- chain2: DefaultFeeQuoterDestChainConfig(),
- },
- },
- },
- },
- {
- Changeset: commoncs.WrapChangeSet(UpdateOffRampSources),
- Config: UpdateOffRampSourcesConfig{
- UpdatesByChain: map[uint64]map[uint64]OffRampSourceUpdate{
- chain2: {
- chain1: {
- IsEnabled: true,
- TestRouter: true,
- },
- },
- },
- },
- },
- {
- Changeset: commoncs.WrapChangeSet(UpdateRouterRamps),
- Config: UpdateRouterRampsConfig{
- TestRouter: true,
- UpdatesByChain: map[uint64]RouterUpdates{
- // onRamp update on source chain
- chain1: {
- OnRampUpdates: map[uint64]bool{
- chain2: true,
- },
- },
- // offramp update on dest chain
- chain2: {
- OffRampUpdates: map[uint64]bool{
- chain1: true,
- },
- },
- },
- },
- },
- })
- require.NoError(t, err)
+ AddLaneWithDefaultPricesAndFeeQuoterConfig(t, &e, state, chain1, chain2, true)
// Need to keep track of the block number for each chain so that event subscription can be done from that block.
startBlocks := make(map[uint64]*uint64)
// Send a message from each chain to every other chain.
diff --git a/deployment/ccip/changeset/cs_ccip_home_test.go b/deployment/ccip/changeset/cs_ccip_home_test.go
index eb22f05a703..884a501ce48 100644
--- a/deployment/ccip/changeset/cs_ccip_home_test.go
+++ b/deployment/ccip/changeset/cs_ccip_home_test.go
@@ -38,7 +38,7 @@ func Test_PromoteCandidate(t *testing.T) {
} {
t.Run(tc.name, func(t *testing.T) {
ctx := testcontext.Get(t)
- tenv := NewMemoryEnvironment(t,
+ tenv, _ := NewMemoryEnvironment(t,
WithChains(2),
WithNodes(4))
state, err := LoadOnchainState(tenv.Env)
@@ -130,7 +130,7 @@ func Test_SetCandidate(t *testing.T) {
} {
t.Run(tc.name, func(t *testing.T) {
ctx := testcontext.Get(t)
- tenv := NewMemoryEnvironment(t,
+ tenv, _ := NewMemoryEnvironment(t,
WithChains(2),
WithNodes(4))
state, err := LoadOnchainState(tenv.Env)
@@ -251,7 +251,7 @@ func Test_RevokeCandidate(t *testing.T) {
} {
t.Run(tc.name, func(t *testing.T) {
ctx := testcontext.Get(t)
- tenv := NewMemoryEnvironment(t,
+ tenv, _ := NewMemoryEnvironment(t,
WithChains(2),
WithNodes(4))
state, err := LoadOnchainState(tenv.Env)
@@ -442,7 +442,7 @@ func Test_UpdateChainConfigs(t *testing.T) {
},
} {
t.Run(tc.name, func(t *testing.T) {
- tenv := NewMemoryEnvironment(t, WithChains(3))
+ tenv, _ := NewMemoryEnvironment(t, WithChains(3))
state, err := LoadOnchainState(tenv.Env)
require.NoError(t, err)
diff --git a/deployment/ccip/changeset/cs_chain_contracts.go b/deployment/ccip/changeset/cs_chain_contracts.go
index e87e66e06b5..196de01124b 100644
--- a/deployment/ccip/changeset/cs_chain_contracts.go
+++ b/deployment/ccip/changeset/cs_chain_contracts.go
@@ -792,8 +792,14 @@ func (cfg UpdateRouterRampsConfig) Validate(e deployment.Environment) error {
if chainState.OffRamp == nil {
return fmt.Errorf("missing onramp onramp for chain %d", chainSel)
}
- if err := commoncs.ValidateOwnership(e.GetContext(), cfg.MCMS != nil, e.Chains[chainSel].DeployerKey.From, chainState.Timelock.Address(), chainState.Router); err != nil {
- return err
+ if cfg.TestRouter {
+ if err := commoncs.ValidateOwnership(e.GetContext(), cfg.MCMS != nil, e.Chains[chainSel].DeployerKey.From, chainState.Timelock.Address(), chainState.TestRouter); err != nil {
+ return err
+ }
+ } else {
+ if err := commoncs.ValidateOwnership(e.GetContext(), cfg.MCMS != nil, e.Chains[chainSel].DeployerKey.From, chainState.Timelock.Address(), chainState.Router); err != nil {
+ return err
+ }
}
for source := range update.OffRampUpdates {
diff --git a/deployment/ccip/changeset/cs_chain_contracts_test.go b/deployment/ccip/changeset/cs_chain_contracts_test.go
index adbcc078373..544362a1b1e 100644
--- a/deployment/ccip/changeset/cs_chain_contracts_test.go
+++ b/deployment/ccip/changeset/cs_chain_contracts_test.go
@@ -32,7 +32,7 @@ func TestUpdateOnRampsDests(t *testing.T) {
ctx := testcontext.Get(t)
// Default env just has 2 chains with all contracts
// deployed but no lanes.
- tenv := NewMemoryEnvironment(t)
+ tenv, _ := NewMemoryEnvironment(t)
state, err := LoadOnchainState(tenv.Env)
require.NoError(t, err)
@@ -106,7 +106,7 @@ func TestUpdateOffRampsSources(t *testing.T) {
} {
t.Run(tc.name, func(t *testing.T) {
ctx := testcontext.Get(t)
- tenv := NewMemoryEnvironment(t)
+ tenv, _ := NewMemoryEnvironment(t)
state, err := LoadOnchainState(tenv.Env)
require.NoError(t, err)
@@ -176,7 +176,7 @@ func TestUpdateFQDests(t *testing.T) {
} {
t.Run(tc.name, func(t *testing.T) {
ctx := testcontext.Get(t)
- tenv := NewMemoryEnvironment(t)
+ tenv, _ := NewMemoryEnvironment(t)
state, err := LoadOnchainState(tenv.Env)
require.NoError(t, err)
@@ -244,7 +244,7 @@ func TestUpdateRouterRamps(t *testing.T) {
} {
t.Run(tc.name, func(t *testing.T) {
ctx := testcontext.Get(t)
- tenv := NewMemoryEnvironment(t)
+ tenv, _ := NewMemoryEnvironment(t)
state, err := LoadOnchainState(tenv.Env)
require.NoError(t, err)
@@ -320,7 +320,7 @@ func TestUpdateNonceManagersCS(t *testing.T) {
},
} {
t.Run(tc.name, func(t *testing.T) {
- tenv := NewMemoryEnvironment(t)
+ tenv, _ := NewMemoryEnvironment(t)
state, err := LoadOnchainState(tenv.Env)
require.NoError(t, err)
diff --git a/deployment/ccip/changeset/cs_deploy_chain_test.go b/deployment/ccip/changeset/cs_deploy_chain_test.go
index a72b1b1568b..1f77be3ca5a 100644
--- a/deployment/ccip/changeset/cs_deploy_chain_test.go
+++ b/deployment/ccip/changeset/cs_deploy_chain_test.go
@@ -101,7 +101,7 @@ func TestDeployChainContractsChangeset(t *testing.T) {
func TestDeployCCIPContracts(t *testing.T) {
t.Parallel()
- e := NewMemoryEnvironment(t)
+ e, _ := NewMemoryEnvironment(t)
// Deploy all the CCIP contracts.
state, err := LoadOnchainState(e.Env)
require.NoError(t, err)
diff --git a/deployment/ccip/changeset/cs_home_chain_test.go b/deployment/ccip/changeset/cs_home_chain_test.go
index e96cd878305..135e60f4eb1 100644
--- a/deployment/ccip/changeset/cs_home_chain_test.go
+++ b/deployment/ccip/changeset/cs_home_chain_test.go
@@ -62,7 +62,7 @@ func TestDeployHomeChain(t *testing.T) {
}
func TestRemoveDonsValidate(t *testing.T) {
- e := NewMemoryEnvironment(t)
+ e, _ := NewMemoryEnvironment(t)
s, err := LoadOnchainState(e.Env)
require.NoError(t, err)
homeChain := s.Chains[e.HomeChainSel]
@@ -117,7 +117,7 @@ func TestRemoveDonsValidate(t *testing.T) {
}
func TestRemoveDons(t *testing.T) {
- e := NewMemoryEnvironment(t)
+ e, _ := NewMemoryEnvironment(t)
s, err := LoadOnchainState(e.Env)
require.NoError(t, err)
homeChain := s.Chains[e.HomeChainSel]
diff --git a/deployment/ccip/changeset/cs_update_rmn_config_test.go b/deployment/ccip/changeset/cs_update_rmn_config_test.go
index e7543e22cb7..093fc9e337d 100644
--- a/deployment/ccip/changeset/cs_update_rmn_config_test.go
+++ b/deployment/ccip/changeset/cs_update_rmn_config_test.go
@@ -65,7 +65,7 @@ func TestUpdateRMNConfig(t *testing.T) {
}
func updateRMNConfig(t *testing.T, tc updateRMNConfigTestCase) {
- e := NewMemoryEnvironment(t)
+ e, _ := NewMemoryEnvironment(t)
state, err := LoadOnchainState(e.Env)
require.NoError(t, err)
@@ -220,7 +220,7 @@ func buildRMNRemoteAddressPerChain(e deployment.Environment, state CCIPOnChainSt
func TestSetRMNRemoteOnRMNProxy(t *testing.T) {
t.Parallel()
- e := NewMemoryEnvironment(t, WithNoJobsAndContracts())
+ e, _ := NewMemoryEnvironment(t, WithNoJobsAndContracts())
allChains := e.Env.AllChainSelectors()
mcmsCfg := make(map[uint64]commontypes.MCMSWithTimelockConfig)
var err error
@@ -265,6 +265,8 @@ func TestSetRMNRemoteOnRMNProxy(t *testing.T) {
CallProxy: state.Chains[chain].CallProxy,
}
}
+ envNodes, err := deployment.NodeInfo(e.Env.NodeIDs, e.Env.Offchain)
+ require.NoError(t, err)
e.Env, err = commonchangeset.ApplyChangesets(t, e.Env, timelockContractsPerChain, []commonchangeset.ChangesetApplication{
// transfer ownership of RMNProxy to timelock
{
@@ -274,6 +276,19 @@ func TestSetRMNRemoteOnRMNProxy(t *testing.T) {
MinDelay: 0,
},
},
+
+ {
+ Changeset: commonchangeset.WrapChangeSet(DeployHomeChain),
+ Config: DeployHomeChainConfig{
+ HomeChainSel: e.HomeChainSel,
+ RMNDynamicConfig: NewTestRMNDynamicConfig(),
+ RMNStaticConfig: NewTestRMNStaticConfig(),
+ NodeOperators: NewTestNodeOperator(e.Env.Chains[e.HomeChainSel].DeployerKey.From),
+ NodeP2PIDsPerNodeOpAdmin: map[string][][32]byte{
+ "NodeOperator": envNodes.NonBootstraps().PeerIDs(),
+ },
+ },
+ },
{
Changeset: commonchangeset.WrapChangeSet(DeployChainContracts),
Config: DeployChainContractsConfig{
diff --git a/deployment/ccip/changeset/state_test.go b/deployment/ccip/changeset/state_test.go
index 3587332fff2..f7d06efa66d 100644
--- a/deployment/ccip/changeset/state_test.go
+++ b/deployment/ccip/changeset/state_test.go
@@ -7,7 +7,7 @@ import (
)
func TestSmokeState(t *testing.T) {
- tenv := NewMemoryEnvironment(t, WithChains(3))
+ tenv, _ := NewMemoryEnvironment(t, WithChains(3))
state, err := LoadOnchainState(tenv.Env)
require.NoError(t, err)
_, err = state.View(tenv.Env.AllChainSelectors())
diff --git a/deployment/ccip/changeset/test_environment.go b/deployment/ccip/changeset/test_environment.go
index a1307d9820b..3e23f356857 100644
--- a/deployment/ccip/changeset/test_environment.go
+++ b/deployment/ccip/changeset/test_environment.go
@@ -42,21 +42,21 @@ type TestConfigs struct {
Type EnvType // set by env var CCIP_V16_TEST_ENV, defaults to Memory
CreateJob bool
// TODO: This should be CreateContracts so the booleans make sense?
- CreateJobAndContracts bool
- LegacyDeployment bool
- Chains int // only used in memory mode, for docker mode, this is determined by the integration-test config toml input
- ChainIDs []uint64 // only used in memory mode, for docker mode, this is determined by the integration-test config toml input
- NumOfUsersPerChain int // only used in memory mode, for docker mode, this is determined by the integration-test config toml input
- Nodes int // only used in memory mode, for docker mode, this is determined by the integration-test config toml input
- Bootstraps int // only used in memory mode, for docker mode, this is determined by the integration-test config toml input
- IsUSDC bool
- IsUSDCAttestationMissing bool
- IsMultiCall3 bool
- OCRConfigOverride func(CCIPOCRParams) CCIPOCRParams
- RMNEnabled bool
- NumOfRMNNodes int
- LinkPrice *big.Int
- WethPrice *big.Int
+ CreateJobAndContracts bool
+ PrerequisiteDeploymentOnly bool
+ Chains int // only used in memory mode, for docker mode, this is determined by the integration-test config toml input
+ ChainIDs []uint64 // only used in memory mode, for docker mode, this is determined by the integration-test config toml input
+ NumOfUsersPerChain int // only used in memory mode, for docker mode, this is determined by the integration-test config toml input
+ Nodes int // only used in memory mode, for docker mode, this is determined by the integration-test config toml input
+ Bootstraps int // only used in memory mode, for docker mode, this is determined by the integration-test config toml input
+ IsUSDC bool
+ IsUSDCAttestationMissing bool
+ IsMultiCall3 bool
+ OCRConfigOverride func(CCIPOCRParams) CCIPOCRParams
+ RMNEnabled bool
+ NumOfRMNNodes int
+ LinkPrice *big.Int
+ WethPrice *big.Int
}
func (tc *TestConfigs) Validate() error {
@@ -106,9 +106,9 @@ func WithMultiCall3() TestOps {
}
}
-func WithLegacyDeployment() TestOps {
+func WithPrerequisiteDeployment() TestOps {
return func(testCfg *TestConfigs) {
- testCfg.LegacyDeployment = true
+ testCfg.PrerequisiteDeploymentOnly = true
}
}
@@ -183,9 +183,11 @@ func WithBootstraps(numBootstraps int) TestOps {
type TestEnvironment interface {
SetupJobs(t *testing.T)
- StartNodes(t *testing.T, tc *TestConfigs, crConfig deployment.CapabilityRegistryConfig)
- StartChains(t *testing.T, tc *TestConfigs)
+ StartNodes(t *testing.T, crConfig deployment.CapabilityRegistryConfig)
+ StartChains(t *testing.T)
+ TestConfigs() *TestConfigs
DeployedEnvironment() DeployedEnv
+ UpdateDeployedEnvironment(env DeployedEnv)
MockUSDCAttestationServer(t *testing.T, isUSDCAttestationMissing bool) string
}
@@ -233,15 +235,25 @@ func (d *DeployedEnv) SetupJobs(t *testing.T) {
type MemoryEnvironment struct {
DeployedEnv
- Chains map[uint64]deployment.Chain
+ TestConfig *TestConfigs
+ Chains map[uint64]deployment.Chain
+}
+
+func (m *MemoryEnvironment) TestConfigs() *TestConfigs {
+ return m.TestConfig
}
func (m *MemoryEnvironment) DeployedEnvironment() DeployedEnv {
return m.DeployedEnv
}
-func (m *MemoryEnvironment) StartChains(t *testing.T, tc *TestConfigs) {
+func (m *MemoryEnvironment) UpdateDeployedEnvironment(env DeployedEnv) {
+ m.DeployedEnv = env
+}
+
+func (m *MemoryEnvironment) StartChains(t *testing.T) {
ctx := testcontext.Get(t)
+ tc := m.TestConfig
var chains map[uint64]deployment.Chain
var users map[uint64][]*bind.TransactOpts
if len(tc.ChainIDs) > 0 {
@@ -273,9 +285,10 @@ func (m *MemoryEnvironment) StartChains(t *testing.T, tc *TestConfigs) {
}
}
-func (m *MemoryEnvironment) StartNodes(t *testing.T, tc *TestConfigs, crConfig deployment.CapabilityRegistryConfig) {
+func (m *MemoryEnvironment) StartNodes(t *testing.T, crConfig deployment.CapabilityRegistryConfig) {
require.NotNil(t, m.Chains, "start chains first, chains are empty")
require.NotNil(t, m.DeployedEnv, "start chains and initiate deployed env first before starting nodes")
+ tc := m.TestConfig
nodes := memory.NewNodes(t, zapcore.InfoLevel, m.Chains, tc.Nodes, tc.Bootstraps, crConfig)
ctx := testcontext.Get(t)
lggr := logger.Test(t)
@@ -298,32 +311,39 @@ func (m *MemoryEnvironment) MockUSDCAttestationServer(t *testing.T, isUSDCAttest
}
// NewMemoryEnvironment creates an in-memory environment based on the testconfig requested
-func NewMemoryEnvironment(t *testing.T, opts ...TestOps) DeployedEnv {
+func NewMemoryEnvironment(t *testing.T, opts ...TestOps) (DeployedEnv, TestEnvironment) {
testCfg := DefaultTestConfigs()
for _, opt := range opts {
opt(testCfg)
}
require.NoError(t, testCfg.Validate(), "invalid test config")
- env := &MemoryEnvironment{}
- if testCfg.LegacyDeployment {
- return NewLegacyEnvironment(t, testCfg, env)
+ env := &MemoryEnvironment{
+ TestConfig: testCfg,
+ }
+ if testCfg.PrerequisiteDeploymentOnly {
+ dEnv := NewEnvironmentWithPrerequisitesContracts(t, env)
+ env.UpdateDeployedEnvironment(dEnv)
+ return dEnv, env
}
if testCfg.CreateJobAndContracts {
- return NewEnvironmentWithJobsAndContracts(t, testCfg, env)
+ dEnv := NewEnvironmentWithJobsAndContracts(t, env)
+ env.UpdateDeployedEnvironment(dEnv)
+ return dEnv, env
}
if testCfg.CreateJob {
- return NewEnvironmentWithJobs(t, testCfg, env)
+ dEnv := NewEnvironmentWithJobs(t, env)
+ env.UpdateDeployedEnvironment(dEnv)
+ return dEnv, env
}
- return NewEnvironment(t, testCfg, env)
+ dEnv := NewEnvironment(t, env)
+ env.UpdateDeployedEnvironment(dEnv)
+ return dEnv, env
}
-func NewLegacyEnvironment(t *testing.T, tc *TestConfigs, tEnv TestEnvironment) DeployedEnv {
+func NewEnvironmentWithPrerequisitesContracts(t *testing.T, tEnv TestEnvironment) DeployedEnv {
var err error
- tEnv.StartChains(t, tc)
- e := tEnv.DeployedEnvironment()
- require.NotEmpty(t, e.Env.Chains)
- tEnv.StartNodes(t, tc, deployment.CapabilityRegistryConfig{})
- e = tEnv.DeployedEnvironment()
+ tc := tEnv.TestConfigs()
+ e := NewEnvironment(t, tEnv)
allChains := e.Env.AllChainSelectors()
mcmsCfg := make(map[uint64]commontypes.MCMSWithTimelockConfig)
@@ -341,6 +361,7 @@ func NewLegacyEnvironment(t *testing.T, tc *TestConfigs, tEnv TestEnvironment) D
opts = append(opts, WithMultiCall3Enabled())
}
}
+ // no RMNConfig will ensure that mock RMN is deployed
opts = append(opts, WithLegacyDeploymentEnabled(LegacyDeploymentConfig{
PriceRegStalenessThreshold: 60 * 60 * 24 * 14, // two weeks
}))
@@ -367,40 +388,30 @@ func NewLegacyEnvironment(t *testing.T, tc *TestConfigs, tEnv TestEnvironment) D
},
})
require.NoError(t, err)
+ tEnv.UpdateDeployedEnvironment(e)
return e
}
-func NewEnvironment(t *testing.T, tc *TestConfigs, tEnv TestEnvironment) DeployedEnv {
+func NewEnvironment(t *testing.T, tEnv TestEnvironment) DeployedEnv {
lggr := logger.Test(t)
- tEnv.StartChains(t, tc)
+ tc := tEnv.TestConfigs()
+ tEnv.StartChains(t)
dEnv := tEnv.DeployedEnvironment()
require.NotEmpty(t, dEnv.FeedChainSel)
require.NotEmpty(t, dEnv.HomeChainSel)
require.NotEmpty(t, dEnv.Env.Chains)
ab := deployment.NewMemoryAddressBook()
crConfig := DeployTestContracts(t, lggr, ab, dEnv.HomeChainSel, dEnv.FeedChainSel, dEnv.Env.Chains, tc.LinkPrice, tc.WethPrice)
- tEnv.StartNodes(t, tc, crConfig)
+ tEnv.StartNodes(t, crConfig)
dEnv = tEnv.DeployedEnvironment()
- // TODO: Should use ApplyChangesets here.
- envNodes, err := deployment.NodeInfo(dEnv.Env.NodeIDs, dEnv.Env.Offchain)
- require.NoError(t, err)
dEnv.Env.ExistingAddresses = ab
- _, err = deployHomeChain(lggr, dEnv.Env, dEnv.Env.ExistingAddresses, dEnv.Env.Chains[dEnv.HomeChainSel],
- NewTestRMNStaticConfig(),
- NewTestRMNDynamicConfig(),
- NewTestNodeOperator(dEnv.Env.Chains[dEnv.HomeChainSel].DeployerKey.From),
- map[string][][32]byte{
- "NodeOperator": envNodes.NonBootstraps().PeerIDs(),
- },
- )
- require.NoError(t, err)
-
return dEnv
}
-func NewEnvironmentWithJobsAndContracts(t *testing.T, tc *TestConfigs, tEnv TestEnvironment) DeployedEnv {
+func NewEnvironmentWithJobsAndContracts(t *testing.T, tEnv TestEnvironment) DeployedEnv {
var err error
- e := NewEnvironment(t, tc, tEnv)
+ tc := tEnv.TestConfigs()
+ e := NewEnvironment(t, tEnv)
allChains := e.Env.AllChainSelectors()
mcmsCfg := make(map[uint64]commontypes.MCMSWithTimelockConfig)
@@ -441,6 +452,43 @@ func NewEnvironmentWithJobsAndContracts(t *testing.T, tc *TestConfigs, tEnv Test
Changeset: commonchangeset.WrapChangeSet(commonchangeset.DeployMCMSWithTimelock),
Config: mcmsCfg,
},
+ })
+ require.NoError(t, err)
+ tEnv.UpdateDeployedEnvironment(e)
+ e = AddCCIPContractsToEnvironment(t, e.Env.AllChainSelectors(), tEnv)
+ // now we update RMNProxy to point to RMNRemote
+ e.Env, err = commonchangeset.ApplyChangesets(t, e.Env, nil, []commonchangeset.ChangesetApplication{
+ {
+ Changeset: commonchangeset.WrapChangeSet(SetRMNRemoteOnRMNProxy),
+ Config: SetRMNRemoteOnRMNProxyConfig{
+ ChainSelectors: allChains,
+ },
+ },
+ })
+ require.NoError(t, err)
+ return e
+}
+
+func AddCCIPContractsToEnvironment(t *testing.T, allChains []uint64, tEnv TestEnvironment) DeployedEnv {
+ tc := tEnv.TestConfigs()
+ e := tEnv.DeployedEnvironment()
+ envNodes, err := deployment.NodeInfo(e.Env.NodeIDs, e.Env.Offchain)
+ require.NoError(t, err)
+ // Need to deploy prerequisites first so that we can form the USDC config
+ // no proposals to be made, timelock can be passed as nil here
+ e.Env, err = commonchangeset.ApplyChangesets(t, e.Env, nil, []commonchangeset.ChangesetApplication{
+ {
+ Changeset: commonchangeset.WrapChangeSet(DeployHomeChain),
+ Config: DeployHomeChainConfig{
+ HomeChainSel: e.HomeChainSel,
+ RMNDynamicConfig: NewTestRMNDynamicConfig(),
+ RMNStaticConfig: NewTestRMNStaticConfig(),
+ NodeOperators: NewTestNodeOperator(e.Env.Chains[e.HomeChainSel].DeployerKey.From),
+ NodeP2PIDsPerNodeOpAdmin: map[string][][32]byte{
+ TestNodeOperator: envNodes.NonBootstraps().PeerIDs(),
+ },
+ },
+ },
{
Changeset: commonchangeset.WrapChangeSet(DeployChainContracts),
Config: DeployChainContractsConfig{
@@ -448,12 +496,6 @@ func NewEnvironmentWithJobsAndContracts(t *testing.T, tc *TestConfigs, tEnv Test
HomeChainSelector: e.HomeChainSel,
},
},
- {
- Changeset: commonchangeset.WrapChangeSet(SetRMNRemoteOnRMNProxy),
- Config: SetRMNRemoteOnRMNProxyConfig{
- ChainSelectors: allChains,
- },
- },
})
require.NoError(t, err)
@@ -600,13 +642,14 @@ func NewEnvironmentWithJobsAndContracts(t *testing.T, tc *TestConfigs, tEnv Test
require.NotNil(t, state.Chains[chain].OffRamp)
require.NotNil(t, state.Chains[chain].OnRamp)
}
+ tEnv.UpdateDeployedEnvironment(e)
return e
}
// NewEnvironmentWithJobs creates a new CCIP environment
// with capreg, fee tokens, feeds, nodes and jobs set up.
-func NewEnvironmentWithJobs(t *testing.T, tc *TestConfigs, tEnv TestEnvironment) DeployedEnv {
- e := NewEnvironment(t, tc, tEnv)
+func NewEnvironmentWithJobs(t *testing.T, tEnv TestEnvironment) DeployedEnv {
+ e := NewEnvironment(t, tEnv)
e.SetupJobs(t)
return e
}
diff --git a/deployment/ccip/changeset/test_params.go b/deployment/ccip/changeset/test_params.go
index a76a610a178..90331b50675 100644
--- a/deployment/ccip/changeset/test_params.go
+++ b/deployment/ccip/changeset/test_params.go
@@ -7,6 +7,10 @@ import (
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry_1_1_0"
)
+const (
+ TestNodeOperator = "NodeOperator"
+)
+
func NewTestRMNStaticConfig() rmn_home.RMNHomeStaticConfig {
return rmn_home.RMNHomeStaticConfig{
Nodes: []rmn_home.RMNHomeNode{},
@@ -25,7 +29,7 @@ func NewTestNodeOperator(admin common.Address) []capabilities_registry.Capabilit
return []capabilities_registry.CapabilitiesRegistryNodeOperator{
{
Admin: admin,
- Name: "NodeOperator",
+ Name: TestNodeOperator,
},
}
}
diff --git a/deployment/ccip/changeset/v1_5/e2e_test.go b/deployment/ccip/changeset/v1_5/e2e_test.go
index 11bb566c641..2019758b179 100644
--- a/deployment/ccip/changeset/v1_5/e2e_test.go
+++ b/deployment/ccip/changeset/v1_5/e2e_test.go
@@ -15,9 +15,9 @@ import (
// This test only works if the destination chain id is 1337
// Otherwise it shows error for offchain and onchain config digest mismatch
func TestE2ELegacy(t *testing.T) {
- e := changeset.NewMemoryEnvironment(
+ e, _ := changeset.NewMemoryEnvironment(
t,
- changeset.WithLegacyDeployment(),
+ changeset.WithPrerequisiteDeployment(),
changeset.WithChains(3),
changeset.WithChainIds([]uint64{chainselectors.GETH_TESTNET.EvmChainID}))
state, err := changeset.LoadOnchainState(e.Env)
diff --git a/deployment/ccip/changeset/view_test.go b/deployment/ccip/changeset/view_test.go
index 35193979849..16d1f8a0dfc 100644
--- a/deployment/ccip/changeset/view_test.go
+++ b/deployment/ccip/changeset/view_test.go
@@ -8,7 +8,7 @@ import (
func TestSmokeView(t *testing.T) {
t.Parallel()
- tenv := NewMemoryEnvironment(t, WithChains(3))
+ tenv, _ := NewMemoryEnvironment(t, WithChains(3))
_, err := ViewCCIP(tenv.Env)
require.NoError(t, err)
}
diff --git a/deployment/common/changeset/transfer_to_mcms_with_timelock.go b/deployment/common/changeset/transfer_to_mcms_with_timelock.go
index ab0883cc9a7..da932a95b01 100644
--- a/deployment/common/changeset/transfer_to_mcms_with_timelock.go
+++ b/deployment/common/changeset/transfer_to_mcms_with_timelock.go
@@ -40,7 +40,7 @@ func LoadOwnableContract(addr common.Address, client bind.ContractBackend) (comm
}
owner, err := c.Owner(nil)
if err != nil {
- return common.Address{}, nil, fmt.Errorf("failed to get owner of contract: %w", err)
+ return common.Address{}, nil, fmt.Errorf("failed to get owner of contract %s: %w", c.Address(), err)
}
return owner, c, nil
}
diff --git a/deployment/go.mod b/deployment/go.mod
index 9595794978c..2daefd78f11 100644
--- a/deployment/go.mod
+++ b/deployment/go.mod
@@ -29,7 +29,7 @@ require (
github.com/sethvargo/go-retry v0.2.4
github.com/smartcontractkit/ccip-owner-contracts v0.0.0-salt-fix
github.com/smartcontractkit/chain-selectors v1.0.36
- github.com/smartcontractkit/chainlink-ccip v0.0.0-20250109124515-ff9d86b874ba
+ github.com/smartcontractkit/chainlink-ccip v0.0.0-20250110181647-9dba278f2103
github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250103152858-8973fd0c912b
github.com/smartcontractkit/chainlink-common v0.4.1-0.20250108194320-2ebd63bbb16e
github.com/smartcontractkit/chainlink-protos/job-distributor v0.6.0
diff --git a/deployment/go.sum b/deployment/go.sum
index 4800155620c..5e7d0ad9ced 100644
--- a/deployment/go.sum
+++ b/deployment/go.sum
@@ -1384,8 +1384,8 @@ github.com/smartcontractkit/chain-selectors v1.0.36 h1:KSpO8I+JOiuyN4FuXsV471sPo
github.com/smartcontractkit/chain-selectors v1.0.36/go.mod h1:xsKM0aN3YGcQKTPRPDDtPx2l4mlTN1Djmg0VVXV40b8=
github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgBc2xpDKBco/Q4h4ydl6+UUU=
github.com/smartcontractkit/chainlink-automation v0.8.1/go.mod h1:Iij36PvWZ6blrdC5A/nrQUBuf3MH3JvsBB9sSyc9W08=
-github.com/smartcontractkit/chainlink-ccip v0.0.0-20250109124515-ff9d86b874ba h1:gisAer1YxKKui6LhxDgfuZ3OyrHVjHm/oK/0idusFeI=
-github.com/smartcontractkit/chainlink-ccip v0.0.0-20250109124515-ff9d86b874ba/go.mod h1:ncjd6mPZSRlelEqH/2KeLE1pU3UlqzBSn8RYkEoECzY=
+github.com/smartcontractkit/chainlink-ccip v0.0.0-20250110181647-9dba278f2103 h1:TeWnxdgSO2cYCKcBMwdkRcs/YdhSvXoWqqw7QWz/KeI=
+github.com/smartcontractkit/chainlink-ccip v0.0.0-20250110181647-9dba278f2103/go.mod h1:ncjd6mPZSRlelEqH/2KeLE1pU3UlqzBSn8RYkEoECzY=
github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250103152858-8973fd0c912b h1:UBXi9Yj8YSMHDDaxQLu273x1fWjyEL9xP58nuJsqZfg=
github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250103152858-8973fd0c912b/go.mod h1:Bmwq4lNb5tE47sydN0TKetcLEGbgl+VxHEWp4S0LI60=
github.com/smartcontractkit/chainlink-common v0.4.1-0.20250108194320-2ebd63bbb16e h1:8BStiP1F4W8AvjBRga0TYtjvAtkwN8oHYnHJztAlSF4=
diff --git a/go.mod b/go.mod
index ccedade99b3..3d4c747034e 100644
--- a/go.mod
+++ b/go.mod
@@ -78,7 +78,7 @@ require (
github.com/shopspring/decimal v1.4.0
github.com/smartcontractkit/chain-selectors v1.0.34
github.com/smartcontractkit/chainlink-automation v0.8.1
- github.com/smartcontractkit/chainlink-ccip v0.0.0-20250109124515-ff9d86b874ba
+ github.com/smartcontractkit/chainlink-ccip v0.0.0-20250110181647-9dba278f2103
github.com/smartcontractkit/chainlink-common v0.4.1-0.20250108194320-2ebd63bbb16e
github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e
github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241216163550-fa030d178ba3
diff --git a/go.sum b/go.sum
index 6e8c1b4b5f3..44fb07ef951 100644
--- a/go.sum
+++ b/go.sum
@@ -1150,8 +1150,8 @@ github.com/smartcontractkit/chain-selectors v1.0.34 h1:MJ17OGu8+jjl426pcKrJkCf3f
github.com/smartcontractkit/chain-selectors v1.0.34/go.mod h1:xsKM0aN3YGcQKTPRPDDtPx2l4mlTN1Djmg0VVXV40b8=
github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgBc2xpDKBco/Q4h4ydl6+UUU=
github.com/smartcontractkit/chainlink-automation v0.8.1/go.mod h1:Iij36PvWZ6blrdC5A/nrQUBuf3MH3JvsBB9sSyc9W08=
-github.com/smartcontractkit/chainlink-ccip v0.0.0-20250109124515-ff9d86b874ba h1:gisAer1YxKKui6LhxDgfuZ3OyrHVjHm/oK/0idusFeI=
-github.com/smartcontractkit/chainlink-ccip v0.0.0-20250109124515-ff9d86b874ba/go.mod h1:ncjd6mPZSRlelEqH/2KeLE1pU3UlqzBSn8RYkEoECzY=
+github.com/smartcontractkit/chainlink-ccip v0.0.0-20250110181647-9dba278f2103 h1:TeWnxdgSO2cYCKcBMwdkRcs/YdhSvXoWqqw7QWz/KeI=
+github.com/smartcontractkit/chainlink-ccip v0.0.0-20250110181647-9dba278f2103/go.mod h1:ncjd6mPZSRlelEqH/2KeLE1pU3UlqzBSn8RYkEoECzY=
github.com/smartcontractkit/chainlink-common v0.4.1-0.20250108194320-2ebd63bbb16e h1:8BStiP1F4W8AvjBRga0TYtjvAtkwN8oHYnHJztAlSF4=
github.com/smartcontractkit/chainlink-common v0.4.1-0.20250108194320-2ebd63bbb16e/go.mod h1:yti7e1+G9hhkYhj+L5sVUULn9Bn3bBL5/AxaNqdJ5YQ=
github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e h1:PRoeby6ZlTuTkv2f+7tVU4+zboTfRzI+beECynF4JQ0=
diff --git a/integration-tests/contracts/ccipreader_test.go b/integration-tests/contracts/ccipreader_test.go
index ace0783365c..b7ea027a568 100644
--- a/integration-tests/contracts/ccipreader_test.go
+++ b/integration-tests/contracts/ccipreader_test.go
@@ -590,7 +590,7 @@ func TestCCIPReader_GetExpectedNextSequenceNumber(t *testing.T) {
t.Parallel()
ctx := tests.Context(t)
//env := NewMemoryEnvironmentContractsOnly(t, logger.TestLogger(t), 2, 4, nil)
- env := changeset.NewMemoryEnvironment(t)
+ env, _ := changeset.NewMemoryEnvironment(t)
state, err := changeset.LoadOnchainState(env.Env)
require.NoError(t, err)
@@ -700,7 +700,7 @@ func TestCCIPReader_Nonces(t *testing.T) {
func Test_GetChainFeePriceUpdates(t *testing.T) {
t.Parallel()
ctx := tests.Context(t)
- env := changeset.NewMemoryEnvironment(t)
+ env, _ := changeset.NewMemoryEnvironment(t)
state, err := changeset.LoadOnchainState(env.Env)
require.NoError(t, err)
@@ -756,7 +756,7 @@ func Test_GetChainFeePriceUpdates(t *testing.T) {
func Test_LinkPriceUSD(t *testing.T) {
t.Parallel()
ctx := tests.Context(t)
- env := changeset.NewMemoryEnvironment(t)
+ env, _ := changeset.NewMemoryEnvironment(t)
state, err := changeset.LoadOnchainState(env.Env)
require.NoError(t, err)
@@ -791,7 +791,7 @@ func Test_LinkPriceUSD(t *testing.T) {
func Test_GetMedianDataAvailabilityGasConfig(t *testing.T) {
t.Parallel()
ctx := tests.Context(t)
- env := changeset.NewMemoryEnvironment(t, changeset.WithChains(4))
+ env, _ := changeset.NewMemoryEnvironment(t, changeset.WithChains(4))
state, err := changeset.LoadOnchainState(env.Env)
require.NoError(t, err)
@@ -850,7 +850,7 @@ func Test_GetMedianDataAvailabilityGasConfig(t *testing.T) {
func Test_GetWrappedNativeTokenPriceUSD(t *testing.T) {
t.Parallel()
ctx := tests.Context(t)
- env := changeset.NewMemoryEnvironment(t)
+ env, _ := changeset.NewMemoryEnvironment(t)
state, err := changeset.LoadOnchainState(env.Env)
require.NoError(t, err)
diff --git a/integration-tests/go.mod b/integration-tests/go.mod
index bb1952af8f3..cc161d6b92a 100644
--- a/integration-tests/go.mod
+++ b/integration-tests/go.mod
@@ -46,7 +46,7 @@ require (
github.com/slack-go/slack v0.15.0
github.com/smartcontractkit/chain-selectors v1.0.36
github.com/smartcontractkit/chainlink-automation v0.8.1
- github.com/smartcontractkit/chainlink-ccip v0.0.0-20250109124515-ff9d86b874ba
+ github.com/smartcontractkit/chainlink-ccip v0.0.0-20250110181647-9dba278f2103
github.com/smartcontractkit/chainlink-common v0.4.1-0.20250108194320-2ebd63bbb16e
github.com/smartcontractkit/chainlink-protos/job-distributor v0.6.0
github.com/smartcontractkit/chainlink-testing-framework/havoc v1.50.2
diff --git a/integration-tests/go.sum b/integration-tests/go.sum
index 2377fe29ce3..52c0922d743 100644
--- a/integration-tests/go.sum
+++ b/integration-tests/go.sum
@@ -1408,8 +1408,8 @@ github.com/smartcontractkit/chain-selectors v1.0.36 h1:KSpO8I+JOiuyN4FuXsV471sPo
github.com/smartcontractkit/chain-selectors v1.0.36/go.mod h1:xsKM0aN3YGcQKTPRPDDtPx2l4mlTN1Djmg0VVXV40b8=
github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgBc2xpDKBco/Q4h4ydl6+UUU=
github.com/smartcontractkit/chainlink-automation v0.8.1/go.mod h1:Iij36PvWZ6blrdC5A/nrQUBuf3MH3JvsBB9sSyc9W08=
-github.com/smartcontractkit/chainlink-ccip v0.0.0-20250109124515-ff9d86b874ba h1:gisAer1YxKKui6LhxDgfuZ3OyrHVjHm/oK/0idusFeI=
-github.com/smartcontractkit/chainlink-ccip v0.0.0-20250109124515-ff9d86b874ba/go.mod h1:ncjd6mPZSRlelEqH/2KeLE1pU3UlqzBSn8RYkEoECzY=
+github.com/smartcontractkit/chainlink-ccip v0.0.0-20250110181647-9dba278f2103 h1:TeWnxdgSO2cYCKcBMwdkRcs/YdhSvXoWqqw7QWz/KeI=
+github.com/smartcontractkit/chainlink-ccip v0.0.0-20250110181647-9dba278f2103/go.mod h1:ncjd6mPZSRlelEqH/2KeLE1pU3UlqzBSn8RYkEoECzY=
github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250103152858-8973fd0c912b h1:UBXi9Yj8YSMHDDaxQLu273x1fWjyEL9xP58nuJsqZfg=
github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250103152858-8973fd0c912b/go.mod h1:Bmwq4lNb5tE47sydN0TKetcLEGbgl+VxHEWp4S0LI60=
github.com/smartcontractkit/chainlink-common v0.4.1-0.20250108194320-2ebd63bbb16e h1:8BStiP1F4W8AvjBRga0TYtjvAtkwN8oHYnHJztAlSF4=
diff --git a/integration-tests/load/go.mod b/integration-tests/load/go.mod
index 2c0703ed3b5..fbf204e160d 100644
--- a/integration-tests/load/go.mod
+++ b/integration-tests/load/go.mod
@@ -410,7 +410,7 @@ require (
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/smartcontractkit/chain-selectors v1.0.36 // indirect
github.com/smartcontractkit/chainlink-automation v0.8.1 // indirect
- github.com/smartcontractkit/chainlink-ccip v0.0.0-20250109124515-ff9d86b874ba // indirect
+ github.com/smartcontractkit/chainlink-ccip v0.0.0-20250110181647-9dba278f2103 // indirect
github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e // indirect
github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241216163550-fa030d178ba3 // indirect
github.com/smartcontractkit/chainlink-feeds v0.1.1 // indirect
diff --git a/integration-tests/load/go.sum b/integration-tests/load/go.sum
index 036b3992aaf..0ed3bcbf368 100644
--- a/integration-tests/load/go.sum
+++ b/integration-tests/load/go.sum
@@ -1397,8 +1397,8 @@ github.com/smartcontractkit/chain-selectors v1.0.36 h1:KSpO8I+JOiuyN4FuXsV471sPo
github.com/smartcontractkit/chain-selectors v1.0.36/go.mod h1:xsKM0aN3YGcQKTPRPDDtPx2l4mlTN1Djmg0VVXV40b8=
github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgBc2xpDKBco/Q4h4ydl6+UUU=
github.com/smartcontractkit/chainlink-automation v0.8.1/go.mod h1:Iij36PvWZ6blrdC5A/nrQUBuf3MH3JvsBB9sSyc9W08=
-github.com/smartcontractkit/chainlink-ccip v0.0.0-20250109124515-ff9d86b874ba h1:gisAer1YxKKui6LhxDgfuZ3OyrHVjHm/oK/0idusFeI=
-github.com/smartcontractkit/chainlink-ccip v0.0.0-20250109124515-ff9d86b874ba/go.mod h1:ncjd6mPZSRlelEqH/2KeLE1pU3UlqzBSn8RYkEoECzY=
+github.com/smartcontractkit/chainlink-ccip v0.0.0-20250110181647-9dba278f2103 h1:TeWnxdgSO2cYCKcBMwdkRcs/YdhSvXoWqqw7QWz/KeI=
+github.com/smartcontractkit/chainlink-ccip v0.0.0-20250110181647-9dba278f2103/go.mod h1:ncjd6mPZSRlelEqH/2KeLE1pU3UlqzBSn8RYkEoECzY=
github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250103152858-8973fd0c912b h1:UBXi9Yj8YSMHDDaxQLu273x1fWjyEL9xP58nuJsqZfg=
github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250103152858-8973fd0c912b/go.mod h1:Bmwq4lNb5tE47sydN0TKetcLEGbgl+VxHEWp4S0LI60=
github.com/smartcontractkit/chainlink-common v0.4.1-0.20250108194320-2ebd63bbb16e h1:8BStiP1F4W8AvjBRga0TYtjvAtkwN8oHYnHJztAlSF4=
diff --git a/integration-tests/smoke/ccip/ccip_batching_test.go b/integration-tests/smoke/ccip/ccip_batching_test.go
index 3752faa4e6e..6c204fa45a3 100644
--- a/integration-tests/smoke/ccip/ccip_batching_test.go
+++ b/integration-tests/smoke/ccip/ccip_batching_test.go
@@ -39,7 +39,7 @@ type batchTestSetup struct {
func newBatchTestSetup(t *testing.T) batchTestSetup {
// Setup 3 chains, with 2 lanes going to the dest.
- e, _ := testsetups.NewIntegrationEnvironment(
+ e, _, _ := testsetups.NewIntegrationEnvironment(
t,
changeset.WithMultiCall3(),
changeset.WithChains(3),
diff --git a/integration-tests/smoke/ccip/ccip_fee_boosting_test.go b/integration-tests/smoke/ccip/ccip_fee_boosting_test.go
index 3b0ebf22455..49f603a39f4 100644
--- a/integration-tests/smoke/ccip/ccip_fee_boosting_test.go
+++ b/integration-tests/smoke/ccip/ccip_fee_boosting_test.go
@@ -34,7 +34,7 @@ var (
)
func Test_CCIPFeeBoosting(t *testing.T) {
- e, _ := testsetups.NewIntegrationEnvironment(
+ e, _, _ := testsetups.NewIntegrationEnvironment(
t,
changeset.WithOCRConfigOverride(func(params changeset.CCIPOCRParams) changeset.CCIPOCRParams {
// Only 1 boost (=OCR round) is enough to cover the fee
diff --git a/integration-tests/smoke/ccip/ccip_fees_test.go b/integration-tests/smoke/ccip/ccip_fees_test.go
index 57a6bc58d82..d0f9cc71683 100644
--- a/integration-tests/smoke/ccip/ccip_fees_test.go
+++ b/integration-tests/smoke/ccip/ccip_fees_test.go
@@ -101,7 +101,7 @@ func setupTokens(
func Test_CCIPFees(t *testing.T) {
t.Parallel()
- tenv, _ := testsetups.NewIntegrationEnvironment(
+ tenv, _, _ := testsetups.NewIntegrationEnvironment(
t,
changeset.WithMultiCall3(),
)
diff --git a/integration-tests/smoke/ccip/ccip_gas_price_updates_test.go b/integration-tests/smoke/ccip/ccip_gas_price_updates_test.go
index 2c1d97f6c12..09023f1d321 100644
--- a/integration-tests/smoke/ccip/ccip_gas_price_updates_test.go
+++ b/integration-tests/smoke/ccip/ccip_gas_price_updates_test.go
@@ -26,7 +26,7 @@ func Test_CCIPGasPriceUpdates(t *testing.T) {
callOpts := &bind.CallOpts{Context: ctx}
var gasPriceExpiry = 5 * time.Second
- e, _ := testsetups.NewIntegrationEnvironment(t,
+ e, _, _ := testsetups.NewIntegrationEnvironment(t,
changeset.WithOCRConfigOverride(func(params changeset.CCIPOCRParams) changeset.CCIPOCRParams {
params.CommitOffChainConfig.RemoteGasPriceBatchWriteFrequency = *config.MustNewDuration(gasPriceExpiry)
return params
diff --git a/integration-tests/smoke/ccip/ccip_legacy_test.go b/integration-tests/smoke/ccip/ccip_legacy_test.go
deleted file mode 100644
index 2b5b6d77b58..00000000000
--- a/integration-tests/smoke/ccip/ccip_legacy_test.go
+++ /dev/null
@@ -1,51 +0,0 @@
-package smoke
-
-import (
- "context"
- "testing"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/stretchr/testify/require"
-
- "github.com/smartcontractkit/chainlink/deployment/ccip/changeset"
- "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/v1_5"
- testsetups "github.com/smartcontractkit/chainlink/integration-tests/testsetups/ccip"
- "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router"
-)
-
-// This test does not run in CI, it is only written as an example of how to write a test for the legacy CCIP
-func TestE2ELegacy(t *testing.T) {
- e, _ := testsetups.NewIntegrationEnvironment(t, changeset.WithLegacyDeployment())
- state, err := changeset.LoadOnchainState(e.Env)
- require.NoError(t, err)
- allChains := e.Env.AllChainSelectors()
- require.Len(t, allChains, 2)
- src, dest := allChains[0], allChains[1]
- srcChain := e.Env.Chains[src]
- destChain := e.Env.Chains[dest]
- pairs := []changeset.SourceDestPair{
- {SourceChainSelector: src, DestChainSelector: dest},
- }
- e.Env = v1_5.AddLanes(t, e.Env, state, pairs)
- // reload state after adding lanes
- state, err = changeset.LoadOnchainState(e.Env)
- require.NoError(t, err)
- sentEvent, err := v1_5.SendRequest(t, e.Env, state,
- changeset.WithSourceChain(src),
- changeset.WithDestChain(dest),
- changeset.WithTestRouter(false),
- changeset.WithEvm2AnyMessage(router.ClientEVM2AnyMessage{
- Receiver: common.LeftPadBytes(state.Chains[dest].Receiver.Address().Bytes(), 32),
- Data: []byte("hello"),
- TokenAmounts: nil,
- FeeToken: common.HexToAddress("0x0"),
- ExtraArgs: nil,
- }),
- )
- require.NoError(t, err)
- require.NotNil(t, sentEvent)
- destStartBlock, err := destChain.Client.HeaderByNumber(context.Background(), nil)
- require.NoError(t, err)
- v1_5.WaitForCommit(t, srcChain, destChain, state.Chains[dest].CommitStore[src], sentEvent.Message.SequenceNumber)
- v1_5.WaitForExecute(t, srcChain, destChain, state.Chains[dest].EVM2EVMOffRamp[src], []uint64{sentEvent.Message.SequenceNumber}, destStartBlock.Number.Uint64())
-}
diff --git a/integration-tests/smoke/ccip/ccip_message_limitations_test.go b/integration-tests/smoke/ccip/ccip_message_limitations_test.go
index f9299b735d0..2882a34af63 100644
--- a/integration-tests/smoke/ccip/ccip_message_limitations_test.go
+++ b/integration-tests/smoke/ccip/ccip_message_limitations_test.go
@@ -23,7 +23,7 @@ func Test_CCIPMessageLimitations(t *testing.T) {
ctx := testcontext.Get(t)
callOpts := &bind.CallOpts{Context: ctx}
- testEnv, _ := testsetups.NewIntegrationEnvironment(t)
+ testEnv, _, _ := testsetups.NewIntegrationEnvironment(t)
chains := maps.Keys(testEnv.Env.Chains)
onChainState, err := changeset.LoadOnchainState(testEnv.Env)
diff --git a/integration-tests/smoke/ccip/ccip_messaging_test.go b/integration-tests/smoke/ccip/ccip_messaging_test.go
index 8ee18a31918..f010bab5ee1 100644
--- a/integration-tests/smoke/ccip/ccip_messaging_test.go
+++ b/integration-tests/smoke/ccip/ccip_messaging_test.go
@@ -47,7 +47,7 @@ type messagingTestCaseOutput struct {
func Test_CCIPMessaging(t *testing.T) {
// Setup 2 chains and a single lane.
ctx := changeset.Context(t)
- e, _ := testsetups.NewIntegrationEnvironment(t)
+ e, _, _ := testsetups.NewIntegrationEnvironment(t)
state, err := changeset.LoadOnchainState(e.Env)
require.NoError(t, err)
diff --git a/integration-tests/smoke/ccip/ccip_migration_to_v_1_6_test.go b/integration-tests/smoke/ccip/ccip_migration_to_v_1_6_test.go
new file mode 100644
index 00000000000..0559054f2d6
--- /dev/null
+++ b/integration-tests/smoke/ccip/ccip_migration_to_v_1_6_test.go
@@ -0,0 +1,317 @@
+package smoke
+
+import (
+ "context"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/stretchr/testify/require"
+
+ chainselectors "github.com/smartcontractkit/chain-selectors"
+
+ "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext"
+
+ "github.com/smartcontractkit/chainlink/deployment/ccip/changeset"
+ "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/v1_5"
+ commonchangeset "github.com/smartcontractkit/chainlink/deployment/common/changeset"
+ testsetups "github.com/smartcontractkit/chainlink/integration-tests/testsetups/ccip"
+ "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router"
+)
+
+func TestMigrateFromV1_5ToV1_6(t *testing.T) {
+ // Deploy CCIP 1.5 with 3 chains and 4 nodes + 1 bootstrap
+ // Deploy 1.5 contracts (excluding pools and real RMN, use MockRMN to start, but including MCMS) .
+ e, _, tEnv := testsetups.NewIntegrationEnvironment(
+ t,
+ changeset.WithPrerequisiteDeployment(),
+ changeset.WithChains(3),
+ changeset.WithUsersPerChain(2),
+ // for in-memory test it is important to set the dest chain id as 1337 otherwise the config digest will not match
+ // between nodes' calculated digest and the digest set on the contract
+ changeset.WithChainIds([]uint64{chainselectors.GETH_TESTNET.EvmChainID}),
+ )
+ state, err := changeset.LoadOnchainState(e.Env)
+ require.NoError(t, err)
+ allChainsExcept1337 := e.Env.AllChainSelectorsExcluding([]uint64{chainselectors.GETH_TESTNET.Selector})
+ require.Contains(t, e.Env.AllChainSelectors(), chainselectors.GETH_TESTNET.Selector)
+ require.Len(t, allChainsExcept1337, 2)
+ src1, src2, dest := allChainsExcept1337[0], allChainsExcept1337[1], chainselectors.GETH_TESTNET.Selector
+ pairs := []changeset.SourceDestPair{
+ // as mentioned in the comment above, the dest chain id should be 1337
+ {SourceChainSelector: src1, DestChainSelector: dest},
+ {SourceChainSelector: src2, DestChainSelector: dest},
+ }
+ // wire up all lanes
+ // deploy onRamp, commit store, offramp , set ocr2config and send corresponding jobs
+ e.Env = v1_5.AddLanes(t, e.Env, state, pairs)
+
+ // reload state after adding lanes
+ state, err = changeset.LoadOnchainState(e.Env)
+ require.NoError(t, err)
+ tEnv.UpdateDeployedEnvironment(e)
+ // ensure that all lanes are functional
+ for _, pair := range pairs {
+ sentEvent, err := v1_5.SendRequest(t, e.Env, state,
+ changeset.WithSourceChain(pair.SourceChainSelector),
+ changeset.WithDestChain(pair.DestChainSelector),
+ changeset.WithTestRouter(false),
+ changeset.WithEvm2AnyMessage(router.ClientEVM2AnyMessage{
+ Receiver: common.LeftPadBytes(state.Chains[pair.DestChainSelector].Receiver.Address().Bytes(), 32),
+ Data: []byte("hello"),
+ TokenAmounts: nil,
+ FeeToken: common.HexToAddress("0x0"),
+ ExtraArgs: nil,
+ }),
+ )
+ require.NoError(t, err)
+ require.NotNil(t, sentEvent)
+ destChain := e.Env.Chains[pair.DestChainSelector]
+ destStartBlock, err := destChain.Client.HeaderByNumber(context.Background(), nil)
+ require.NoError(t, err)
+ v1_5.WaitForCommit(t, e.Env.Chains[pair.SourceChainSelector], destChain, state.Chains[dest].CommitStore[src1], sentEvent.Message.SequenceNumber)
+ v1_5.WaitForExecute(t, e.Env.Chains[pair.SourceChainSelector], destChain, state.Chains[dest].EVM2EVMOffRamp[src1], []uint64{sentEvent.Message.SequenceNumber}, destStartBlock.Number.Uint64())
+ }
+
+ // now that all 1.5 lanes work transfer ownership of the contracts to MCMS
+ contractsByChain := make(map[uint64][]common.Address)
+ for _, chain := range e.Env.AllChainSelectors() {
+ contractsByChain[chain] = []common.Address{
+ state.Chains[chain].Router.Address(),
+ state.Chains[chain].RMNProxy.Address(),
+ state.Chains[chain].PriceRegistry.Address(),
+ state.Chains[chain].TokenAdminRegistry.Address(),
+ state.Chains[chain].MockRMN.Address(),
+ }
+ if state.Chains[chain].EVM2EVMOnRamp != nil {
+ for _, onRamp := range state.Chains[chain].EVM2EVMOnRamp {
+ contractsByChain[chain] = append(contractsByChain[chain], onRamp.Address())
+ }
+ }
+ if state.Chains[chain].EVM2EVMOffRamp != nil {
+ for _, offRamp := range state.Chains[chain].EVM2EVMOffRamp {
+ contractsByChain[chain] = append(contractsByChain[chain], offRamp.Address())
+ }
+ }
+ }
+
+ e.Env, err = commonchangeset.ApplyChangesets(t, e.Env, e.TimelockContracts(t), []commonchangeset.ChangesetApplication{
+ {
+ Changeset: commonchangeset.WrapChangeSet(commonchangeset.TransferToMCMSWithTimelock),
+ Config: commonchangeset.TransferToMCMSWithTimelockConfig{
+ ContractsByChain: contractsByChain,
+ MinDelay: 0,
+ },
+ },
+ })
+ require.NoError(t, err)
+ // add 1.6 contracts to the environment and send 1.6 jobs
+ // First we need to deploy Homechain contracts and restart the nodes with updated cap registry
+ // in this test we have already deployed home chain contracts and the nodes are already running with the deployed cap registry.
+ e = changeset.AddCCIPContractsToEnvironment(t, e.Env.AllChainSelectors(), tEnv)
+ // Set RMNProxy to point to RMNRemote.
+ // nonce manager should point to 1.5 ramps
+ e.Env, err = commonchangeset.ApplyChangesets(t, e.Env, e.TimelockContracts(t), []commonchangeset.ChangesetApplication{
+ {
+ // as we have already transferred ownership for RMNProxy to MCMS, it needs to be done via MCMS proposal
+ Changeset: commonchangeset.WrapChangeSet(changeset.SetRMNRemoteOnRMNProxy),
+ Config: changeset.SetRMNRemoteOnRMNProxyConfig{
+ ChainSelectors: e.Env.AllChainSelectors(),
+ MCMSConfig: &changeset.MCMSConfig{
+ MinDelay: 0,
+ },
+ },
+ },
+ {
+ Changeset: commonchangeset.WrapChangeSet(changeset.UpdateNonceManagersCS),
+ Config: changeset.UpdateNonceManagerConfig{
+ // we only have lanes between src1 --> dest
+ UpdatesByChain: map[uint64]changeset.NonceManagerUpdate{
+ src1: {
+ PreviousRampsArgs: []changeset.PreviousRampCfg{
+ {
+ RemoteChainSelector: dest,
+ EnableOnRamp: true,
+ },
+ },
+ },
+ src2: {
+ PreviousRampsArgs: []changeset.PreviousRampCfg{
+ {
+ RemoteChainSelector: dest,
+ EnableOnRamp: true,
+ },
+ },
+ },
+ dest: {
+ PreviousRampsArgs: []changeset.PreviousRampCfg{
+ {
+ RemoteChainSelector: src1,
+ EnableOffRamp: true,
+ },
+ {
+ RemoteChainSelector: src2,
+ EnableOffRamp: true,
+ },
+ },
+ },
+ },
+ },
+ },
+ })
+ require.NoError(t, err)
+ state, err = changeset.LoadOnchainState(e.Env)
+ require.NoError(t, err)
+
+ // Enable a single 1.6 lane with test router
+ changeset.AddLaneWithDefaultPricesAndFeeQuoterConfig(t, &e, state, src1, dest, true)
+ require.GreaterOrEqual(t, len(e.Users[src1]), 2)
+ startBlocks := make(map[uint64]*uint64)
+ latesthdr, err := e.Env.Chains[dest].Client.HeaderByNumber(testcontext.Get(t), nil)
+ require.NoError(t, err)
+ block := latesthdr.Number.Uint64()
+ startBlocks[dest] = &block
+ expectedSeqNumExec := make(map[changeset.SourceDestPair][]uint64)
+ msgSentEvent, err := changeset.DoSendRequest(
+ t, e.Env, state,
+ changeset.WithSourceChain(src1),
+ changeset.WithDestChain(dest),
+ changeset.WithTestRouter(true),
+ // Send traffic across single 1.6 lane with a DIFFERENT ( very important to not mess with real sender nonce) sender
+ // from test router to ensure 1.6 is working.
+ changeset.WithSender(e.Users[src1][1]),
+ changeset.WithEvm2AnyMessage(router.ClientEVM2AnyMessage{
+ Receiver: common.LeftPadBytes(state.Chains[dest].Receiver.Address().Bytes(), 32),
+ Data: []byte("hello"),
+ TokenAmounts: nil,
+ FeeToken: common.HexToAddress("0x0"),
+ ExtraArgs: nil,
+ }))
+ require.NoError(t, err)
+
+ expectedSeqNumExec[changeset.SourceDestPair{
+ SourceChainSelector: src1,
+ DestChainSelector: dest,
+ }] = []uint64{msgSentEvent.SequenceNumber}
+
+ // Wait for all exec reports to land
+ changeset.ConfirmExecWithSeqNrsForAll(t, e.Env, state, expectedSeqNumExec, startBlocks)
+
+ // send a message from real router, the send requested event should be received in 1.5 onRamp
+ // the request should get delivered to 1.5 offRamp
+ sentEventBeforeSwitch, err := v1_5.SendRequest(t, e.Env, state,
+ changeset.WithSourceChain(src1),
+ changeset.WithDestChain(dest),
+ changeset.WithTestRouter(false),
+ changeset.WithEvm2AnyMessage(router.ClientEVM2AnyMessage{
+ Receiver: common.LeftPadBytes(state.Chains[dest].Receiver.Address().Bytes(), 32),
+ Data: []byte("hello"),
+ TokenAmounts: nil,
+ FeeToken: common.HexToAddress("0x0"),
+ ExtraArgs: nil,
+ }),
+ )
+ require.NoError(t, err)
+ require.NotNil(t, sentEventBeforeSwitch)
+
+ // now that the 1.6 lane is working, we can enable the real router
+ e.Env, err = commonchangeset.ApplyChangesets(t, e.Env, e.TimelockContracts(t), []commonchangeset.ChangesetApplication{
+ {
+ Changeset: commonchangeset.WrapChangeSet(changeset.UpdateOnRampsDests),
+ Config: changeset.UpdateOnRampDestsConfig{
+ UpdatesByChain: map[uint64]map[uint64]changeset.OnRampDestinationUpdate{
+ src1: {
+ dest: {
+ IsEnabled: true,
+ TestRouter: false,
+ AllowListEnabled: false,
+ },
+ },
+ },
+ },
+ },
+ {
+ Changeset: commonchangeset.WrapChangeSet(changeset.UpdateOffRampSources),
+ Config: changeset.UpdateOffRampSourcesConfig{
+ UpdatesByChain: map[uint64]map[uint64]changeset.OffRampSourceUpdate{
+ dest: {
+ src1: {
+ IsEnabled: true,
+ TestRouter: false,
+ },
+ },
+ },
+ },
+ },
+ {
+ // this needs to be MCMS proposal as the router contract is owned by MCMS
+ Changeset: commonchangeset.WrapChangeSet(changeset.UpdateRouterRamps),
+ Config: changeset.UpdateRouterRampsConfig{
+ TestRouter: false,
+ MCMS: &changeset.MCMSConfig{
+ MinDelay: 0,
+ },
+ UpdatesByChain: map[uint64]changeset.RouterUpdates{
+ // onRamp update on source chain
+ src1: {
+ OnRampUpdates: map[uint64]bool{
+ dest: true,
+ },
+ },
+ // offramp update on dest chain
+ dest: {
+ OffRampUpdates: map[uint64]bool{
+ src1: true,
+ },
+ },
+ },
+ },
+ },
+ })
+ require.NoError(t, err)
+
+ // send a message from real router the send requested event should be received in 1.6 onRamp
+ // the request should get delivered to 1.6 offRamp
+ destStartBlock, err := e.Env.Chains[dest].Client.HeaderByNumber(context.Background(), nil)
+ require.NoError(t, err)
+ sentEventAfterSwitch, err := changeset.DoSendRequest(
+ t, e.Env, state,
+ changeset.WithSourceChain(src1),
+ changeset.WithDestChain(dest),
+ changeset.WithTestRouter(false),
+ changeset.WithEvm2AnyMessage(router.ClientEVM2AnyMessage{
+ Receiver: common.LeftPadBytes(state.Chains[dest].Receiver.Address().Bytes(), 32),
+ Data: []byte("hello"),
+ TokenAmounts: nil,
+ FeeToken: common.HexToAddress("0x0"),
+ ExtraArgs: nil,
+ }))
+ require.NoError(t, err)
+ // verify that before switch message is received in 1.5 offRamp
+ v1_5.WaitForExecute(t, e.Env.Chains[src1], e.Env.Chains[dest], state.Chains[dest].EVM2EVMOffRamp[src1],
+ []uint64{sentEventBeforeSwitch.Message.SequenceNumber}, destStartBlock.Number.Uint64())
+
+ // verify that after switch message is received in 1.6 offRamp
+ expectedSeqNumExec[changeset.SourceDestPair{
+ SourceChainSelector: src1,
+ DestChainSelector: dest,
+ }] = []uint64{sentEventAfterSwitch.SequenceNumber}
+ changeset.ConfirmExecWithSeqNrsForAll(t, e.Env, state, expectedSeqNumExec, startBlocks)
+
+ // confirm that the other lane src2->dest is still working with v1.5
+ sentEventOnOtherLane, err := v1_5.SendRequest(t, e.Env, state,
+ changeset.WithSourceChain(src2),
+ changeset.WithDestChain(dest),
+ changeset.WithTestRouter(false),
+ changeset.WithEvm2AnyMessage(router.ClientEVM2AnyMessage{
+ Receiver: common.LeftPadBytes(state.Chains[dest].Receiver.Address().Bytes(), 32),
+ Data: []byte("hello"),
+ TokenAmounts: nil,
+ FeeToken: common.HexToAddress("0x0"),
+ ExtraArgs: nil,
+ }),
+ )
+ require.NoError(t, err)
+ require.NotNil(t, sentEventOnOtherLane)
+ v1_5.WaitForExecute(t, e.Env.Chains[src2], e.Env.Chains[dest], state.Chains[dest].EVM2EVMOffRamp[src2],
+ []uint64{sentEventOnOtherLane.Message.SequenceNumber}, destStartBlock.Number.Uint64())
+}
diff --git a/integration-tests/smoke/ccip/ccip_ooo_execution_test.go b/integration-tests/smoke/ccip/ccip_ooo_execution_test.go
index e3da473984d..272c87e9996 100644
--- a/integration-tests/smoke/ccip/ccip_ooo_execution_test.go
+++ b/integration-tests/smoke/ccip/ccip_ooo_execution_test.go
@@ -33,7 +33,7 @@ import (
func Test_OutOfOrderExecution(t *testing.T) {
lggr := logger.TestLogger(t)
ctx := tests.Context(t)
- tenv, _ := testsetups.NewIntegrationEnvironment(
+ tenv, _, _ := testsetups.NewIntegrationEnvironment(
t,
changeset.WithUSDC(),
changeset.WithUSDCAttestationMissing(),
diff --git a/integration-tests/smoke/ccip/ccip_rmn_test.go b/integration-tests/smoke/ccip/ccip_rmn_test.go
index 7036260d130..67f99cd5a7b 100644
--- a/integration-tests/smoke/ccip/ccip_rmn_test.go
+++ b/integration-tests/smoke/ccip/ccip_rmn_test.go
@@ -247,7 +247,7 @@ func runRmnTestCase(t *testing.T, tc rmnTestCase) {
ctx := testcontext.Get(t)
t.Logf("Running RMN test case: %s", tc.name)
- envWithRMN, rmnCluster := testsetups.NewIntegrationEnvironment(t,
+ envWithRMN, rmnCluster, _ := testsetups.NewIntegrationEnvironment(t,
changeset.WithRMNEnabled(len(tc.rmnNodes)),
)
t.Logf("envWithRmn: %#v", envWithRMN)
diff --git a/integration-tests/smoke/ccip/ccip_token_price_updates_test.go b/integration-tests/smoke/ccip/ccip_token_price_updates_test.go
index fb7ddc847d4..e54790de24c 100644
--- a/integration-tests/smoke/ccip/ccip_token_price_updates_test.go
+++ b/integration-tests/smoke/ccip/ccip_token_price_updates_test.go
@@ -28,7 +28,7 @@ func Test_CCIPTokenPriceUpdates(t *testing.T) {
callOpts := &bind.CallOpts{Context: ctx}
var tokenPriceExpiry = 5 * time.Second
- e, _ := testsetups.NewIntegrationEnvironment(t,
+ e, _, _ := testsetups.NewIntegrationEnvironment(t,
changeset.WithOCRConfigOverride(func(params changeset.CCIPOCRParams) changeset.CCIPOCRParams {
params.CommitOffChainConfig.TokenPriceBatchWriteFrequency = *config.MustNewDuration(tokenPriceExpiry)
return params
diff --git a/integration-tests/smoke/ccip/ccip_token_transfer_test.go b/integration-tests/smoke/ccip/ccip_token_transfer_test.go
index c5cabfe63e4..7946d6f8c7c 100644
--- a/integration-tests/smoke/ccip/ccip_token_transfer_test.go
+++ b/integration-tests/smoke/ccip/ccip_token_transfer_test.go
@@ -22,7 +22,7 @@ func TestTokenTransfer(t *testing.T) {
lggr := logger.TestLogger(t)
ctx := tests.Context(t)
- tenv, _ := testsetups.NewIntegrationEnvironment(t,
+ tenv, _, _ := testsetups.NewIntegrationEnvironment(t,
changeset.WithUsersPerChain(3))
e := tenv.Env
diff --git a/integration-tests/smoke/ccip/ccip_usdc_test.go b/integration-tests/smoke/ccip/ccip_usdc_test.go
index 7bea68a9cbf..33af1570943 100644
--- a/integration-tests/smoke/ccip/ccip_usdc_test.go
+++ b/integration-tests/smoke/ccip/ccip_usdc_test.go
@@ -32,7 +32,7 @@ import (
func TestUSDCTokenTransfer(t *testing.T) {
lggr := logger.TestLogger(t)
ctx := tests.Context(t)
- tenv, _ := testsetups.NewIntegrationEnvironment(t,
+ tenv, _, _ := testsetups.NewIntegrationEnvironment(t,
changeset.WithUsersPerChain(3),
changeset.WithChains(3),
changeset.WithUSDC(),
diff --git a/integration-tests/testsetups/ccip/test_helpers.go b/integration-tests/testsetups/ccip/test_helpers.go
index 6725ac1df9b..889c6b1cdf5 100644
--- a/integration-tests/testsetups/ccip/test_helpers.go
+++ b/integration-tests/testsetups/ccip/test_helpers.go
@@ -53,17 +53,26 @@ import (
// DeployedLocalDevEnvironment is a helper struct for setting up a local dev environment with docker
type DeployedLocalDevEnvironment struct {
changeset.DeployedEnv
- testEnv *test_env.CLClusterTestEnv
- DON *devenv.DON
- devEnvTestCfg tc.TestConfig
- devEnvCfg *devenv.EnvironmentConfig
+ testEnv *test_env.CLClusterTestEnv
+ DON *devenv.DON
+ GenericTCConfig *changeset.TestConfigs
+ devEnvTestCfg tc.TestConfig
+ devEnvCfg *devenv.EnvironmentConfig
}
func (l *DeployedLocalDevEnvironment) DeployedEnvironment() changeset.DeployedEnv {
return l.DeployedEnv
}
-func (l *DeployedLocalDevEnvironment) StartChains(t *testing.T, _ *changeset.TestConfigs) {
+func (l *DeployedLocalDevEnvironment) UpdateDeployedEnvironment(env changeset.DeployedEnv) {
+ l.DeployedEnv = env
+}
+
+func (l *DeployedLocalDevEnvironment) TestConfigs() *changeset.TestConfigs {
+ return l.GenericTCConfig
+}
+
+func (l *DeployedLocalDevEnvironment) StartChains(t *testing.T) {
lggr := logger.TestLogger(t)
ctx := testcontext.Get(t)
envConfig, testEnv, cfg := CreateDockerEnv(t)
@@ -91,7 +100,7 @@ func (l *DeployedLocalDevEnvironment) StartChains(t *testing.T, _ *changeset.Tes
l.DeployedEnv.ReplayBlocks = replayBlocks
}
-func (l *DeployedLocalDevEnvironment) StartNodes(t *testing.T, _ *changeset.TestConfigs, crConfig deployment.CapabilityRegistryConfig) {
+func (l *DeployedLocalDevEnvironment) StartNodes(t *testing.T, crConfig deployment.CapabilityRegistryConfig) {
require.NotNil(t, l.testEnv, "docker env is empty, start chains first")
require.NotEmpty(t, l.devEnvTestCfg, "integration test config is empty, start chains first")
require.NotNil(t, l.devEnvCfg, "dev environment config is empty, start chains first")
@@ -143,7 +152,7 @@ func (l *DeployedLocalDevEnvironment) RestartChainlinkNodes(t *testing.T) error
// if CCIP_V16_TEST_ENV is set to 'docker', it creates a docker environment with test config provided under testconfig/ccip/ccip.toml
// It also creates a RMN cluster if the test config has RMN enabled
// It returns the deployed environment and RMN cluster ( in case of RMN enabled)
-func NewIntegrationEnvironment(t *testing.T, opts ...changeset.TestOps) (changeset.DeployedEnv, devenv.RMNCluster) {
+func NewIntegrationEnvironment(t *testing.T, opts ...changeset.TestOps) (changeset.DeployedEnv, devenv.RMNCluster, changeset.TestEnvironment) {
testCfg := changeset.DefaultTestConfigs()
for _, opt := range opts {
opt(testCfg)
@@ -153,17 +162,20 @@ func NewIntegrationEnvironment(t *testing.T, opts ...changeset.TestOps) (changes
require.NoError(t, testCfg.Validate(), "invalid test config")
switch testCfg.Type {
case changeset.Memory:
- memEnv := changeset.NewMemoryEnvironment(t, opts...)
- return memEnv, devenv.RMNCluster{}
+ dEnv, memEnv := changeset.NewMemoryEnvironment(t, opts...)
+ return dEnv, devenv.RMNCluster{}, memEnv
case changeset.Docker:
- dockerEnv := &DeployedLocalDevEnvironment{}
- if testCfg.LegacyDeployment {
- deployedEnv := changeset.NewLegacyEnvironment(t, testCfg, dockerEnv)
+ dockerEnv := &DeployedLocalDevEnvironment{
+ GenericTCConfig: testCfg,
+ }
+ if testCfg.PrerequisiteDeploymentOnly {
+ deployedEnv := changeset.NewEnvironmentWithPrerequisitesContracts(t, dockerEnv)
require.NotNil(t, dockerEnv.testEnv, "empty docker environment")
- return deployedEnv, devenv.RMNCluster{}
+ dockerEnv.UpdateDeployedEnvironment(deployedEnv)
+ return deployedEnv, devenv.RMNCluster{}, dockerEnv
}
if testCfg.RMNEnabled {
- deployedEnv := changeset.NewEnvironmentWithJobsAndContracts(t, testCfg, dockerEnv)
+ deployedEnv := changeset.NewEnvironmentWithJobsAndContracts(t, dockerEnv)
l := logging.GetTestLogger(t)
require.NotNil(t, dockerEnv.testEnv, "empty docker environment")
config := GenerateTestRMNConfig(t, testCfg.NumOfRMNNodes, deployedEnv, MustNetworksToRPCMap(dockerEnv.testEnv.EVMNetworks))
@@ -178,25 +190,29 @@ func NewIntegrationEnvironment(t *testing.T, opts ...changeset.TestOps) (changes
dockerEnv.devEnvTestCfg.CCIP.RMNConfig.GetAFN2ProxyVersion(),
)
require.NoError(t, err)
- return deployedEnv, *rmnCluster
+ dockerEnv.UpdateDeployedEnvironment(deployedEnv)
+ return deployedEnv, *rmnCluster, dockerEnv
}
if testCfg.CreateJobAndContracts {
- deployedEnv := changeset.NewEnvironmentWithJobsAndContracts(t, testCfg, dockerEnv)
+ deployedEnv := changeset.NewEnvironmentWithJobsAndContracts(t, dockerEnv)
require.NotNil(t, dockerEnv.testEnv, "empty docker environment")
- return deployedEnv, devenv.RMNCluster{}
+ dockerEnv.UpdateDeployedEnvironment(deployedEnv)
+ return deployedEnv, devenv.RMNCluster{}, dockerEnv
}
if testCfg.CreateJob {
- deployedEnv := changeset.NewEnvironmentWithJobs(t, testCfg, dockerEnv)
+ deployedEnv := changeset.NewEnvironmentWithJobs(t, dockerEnv)
require.NotNil(t, dockerEnv.testEnv, "empty docker environment")
- return deployedEnv, devenv.RMNCluster{}
+ dockerEnv.UpdateDeployedEnvironment(deployedEnv)
+ return deployedEnv, devenv.RMNCluster{}, dockerEnv
}
- deployedEnv := changeset.NewEnvironment(t, testCfg, dockerEnv)
+ deployedEnv := changeset.NewEnvironment(t, dockerEnv)
require.NotNil(t, dockerEnv.testEnv, "empty docker environment")
- return deployedEnv, devenv.RMNCluster{}
+ dockerEnv.UpdateDeployedEnvironment(deployedEnv)
+ return deployedEnv, devenv.RMNCluster{}, dockerEnv
default:
require.Failf(t, "Type %s not supported in integration tests choose between %s and %s", string(testCfg.Type), changeset.Memory, changeset.Docker)
}
- return changeset.DeployedEnv{}, devenv.RMNCluster{}
+ return changeset.DeployedEnv{}, devenv.RMNCluster{}, nil
}
func MustNetworksToRPCMap(evmNetworks []*blockchain.EVMNetwork) map[uint64]string {
From adf13dc1f11e2321a9b67a483c6f1e0594e77c85 Mon Sep 17 00:00:00 2001
From: HenryNguyen5 <6404866+HenryNguyen5@users.noreply.github.com>
Date: Fri, 10 Jan 2025 14:45:03 -0800
Subject: [PATCH 34/91] Keystone in CRIB (#14326)
* Add node api wrapper for ergonomic cmd usage
* Add streams trigger template
* Add mock external adapter for v03 mercury
* First pass of streams trigger provisioning
* WIP: Add capabilities registry provisioner script
* Update nix flake
* Fixup provisioning scripts
* Change default chainid to be 1337
* Add nil check for balances
* Add ability to skip tls verification for local dev
* Gently fail on not loading contracts for ocr job deletion
* fixup! Change default chainid to be 1337
* Formatting
* Change ocr file flag default
* Allow for multiple OCR2KB selection in key fetching
* Support on/offchain transmitter OCR3 config generation
* Properly reset clientmethod on each invocation
* Add mercury contract deployment feature
* Get oracles to successfully connect to each other
* Keep OCR3 and OCR2 config separate
* Add goreleaser setup for mock ea
* Add support for updating bridges
* Add UpdateBridge CLI command
* Cleanup comments
* Fix typo
* Add revert detection and revert reason extraction
* Add missing env field to CR struct
* Fix CR deployment bugs
* Fix trigger capability typo
* Add external registry and capability p2p config gen
* Add redial support for logging in
* Fix capability registration
* HACK: Add keystone workflow deployment to streams trigger cmd
* Typo
* Log ocr3 config more extensively
* Set isPublic to false for all-in-one DON
* Use nodeapi for deleting ocr3 jobs
* Have mock EA return consistent prices every 10 sec
* Remove pluginconfig to properly enable trigger
* Add additional logging
* Fix rebase errors
* Shim ksdeploy types
* Fix goreleaser config for mock ea
* Dont depend on cgo for mock EA
* Handle aptos key creation
* Tune mercury OCR rounds to be less freq
* Use deployments rather than pods
* Overhaul node host + url handling
* Add dummy encryption public key to nodes
* Add missing ctx
* Initial multidon support
* Fix ingress generation for postprevision
* Fix argument ordering
* Fix nodelist sorting and evmconfig.workflow configuration
* Update tests
* Assign keystone workflows to workflow nodes
* Expose capabilities on WorkflowDON
* Skip bootstrap node for keystone workflows
* Refactor nodelists + pubkeys -> nodesets
* Skip adding bootstrap nodes to capability registry
* Skip bootstrap node for mercury OCR config
* Formatting
* Fix stale print statement
* Bump nodeset size to minimum 5 since > 2F+1
* Use service name for DNS resolution
* Update tests
* Fix missing / incorrect fields for keystone workflow
* Update gomods
* Simplify node key handling
* Formatting
* Add test mocks
* Refactor - Use single entrypoint for provisiong keystone in CRIB
* Add OCR3 caching
* Refactor provisioning flags and improve argument validation in keystone script
* Refactor: Remove stale references and fix refactor related bugs
* Create artefacts dir if it doesnt exist
* Simplify jobspec and bridge handling
* Remove unneeded token transfer calls
* Refactor: Cleanup logging, add more tx caching
* Fix post-rebase errors
* Fix OCR3 digest comparison
* Remove extra cmd cruft
* Remove unused func
* Undo transmitter changes
* Revert "Add test mocks"
This reverts commit 75cafe96a08d6495c8040076a053e3e1a33e4154.
* Fix caching
* Remove deprecated assertion call
* Update gomod
* Fix linter warns
* Add changeset
* Add additional logging around node sets and key fetching
* Run gomodtidy
* Fix additional lints
* Harden API request logic
* Readme WIP
* Clean up lints, remove old readme
* Increase retry interval
* Update goreleaser to 2.4.4-pro
* Handle non postfix path
* Resolve darwin shell hook from git root
* Bump streams trigger cap to 1.1.0
* Create toolkit sub-cli
* Cleanup toolkit
* Reverse URLs for nodelists
* Update snapshots
* Remove unneeded gosec ignore
* Update gomods
* Log when we set ocr3 config
* Cleanup argument parsing
* Fix nodes list parsing
* Fix lints + address feedback
* Update gomods to point to this branches pseudo version
* Update gomods to point to this branches pseudo version
* Bump wrappers to 1.1.0
* fix test indentation, quoting
* revert bad merge to main; workflow.go, cap_encoder*
* linter
---------
Co-authored-by: Justin Kaseman
Co-authored-by: krehermann <16602512+krehermann@users.noreply.github.com>
---
.changeset/loud-birds-remain.md | 5 +
core/cmd/bridge_commands.go | 23 +
core/cmd/bridge_commands_test.go | 42 ++
core/scripts/common/helpers.go | 16 +-
core/scripts/go.mod | 8 +-
.../keystone/01_deploy_contracts-sample.sh | 11 -
.../keystone/02_deploy_jobspecs-sample.sh | 7 -
core/scripts/keystone/03_gen_crib-sample.sh | 6 -
.../keystone/04_delete_ocr3_jobs-sample.sh | 3 -
...initialize_capabilities_registry-sample.sh | 8 -
core/scripts/keystone/README.md | 91 ---
core/scripts/keystone/artefacts/README.md | 1 -
core/scripts/keystone/main.go | 8 +-
.../keystone/src/01_deploy_contracts_cmd.go | 226 -------
.../keystone/src/01_provision_keystone.go | 217 +++++++
core/scripts/keystone/src/01_toolkit.go | 212 +++++++
core/scripts/keystone/src/01_toolkit_test.go | 49 ++
.../keystone/src/02_deploy_jobspecs_cmd.go | 165 -----
.../src/02_deploy_keystone_workflows.go | 134 ++++
.../src/02_deploy_keystone_workflows_test.go | 21 +
.../keystone/src/02_fund_transmitters.go | 52 ++
.../src/02_provision_capabilities_registry.go | 68 +++
.../scripts/keystone/src/02_provision_crib.go | 310 ++++++++++
.../keystone/src/02_provision_crib_test.go | 44 ++
.../src/02_provision_forwarder_contract.go | 33 +
.../src/02_provision_ocr3_capability.go | 286 +++++++++
.../src/02_provision_ocr3_capability_test.go | 67 ++
...02_provision_streams_trigger_capability.go | 522 ++++++++++++++++
...ovision_streams_trigger_capability_test.go | 57 ++
.../src/03_gen_crib_cluster_overrides_cmd.go | 86 ---
.../03_gen_crib_cluster_overrides_cmd_test.go | 19 -
.../keystone/src/04_delete_ocr3_jobs_cmd.go | 101 ---
...deploy_initialize_capabilities_registry.go | 82 +--
.../keystone/src/06_deploy_workflows_cmd.go | 71 ---
.../keystone/src/07_delete_workflows_cmd.go | 74 ---
.../src/88_capabilities_registry_helpers.go | 578 ++++++++++++++++++
.../keystone/src/88_contracts_helpers.go | 192 ++++++
core/scripts/keystone/src/88_gen_jobspecs.go | 91 ---
.../keystone/src/88_gen_jobspecs_test.go | 37 --
.../keystone/src/88_gen_ocr3_config.go | 20 -
.../keystone/src/88_gen_ocr3_config_test.go | 31 -
.../keystone/src/88_jobspecs_helpers.go | 53 ++
core/scripts/keystone/src/88_ocr_helpers.go | 69 +++
core/scripts/keystone/src/99_app.go | 386 +++++++++++-
core/scripts/keystone/src/99_crib_client.go | 71 ++-
core/scripts/keystone/src/99_fetch_keys.go | 397 ++++++------
core/scripts/keystone/src/99_files.go | 67 +-
core/scripts/keystone/src/99_files_test.go | 36 --
core/scripts/keystone/src/99_k8s_client.go | 74 ++-
core/scripts/keystone/src/99_nodes.go | 72 ---
.../02_deploy_keystone_workflows_test.snap | 57 ++
.../__snapshots__/02_provision_crib_test.snap | 415 +++++++++++++
.../02_provision_ocr3_capability_test.snap | 65 ++
...ision_streams_trigger_capability_test.snap | 50 ++
...3_gen_crib_cluster_overrides_cmd_test.snap | 44 --
.../__snapshots__/88_gen_jobspecs_test.snap | 140 -----
.../88_gen_ocr3_config_test.snap | 23 -
.../src/external-adapter/.goreleaser.yaml | 49 ++
.../external-adapter/99_external_adapter.go | 154 +++++
.../keystone/src/external-adapter/Dockerfile | 5 +
.../keystone/src/testdata/NodeList.txt | 5 -
.../keystone/src/testdata/PublicKeys.json | 57 --
.../keystone/src/testdata/node_sets.json | 298 +++++++++
.../scripts/keystone/templates/bootstrap.toml | 9 -
.../keystone/templates/crib-overrides.yaml | 41 --
core/scripts/keystone/templates/oracle.toml | 27 -
core/services/job/models.go | 2 +-
deployment/go.mod | 2 +-
integration-tests/go.mod | 4 +-
integration-tests/load/go.mod | 6 +-
shell.nix | 3 +-
tools/goreleaser-config/go.mod | 2 +-
72 files changed, 4851 insertions(+), 1906 deletions(-)
create mode 100644 .changeset/loud-birds-remain.md
delete mode 100755 core/scripts/keystone/01_deploy_contracts-sample.sh
delete mode 100755 core/scripts/keystone/02_deploy_jobspecs-sample.sh
delete mode 100755 core/scripts/keystone/03_gen_crib-sample.sh
delete mode 100755 core/scripts/keystone/04_delete_ocr3_jobs-sample.sh
delete mode 100755 core/scripts/keystone/05_deploy_and_initialize_capabilities_registry-sample.sh
delete mode 100644 core/scripts/keystone/README.md
delete mode 100644 core/scripts/keystone/artefacts/README.md
delete mode 100644 core/scripts/keystone/src/01_deploy_contracts_cmd.go
create mode 100644 core/scripts/keystone/src/01_provision_keystone.go
create mode 100644 core/scripts/keystone/src/01_toolkit.go
create mode 100644 core/scripts/keystone/src/01_toolkit_test.go
delete mode 100644 core/scripts/keystone/src/02_deploy_jobspecs_cmd.go
create mode 100644 core/scripts/keystone/src/02_deploy_keystone_workflows.go
create mode 100644 core/scripts/keystone/src/02_deploy_keystone_workflows_test.go
create mode 100644 core/scripts/keystone/src/02_fund_transmitters.go
create mode 100644 core/scripts/keystone/src/02_provision_capabilities_registry.go
create mode 100644 core/scripts/keystone/src/02_provision_crib.go
create mode 100644 core/scripts/keystone/src/02_provision_crib_test.go
create mode 100644 core/scripts/keystone/src/02_provision_forwarder_contract.go
create mode 100644 core/scripts/keystone/src/02_provision_ocr3_capability.go
create mode 100644 core/scripts/keystone/src/02_provision_ocr3_capability_test.go
create mode 100644 core/scripts/keystone/src/02_provision_streams_trigger_capability.go
create mode 100644 core/scripts/keystone/src/02_provision_streams_trigger_capability_test.go
delete mode 100644 core/scripts/keystone/src/03_gen_crib_cluster_overrides_cmd.go
delete mode 100644 core/scripts/keystone/src/03_gen_crib_cluster_overrides_cmd_test.go
delete mode 100644 core/scripts/keystone/src/04_delete_ocr3_jobs_cmd.go
delete mode 100644 core/scripts/keystone/src/06_deploy_workflows_cmd.go
delete mode 100644 core/scripts/keystone/src/07_delete_workflows_cmd.go
create mode 100644 core/scripts/keystone/src/88_capabilities_registry_helpers.go
create mode 100644 core/scripts/keystone/src/88_contracts_helpers.go
delete mode 100644 core/scripts/keystone/src/88_gen_jobspecs.go
delete mode 100644 core/scripts/keystone/src/88_gen_jobspecs_test.go
delete mode 100644 core/scripts/keystone/src/88_gen_ocr3_config.go
delete mode 100644 core/scripts/keystone/src/88_gen_ocr3_config_test.go
create mode 100644 core/scripts/keystone/src/88_jobspecs_helpers.go
create mode 100644 core/scripts/keystone/src/88_ocr_helpers.go
delete mode 100644 core/scripts/keystone/src/99_files_test.go
delete mode 100644 core/scripts/keystone/src/99_nodes.go
create mode 100755 core/scripts/keystone/src/__snapshots__/02_deploy_keystone_workflows_test.snap
create mode 100755 core/scripts/keystone/src/__snapshots__/02_provision_crib_test.snap
create mode 100755 core/scripts/keystone/src/__snapshots__/02_provision_ocr3_capability_test.snap
create mode 100755 core/scripts/keystone/src/__snapshots__/02_provision_streams_trigger_capability_test.snap
delete mode 100755 core/scripts/keystone/src/__snapshots__/03_gen_crib_cluster_overrides_cmd_test.snap
delete mode 100755 core/scripts/keystone/src/__snapshots__/88_gen_jobspecs_test.snap
delete mode 100755 core/scripts/keystone/src/__snapshots__/88_gen_ocr3_config_test.snap
create mode 100644 core/scripts/keystone/src/external-adapter/.goreleaser.yaml
create mode 100644 core/scripts/keystone/src/external-adapter/99_external_adapter.go
create mode 100644 core/scripts/keystone/src/external-adapter/Dockerfile
delete mode 100644 core/scripts/keystone/src/testdata/NodeList.txt
delete mode 100644 core/scripts/keystone/src/testdata/PublicKeys.json
create mode 100644 core/scripts/keystone/src/testdata/node_sets.json
delete mode 100644 core/scripts/keystone/templates/bootstrap.toml
delete mode 100644 core/scripts/keystone/templates/crib-overrides.yaml
delete mode 100644 core/scripts/keystone/templates/oracle.toml
diff --git a/.changeset/loud-birds-remain.md b/.changeset/loud-birds-remain.md
new file mode 100644
index 00000000000..eb1e8f8a9ca
--- /dev/null
+++ b/.changeset/loud-birds-remain.md
@@ -0,0 +1,5 @@
+---
+"chainlink": minor
+---
+
+#internal Add unexposed shell cmd for updating a bridge
diff --git a/core/cmd/bridge_commands.go b/core/cmd/bridge_commands.go
index 398d466c43a..cd314b23218 100644
--- a/core/cmd/bridge_commands.go
+++ b/core/cmd/bridge_commands.go
@@ -128,6 +128,29 @@ func (s *Shell) CreateBridge(c *cli.Context) (err error) {
return s.renderAPIResponse(resp, &BridgePresenter{})
}
+func (s *Shell) UpdateBridge(c *cli.Context) (err error) {
+ if !c.Args().Present() {
+ return s.errorOut(errors.New("must pass the name of the bridge to be updated"))
+ }
+ bridgeName := c.Args().First()
+ buf, err := getBufferFromJSON(c.Args().Get(1))
+ if err != nil {
+ return s.errorOut(err)
+ }
+
+ resp, err := s.HTTP.Patch(s.ctx(), "/v2/bridge_types/"+bridgeName, buf)
+ if err != nil {
+ return s.errorOut(err)
+ }
+ defer func() {
+ if cerr := resp.Body.Close(); cerr != nil {
+ err = multierr.Append(err, cerr)
+ }
+ }()
+
+ return s.renderAPIResponse(resp, &BridgePresenter{})
+}
+
// RemoveBridge removes a specific Bridge by name.
func (s *Shell) RemoveBridge(c *cli.Context) (err error) {
if !c.Args().Present() {
diff --git a/core/cmd/bridge_commands_test.go b/core/cmd/bridge_commands_test.go
index f05aac52cd9..5523fc09605 100644
--- a/core/cmd/bridge_commands_test.go
+++ b/core/cmd/bridge_commands_test.go
@@ -3,6 +3,7 @@ package cmd_test
import (
"bytes"
"flag"
+ "fmt"
"testing"
"time"
@@ -191,3 +192,44 @@ func TestShell_RemoveBridge(t *testing.T) {
assert.Equal(t, bt.URL.String(), p.URL)
assert.Equal(t, bt.Confirmations, p.Confirmations)
}
+func TestShell_UpdateBridge(t *testing.T) {
+ t.Parallel()
+
+ app := startNewApplicationV2(t, nil)
+ client, _ := app.NewShellAndRenderer()
+ name := testutils.RandomizeName("updatebridge")
+
+ bt := &bridges.BridgeType{
+ Name: bridges.MustParseBridgeName(name),
+ URL: cltest.WebURL(t, "https://testing.com/bridges"),
+ Confirmations: 0,
+ }
+ require.NoError(t, app.BridgeORM().CreateBridgeType(testutils.Context(t), bt))
+ tests := []struct {
+ name string
+ args []string
+ errored bool
+ }{
+ {"NoArgs", []string{}, true},
+ {"OnlyName", []string{name}, true},
+ {"ValidUpdate", []string{name, fmt.Sprintf(`{ "name": "%s", "url": "http://localhost:3000/updated" }`, name)}, false},
+ {"InvalidJSON", []string{name, `{ "url": "http://localhost:3000/updated"`}, true},
+ }
+
+ for _, tt := range tests {
+ test := tt
+ t.Run(test.name, func(t *testing.T) {
+ set := flag.NewFlagSet("bridge", 0)
+ flagSetApplyFromAction(client.UpdateBridge, set, "")
+
+ require.NoError(t, set.Parse(test.args))
+
+ c := cli.NewContext(nil, set, nil)
+ if test.errored {
+ assert.Error(t, client.UpdateBridge(c))
+ } else {
+ assert.NoError(t, client.UpdateBridge(c))
+ }
+ })
+ }
+}
diff --git a/core/scripts/common/helpers.go b/core/scripts/common/helpers.go
index 57c8c15e405..97ca2dd4929 100644
--- a/core/scripts/common/helpers.go
+++ b/core/scripts/common/helpers.go
@@ -3,10 +3,12 @@ package common
import (
"context"
"crypto/ecdsa"
+ "crypto/tls"
"encoding/hex"
"flag"
"fmt"
"math/big"
+ "net/http"
"os"
"strconv"
"strings"
@@ -69,11 +71,17 @@ func SetupEnv(overrideNonce bool) Environment {
panic("need account key")
}
- ec, err := ethclient.Dial(ethURL)
- PanicErr(err)
-
- jsonRPCClient, err := rpc.Dial(ethURL)
+ insecureSkipVerify := os.Getenv("INSECURE_SKIP_VERIFY") == "true"
+ tr := &http.Transport{
+ // User enables this at their own risk!
+ // #nosec G402
+ TLSClientConfig: &tls.Config{InsecureSkipVerify: insecureSkipVerify},
+ }
+ httpClient := &http.Client{Transport: tr}
+ rpcConfig := rpc.WithHTTPClient(httpClient)
+ jsonRPCClient, err := rpc.DialOptions(context.Background(), ethURL, rpcConfig)
PanicErr(err)
+ ec := ethclient.NewClient(jsonRPCClient)
chainID, err := strconv.ParseInt(chainIDEnv, 10, 64)
PanicErr(err)
diff --git a/core/scripts/go.mod b/core/scripts/go.mod
index b86baf9a203..897962a6454 100644
--- a/core/scripts/go.mod
+++ b/core/scripts/go.mod
@@ -34,6 +34,8 @@ require (
github.com/shopspring/decimal v1.4.0
github.com/smartcontractkit/chainlink-automation v0.8.1
github.com/smartcontractkit/chainlink-common v0.4.1-0.20250108194320-2ebd63bbb16e
+ github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241216163550-fa030d178ba3
+ github.com/smartcontractkit/chainlink-testing-framework/lib v1.50.13
github.com/smartcontractkit/libocr v0.0.0-20241223215956-e5b78d8e3919
github.com/spf13/cobra v1.8.1
github.com/spf13/viper v1.19.0
@@ -41,7 +43,9 @@ require (
github.com/umbracle/ethgo v0.1.3
github.com/umbracle/fastrlp v0.0.0-20220527094140-59d5dd30e722
github.com/urfave/cli v1.22.14
+ go.uber.org/zap v1.27.0
google.golang.org/protobuf v1.35.1
+ gopkg.in/yaml.v3 v3.0.1
k8s.io/api v0.31.1
k8s.io/apimachinery v0.31.1
k8s.io/client-go v0.31.1
@@ -309,14 +313,12 @@ require (
github.com/smartcontractkit/chainlink-ccip v0.0.0-20250110181647-9dba278f2103 // indirect
github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250103152858-8973fd0c912b // indirect
github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e // indirect
- github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241216163550-fa030d178ba3 // indirect
github.com/smartcontractkit/chainlink-feeds v0.1.1 // indirect
github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20241220173418-09e17ddbeb20 // indirect
github.com/smartcontractkit/chainlink-protos/job-distributor v0.6.0 // indirect
github.com/smartcontractkit/chainlink-protos/orchestrator v0.4.0 // indirect
github.com/smartcontractkit/chainlink-solana v1.1.1-0.20250110142550-e2a9566d39f3 // indirect
github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241202202529-2033490e77b8 // indirect
- github.com/smartcontractkit/chainlink-testing-framework/lib v1.50.13 // indirect
github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7 // indirect
github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20241009055228-33d0c0bf38de // indirect
github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20241009055228-33d0c0bf38de // indirect
@@ -381,7 +383,6 @@ require (
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/ratelimit v0.3.1 // indirect
- go.uber.org/zap v1.27.0 // indirect
golang.org/x/arch v0.11.0 // indirect
golang.org/x/crypto v0.31.0 // indirect
golang.org/x/exp v0.0.0-20241210194714-1829a127f884 // indirect
@@ -405,7 +406,6 @@ require (
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/klog/v2 v2.130.1 // indirect
k8s.io/kube-openapi v0.0.0-20240709000822-3c01b740850f // indirect
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect
diff --git a/core/scripts/keystone/01_deploy_contracts-sample.sh b/core/scripts/keystone/01_deploy_contracts-sample.sh
deleted file mode 100755
index 89e77f4556f..00000000000
--- a/core/scripts/keystone/01_deploy_contracts-sample.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/bin/bash
-
-go run main.go \
- deploy-contracts \
- --ocrfile=ocr_config.json \
- --chainid=11155111 \
- --ethurl=ETH_URL \
- --accountkey=ACCOUNT_KEY \
- --onlysetconfig=false \
- --skipfunding=false \
- --dryrun=false
diff --git a/core/scripts/keystone/02_deploy_jobspecs-sample.sh b/core/scripts/keystone/02_deploy_jobspecs-sample.sh
deleted file mode 100755
index e99d54e0d3b..00000000000
--- a/core/scripts/keystone/02_deploy_jobspecs-sample.sh
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/bin/bash
-
-go run main.go \
- deploy-jobspecs \
- --chainid=11155111 \
- --p2pport=6690 \
- --onlyreplay=false
diff --git a/core/scripts/keystone/03_gen_crib-sample.sh b/core/scripts/keystone/03_gen_crib-sample.sh
deleted file mode 100755
index 9193ef4f75b..00000000000
--- a/core/scripts/keystone/03_gen_crib-sample.sh
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/bin/bash
-
-go run main.go \
- generate-crib \
- --chainid=11155111 \
- --outpath=/tmp
diff --git a/core/scripts/keystone/04_delete_ocr3_jobs-sample.sh b/core/scripts/keystone/04_delete_ocr3_jobs-sample.sh
deleted file mode 100755
index 3f3b50b055c..00000000000
--- a/core/scripts/keystone/04_delete_ocr3_jobs-sample.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/bash
-
-go run main.go delete-ocr3-jobs
diff --git a/core/scripts/keystone/05_deploy_and_initialize_capabilities_registry-sample.sh b/core/scripts/keystone/05_deploy_and_initialize_capabilities_registry-sample.sh
deleted file mode 100755
index 21c764be0e8..00000000000
--- a/core/scripts/keystone/05_deploy_and_initialize_capabilities_registry-sample.sh
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/bin/bash
-
-go run main.go \
- deploy-and-initialize-capabilities-registry \
- --chainid=11155111 \
- --ethurl=$ETH_URL \
- --accountkey=$ACCOUNT_KEY \
- --craddress=$CR_ADDRESS \ // 0x0d36aAC2Fd9d6d1C1F59251be6A2B337af27C52B
diff --git a/core/scripts/keystone/README.md b/core/scripts/keystone/README.md
deleted file mode 100644
index f08f738cb78..00000000000
--- a/core/scripts/keystone/README.md
+++ /dev/null
@@ -1,91 +0,0 @@
-# Provisioning a CRIB keystone cluster
-
-Kudos to Functions team for inspiration.
-
-This document outlines the steps to provision a CRIB keystone cluster for testing OCR3.
-
-## Pre-requisites
-
-### Blockchain Node
-
-An HTTP URL to a blockchain node, such as a Geth node. This should be the same blockchain node that you used to deploy the chainlink node cluster.
-
-### Private Key
-
-A private key to a testing wallet to use for deployment and funding. This wallet should have some native token on the chain you're deploying to. For Sepolia, around 2 ETH should be sufficient.
-
-The easiest way to set this up is to download [Metamask](https://metamask.io/) and create a new wallet. Once you have created a wallet, you can export the private key by clicking on the three dots next to the wallet name, selecting "Account Details", and then "Show Private Key".
-
-## Usage
-
-### Your first deployment
-
-Using devspace, we can deploy a cluster and provision it via the `keystone` devspace profile. You'll want to follow the instructions in the [CRIB README](../../../crib/README.md) to set up your environment and deploy the cluster.
-
-**NOTE**: You'll want to deploy using the `keystone` profile, not the default profile file.
-
-```bash
-# From /crib
-devspace deploy --profile keystone
-```
-
-For convenience, setting the TTL to be a much longer value is helpful, otherwise the testnet native tokens that you send to nodes will be lost. You can set this in your crib `.env` file, or interactively via:
-
-```bash
-# From /crib
-devspace run ttl ${namespace} 7d
-```
-
-Everytime the interactive command is run, the TTL is reset.
-
-### Iterate
-Let's say you made some changes to the codebase, and you want to see that reflected within the cluster. Simply redeploy via:
-```bash
-devspace deploy --profile keystone
-```
-
-### Restarting from a fresh slate
-
-If you want to redeploy all resources, then you'll want to do the following:
-
-```bash
-# From /crib
-devspace purge --profile keystone # Remove all k8s resources
-DEVSPACE_NAMESPACE=crib- crib init # Purge currently leaves some hanging resources, make a new namespace
-devspace deploy --profile keysone --clean # Wipe any keystone related persisted data, like artefacts and caches.
-```
-
-## What does Provisioning a CRIB keystone cluster do?
-
-### Provision On-Chain Resources
-
-This will provision on-chain resources, namely:
-
-1. Deploy the forwarder contract
-2. Deploy OCR3 config contract
-3. Setting the configuration for the OCR3 contract
-4. Funding transmitters with native tokens
-
-When the on-chain resources are deployed, a json file within `artefacts` will be generated. This file will contain the addresses of the forwarder contract, the OCR3 config contract, and the block number at which the configuration was set. Be careful about deleting this file, as if you lose it, you will need to redeploy the contracts and run through all proceeding steps.
-
-### Job Spec Deployment
-
-The next step is to deploy the OCR3 job specs to the chainlink node cluster. This will create a bootstrapping job for the first node of the cluster (determined via alphabetical order) and an OCR job for each other node in the cluster.
-
-### Update Per-Node TOML Configuration
-
-While we already have the chainlink node cluster deployed, we need to update the TOML configuration for each node to configure the `ChainWriter`.
-After updated TOML configuration overrides are generated per node, the cluster is redeployed such that the updates that effect without wiping the databases.
-
-## Future Work
-
-### Keystone workflow deployment
-Workflow style job spec deployments are not currently support, but it should be a minor modification to the existing OCR job spec deployment logic
-
-### Multi-DON support
-Multiple DONs are not currently supported
-- the devspace profile will need to be expanded so that we have multiple deployments, one per DON.
-- network policy / open ports will likely have to be adjusted in the chart
-
-### Smarter jobspec deployment
-Currently, job specs deployment logic is dumb. The scripts don't check if the jobspec to deploy already exists. If you need to redeploy a job spec that has the same name as a currently uploaded one, you'll want to delete the existing job specs via `./04_delete_ocr3_jobs.sh`.
diff --git a/core/scripts/keystone/artefacts/README.md b/core/scripts/keystone/artefacts/README.md
deleted file mode 100644
index 68f06dbd1c8..00000000000
--- a/core/scripts/keystone/artefacts/README.md
+++ /dev/null
@@ -1 +0,0 @@
-All generated artefacts will be saved here.
\ No newline at end of file
diff --git a/core/scripts/keystone/main.go b/core/scripts/keystone/main.go
index 3486830ca32..4bd8dea0e5f 100644
--- a/core/scripts/keystone/main.go
+++ b/core/scripts/keystone/main.go
@@ -15,13 +15,9 @@ type command interface {
func main() {
commands := []command{
- src.NewDeployContractsCommand(),
- src.NewDeployJobSpecsCommand(),
- src.NewGenerateCribClusterOverridesCommand(),
- src.NewDeleteJobsCommand(),
+ src.NewProvisionKeystoneCommand(),
src.NewDeployAndInitializeCapabilitiesRegistryCommand(),
- src.NewDeployWorkflowsCommand(),
- src.NewDeleteWorkflowsCommand(),
+ src.NewToolkit(),
}
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
deleted file mode 100644
index 14c8d989063..00000000000
--- a/core/scripts/keystone/src/01_deploy_contracts_cmd.go
+++ /dev/null
@@ -1,226 +0,0 @@
-package src
-
-import (
- "context"
- "encoding/json"
- "flag"
- "fmt"
- "math/big"
- "os"
- "path/filepath"
-
- "github.com/ethereum/go-ethereum/common"
-
- helpers "github.com/smartcontractkit/chainlink/core/scripts/common"
- "github.com/smartcontractkit/chainlink/deployment/keystone/changeset"
- forwarder "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/forwarder_1_0_0"
- ocr3_capability "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/ocr3_capability_1_0_0"
-)
-
-type deployedContracts struct {
- OCRContract common.Address `json:"ocrContract"`
- ForwarderContract common.Address `json:"forwarderContract"`
- // The block number of the transaction that set the config on the OCR3 contract. We use this to replay blocks from this point on
- // when we load the OCR3 job specs on the nodes.
- SetConfigTxBlock uint64 `json:"setConfigTxBlock"`
-}
-
-type deployContracts struct{}
-
-func NewDeployContractsCommand() *deployContracts {
- return &deployContracts{}
-}
-
-func (g *deployContracts) Name() string {
- return "deploy-contracts"
-}
-
-// Run expects the follow environment variables to be set:
-//
-// 1. Deploys the OCR3 contract
-// 2. Deploys the Forwarder contract
-// 3. Sets the config on the OCR3 contract
-// 4. Writes the deployed contract addresses to a file
-// 5. Funds the transmitters
-func (g *deployContracts) Run(args []string) {
- fs := flag.NewFlagSet(g.Name(), flag.ExitOnError)
- ocrConfigFile := fs.String("ocrfile", "config_example.json", "path to OCR config file")
- // create flags for all of the env vars then set the env vars to normalize the interface
- // this is a bit of a hack but it's the easiest way to make this work
- ethUrl := fs.String("ethurl", "", "URL of the Ethereum node")
- chainID := fs.Int64("chainid", 11155111, "chain ID of the Ethereum network to deploy to")
- accountKey := fs.String("accountkey", "", "private key of the account to deploy from")
- 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 ||
- *chainID == 0 || chainID == nil ||
- *accountKey == "" || accountKey == nil {
- fs.Usage()
- 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(*nodeList, *publicKeys, *ocrConfigFile, *skipFunding, *dryRun, *onlySetConfig, *artefactsDir)
-}
-
-// deploy does the following:
-// 1. Deploys the OCR3 contract
-// 2. Deploys the Forwarder contract
-// 3. Sets the config on the OCR3 contract
-// 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,
- publicKeys,
- )
-
- if dryRun {
- fmt.Println("Dry run, skipping deployment and funding")
- return
- }
-
- if onlySetConfig {
- fmt.Println("Skipping deployment of contracts and skipping funding transmitters, only setting config")
- setOCR3Config(env, ocrConfig, artefacts)
- return
- }
-
- if ContractsAlreadyDeployed(artefacts) {
- fmt.Println("Contracts already deployed")
- return
- }
-
- fmt.Println("Deploying keystone ocr3 contract...")
- ocrContract := DeployKeystoneOCR3Capability(env)
- fmt.Println("Deploying keystone forwarder contract...")
- forwarderContract := DeployForwarder(env)
-
- fmt.Println("Writing deployed contract addresses to file...")
- contracts := deployedContracts{
- OCRContract: ocrContract.Address(),
- ForwarderContract: forwarderContract.Address(),
- }
- jsonBytes, err := json.Marshal(contracts)
- PanicErr(err)
-
- err = os.WriteFile(DeployedContractsFilePath(artefacts), jsonBytes, 0600)
- PanicErr(err)
-
- setOCR3Config(env, ocrConfig, artefacts)
-
- if skipFunding {
- fmt.Println("Skipping funding transmitters")
- return
- }
- fmt.Println("Funding transmitters...")
- transmittersStr := []string{}
- for _, t := range ocrConfig.Transmitters {
- transmittersStr = append(transmittersStr, t.String())
- }
-
- helpers.FundNodes(env, transmittersStr, big.NewInt(50000000000000000)) // 0.05 ETH
-}
-
-func setOCR3Config(
- env helpers.Environment,
- ocrConfig changeset.OCR3OnchainConfig,
- artefacts string,
-) {
- loadedContracts, err := LoadDeployedContracts(artefacts)
- PanicErr(err)
-
- ocrContract, err := ocr3_capability.NewOCR3Capability(loadedContracts.OCRContract, env.Ec)
- PanicErr(err)
- fmt.Println("Setting OCR3 contract config...")
- tx, err := ocrContract.SetConfig(env.Owner,
- ocrConfig.Signers,
- ocrConfig.Transmitters,
- ocrConfig.F,
- ocrConfig.OnchainConfig,
- ocrConfig.OffchainConfigVersion,
- ocrConfig.OffchainConfig,
- )
- PanicErr(err)
- receipt := helpers.ConfirmTXMined(context.Background(), env.Ec, tx, env.ChainID)
-
- // Write blocknumber of the transaction to the deployed contracts file
- loadedContracts.SetConfigTxBlock = receipt.BlockNumber.Uint64()
- jsonBytes, err := json.Marshal(loadedContracts)
- PanicErr(err)
- err = os.WriteFile(DeployedContractsFilePath(artefacts), jsonBytes, 0600)
- PanicErr(err)
-}
-
-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(artefacts))
- if err != nil {
- return deployedContracts{}, err
- }
-
- var contracts deployedContracts
- err = json.Unmarshal(jsonBytes, &contracts)
- return contracts, err
-}
-
-func ContractsAlreadyDeployed(artefacts string) bool {
- _, err := os.Stat(DeployedContractsFilePath(artefacts))
- return err == nil
-}
-
-func DeployedContractsFilePath(artefacts string) string {
- return filepath.Join(artefacts, deployedContractsJSON)
-}
-
-func DeployForwarder(e helpers.Environment) *forwarder.KeystoneForwarder {
- _, tx, contract, err := forwarder.DeployKeystoneForwarder(e.Owner, e.Ec)
- PanicErr(err)
- helpers.ConfirmContractDeployed(context.Background(), e.Ec, tx, e.ChainID)
-
- return contract
-}
-
-func DeployKeystoneOCR3Capability(e helpers.Environment) *ocr3_capability.OCR3Capability {
- _, tx, contract, err := ocr3_capability.DeployOCR3Capability(e.Owner, e.Ec)
- PanicErr(err)
- helpers.ConfirmContractDeployed(context.Background(), e.Ec, tx, e.ChainID)
-
- return contract
-}
diff --git a/core/scripts/keystone/src/01_provision_keystone.go b/core/scripts/keystone/src/01_provision_keystone.go
new file mode 100644
index 00000000000..c7a2dd97127
--- /dev/null
+++ b/core/scripts/keystone/src/01_provision_keystone.go
@@ -0,0 +1,217 @@
+package src
+
+import (
+ "flag"
+ "fmt"
+ "os"
+ "path/filepath"
+ "strconv"
+
+ helpers "github.com/smartcontractkit/chainlink/core/scripts/common"
+ kcr "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry_1_1_0"
+)
+
+type provisionKeystone struct{}
+
+func NewProvisionKeystoneCommand() *provisionKeystone {
+ return &provisionKeystone{}
+}
+
+func (g *provisionKeystone) Name() string {
+ return "provision-keystone"
+}
+
+func (g *provisionKeystone) Run(args []string) {
+ fs := flag.NewFlagSet(g.Name(), flag.ExitOnError)
+
+ // common flags
+ artefactsDir := fs.String("artefacts", defaultArtefactsDir, "Custom artefacts directory location")
+ nodeSetSize := fs.Int("nodesetsize", 5, "number of nodes in a nodeset")
+ nodeSetsPath := fs.String("nodesets", defaultNodeSetsPath, "Custom node sets location")
+ chainID := fs.Int64("chainid", 1337, "chain ID of the Ethereum network to deploy to")
+
+ // preprovisioning flags
+ preprovison := fs.Bool("preprovision", false, "Preprovision crib")
+
+ // provisioning flags
+ ethURL := fs.String("ethurl", "", "URL of the Ethereum node")
+ accountKey := fs.String("accountkey", "", "private key of the account to deploy from")
+ ocrConfigFile := fs.String("ocrfile", "ocr_config.json", "path to OCR config file")
+ p2pPort := fs.Int64("p2pport", 6690, "p2p port")
+ capabilitiesP2PPort := fs.Int64("capabilitiesp2pport", 6691, "p2p port for capabilities")
+ preprovisionConfigName := fs.String("preprovisionconfig", "crib-preprovision.yaml", "Name of the preprovision config file, stored in the artefacts directory")
+ postprovisionConfigName := fs.String("postprovisionconfig", "crib-postprovision.yaml", "Name of the postprovision config file, stored in the artefacts directory")
+ // additional flags
+ clean := fs.Bool("clean", false, "Clean up resources before provisioning")
+
+ err := fs.Parse(args)
+
+ if err != nil || (!*preprovison && (*ethURL == "" || *accountKey == "")) {
+ fs.Usage()
+ os.Exit(1)
+ }
+
+ if *preprovison {
+ fmt.Println()
+ fmt.Println()
+ fmt.Println("========================")
+ fmt.Println("Writing Preprovisioning Config")
+ fmt.Println("========================")
+ fmt.Println()
+ fmt.Println()
+ writePreprovisionConfig(*nodeSetSize, filepath.Join(*artefactsDir, *preprovisionConfigName))
+ return
+ }
+
+ // We always want to start with a clean slate
+ /// when it comes to nodesets
+ err = os.RemoveAll(*nodeSetsPath)
+ PanicErr(err)
+ fmt.Println("Collecting node sets...")
+ nodeSets := downloadNodeSets(*chainID, *nodeSetsPath, *nodeSetSize)
+
+ if *clean {
+ fmt.Println("Cleaning up resources")
+ for _, node := range nodeSets.Workflow.Nodes {
+ clearJobs(newNodeAPI(node))
+ }
+ for _, node := range nodeSets.StreamsTrigger.Nodes {
+ clearJobs(newNodeAPI(node))
+ }
+ os.RemoveAll(*artefactsDir)
+ }
+
+ // Kinda hacky but it prevents us from refactoring the setupenv function which
+ // is used in many other places
+ os.Setenv("ETH_URL", *ethURL)
+ os.Setenv("ETH_CHAIN_ID", strconv.FormatInt(*chainID, 10))
+ os.Setenv("ACCOUNT_KEY", *accountKey)
+ os.Setenv("INSECURE_SKIP_VERIFY", "true")
+ env := helpers.SetupEnv(false)
+
+ provisionStreamsDON(
+ env,
+ nodeSets.StreamsTrigger,
+ *chainID,
+ *p2pPort,
+ *ocrConfigFile,
+ *artefactsDir,
+ )
+
+ reg := provisionCapabilitiesRegistry(
+ env,
+ nodeSets,
+ *chainID,
+ *artefactsDir,
+ )
+
+ onchainMeta := provisionWorkflowDON(
+ env,
+ nodeSets.Workflow,
+ *chainID,
+ *p2pPort,
+ *ocrConfigFile,
+ *artefactsDir,
+ reg,
+ )
+
+ fmt.Println()
+ fmt.Println()
+ fmt.Println("========================")
+ fmt.Println("Writing Postprovision Config")
+ fmt.Println("========================")
+ fmt.Println()
+ fmt.Println()
+
+ writePostProvisionConfig(
+ nodeSets,
+ *chainID,
+ *capabilitiesP2PPort,
+ onchainMeta.Forwarder.Address().Hex(),
+ onchainMeta.CapabilitiesRegistry.Address().Hex(),
+ filepath.Join(*artefactsDir, *postprovisionConfigName),
+ )
+}
+
+func provisionCapabilitiesRegistry(
+ env helpers.Environment,
+ nodeSets NodeSets,
+ chainID int64,
+ artefactsDir string,
+) kcr.CapabilitiesRegistryInterface {
+ fmt.Println()
+ fmt.Println()
+ fmt.Println("========================")
+ fmt.Println("Provisioning Capabilities Registry DON")
+ fmt.Println("========================")
+ fmt.Println()
+ fmt.Println()
+ reg := provisionCapabillitiesRegistry(
+ env,
+ nodeSets,
+ chainID,
+ artefactsDir,
+ )
+ return reg
+}
+
+func provisionStreamsDON(
+ env helpers.Environment,
+ nodeSet NodeSet,
+ chainID int64,
+ p2pPort int64,
+ ocrConfigFilePath string,
+ artefactsDir string,
+) {
+ fmt.Println()
+ fmt.Println()
+ fmt.Println("========================")
+ fmt.Println("Provisioning streams DON")
+ fmt.Println("========================")
+ fmt.Println()
+ fmt.Println()
+ setupStreamsTrigger(
+ env,
+ nodeSet,
+ chainID,
+ p2pPort,
+ ocrConfigFilePath,
+ artefactsDir,
+ )
+}
+
+func provisionWorkflowDON(
+ env helpers.Environment,
+ nodeSet NodeSet,
+ chainID int64,
+ p2pPort int64,
+ ocrConfigFile string,
+ artefactsDir string,
+ reg kcr.CapabilitiesRegistryInterface,
+) (onchainMeta *onchainMeta) {
+ fmt.Println()
+ fmt.Println()
+ fmt.Println("========================")
+ fmt.Println("Provisioning workflow DON")
+ fmt.Println("========================")
+ fmt.Println()
+ fmt.Println()
+ deployForwarder(env, artefactsDir)
+
+ onchainMeta, _ = provisionOCR3(
+ env,
+ nodeSet,
+ chainID,
+ p2pPort,
+ ocrConfigFile,
+ artefactsDir,
+ )
+ distributeFunds(nodeSet.NodeKeys, env)
+
+ // We don't technically need the capability registry as a dependency
+ // as we just use it for a sanity check
+ // We could remove it so that we can execute provisioning in parallel
+ deployKeystoneWorkflowsTo(nodeSet, reg)
+
+ return onchainMeta
+}
diff --git a/core/scripts/keystone/src/01_toolkit.go b/core/scripts/keystone/src/01_toolkit.go
new file mode 100644
index 00000000000..6fe896667ce
--- /dev/null
+++ b/core/scripts/keystone/src/01_toolkit.go
@@ -0,0 +1,212 @@
+// This sub CLI acts as a temporary shim for external aptos support
+
+package src
+
+import (
+ "bufio"
+ "errors"
+ "flag"
+ "fmt"
+ "net/url"
+ "os"
+ "strconv"
+ "strings"
+
+ helpers "github.com/smartcontractkit/chainlink/core/scripts/common"
+)
+
+type Toolkit struct{}
+
+func (t *Toolkit) Name() string {
+ return "toolkit"
+}
+
+func NewToolkit() *Toolkit {
+ return &Toolkit{}
+}
+
+func (t *Toolkit) Run(args []string) {
+ if len(args) < 1 {
+ fmt.Println("Available commands:")
+ fmt.Println(" deploy-workflows")
+ fmt.Println(" deploy-ocr3-contracts")
+ fmt.Println(" deploy-ocr3-jobspecs")
+ os.Exit(1)
+ }
+
+ command := args[0]
+ cmdArgs := args[1:]
+
+ switch command {
+ case "get-aptos-keys":
+ t.AptosKeys(cmdArgs)
+ case "deploy-workflows":
+ t.DeployWorkflows(cmdArgs)
+ case "deploy-ocr3-contracts":
+ t.ProvisionOCR3Contracts(cmdArgs)
+ case "deploy-ocr3-jobspecs":
+ t.DeployOCR3JobSpecs(cmdArgs)
+ default:
+ fmt.Printf("Unknown command: %s\n", command)
+ os.Exit(1)
+ }
+}
+
+func (t *Toolkit) AptosKeys(args []string) {
+ fs := flag.NewFlagSet("get-aptos-keys", flag.ExitOnError)
+ nodesListPath := fs.String("nodes", ".cache/NodesList.txt", "Path to file with list of nodes")
+ artefacts := fs.String("artefacts", defaultArtefactsDir, "Custom artefacts directory location")
+ chainID := fs.Int64("chainid", 1337, "Chain ID")
+
+ if err := fs.Parse(args); err != nil {
+ fs.Usage()
+ os.Exit(1)
+ }
+
+ nodes := mustReadNodesList(*nodesListPath)
+ keys := mustFetchNodeKeys(*chainID, nodes, true)
+
+ mustWriteJSON(*artefacts+"/pubnodekeys.json", keys)
+}
+
+func (t *Toolkit) ProvisionOCR3Contracts(args []string) {
+ fs := flag.NewFlagSet("deploy-ocr3-contracts", flag.ExitOnError)
+ ethURL := fs.String("ethurl", "", "URL of the Ethereum node")
+ accountKey := fs.String("accountkey", "", "Private key of the deployer account")
+ chainID := fs.Int64("chainid", 1337, "Chain ID")
+ nodesListPath := fs.String("nodes", ".cache/NodesList.txt", "Path to file with list of nodes")
+ artefactsDir := fs.String("artefacts", defaultArtefactsDir, "Custom artefacts directory location")
+ ocrConfigFile := fs.String("ocrfile", "ocr_config.json", "Path to OCR config file")
+
+ if err := fs.Parse(args); err != nil || *ethURL == "" || *accountKey == "" {
+ fs.Usage()
+ os.Exit(1)
+ }
+
+ // Set environment variables required by setupenv
+ os.Setenv("ETH_URL", *ethURL)
+ os.Setenv("ETH_CHAIN_ID", strconv.FormatInt(*chainID, 10))
+ os.Setenv("ACCOUNT_KEY", *accountKey)
+ os.Setenv("INSECURE_SKIP_VERIFY", "true")
+
+ env := helpers.SetupEnv(false)
+
+ nodes := mustReadNodesList(*nodesListPath)
+ nodeKeys := mustFetchNodeKeys(*chainID, nodes, true)
+
+ deployOCR3Contract(nodeKeys, env, *ocrConfigFile, *artefactsDir)
+}
+
+func (t *Toolkit) DeployOCR3JobSpecs(args []string) {
+ fs := flag.NewFlagSet("deploy-ocr3-jobspecs", flag.ExitOnError)
+
+ ethURL := fs.String("ethurl", "", "URL of the Ethereum node")
+ accountKey := fs.String("accountkey", "", "Private key of the deployer account")
+ chainID := fs.Int64("chainid", 1337, "Chain ID")
+ nodesListPath := fs.String("nodes", ".cache/NodesList.txt", "Path to file with list of nodes")
+ p2pPort := fs.Int64("p2pport", 6690, "P2P port")
+ artefactsDir := fs.String("artefacts", defaultArtefactsDir, "Custom artefacts directory location")
+
+ if err := fs.Parse(args); err != nil || *ethURL == "" || *accountKey == "" {
+ fs.Usage()
+ os.Exit(1)
+ }
+
+ os.Setenv("ETH_URL", *ethURL)
+ os.Setenv("ETH_CHAIN_ID", strconv.FormatInt(*chainID, 10))
+ os.Setenv("ACCOUNT_KEY", *accountKey)
+ os.Setenv("INSECURE_SKIP_VERIFY", "true")
+
+ env := helpers.SetupEnv(false)
+
+ nodes := mustReadNodesList(*nodesListPath)
+ nodeKeys := mustFetchNodeKeys(*chainID, nodes, true)
+ o := LoadOnchainMeta(*artefactsDir, env)
+
+ deployOCR3JobSpecs(
+ nodes,
+ *chainID,
+ nodeKeys,
+ *p2pPort,
+ o,
+ )
+}
+
+func (t *Toolkit) DeployWorkflows(args []string) {
+ fs := flag.NewFlagSet("deploy-workflows", flag.ExitOnError)
+ workflowFile := fs.String("workflow", "", "Path to workflow file")
+ nodesList := fs.String("nodes", ".cache/NodesList.txt", "Path to file with list of nodes")
+
+ if err := fs.Parse(args); err != nil || *workflowFile == "" {
+ fs.Usage()
+ os.Exit(1)
+ }
+
+ nodesWithCreds := mustReadNodesList(*nodesList)
+
+ for _, node := range nodesWithCreds {
+ api := newNodeAPI(node)
+ workflowContent, err := os.ReadFile(*workflowFile)
+ PanicErr(err)
+
+ upsertJob(api, "workflow", string(workflowContent))
+ fmt.Println("Workflow deployed successfully")
+ }
+}
+
+// Reads in a list of nodes from a file, where each line is in the format:
+// http://localhost:50100 http://chainlink.core.1:50100 notreal@fakeemail.ch fj293fbBnlQ!f9vNs
+func mustReadNodesList(path string) []NodeWithCreds {
+ fmt.Println("Reading nodes list from", path)
+ nodesList, err := readLines(path)
+ helpers.PanicErr(err)
+
+ nodes := make([]NodeWithCreds, 0, len(nodesList))
+ for _, r := range nodesList {
+ rr := strings.TrimSpace(r)
+ if len(rr) == 0 {
+ continue
+ }
+ s := strings.Split(rr, " ")
+ if len(s) != 4 {
+ helpers.PanicErr(errors.New("wrong nodes list format"))
+ }
+
+ r := SimpleURL{
+ Scheme: "http",
+ Host: s[0],
+ }
+ u := SimpleURL{
+ Scheme: "http",
+ Host: s[1],
+ }
+ remoteURL, err := url.Parse(u.String())
+ PanicErr(err)
+ nodes = append(nodes, NodeWithCreds{
+ URL: u,
+ RemoteURL: r,
+ // This is the equivalent of "chainlink.core.1" in our above example
+ ServiceName: remoteURL.Hostname(),
+ APILogin: s[2],
+ APIPassword: s[3],
+ KeystorePassword: "",
+ })
+ }
+
+ return nodes
+}
+
+func readLines(path string) ([]string, error) {
+ file, err := os.Open(path)
+ if err != nil {
+ return nil, err
+ }
+ defer file.Close()
+
+ var lines []string
+ scanner := bufio.NewScanner(file)
+ for scanner.Scan() {
+ lines = append(lines, scanner.Text())
+ }
+ return lines, scanner.Err()
+}
diff --git a/core/scripts/keystone/src/01_toolkit_test.go b/core/scripts/keystone/src/01_toolkit_test.go
new file mode 100644
index 00000000000..6f4a083940e
--- /dev/null
+++ b/core/scripts/keystone/src/01_toolkit_test.go
@@ -0,0 +1,49 @@
+package src
+
+import (
+ "os"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestMustReadNodesList(t *testing.T) {
+ t.Run("valid nodes list", func(t *testing.T) {
+ content := "localhost:50100 chainlink.core.1:50100 user1 pass1\nlocalhost:50101 chainlink.core.2:50101 user2 pass2"
+ filePath := writeTempFile(t, content)
+ defer os.Remove(filePath)
+
+ nodes := mustReadNodesList(filePath)
+ assert.Len(t, nodes, 2)
+
+ assert.Equal(t, "user1", nodes[0].APILogin)
+ assert.Equal(t, "user2", nodes[1].APILogin)
+
+ assert.Equal(t, "pass1", nodes[0].APIPassword)
+ assert.Equal(t, "pass2", nodes[1].APIPassword)
+
+ assert.Equal(t, "http://localhost:50100", nodes[0].RemoteURL.String())
+ assert.Equal(t, "http://localhost:50101", nodes[1].RemoteURL.String())
+
+ assert.Equal(t, "chainlink.core.1", nodes[0].ServiceName)
+ assert.Equal(t, "chainlink.core.2", nodes[1].ServiceName)
+
+ assert.Equal(t, "http://chainlink.core.1:50100", nodes[0].URL.String())
+ assert.Equal(t, "http://chainlink.core.2:50101", nodes[1].URL.String())
+ })
+}
+
+func writeTempFile(t *testing.T, content string) string {
+ file, err := os.CreateTemp("", "nodeslist")
+ if err != nil {
+ t.Fatalf("failed to create temp file: %v", err)
+ }
+ defer file.Close()
+
+ _, err = file.WriteString(content)
+ if err != nil {
+ t.Fatalf("failed to write to temp file: %v", err)
+ }
+
+ return file.Name()
+}
diff --git a/core/scripts/keystone/src/02_deploy_jobspecs_cmd.go b/core/scripts/keystone/src/02_deploy_jobspecs_cmd.go
deleted file mode 100644
index 275943d6388..00000000000
--- a/core/scripts/keystone/src/02_deploy_jobspecs_cmd.go
+++ /dev/null
@@ -1,165 +0,0 @@
-package src
-
-import (
- "bytes"
- "errors"
- "flag"
- "fmt"
- "os"
- "reflect"
- "runtime"
- "strings"
-
- "github.com/urfave/cli"
-
- helpers "github.com/smartcontractkit/chainlink/core/scripts/common"
- "github.com/smartcontractkit/chainlink/v2/core/cmd"
-)
-
-type deployJobSpecs struct{}
-
-func NewDeployJobSpecsCommand() *deployJobSpecs {
- return &deployJobSpecs{}
-}
-
-func (g *deployJobSpecs) Name() string {
- return "deploy-jobspecs"
-}
-
-func (g *deployJobSpecs) Run(args []string) {
- fs := flag.NewFlagSet(g.Name(), flag.ContinueOnError)
- 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()
- os.Exit(1)
- }
- if *onlyReplay {
- fmt.Println("Only replaying OCR3 contract setConfig transaction")
- } else {
- fmt.Println("Deploying OCR3 job specs")
- }
-
- 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(
- *publicKeys,
- *nodeList,
- *templatesLocation,
- *chainID, *p2pPort, deployedContracts.OCRContract.Hex(),
- )
- flattenedSpecs := []hostSpec{jobspecs.bootstrap}
- flattenedSpecs = append(flattenedSpecs, jobspecs.oracles...)
-
- // sanity check arr lengths
- if len(nodes) != len(flattenedSpecs) {
- PanicErr(errors.New("Mismatched node and job spec lengths"))
- }
-
- for i, n := range nodes {
- 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()
-
- if !*onlyReplay {
- specToDeploy := flattenedSpecs[i].spec.ToString()
- specFragment := flattenedSpecs[i].spec[0:1]
- fmt.Printf("Deploying jobspec: %s\n... \n", specFragment)
- fs := flag.NewFlagSet("test", flag.ExitOnError)
- err = fs.Parse([]string{specToDeploy})
-
- helpers.PanicErr(err)
- err = client.CreateJob(cli.NewContext(app, fs, nil))
- if err != nil {
- fmt.Println("Failed to deploy job spec:", specFragment, "Error:", err)
- }
- output.Reset()
- }
-
- replayFs := flag.NewFlagSet("test", flag.ExitOnError)
- flagSetApplyFromAction(client.ReplayFromBlock, replayFs, "")
- err = replayFs.Set("block-number", fmt.Sprint(deployedContracts.SetConfigTxBlock))
- helpers.PanicErr(err)
- err = replayFs.Set("evm-chain-id", fmt.Sprint(*chainID))
- helpers.PanicErr(err)
-
- fmt.Printf("Replaying from block: %d\n", deployedContracts.SetConfigTxBlock)
- fmt.Printf("EVM Chain ID: %d\n\n", *chainID)
- replayCtx := cli.NewContext(app, replayFs, nil)
- err = client.ReplayFromBlock(replayCtx)
- helpers.PanicErr(err)
- }
-}
-
-// flagSetApplyFromAction applies the flags from action to the flagSet.
-//
-// `parentCommand` will filter the app commands and only applies the flags if the command/subcommand has a parent with that name, if left empty no filtering is done
-//
-// Taken from: https://github.com/smartcontractkit/chainlink/blob/develop/core/cmd/shell_test.go#L590
-func flagSetApplyFromAction(action interface{}, flagSet *flag.FlagSet, parentCommand string) {
- cliApp := cmd.Shell{}
- app := cmd.NewApp(&cliApp)
-
- foundName := parentCommand == ""
- actionFuncName := getFuncName(action)
-
- for _, command := range app.Commands {
- flags := recursiveFindFlagsWithName(actionFuncName, command, parentCommand, foundName)
-
- for _, flag := range flags {
- flag.Apply(flagSet)
- }
- }
-}
-
-func recursiveFindFlagsWithName(actionFuncName string, command cli.Command, parent string, foundName bool) []cli.Flag {
- if command.Action != nil {
- if actionFuncName == getFuncName(command.Action) && foundName {
- return command.Flags
- }
- }
-
- for _, subcommand := range command.Subcommands {
- if !foundName {
- foundName = strings.EqualFold(subcommand.Name, parent)
- }
-
- found := recursiveFindFlagsWithName(actionFuncName, subcommand, parent, foundName)
- if found != nil {
- return found
- }
- }
- return nil
-}
-
-func getFuncName(i interface{}) string {
- return runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name()
-}
diff --git a/core/scripts/keystone/src/02_deploy_keystone_workflows.go b/core/scripts/keystone/src/02_deploy_keystone_workflows.go
new file mode 100644
index 00000000000..6c6580e21f0
--- /dev/null
+++ b/core/scripts/keystone/src/02_deploy_keystone_workflows.go
@@ -0,0 +1,134 @@
+package src
+
+import (
+ "bytes"
+ "fmt"
+ "text/template"
+
+ "github.com/ethereum/go-ethereum/accounts/abi/bind"
+
+ kcr "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry_1_1_0"
+)
+
+func deployKeystoneWorkflowsTo(nodeSet NodeSet, reg kcr.CapabilitiesRegistryInterface) {
+ fmt.Println("Deploying Keystone workflow jobs")
+ caps, err := reg.GetCapabilities(&bind.CallOpts{})
+ PanicErr(err)
+
+ streams := NewStreamsTriggerV1Capability()
+ ocr3 := NewOCR3V1ConsensusCapability()
+ testnetWrite := NewEthereumGethTestnetV1WriteCapability()
+
+ capSet := NewCapabilitySet(streams, ocr3, testnetWrite)
+ expectedHashedCIDs := capSet.HashedIDs(reg)
+
+ // Check that the capabilities are registered
+ for _, c := range caps {
+ found := false
+ for _, expected := range expectedHashedCIDs {
+ if c.HashedId == expected {
+ found = true
+ break
+ }
+ }
+
+ if !found {
+ panic(fmt.Sprintf("Capability %s not found in registry", c.HashedId))
+ }
+ }
+
+ feedIDs := []string{}
+ for _, feed := range feeds {
+ feedIDs = append(feedIDs, fmt.Sprintf("0x%x", feed.id))
+ }
+ workflowConfig := WorkflowJobSpecConfig{
+ JobSpecName: "keystone_workflow",
+ WorkflowOwnerAddress: "0x1234567890abcdef1234567890abcdef12345678",
+ FeedIDs: feedIDs,
+ TargetID: testnetWrite.GetID(),
+ ConsensusID: ocr3.GetID(),
+ TriggerID: streams.GetID(),
+ TargetAddress: "0x1234567890abcdef1234567890abcdef12345678",
+ }
+ jobSpecStr := createKeystoneWorkflowJob(workflowConfig)
+ for _, n := range nodeSet.Nodes[1:] { // skip the bootstrap node
+ api := newNodeAPI(n)
+ upsertJob(api, workflowConfig.JobSpecName, jobSpecStr)
+ }
+}
+
+type WorkflowJobSpecConfig struct {
+ JobSpecName string
+ WorkflowOwnerAddress string
+ FeedIDs []string
+ TargetID string
+ ConsensusID string
+ TriggerID string
+ TargetAddress string
+}
+
+func createKeystoneWorkflowJob(workflowConfig WorkflowJobSpecConfig) string {
+ const keystoneWorkflowTemplate = `
+type = "workflow"
+schemaVersion = 1
+name = "{{ .JobSpecName }}"
+workflow = """
+name: "ccip_kiab1"
+owner: '{{ .WorkflowOwnerAddress }}'
+triggers:
+ - id: streams-trigger@1.1.0
+ config:
+ maxFrequencyMs: 10000
+ feedIds:
+{{- range .FeedIDs }}
+ - '{{ . }}'
+{{- end }}
+
+consensus:
+ - id: offchain_reporting@1.0.0
+ ref: ccip_feeds
+ inputs:
+ observations:
+ - $(trigger.outputs)
+ config:
+ report_id: '0001'
+ key_id: 'evm'
+ aggregation_method: data_feeds
+ aggregation_config:
+ feeds:
+{{- range .FeedIDs }}
+ '{{ . }}':
+ deviation: '0.05'
+ heartbeat: 1800
+{{- end }}
+ encoder: EVM
+ encoder_config:
+ abi: "(bytes32 FeedID, uint224 Price, uint32 Timestamp)[] Reports"
+ abi: (bytes32 FeedID, uint224 Price, uint32 Timestamp)[] Reports
+
+targets:
+ - id: {{ .TargetID }}
+ inputs:
+ signed_report: $(ccip_feeds.outputs)
+ config:
+ address: '{{ .TargetAddress }}'
+ deltaStage: 5s
+ schedule: oneAtATime
+
+"""
+workflowOwner = "{{ .WorkflowOwnerAddress }}"
+`
+
+ tmpl, err := template.New("workflow").Parse(keystoneWorkflowTemplate)
+
+ if err != nil {
+ panic(err)
+ }
+ var renderedTemplate bytes.Buffer
+ err = tmpl.Execute(&renderedTemplate, workflowConfig)
+ if err != nil {
+ panic(err)
+ }
+
+ return renderedTemplate.String()
+}
diff --git a/core/scripts/keystone/src/02_deploy_keystone_workflows_test.go b/core/scripts/keystone/src/02_deploy_keystone_workflows_test.go
new file mode 100644
index 00000000000..bef6a768dce
--- /dev/null
+++ b/core/scripts/keystone/src/02_deploy_keystone_workflows_test.go
@@ -0,0 +1,21 @@
+package src
+
+import (
+ "testing"
+
+ "github.com/gkampitakis/go-snaps/snaps"
+)
+
+func TestCreateKeystoneWorkflowJob(t *testing.T) {
+ workflowConfig := WorkflowJobSpecConfig{
+ JobSpecName: "keystone_workflow",
+ WorkflowOwnerAddress: "0x1234567890abcdef1234567890abcdef12345678",
+ FeedIDs: []string{"feed1", "feed2", "feed3"},
+ TargetID: "target_id",
+ TargetAddress: "0xabcdefabcdefabcdefabcdefabcdefabcdef",
+ }
+
+ output := createKeystoneWorkflowJob(workflowConfig)
+
+ snaps.MatchSnapshot(t, output)
+}
diff --git a/core/scripts/keystone/src/02_fund_transmitters.go b/core/scripts/keystone/src/02_fund_transmitters.go
new file mode 100644
index 00000000000..751a90d22d8
--- /dev/null
+++ b/core/scripts/keystone/src/02_fund_transmitters.go
@@ -0,0 +1,52 @@
+package src
+
+import (
+ "context"
+ "fmt"
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common"
+
+ "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/conversions"
+ helpers "github.com/smartcontractkit/chainlink/core/scripts/common"
+)
+
+func distributeFunds(nodeKeys []NodeKeys, env helpers.Environment) {
+ fmt.Println("Funding transmitters...")
+ transmittersStr := []string{}
+ fundingAmount := big.NewInt(500000000000000000) // 0.5 ETH
+ minThreshold := big.NewInt(50000000000000000) // 0.05 ETH
+
+ for _, n := range nodeKeys {
+ balance, err := getBalance(n.EthAddress, env)
+ if err != nil {
+ fmt.Printf("Error fetching balance for %s: %v\n", n.EthAddress, err)
+ continue
+ }
+ if balance.Cmp(minThreshold) < 0 {
+ fmt.Printf(
+ "Transmitter %s has insufficient funds, funding with %s ETH. Current balance: %s, threshold: %s\n",
+ n.EthAddress,
+ conversions.WeiToEther(fundingAmount).String(),
+ conversions.WeiToEther(balance).String(),
+ conversions.WeiToEther(minThreshold).String(),
+ )
+ transmittersStr = append(transmittersStr, n.EthAddress)
+ }
+ }
+
+ if len(transmittersStr) > 0 {
+ helpers.FundNodes(env, transmittersStr, fundingAmount)
+ } else {
+ fmt.Println("All transmitters have sufficient funds.")
+ }
+}
+
+func getBalance(address string, env helpers.Environment) (*big.Int, error) {
+ balance, err := env.Ec.BalanceAt(context.Background(), common.HexToAddress(address), nil)
+ if err != nil {
+ return nil, err
+ }
+
+ return balance, nil
+}
diff --git a/core/scripts/keystone/src/02_provision_capabilities_registry.go b/core/scripts/keystone/src/02_provision_capabilities_registry.go
new file mode 100644
index 00000000000..aa0f203f96b
--- /dev/null
+++ b/core/scripts/keystone/src/02_provision_capabilities_registry.go
@@ -0,0 +1,68 @@
+package src
+
+import (
+ "context"
+ "fmt"
+
+ helpers "github.com/smartcontractkit/chainlink/core/scripts/common"
+ kcr "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry_1_1_0"
+)
+
+func provisionCapabillitiesRegistry(env helpers.Environment, nodeSets NodeSets, chainID int64, artefactsDir string) kcr.CapabilitiesRegistryInterface {
+ fmt.Printf("Provisioning capabilities registry on chain %d\n", chainID)
+ ctx := context.Background()
+ reg := deployCR(ctx, artefactsDir, env)
+ crProvisioner := NewCapabilityRegistryProvisioner(reg, env)
+ streamsTriggerCapSet := NewCapabilitySet(NewStreamsTriggerV1Capability())
+ workflowCapSet := NewCapabilitySet(NewOCR3V1ConsensusCapability(), NewEthereumGethTestnetV1WriteCapability())
+ workflowDON := nodeKeysToDON(nodeSets.Workflow.Name, nodeSets.Workflow.NodeKeys[1:], workflowCapSet)
+ streamsTriggerDON := nodeKeysToDON(nodeSets.StreamsTrigger.Name, nodeSets.StreamsTrigger.NodeKeys[1:], streamsTriggerCapSet)
+
+ crProvisioner.AddCapabilities(ctx, MergeCapabilitySets(streamsTriggerCapSet, workflowCapSet))
+ dons := map[string]DON{workflowDON.Name: workflowDON, streamsTriggerDON.Name: streamsTriggerDON}
+ nodeOperator := NewNodeOperator(env.Owner.From, "MY_NODE_OPERATOR", dons)
+ crProvisioner.AddNodeOperator(ctx, nodeOperator)
+
+ crProvisioner.AddNodes(ctx, nodeOperator, nodeSets.Workflow.Name, nodeSets.StreamsTrigger.Name)
+
+ crProvisioner.AddDON(ctx, nodeOperator, nodeSets.Workflow.Name, true, true)
+ crProvisioner.AddDON(ctx, nodeOperator, nodeSets.StreamsTrigger.Name, true, false)
+
+ return reg
+}
+
+// nodeKeysToDON converts a slice of NodeKeys into a DON struct with the given name and CapabilitySet.
+func nodeKeysToDON(donName string, nodeKeys []NodeKeys, capSet CapabilitySet) DON {
+ peers := []peer{}
+ for _, n := range nodeKeys {
+ p := peer{
+ PeerID: n.P2PPeerID,
+ Signer: n.OCR2OnchainPublicKey,
+ }
+ peers = append(peers, p)
+ }
+ return DON{
+ F: 1,
+ Name: donName,
+ Peers: peers,
+ CapabilitySet: capSet,
+ }
+}
+
+func deployCR(ctx context.Context, artefactsDir string, env helpers.Environment) kcr.CapabilitiesRegistryInterface {
+ o := LoadOnchainMeta(artefactsDir, env)
+ // We always redeploy the capabilities registry to ensure it is up to date
+ // since we don't have diffing logic to determine if it has changed
+ // if o.CapabilitiesRegistry != nil {
+ // fmt.Println("CapabilitiesRegistry already deployed, skipping...")
+ // return o.CapabilitiesRegistry
+ // }
+
+ _, tx, capabilitiesRegistry, innerErr := kcr.DeployCapabilitiesRegistry(env.Owner, env.Ec)
+ PanicErr(innerErr)
+ helpers.ConfirmContractDeployed(ctx, env.Ec, tx, env.ChainID)
+
+ o.CapabilitiesRegistry = capabilitiesRegistry
+ WriteOnchainMeta(o, artefactsDir)
+ return capabilitiesRegistry
+}
diff --git a/core/scripts/keystone/src/02_provision_crib.go b/core/scripts/keystone/src/02_provision_crib.go
new file mode 100644
index 00000000000..bf3a31f8b7e
--- /dev/null
+++ b/core/scripts/keystone/src/02_provision_crib.go
@@ -0,0 +1,310 @@
+package src
+
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+ "strconv"
+
+ ocrcommontypes "github.com/smartcontractkit/libocr/commontypes"
+ "gopkg.in/yaml.v3"
+
+ helpers "github.com/smartcontractkit/chainlink/core/scripts/common"
+ evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml"
+ evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types"
+ "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big"
+ "github.com/smartcontractkit/chainlink/v2/core/config/toml"
+ "github.com/smartcontractkit/chainlink/v2/core/services/chainlink"
+)
+
+type Helm struct {
+ Helm Chart `yaml:"helm"`
+}
+
+type Chart struct {
+ HelmValues HelmValues `yaml:"values"`
+}
+
+type HelmValues struct {
+ Chainlink Chainlink `yaml:"chainlink,omitempty"`
+ Ingress Ingress `yaml:"ingress,omitempty"`
+}
+
+type Ingress struct {
+ Hosts []Host `yaml:"hosts,omitempty"`
+}
+
+type Host struct {
+ Host string `yaml:"host,omitempty"`
+ HTTP HTTP `yaml:"http,omitempty"`
+}
+
+type HTTP struct {
+ Paths []Path `yaml:"paths,omitempty"`
+}
+
+type Path struct {
+ Path string `yaml:"path,omitempty"`
+ Backend Backend `yaml:"backend,omitempty"`
+}
+
+type Backend struct {
+ Service Service `yaml:"service,omitempty"`
+}
+
+type Service struct {
+ Name string `yaml:"name,omitempty"`
+ Port Port `yaml:"port,omitempty"`
+}
+
+type Port struct {
+ Number int `yaml:"number,omitempty"`
+}
+
+type Chainlink struct {
+ Nodes map[string]Node `yaml:"nodes,omitempty"`
+}
+
+type Node struct {
+ Image string `yaml:"image,omitempty"`
+ OverridesToml string `yaml:"overridesToml,omitempty"`
+}
+
+func writePreprovisionConfig(nodeSetSize int, outputPath string) {
+ chart := generatePreprovisionConfig(nodeSetSize)
+
+ writeCribConfig(chart, outputPath)
+}
+
+func writeCribConfig(chart Helm, outputPath string) {
+ yamlData, err := yaml.Marshal(chart)
+ helpers.PanicErr(err)
+
+ if outputPath == "-" {
+ _, err = os.Stdout.Write(yamlData)
+ helpers.PanicErr(err)
+ } else {
+ ensureArtefactsDir(filepath.Dir(outputPath))
+ err = os.WriteFile(outputPath, yamlData, 0600)
+ helpers.PanicErr(err)
+ }
+}
+
+func generatePreprovisionConfig(nodeSetSize int) Helm {
+ nodeSets := []string{"ks-wf-", "ks-str-trig-"}
+ nodes := make(map[string]Node)
+ nodeNames := []string{}
+
+ for nodeSetIndex, prefix := range nodeSets {
+ // Bootstrap node
+ btNodeName := fmt.Sprintf("%d-%sbt-node1", nodeSetIndex, prefix)
+ nodeNames = append(nodeNames, btNodeName)
+ nodes[btNodeName] = Node{
+ Image: "${runtime.images.app}",
+ }
+
+ // Other nodes
+ for i := 2; i <= nodeSetSize; i++ {
+ nodeName := fmt.Sprintf("%d-%snode%d", nodeSetIndex, prefix, i)
+ nodeNames = append(nodeNames, nodeName)
+ nodes[nodeName] = Node{
+ Image: "${runtime.images.app}",
+ }
+ }
+ }
+
+ ingress := generateIngress(nodeNames)
+
+ helm := Helm{
+ Chart{
+ HelmValues: HelmValues{
+ Chainlink: Chainlink{
+ Nodes: nodes,
+ },
+ Ingress: ingress,
+ },
+ },
+ }
+
+ return helm
+}
+
+func writePostProvisionConfig(
+ nodeSets NodeSets,
+ chainID int64,
+ capabilitiesP2PPort int64,
+ forwarderAddress string,
+ capabilitiesRegistryAddress string,
+ outputPath string,
+) {
+ chart := generatePostprovisionConfig(
+ nodeSets,
+ chainID,
+ capabilitiesP2PPort,
+ forwarderAddress,
+ capabilitiesRegistryAddress,
+ )
+
+ writeCribConfig(chart, outputPath)
+}
+
+func generatePostprovisionConfig(
+ nodeSets NodeSets,
+ chainID int64,
+ capabilitiesP2PPort int64,
+ forwarderAddress string,
+ capabillitiesRegistryAddress string,
+) Helm {
+ nodes := make(map[string]Node)
+ nodeNames := []string{}
+ var capabilitiesBootstrapper *ocrcommontypes.BootstrapperLocator
+
+ // Build nodes for each NodeSet
+ for nodeSetIndex, nodeSet := range []NodeSet{nodeSets.Workflow, nodeSets.StreamsTrigger} {
+ // Bootstrap node
+ btNodeName := fmt.Sprintf("%d-%sbt-node1", nodeSetIndex, nodeSet.Prefix)
+ // Note this line ordering is important,
+ // we assign capabilitiesBootstrapper after we generate overrides so that
+ // we do not include the bootstrapper config to itself
+ overridesToml := generateOverridesToml(
+ chainID,
+ capabilitiesP2PPort,
+ capabillitiesRegistryAddress,
+ "",
+ "",
+ capabilitiesBootstrapper,
+ nodeSet.Name,
+ )
+ nodes[btNodeName] = Node{
+ Image: "${runtime.images.app}",
+ OverridesToml: overridesToml,
+ }
+ if nodeSet.Name == WorkflowNodeSetName {
+ workflowBtNodeKey := nodeSets.Workflow.NodeKeys[0] // First node key as bootstrapper
+ wfBt, err := ocrcommontypes.NewBootstrapperLocator(workflowBtNodeKey.P2PPeerID, []string{fmt.Sprintf("%s:%d", nodeSets.Workflow.Nodes[0].ServiceName, capabilitiesP2PPort)})
+ helpers.PanicErr(err)
+ capabilitiesBootstrapper = wfBt
+ }
+ nodeNames = append(nodeNames, btNodeName)
+
+ // Other nodes
+ for i, nodeKey := range nodeSet.NodeKeys[1:] { // Start from second key
+ nodeName := fmt.Sprintf("%d-%snode%d", nodeSetIndex, nodeSet.Prefix, i+2)
+ nodeNames = append(nodeNames, nodeName)
+ overridesToml := generateOverridesToml(
+ chainID,
+ capabilitiesP2PPort,
+ capabillitiesRegistryAddress,
+ nodeKey.EthAddress,
+ forwarderAddress,
+ capabilitiesBootstrapper,
+ nodeSet.Name,
+ )
+ nodes[nodeName] = Node{
+ Image: "${runtime.images.app}",
+ OverridesToml: overridesToml,
+ }
+ }
+ }
+
+ ingress := generateIngress(nodeNames)
+
+ helm := Helm{
+ Chart{
+ HelmValues: HelmValues{
+ Chainlink: Chainlink{
+ Nodes: nodes,
+ },
+ Ingress: ingress,
+ },
+ },
+ }
+
+ return helm
+}
+
+func generateOverridesToml(
+ chainID int64,
+ capabilitiesP2PPort int64,
+ externalRegistryAddress string,
+ fromAddress string,
+ forwarderAddress string,
+ capabilitiesBootstrapper *ocrcommontypes.BootstrapperLocator,
+ nodeSetName string,
+) string {
+ evmConfig := &evmcfg.EVMConfig{
+ ChainID: big.NewI(chainID),
+ Nodes: nil, // We have the rpc nodes set globally
+ }
+
+ conf := chainlink.Config{
+ Core: toml.Core{
+ Capabilities: toml.Capabilities{
+ ExternalRegistry: toml.ExternalRegistry{
+ Address: ptr(externalRegistryAddress),
+ NetworkID: ptr("evm"),
+ ChainID: ptr(strconv.FormatInt(chainID, 10)),
+ },
+ Peering: toml.P2P{
+ V2: toml.P2PV2{
+ Enabled: ptr(true),
+ ListenAddresses: ptr([]string{fmt.Sprintf("0.0.0.0:%d", capabilitiesP2PPort)}),
+ },
+ },
+ },
+ },
+ }
+
+ if capabilitiesBootstrapper != nil {
+ conf.Core.Capabilities.Peering.V2.DefaultBootstrappers = ptr([]ocrcommontypes.BootstrapperLocator{*capabilitiesBootstrapper})
+
+ if nodeSetName == WorkflowNodeSetName {
+ evmConfig.Workflow = evmcfg.Workflow{
+ FromAddress: ptr(evmtypes.MustEIP55Address(fromAddress)),
+ ForwarderAddress: ptr(evmtypes.MustEIP55Address(forwarderAddress)),
+ }
+ }
+ }
+
+ conf.EVM = evmcfg.EVMConfigs{
+ evmConfig,
+ }
+
+ confStr, err := conf.TOMLString()
+ helpers.PanicErr(err)
+
+ return confStr
+}
+
+// New function to generate Ingress
+func generateIngress(nodeNames []string) Ingress {
+ hosts := make([]Host, 0, len(nodeNames))
+
+ for _, nodeName := range nodeNames {
+ host := Host{
+ Host: fmt.Sprintf("${DEVSPACE_NAMESPACE}-%s.${DEVSPACE_INGRESS_BASE_DOMAIN}", nodeName),
+ HTTP: HTTP{
+ Paths: []Path{
+ {
+ Path: "/",
+ Backend: Backend{
+ Service: Service{
+ Name: "app-" + nodeName,
+ Port: Port{
+ Number: 6688,
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+ hosts = append(hosts, host)
+ }
+
+ return Ingress{
+ Hosts: hosts,
+ }
+}
+
+func ptr[T any](t T) *T { return &t }
diff --git a/core/scripts/keystone/src/02_provision_crib_test.go b/core/scripts/keystone/src/02_provision_crib_test.go
new file mode 100644
index 00000000000..4e4cbf1efd5
--- /dev/null
+++ b/core/scripts/keystone/src/02_provision_crib_test.go
@@ -0,0 +1,44 @@
+package src
+
+import (
+ "strings"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/gkampitakis/go-snaps/snaps"
+ "gopkg.in/yaml.v3"
+)
+
+func TestGeneratePostprovisionConfig(t *testing.T) {
+ chainID := int64(1337)
+ capabilitiesP2PPort := int64(6691)
+ nodeSetsPath := "./testdata/node_sets.json"
+ nodeSetSize := 5
+ forwarderAddress := common.Address([20]byte{0: 1}).Hex()
+ capabilitiesRegistryAddress := common.Address([20]byte{0: 2}).Hex()
+ nodeSets := downloadNodeSets(chainID, nodeSetsPath, nodeSetSize)
+
+ chart := generatePostprovisionConfig(nodeSets, chainID, capabilitiesP2PPort, forwarderAddress, capabilitiesRegistryAddress)
+
+ yamlData, err := yaml.Marshal(chart)
+ if err != nil {
+ t.Fatalf("Failed to marshal chart: %v", err)
+ }
+
+ linesStr := strings.Split(string(yamlData), "\n")
+ snaps.MatchSnapshot(t, strings.Join(linesStr, "\n"))
+}
+
+func TestGeneratePreprovisionConfig(t *testing.T) {
+ nodeSetSize := 5
+
+ chart := generatePreprovisionConfig(nodeSetSize)
+
+ yamlData, err := yaml.Marshal(chart)
+ if err != nil {
+ t.Fatalf("Failed to marshal chart: %v", err)
+ }
+
+ linesStr := strings.Split(string(yamlData), "\n")
+ snaps.MatchSnapshot(t, strings.Join(linesStr, "\n"))
+}
diff --git a/core/scripts/keystone/src/02_provision_forwarder_contract.go b/core/scripts/keystone/src/02_provision_forwarder_contract.go
new file mode 100644
index 00000000000..4cc3d7f70fa
--- /dev/null
+++ b/core/scripts/keystone/src/02_provision_forwarder_contract.go
@@ -0,0 +1,33 @@
+package src
+
+import (
+ "context"
+ "fmt"
+
+ helpers "github.com/smartcontractkit/chainlink/core/scripts/common"
+ "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/forwarder"
+)
+
+func deployForwarder(
+ env helpers.Environment,
+ artefacts string,
+) {
+ o := LoadOnchainMeta(artefacts, env)
+ if o.Forwarder != nil {
+ fmt.Println("Forwarder contract already deployed, skipping")
+ return
+ }
+
+ fmt.Println("Deploying forwarder contract...")
+ forwarderContract := DeployForwarder(env)
+ o.Forwarder = forwarderContract
+ WriteOnchainMeta(o, artefacts)
+}
+
+func DeployForwarder(e helpers.Environment) *forwarder.KeystoneForwarder {
+ _, tx, contract, err := forwarder.DeployKeystoneForwarder(e.Owner, e.Ec)
+ PanicErr(err)
+ helpers.ConfirmContractDeployed(context.Background(), e.Ec, tx, e.ChainID)
+
+ return contract
+}
diff --git a/core/scripts/keystone/src/02_provision_ocr3_capability.go b/core/scripts/keystone/src/02_provision_ocr3_capability.go
new file mode 100644
index 00000000000..cd80bf4238b
--- /dev/null
+++ b/core/scripts/keystone/src/02_provision_ocr3_capability.go
@@ -0,0 +1,286 @@
+package src
+
+import (
+ "bytes"
+ "strconv"
+ "text/template"
+
+ "context"
+ "flag"
+ "fmt"
+
+ "github.com/smartcontractkit/chainlink/deployment"
+
+ ksdeploy "github.com/smartcontractkit/chainlink/deployment/keystone/changeset"
+
+ "github.com/smartcontractkit/libocr/offchainreporting2plus/types"
+
+ helpers "github.com/smartcontractkit/chainlink/core/scripts/common"
+ "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/ocr3_capability"
+ "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm"
+)
+
+func provisionOCR3(
+ env helpers.Environment,
+ nodeSet NodeSet,
+ chainID int64,
+ p2pPort int64,
+ ocrConfigFile string,
+ artefactsDir string,
+) (onchainMeta *onchainMeta, cacheHit bool) {
+ nodeKeys := nodeSet.NodeKeys
+ nodes := nodeSet.Nodes
+
+ onchainMeta, cacheHit = deployOCR3Contract(
+ nodeKeys,
+ env,
+ ocrConfigFile,
+ artefactsDir,
+ )
+
+ deployOCR3JobSpecs(
+ nodes,
+ chainID,
+ nodeKeys,
+ p2pPort,
+ onchainMeta,
+ )
+
+ return
+}
+
+func deployOCR3Contract(
+ nodeKeys []NodeKeys,
+ env helpers.Environment,
+ configFile string,
+ artefacts string,
+) (o *onchainMeta, cacheHit bool) {
+ o = LoadOnchainMeta(artefacts, env)
+ ocrConf := generateOCR3Config(
+ nodeKeys,
+ configFile,
+ )
+
+ if o.OCR3 != nil {
+ // types.ConfigDigestPrefixKeystoneOCR3Capability
+ fmt.Println("OCR3 Contract already deployed, checking config...")
+ latestConfigDigestBytes, err := o.OCR3.LatestConfigDetails(nil)
+ PanicErr(err)
+ latestConfigDigest, err := types.BytesToConfigDigest(latestConfigDigestBytes.ConfigDigest[:])
+ PanicErr(err)
+
+ cc := ocrConfToContractConfig(ocrConf, latestConfigDigestBytes.ConfigCount)
+ digester := evm.OCR3CapabilityOffchainConfigDigester{
+ ChainID: uint64(env.ChainID), //nolint:gosec // this won't overflow
+ ContractAddress: o.OCR3.Address(),
+ }
+ digest, err := digester.ConfigDigest(context.Background(), cc)
+ PanicErr(err)
+
+ if digest.Hex() == latestConfigDigest.Hex() {
+ fmt.Printf("OCR3 Contract already deployed with the same config (digest: %s), skipping...\n", digest.Hex())
+ return o, false
+ }
+
+ fmt.Printf("OCR3 Contract contains a different config, updating...\nOld digest: %s\nNew digest: %s\n", latestConfigDigest.Hex(), digest.Hex())
+ setOCRConfig(o, env, ocrConf, artefacts)
+ return o, true
+ }
+
+ fmt.Println("Deploying keystone ocr3 contract...")
+ _, tx, ocrContract, err := ocr3_capability.DeployOCR3Capability(env.Owner, env.Ec)
+ PanicErr(err)
+ helpers.ConfirmContractDeployed(context.Background(), env.Ec, tx, env.ChainID)
+ o.OCR3 = ocrContract
+ setOCRConfig(o, env, ocrConf, artefacts)
+
+ return o, true
+}
+
+func generateOCR3Config(nodeKeys []NodeKeys, configFile string) ksdeploy.OCR3OnchainConfig {
+ topLevelCfg := mustReadOCR3Config(configFile)
+ cfg := topLevelCfg.OracleConfig
+ secrets := deployment.XXXGenerateTestOCRSecrets()
+ c, err := ksdeploy.GenerateOCR3Config(cfg, nodeKeysToKsDeployNodeKeys(nodeKeys[1:]), secrets) // skip the bootstrap node
+ helpers.PanicErr(err)
+ return c
+}
+
+func setOCRConfig(o *onchainMeta, env helpers.Environment, ocrConf ksdeploy.OCR3OnchainConfig, artefacts string) {
+ fmt.Println("Setting OCR3 contract config...")
+ tx, err := o.OCR3.SetConfig(env.Owner,
+ ocrConf.Signers,
+ ocrConf.Transmitters,
+ ocrConf.F,
+ ocrConf.OnchainConfig,
+ ocrConf.OffchainConfigVersion,
+ ocrConf.OffchainConfig,
+ )
+ PanicErr(err)
+ receipt := helpers.ConfirmTXMined(context.Background(), env.Ec, tx, env.ChainID)
+ o.SetConfigTxBlock = receipt.BlockNumber.Uint64()
+ WriteOnchainMeta(o, artefacts)
+}
+
+func deployOCR3JobSpecs(
+ nodes []NodeWithCreds,
+ chainID int64,
+ nodeKeys []NodeKeys,
+ p2pPort int64,
+ onchainMeta *onchainMeta,
+) {
+ ocrAddress := onchainMeta.OCR3.Address().Hex()
+ bootstrapURI := fmt.Sprintf("%s@%s:%d", nodeKeys[0].P2PPeerID, nodes[0].ServiceName, p2pPort)
+
+ var specName string
+ for i, n := range nodes {
+ var spec string
+
+ if i == 0 {
+ bootstrapSpecConfig := BootstrapJobSpecConfig{
+ JobSpecName: "ocr3_bootstrap",
+ OCRConfigContractAddress: ocrAddress,
+ ChainID: chainID,
+ }
+ specName = bootstrapSpecConfig.JobSpecName
+ spec = createBootstrapJobSpec(bootstrapSpecConfig)
+ } else {
+ oc := OracleJobSpecConfig{
+ JobSpecName: "ocr3_oracle",
+ OCRConfigContractAddress: ocrAddress,
+ OCRKeyBundleID: nodeKeys[i].OCR2BundleID,
+ BootstrapURI: bootstrapURI,
+ TransmitterID: nodeKeys[i].EthAddress,
+ ChainID: chainID,
+ AptosKeyBundleID: nodeKeys[i].AptosBundleID,
+ }
+ specName = oc.JobSpecName
+ spec = createOracleJobSpec(oc)
+ }
+
+ api := newNodeAPI(n)
+ upsertJob(api, specName, spec)
+
+ fmt.Printf("Replaying from block: %d\n", onchainMeta.SetConfigTxBlock)
+ fmt.Printf("EVM Chain ID: %d\n\n", chainID)
+ api.withFlags(api.methods.ReplayFromBlock, func(fs *flag.FlagSet) {
+ err := fs.Set("block-number", strconv.FormatUint(onchainMeta.SetConfigTxBlock, 10))
+ helpers.PanicErr(err)
+ err = fs.Set("evm-chain-id", strconv.FormatInt(chainID, 10))
+ helpers.PanicErr(err)
+ }).mustExec()
+ }
+}
+
+func mustReadOCR3Config(fileName string) (output ksdeploy.TopLevelConfigSource) {
+ return mustReadJSON[ksdeploy.TopLevelConfigSource](fileName)
+}
+
+func nodeKeysToKsDeployNodeKeys(nks []NodeKeys) []ksdeploy.NodeKeys {
+ keys := []ksdeploy.NodeKeys{}
+ for _, nk := range nks {
+ keys = append(keys, ksdeploy.NodeKeys{
+ EthAddress: nk.EthAddress,
+ AptosAccount: nk.AptosAccount,
+ AptosBundleID: nk.AptosBundleID,
+ AptosOnchainPublicKey: nk.AptosOnchainPublicKey,
+ P2PPeerID: nk.P2PPeerID,
+ OCR2BundleID: nk.OCR2BundleID,
+ OCR2OnchainPublicKey: nk.OCR2OnchainPublicKey,
+ OCR2OffchainPublicKey: nk.OCR2OffchainPublicKey,
+ OCR2ConfigPublicKey: nk.OCR2ConfigPublicKey,
+ CSAPublicKey: nk.CSAPublicKey,
+ })
+ }
+ return keys
+}
+
+// BootstrapJobSpecConfig holds configuration for the bootstrap job spec
+type BootstrapJobSpecConfig struct {
+ JobSpecName string
+ OCRConfigContractAddress string
+ ChainID int64
+}
+
+// OracleJobSpecConfig holds configuration for the oracle job spec
+type OracleJobSpecConfig struct {
+ JobSpecName string
+ OCRConfigContractAddress string
+ OCRKeyBundleID string
+ BootstrapURI string
+ TransmitterID string
+ ChainID int64
+ AptosKeyBundleID string
+}
+
+func createBootstrapJobSpec(config BootstrapJobSpecConfig) string {
+ const bootstrapTemplate = `
+type = "bootstrap"
+schemaVersion = 1
+name = "{{ .JobSpecName }}"
+contractID = "{{ .OCRConfigContractAddress }}"
+relay = "evm"
+
+[relayConfig]
+chainID = "{{ .ChainID }}"
+providerType = "ocr3-capability"
+`
+
+ tmpl, err := template.New("bootstrap").Parse(bootstrapTemplate)
+ if err != nil {
+ panic(err)
+ }
+
+ var rendered bytes.Buffer
+ err = tmpl.Execute(&rendered, config)
+ if err != nil {
+ panic(err)
+ }
+
+ return rendered.String()
+}
+
+func createOracleJobSpec(config OracleJobSpecConfig) string {
+ const oracleTemplate = `
+type = "offchainreporting2"
+schemaVersion = 1
+name = "{{ .JobSpecName }}"
+contractID = "{{ .OCRConfigContractAddress }}"
+ocrKeyBundleID = "{{ .OCRKeyBundleID }}"
+p2pv2Bootstrappers = [
+ "{{ .BootstrapURI }}",
+]
+relay = "evm"
+pluginType = "plugin"
+transmitterID = "{{ .TransmitterID }}"
+
+[relayConfig]
+chainID = "{{ .ChainID }}"
+
+[pluginConfig]
+command = "chainlink-ocr3-capability"
+ocrVersion = 3
+pluginName = "ocr-capability"
+providerType = "ocr3-capability"
+telemetryType = "plugin"
+
+[onchainSigningStrategy]
+strategyName = 'multi-chain'
+[onchainSigningStrategy.config]
+evm = "{{ .OCRKeyBundleID }}"
+aptos = "{{ .AptosKeyBundleID }}"
+`
+
+ tmpl, err := template.New("oracle").Parse(oracleTemplate)
+ if err != nil {
+ panic(err)
+ }
+
+ var rendered bytes.Buffer
+ err = tmpl.Execute(&rendered, config)
+ if err != nil {
+ panic(err)
+ }
+
+ return rendered.String()
+}
diff --git a/core/scripts/keystone/src/02_provision_ocr3_capability_test.go b/core/scripts/keystone/src/02_provision_ocr3_capability_test.go
new file mode 100644
index 00000000000..df4d9a41f5c
--- /dev/null
+++ b/core/scripts/keystone/src/02_provision_ocr3_capability_test.go
@@ -0,0 +1,67 @@
+package src
+
+import (
+ "errors"
+ "fmt"
+ "testing"
+
+ "github.com/gkampitakis/go-snaps/match"
+ "github.com/gkampitakis/go-snaps/snaps"
+)
+
+func TestGenerateOCR3Config(t *testing.T) {
+ // Generate OCR3 config
+ nodeSet := downloadNodeSets(1337, "./testdata/node_sets.json", 4)
+ nodeKeys := nodeSet.Workflow.NodeKeys
+ config := generateOCR3Config(nodeKeys, "./testdata/SampleConfig.json")
+
+ matchOffchainConfig := match.Custom("OffchainConfig", func(s any) (any, error) {
+ // coerce the value to a string
+ s, ok := s.(string)
+ if !ok {
+ return nil, errors.New("offchain config is not a string")
+ }
+
+ // if the string is not empty
+ if s == "" {
+ return nil, errors.New("offchain config is empty")
+ }
+
+ return "", nil
+ })
+
+ snaps.MatchJSON(t, config, matchOffchainConfig)
+}
+
+func TestGenSpecs(t *testing.T) {
+ nodeSetsPath := "./testdata/node_sets.json"
+ chainID := int64(1337)
+ p2pPort := int64(6690)
+ contractAddress := "0xB29934624cAe3765E33115A9530a13f5aEC7fa8A"
+ nodeSet := downloadNodeSets(chainID, nodeSetsPath, 4).Workflow
+
+ // Create Bootstrap Job Spec
+ bootstrapConfig := BootstrapJobSpecConfig{
+ JobSpecName: "ocr3_bootstrap",
+ OCRConfigContractAddress: contractAddress,
+ ChainID: chainID,
+ }
+ bootstrapSpec := createBootstrapJobSpec(bootstrapConfig)
+
+ // Create Oracle Job Spec
+ oracleConfig := OracleJobSpecConfig{
+ JobSpecName: "ocr3_oracle",
+ OCRConfigContractAddress: contractAddress,
+ OCRKeyBundleID: nodeSet.NodeKeys[1].OCR2BundleID,
+ BootstrapURI: fmt.Sprintf("%s@%s:%d", nodeSet.NodeKeys[0].P2PPeerID, nodeSet.Nodes[0].ServiceName, p2pPort),
+ TransmitterID: nodeSet.NodeKeys[1].P2PPeerID,
+ ChainID: chainID,
+ AptosKeyBundleID: nodeSet.NodeKeys[1].AptosBundleID,
+ }
+ oracleSpec := createOracleJobSpec(oracleConfig)
+
+ // Combine Specs
+ generatedSpecs := fmt.Sprintf("%s\n\n%s", bootstrapSpec, oracleSpec)
+
+ snaps.MatchSnapshot(t, generatedSpecs)
+}
diff --git a/core/scripts/keystone/src/02_provision_streams_trigger_capability.go b/core/scripts/keystone/src/02_provision_streams_trigger_capability.go
new file mode 100644
index 00000000000..f476319362b
--- /dev/null
+++ b/core/scripts/keystone/src/02_provision_streams_trigger_capability.go
@@ -0,0 +1,522 @@
+package src
+
+// This package deploys "offchainreporting2" job specs, which setup the streams trigger
+// for the targeted node set
+// See https://github.com/smartcontractkit/chainlink/blob/4d5fc1943bd6a60b49cbc3d263c0aa47dc3cecb7/core/services/ocr2/plugins/mercury/integration_test.go#L92
+// for how to setup the mercury portion of the streams trigger
+// You can see how all fields are being used here: https://github.com/smartcontractkit/chainlink/blob/4d5fc1943bd6a60b49cbc3d263c0aa47dc3cecb7/core/services/ocr2/plugins/mercury/helpers_test.go#L314
+// https://github.com/smartcontractkit/infra-k8s/blob/be47098adfb605d79b5bab6aa601bcf443a6c48b/projects/chainlink/files/chainlink-clusters/cl-keystone-cap-one/config.yaml#L1
+// Trigger gets added to the registry here: https://github.com/smartcontractkit/chainlink/blob/4d5fc1943bd6a60b49cbc3d263c0aa47dc3cecb7/core/services/relay/evm/evm.go#L360
+// See integration workflow here: https://github.com/smartcontractkit/chainlink/blob/4d5fc1943bd6a60b49cbc3d263c0aa47dc3cecb7/core/capabilities/integration_tests/workflow.go#L15
+import (
+ "bytes"
+ "context"
+ "crypto/ed25519"
+ "encoding/binary"
+ "encoding/hex"
+ "encoding/json"
+ "fmt"
+ "html/template"
+ "math"
+ "math/big"
+ "time"
+
+ "net/url"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/shopspring/decimal"
+
+ "github.com/ethereum/go-ethereum/core/types"
+
+ "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper"
+ "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3confighelper"
+
+ ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types"
+
+ mercurytypes "github.com/smartcontractkit/chainlink-common/pkg/types/mercury"
+ datastreamsmercury "github.com/smartcontractkit/chainlink-data-streams/mercury"
+
+ helpers "github.com/smartcontractkit/chainlink/core/scripts/common"
+ "github.com/smartcontractkit/chainlink/deployment"
+ "github.com/smartcontractkit/chainlink/v2/core/bridges"
+ "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm"
+ "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury"
+
+ verifierContract "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/llo-feeds/generated/verifier"
+ "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/llo-feeds/generated/verifier_proxy"
+
+ "github.com/smartcontractkit/chainlink/v2/core/store/models"
+ "github.com/smartcontractkit/chainlink/v2/core/web/presenters"
+)
+
+type feed struct {
+ id [32]byte
+ name string
+
+ // we create a bridge for each feed
+ bridgeName string
+ bridgeURL string
+}
+
+func v3FeedID(id [32]byte) [32]byte {
+ binary.BigEndian.PutUint16(id[:2], 3)
+ return id
+}
+
+var feeds = []feed{
+ {
+ v3FeedID([32]byte{5: 1}),
+ "BTC/USD",
+ "mock-bridge-btc",
+ "http://external-adapter:4001",
+ },
+ {
+ v3FeedID([32]byte{5: 2}),
+ "LINK/USD",
+ "mock-bridge-link",
+ "http://external-adapter:4002",
+ },
+ {
+ v3FeedID([32]byte{5: 3}),
+ "NATIVE/USD",
+ "mock-bridge-native",
+ "http://external-adapter:4003",
+ },
+}
+
+// See /core/services/ocr2/plugins/mercury/integration_test.go
+func setupStreamsTrigger(
+ env helpers.Environment,
+ nodeSet NodeSet,
+ chainID int64,
+ p2pPort int64,
+ ocrConfigFilePath string,
+ artefactsDir string,
+) {
+ fmt.Printf("Deploying streams trigger for chain %d\n", chainID)
+ fmt.Printf("Using OCR config file: %s\n", ocrConfigFilePath)
+
+ fmt.Printf("Deploying Mercury V0.3 contracts\n")
+ verifier := deployMercuryV03Contracts(env, artefactsDir)
+
+ fmt.Printf("Generating Mercury OCR config\n")
+ ocrConfig := generateMercuryOCR2Config(nodeSet.NodeKeys[1:]) // skip the bootstrap node
+
+ for _, feed := range feeds {
+ fmt.Println("Configuring feeds...")
+ fmt.Printf("FeedID: %x\n", feed.id)
+ fmt.Printf("FeedName: %s\n", feed.name)
+ fmt.Printf("BridgeName: %s\n", feed.bridgeName)
+ fmt.Printf("BridgeURL: %s\n", feed.bridgeURL)
+
+ latestConfigDetails, err := verifier.LatestConfigDetails(nil, feed.id)
+ PanicErr(err)
+ latestConfigDigest, err := ocrtypes.BytesToConfigDigest(latestConfigDetails.ConfigDigest[:])
+ PanicErr(err)
+
+ digester := mercury.NewOffchainConfigDigester(
+ feed.id,
+ big.NewInt(chainID),
+ verifier.Address(),
+ ocrtypes.ConfigDigestPrefixMercuryV02,
+ )
+ configDigest, err := digester.ConfigDigest(
+ context.Background(),
+ mercuryOCRConfigToContractConfig(
+ ocrConfig,
+ latestConfigDetails.ConfigCount,
+ ),
+ )
+ PanicErr(err)
+
+ if configDigest.Hex() == latestConfigDigest.Hex() {
+ fmt.Printf("Verifier already deployed with the same config (digest: %s), skipping...\n", configDigest.Hex())
+ } else {
+ fmt.Printf("Verifier contains a different config, updating...\nOld digest: %s\nNew digest: %s\n", latestConfigDigest.Hex(), configDigest.Hex())
+ tx, err := verifier.SetConfig(
+ env.Owner,
+ feed.id,
+ ocrConfig.Signers,
+ ocrConfig.Transmitters,
+ ocrConfig.F,
+ ocrConfig.OnchainConfig,
+ ocrConfig.OffchainConfigVersion,
+ ocrConfig.OffchainConfig,
+ nil,
+ )
+ helpers.ConfirmTXMined(context.Background(), env.Ec, tx, env.ChainID)
+ PanicErr(err)
+ }
+
+ fmt.Printf("Deploying OCR2 job specs for feed %s\n", feed.name)
+ deployOCR2JobSpecsForFeed(nodeSet, verifier, feed, chainID, p2pPort)
+ }
+
+ fmt.Println("Finished deploying streams trigger")
+}
+
+func deployMercuryV03Contracts(env helpers.Environment, artefactsDir string) verifierContract.VerifierInterface {
+ var confirmDeploy = func(tx *types.Transaction, err error) {
+ helpers.ConfirmContractDeployed(context.Background(), env.Ec, tx, env.ChainID)
+ PanicErr(err)
+ }
+ o := LoadOnchainMeta(artefactsDir, env)
+
+ if o.VerifierProxy != nil {
+ fmt.Printf("Verifier proxy contract already deployed at %s\n", o.VerifierProxy.Address())
+ } else {
+ fmt.Printf("Deploying verifier proxy contract\n")
+ _, tx, verifierProxy, err := verifier_proxy.DeployVerifierProxy(env.Owner, env.Ec, common.Address{}) // zero address for access controller disables access control
+ confirmDeploy(tx, err)
+ o.VerifierProxy = verifierProxy
+ WriteOnchainMeta(o, artefactsDir)
+ }
+
+ if o.Verifier == nil {
+ fmt.Printf("Deploying verifier contract\n")
+ _, tx, verifier, err := verifierContract.DeployVerifier(env.Owner, env.Ec, o.VerifierProxy.Address())
+ confirmDeploy(tx, err)
+ o.Verifier = verifier
+ WriteOnchainMeta(o, artefactsDir)
+ } else {
+ fmt.Printf("Verifier contract already deployed at %s\n", o.Verifier.Address().Hex())
+ }
+
+ if o.InitializedVerifierAddress != o.Verifier.Address() {
+ fmt.Printf("Current initialized verifier address (%s) differs from the new verifier address (%s). Initializing verifier.\n", o.InitializedVerifierAddress.Hex(), o.Verifier.Address().Hex())
+ tx, err := o.VerifierProxy.InitializeVerifier(env.Owner, o.Verifier.Address())
+ receipt := helpers.ConfirmTXMined(context.Background(), env.Ec, tx, env.ChainID)
+ PanicErr(err)
+ inited, err := o.VerifierProxy.ParseVerifierInitialized(*receipt.Logs[0])
+ PanicErr(err)
+ o.InitializedVerifierAddress = inited.VerifierAddress
+ WriteOnchainMeta(o, artefactsDir)
+ } else {
+ fmt.Printf("Verifier %s already initialized\n", o.Verifier.Address().Hex())
+ }
+
+ return o.Verifier
+}
+
+func deployOCR2JobSpecsForFeed(nodeSet NodeSet, verifier verifierContract.VerifierInterface, feed feed, chainID int64, p2pPort int64) {
+ // we assign the first node as the bootstrap node
+ for i, n := range nodeSet.NodeKeys {
+ // parallel arrays
+ api := newNodeAPI(nodeSet.Nodes[i])
+ jobSpecName := ""
+ jobSpecStr := ""
+
+ upsertBridge(api, feed.bridgeName, feed.bridgeURL)
+
+ if i == 0 {
+ // Prepare data for Bootstrap Job
+ bootstrapData := MercuryV3BootstrapJobSpecData{
+ FeedName: feed.name,
+ VerifierAddress: verifier.Address().Hex(),
+ FeedID: fmt.Sprintf("%x", feed.id),
+ ChainID: chainID,
+ }
+
+ // Create Bootstrap Job
+ jobSpecName, jobSpecStr = createMercuryV3BootstrapJob(bootstrapData)
+ } else {
+ // Prepare data for Mercury V3 Job
+ mercuryData := MercuryV3JobSpecData{
+ FeedName: "feed-" + feed.name,
+ BootstrapHost: fmt.Sprintf("%s@%s:%d", nodeSet.NodeKeys[0].P2PPeerID, nodeSet.Nodes[0].ServiceName, p2pPort),
+ VerifierAddress: verifier.Address().Hex(),
+ Bridge: feed.bridgeName,
+ NodeCSAKey: n.CSAPublicKey,
+ FeedID: fmt.Sprintf("%x", feed.id),
+ LinkFeedID: fmt.Sprintf("%x", feeds[1].id),
+ NativeFeedID: fmt.Sprintf("%x", feeds[2].id),
+ OCRKeyBundleID: n.OCR2BundleID,
+ ChainID: chainID,
+ }
+
+ // Create Mercury V3 Job
+ jobSpecName, jobSpecStr = createMercuryV3OracleJob(mercuryData)
+ }
+
+ upsertJob(api, jobSpecName, jobSpecStr)
+ }
+}
+
+// Template definitions
+const mercuryV3OCR2bootstrapJobTemplate = `
+type = "bootstrap"
+relay = "evm"
+schemaVersion = 1
+name = "{{ .Name }}"
+contractID = "{{ .VerifierAddress }}"
+feedID = "0x{{ .FeedID }}"
+contractConfigTrackerPollInterval = "1s"
+
+[relayConfig]
+chainID = {{ .ChainID }}
+enableTriggerCapability = true
+`
+
+const mercuryV3OCR2OracleJobTemplate = `
+type = "offchainreporting2"
+schemaVersion = 1
+name = "{{ .Name }}"
+p2pv2Bootstrappers = ["{{ .BootstrapHost }}"]
+forwardingAllowed = false
+maxTaskDuration = "1s"
+contractID = "{{ .VerifierAddress }}"
+feedID = "0x{{ .FeedID }}"
+contractConfigTrackerPollInterval = "1s"
+ocrKeyBundleID = "{{ .OCRKeyBundleID }}"
+relay = "evm"
+pluginType = "mercury"
+transmitterID = "{{ .NodeCSAKey }}"
+observationSource = """
+ price [type=bridge name="{{ .Bridge }}" timeout="50ms" requestData=""];
+
+ benchmark_price [type=jsonparse path="result,mid" index=0];
+ price -> benchmark_price;
+
+ bid_price [type=jsonparse path="result,bid" index=1];
+ price -> bid_price;
+
+ ask_price [type=jsonparse path="result,ask" index=2];
+ price -> ask_price;
+"""
+
+[relayConfig]
+enableTriggerCapability = true
+chainID = "{{ .ChainID }}"
+`
+
+// Data structures
+type MercuryV3BootstrapJobSpecData struct {
+ FeedName string
+ // Automatically generated from FeedName
+ Name string
+ VerifierAddress string
+ FeedID string
+ ChainID int64
+}
+
+type MercuryV3JobSpecData struct {
+ FeedName string
+ // Automatically generated from FeedName
+ Name string
+ BootstrapHost string
+ VerifierAddress string
+ Bridge string
+ NodeCSAKey string
+ FeedID string
+ LinkFeedID string
+ NativeFeedID string
+ OCRKeyBundleID string
+ ChainID int64
+}
+
+// createMercuryV3BootstrapJob creates a bootstrap job specification using the provided data.
+func createMercuryV3BootstrapJob(data MercuryV3BootstrapJobSpecData) (name string, jobSpecStr string) {
+ name = "boot-" + data.FeedName
+ data.Name = name
+
+ fmt.Printf("Creating bootstrap job (%s):\nverifier address: %s\nfeed name: %s\nfeed ID: %s\nchain ID: %d\n",
+ name, data.VerifierAddress, data.FeedName, data.FeedID, data.ChainID)
+
+ tmpl, err := template.New("bootstrapJob").Parse(mercuryV3OCR2bootstrapJobTemplate)
+ PanicErr(err)
+
+ var buf bytes.Buffer
+ err = tmpl.Execute(&buf, data)
+ PanicErr(err)
+
+ jobSpecStr = buf.String()
+
+ return name, jobSpecStr
+}
+
+// createMercuryV3OracleJob creates a Mercury V3 job specification using the provided data.
+func createMercuryV3OracleJob(data MercuryV3JobSpecData) (name string, jobSpecStr string) {
+ name = "mercury-" + data.FeedName
+ data.Name = name
+ fmt.Printf("Creating ocr2 job(%s):\nOCR key bundle ID: %s\nverifier address: %s\nbridge: %s\nnodeCSAKey: %s\nfeed name: %s\nfeed ID: %s\nlink feed ID: %s\nnative feed ID: %s\nchain ID: %d\n",
+ data.Name, data.OCRKeyBundleID, data.VerifierAddress, data.Bridge, data.NodeCSAKey, data.FeedName, data.FeedID, data.LinkFeedID, data.NativeFeedID, data.ChainID)
+
+ tmpl, err := template.New("mercuryV3Job").Parse(mercuryV3OCR2OracleJobTemplate)
+ PanicErr(err)
+
+ var buf bytes.Buffer
+ err = tmpl.Execute(&buf, data)
+ PanicErr(err)
+
+ jobSpecStr = buf.String()
+
+ return data.Name, jobSpecStr
+}
+
+func strToBytes32(str string) [32]byte {
+ pkBytes, err := hex.DecodeString(str)
+ helpers.PanicErr(err)
+
+ pkBytesFixed := [ed25519.PublicKeySize]byte{}
+ n := copy(pkBytesFixed[:], pkBytes)
+ if n != ed25519.PublicKeySize {
+ fmt.Printf("wrong num elements copied (%s): %d != 32\n", str, n)
+ panic("wrong num elements copied")
+ }
+ return pkBytesFixed
+}
+
+func upsertBridge(api *nodeAPI, name string, eaURL string) {
+ u, err := url.Parse(eaURL)
+ helpers.PanicErr(err)
+ url := models.WebURL(*u)
+ // Confirmations and MinimumContractPayment are not used, so we can leave them as 0
+ b := bridges.BridgeTypeRequest{
+ Name: bridges.MustParseBridgeName(name),
+ URL: url,
+ }
+ payloadb, err := json.Marshal(b)
+ helpers.PanicErr(err)
+ payload := string(payloadb)
+
+ bridgeActionType := bridgeAction(api, b)
+ switch bridgeActionType {
+ case shouldCreateBridge:
+ fmt.Printf("Creating bridge (%s): %s\n", name, eaURL)
+ resp := api.withArg(payload).mustExec(api.methods.CreateBridge)
+ resource := mustJSON[presenters.BridgeResource](resp)
+ fmt.Printf("Created bridge: %s %s\n", resource.Name, resource.URL)
+ case shouldUpdateBridge:
+ fmt.Println("Updating existing bridge")
+ api.withArgs(name, payload).mustExec(api.methods.UpdateBridge)
+ fmt.Println("Updated bridge", name)
+ case shouldNoChangeBridge:
+ fmt.Println("No changes needed for bridge", name)
+ }
+}
+
+// create enum for 3 states: create, update, no change
+var (
+ shouldCreateBridge = 0
+ shouldUpdateBridge = 1
+ shouldNoChangeBridge = 2
+)
+
+func bridgeAction(api *nodeAPI, existingBridge bridges.BridgeTypeRequest) int {
+ resp, err := api.withArg(existingBridge.Name.String()).exec(api.methods.ShowBridge)
+ if err != nil {
+ return shouldCreateBridge
+ }
+
+ b := mustJSON[presenters.BridgeResource](resp)
+ fmt.Printf("Found matching bridge: %s with URL: %s\n", b.Name, b.URL)
+ if b.URL == existingBridge.URL.String() {
+ return shouldNoChangeBridge
+ }
+ return shouldUpdateBridge
+}
+
+func generateMercuryOCR2Config(nca []NodeKeys) MercuryOCR2Config {
+ ctx := context.Background()
+ f := uint8(1)
+ rawOnchainConfig := mercurytypes.OnchainConfig{
+ Min: big.NewInt(0),
+ Max: big.NewInt(math.MaxInt64),
+ }
+
+ // Values were taken from Data Streams 250ms feeds, given by @austinborn
+ rawReportingPluginConfig := datastreamsmercury.OffchainConfig{
+ ExpirationWindow: 86400,
+ BaseUSDFee: decimal.NewFromInt(0),
+ }
+
+ onchainConfig, err := (datastreamsmercury.StandardOnchainConfigCodec{}).Encode(ctx, rawOnchainConfig)
+ helpers.PanicErr(err)
+ reportingPluginConfig, err := json.Marshal(rawReportingPluginConfig)
+ helpers.PanicErr(err)
+
+ onchainPubKeys := []common.Address{}
+ for _, n := range nca {
+ onchainPubKeys = append(onchainPubKeys, common.HexToAddress(n.OCR2OnchainPublicKey))
+ }
+
+ offchainPubKeysBytes := []ocrtypes.OffchainPublicKey{}
+ for _, n := range nca {
+ pkBytesFixed := strToBytes32(n.OCR2OffchainPublicKey)
+ offchainPubKeysBytes = append(offchainPubKeysBytes, ocrtypes.OffchainPublicKey(pkBytesFixed))
+ }
+
+ configPubKeysBytes := []ocrtypes.ConfigEncryptionPublicKey{}
+ for _, n := range nca {
+ pkBytesFixed := strToBytes32(n.OCR2ConfigPublicKey)
+ configPubKeysBytes = append(configPubKeysBytes, ocrtypes.ConfigEncryptionPublicKey(pkBytesFixed))
+ }
+
+ identities := []confighelper.OracleIdentityExtra{}
+ for index := range nca {
+ transmitterAccount := ocrtypes.Account(nca[index].CSAPublicKey)
+
+ identities = append(identities, confighelper.OracleIdentityExtra{
+ OracleIdentity: confighelper.OracleIdentity{
+ OnchainPublicKey: onchainPubKeys[index][:],
+ OffchainPublicKey: offchainPubKeysBytes[index],
+ PeerID: nca[index].P2PPeerID,
+ TransmitAccount: transmitterAccount,
+ },
+ ConfigEncryptionPublicKey: configPubKeysBytes[index],
+ })
+ }
+
+ secrets := deployment.XXXGenerateTestOCRSecrets()
+ // Values were taken from Data Streams 250ms feeds, given by @austinborn
+ signers, _, _, onchainConfig, offchainConfigVersion, offchainConfig, err := ocr3confighelper.ContractSetConfigArgsDeterministic(
+ secrets.EphemeralSk,
+ secrets.SharedSecret,
+ 10*time.Second, // DeltaProgress
+ 10*time.Second, // DeltaResend
+ 400*time.Millisecond, // DeltaInitial
+ 5*time.Second, // DeltaRound
+ 0, // DeltaGrace
+ 1*time.Second, // DeltaCertifiedCommitRequest
+ 0, // DeltaStage
+ 25, // rMax
+ []int{len(identities)}, // S
+ identities,
+ reportingPluginConfig, // reportingPluginConfig []byte,
+ nil, // maxDurationInitialization *time.Duration,
+ 0, // maxDurationQuery time.Duration,
+ 250*time.Millisecond, // Max duration observation
+ 0, // Max duration should accept attested report
+ 0, // Max duration should transmit accepted report
+ int(f), // f
+ onchainConfig,
+ )
+ PanicErr(err)
+ signerAddresses, err := evm.OnchainPublicKeyToAddress(signers)
+ PanicErr(err)
+
+ offChainTransmitters := make([][32]byte, len(nca))
+ for i, n := range nca {
+ offChainTransmitters[i] = strToBytes32(n.CSAPublicKey)
+ }
+
+ config := MercuryOCR2Config{
+ Signers: signerAddresses,
+ Transmitters: offChainTransmitters,
+ F: f,
+ OnchainConfig: onchainConfig,
+ OffchainConfigVersion: offchainConfigVersion,
+ OffchainConfig: offchainConfig,
+ }
+
+ return config
+}
+
+type MercuryOCR2Config struct {
+ Signers []common.Address
+ Transmitters [][32]byte
+ F uint8
+ OnchainConfig []byte
+ OffchainConfigVersion uint64
+ OffchainConfig []byte
+}
diff --git a/core/scripts/keystone/src/02_provision_streams_trigger_capability_test.go b/core/scripts/keystone/src/02_provision_streams_trigger_capability_test.go
new file mode 100644
index 00000000000..3a2234ba4d7
--- /dev/null
+++ b/core/scripts/keystone/src/02_provision_streams_trigger_capability_test.go
@@ -0,0 +1,57 @@
+package src
+
+import (
+ "fmt"
+ "net/url"
+ "testing"
+
+ "github.com/gkampitakis/go-snaps/snaps"
+)
+
+var (
+ chainID = int64(123456)
+ feedID = fmt.Sprintf("%x", [32]byte{0: 1})
+ feedName = "BTC/USD"
+ verifierAddress = fmt.Sprintf("0x%x", [20]byte{0: 7})
+)
+
+func TestCreateMercuryV3Job(t *testing.T) {
+ ocrKeyBundleID := "ocr_key_bundle_id"
+ nodeCSAKey := "node_csa_key"
+ bridgeName := "bridge_name"
+ linkFeedID := fmt.Sprintf("%x", [32]byte{0: 2})
+ nativeFeedID := fmt.Sprintf("%x", [32]byte{0: 3})
+ u, err := url.Parse("https://crib-henry-keystone-node1.main.stage.cldev.sh")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ jobConfigData := MercuryV3JobSpecData{
+ BootstrapHost: u.Hostname(),
+ VerifierAddress: verifierAddress,
+ OCRKeyBundleID: ocrKeyBundleID,
+ NodeCSAKey: nodeCSAKey,
+ Bridge: bridgeName,
+ FeedName: feedName,
+ FeedID: feedID,
+ LinkFeedID: linkFeedID,
+ NativeFeedID: nativeFeedID,
+ ChainID: chainID,
+ }
+ _, output := createMercuryV3OracleJob(jobConfigData)
+
+ snaps.MatchSnapshot(t, output)
+}
+
+func TestCreateMercuryBootstrapJob(t *testing.T) {
+ jobConfigData := MercuryV3BootstrapJobSpecData{
+ FeedName: feedName,
+ FeedID: feedID,
+ ChainID: chainID,
+ VerifierAddress: verifierAddress,
+ }
+
+ _, output := createMercuryV3BootstrapJob(jobConfigData)
+
+ snaps.MatchSnapshot(t, output)
+}
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
deleted file mode 100644
index 6b98951459e..00000000000
--- a/core/scripts/keystone/src/03_gen_crib_cluster_overrides_cmd.go
+++ /dev/null
@@ -1,86 +0,0 @@
-package src
-
-import (
- "flag"
- "os"
- "path/filepath"
- "strings"
-
- helpers "github.com/smartcontractkit/chainlink/core/scripts/common"
-)
-
-type generateCribClusterOverrides struct{}
-
-func NewGenerateCribClusterOverridesCommand() *generateCribClusterOverrides {
- return &generateCribClusterOverrides{}
-}
-
-func (g *generateCribClusterOverrides) Name() string {
- return "generate-crib"
-}
-
-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")
-
- templatesDir := "templates"
- err := fs.Parse(args)
- if err != nil || outputPath == nil || *outputPath == "" || chainID == nil || *chainID == 0 {
- fs.Usage()
- os.Exit(1)
- }
-
- 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(nodeList string, pubKeysPath string, chainID *int64, templatesDir string, forwarderAddress string) []string {
- nca := downloadNodePubKeys(nodeList, *chainID, pubKeysPath)
- nodeAddresses := []string{}
-
- for _, node := range nca[1:] {
- nodeAddresses = append(nodeAddresses, node.EthAddress)
- }
-
- lines, err := readLines(filepath.Join(templatesDir, cribOverrideTemplate))
- helpers.PanicErr(err)
- lines = replaceCribPlaceholders(lines, forwarderAddress, nodeAddresses)
- return lines
-}
-
-func replaceCribPlaceholders(
- lines []string,
- forwarderAddress string,
- nodeFromAddresses []string,
-) (output []string) {
- for _, l := range lines {
- l = strings.Replace(l, "{{ forwarder_address }}", forwarderAddress, 1)
- l = strings.Replace(l, "{{ node_2_address }}", nodeFromAddresses[0], 1)
- l = strings.Replace(l, "{{ node_3_address }}", nodeFromAddresses[1], 1)
- l = strings.Replace(l, "{{ node_4_address }}", nodeFromAddresses[2], 1)
- l = strings.Replace(l, "{{ node_5_address }}", nodeFromAddresses[3], 1)
- output = append(output, l)
- }
-
- return output
-}
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
deleted file mode 100644
index 53d43c2342f..00000000000
--- a/core/scripts/keystone/src/03_gen_crib_cluster_overrides_cmd_test.go
+++ /dev/null
@@ -1,19 +0,0 @@
-package src
-
-import (
- "strings"
- "testing"
-
- "github.com/gkampitakis/go-snaps/snaps"
-)
-
-func TestGenerateCribConfig(t *testing.T) {
- chainID := int64(11155111)
- templatesDir := "../templates"
- forwarderAddress := "0x1234567890abcdef"
- publicKeysPath := "./testdata/PublicKeys.json"
-
- 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
deleted file mode 100644
index 136691962dd..00000000000
--- a/core/scripts/keystone/src/04_delete_ocr3_jobs_cmd.go
+++ /dev/null
@@ -1,101 +0,0 @@
-package src
-
-import (
- "bytes"
- "encoding/json"
- "flag"
- "fmt"
- "os"
-
- "github.com/urfave/cli"
-
- helpers "github.com/smartcontractkit/chainlink/core/scripts/common"
-)
-
-type deleteJobs struct{}
-
-type OCRSpec struct {
- ContractID string
-}
-
-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 {
- return &deleteJobs{}
-}
-
-func (g *deleteJobs) Name() string {
- return "delete-ocr3-jobs"
-}
-
-func (g *deleteJobs) Run(args []string) {
- 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 := 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.BootstrapSpec.ContractID == deployedContracts.OCRContract.String() ||
- jobSpec.OffChainReporting2OracleSpec.ContractID == deployedContracts.OCRContract.String() {
- fmt.Println("Deleting OCR3 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/05_deploy_initialize_capabilities_registry.go b/core/scripts/keystone/src/05_deploy_initialize_capabilities_registry.go
index b7fc9df2c88..203c473a4b7 100644
--- a/core/scripts/keystone/src/05_deploy_initialize_capabilities_registry.go
+++ b/core/scripts/keystone/src/05_deploy_initialize_capabilities_registry.go
@@ -2,19 +2,14 @@ package src
import (
"context"
- "encoding/hex"
"flag"
"fmt"
"log"
"os"
- "strings"
"github.com/ethereum/go-ethereum/common"
"google.golang.org/protobuf/proto"
- ragetypes "github.com/smartcontractkit/libocr/ragep2p/types"
-
- capabilitiespb "github.com/smartcontractkit/chainlink-common/pkg/capabilities/pb"
"github.com/smartcontractkit/chainlink-common/pkg/values"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
@@ -30,7 +25,7 @@ type peer struct {
}
var (
- workflowDonPeers = []peer{
+ hardcodedWorkflowDonPeers = []peer{
{
PeerID: "12D3KooWQXfwA26jysiKKPXKuHcJtWTbGSwzoJxj4rYtEJyQTnFj",
Signer: "0xC44686106b85687F741e1d6182a5e2eD2211a115",
@@ -70,74 +65,6 @@ func (c *deployAndInitializeCapabilitiesRegistryCommand) Name() string {
return "deploy-and-initialize-capabilities-registry"
}
-func peerIDToB(peerID string) ([32]byte, error) {
- var peerIDB ragetypes.PeerID
- err := peerIDB.UnmarshalText([]byte(peerID))
- if err != nil {
- return [32]byte{}, err
- }
-
- return peerIDB, nil
-}
-
-func peers(ps []peer) ([][32]byte, error) {
- out := [][32]byte{}
- for _, p := range ps {
- b, err := peerIDToB(p.PeerID)
- if err != nil {
- return nil, err
- }
-
- out = append(out, b)
- }
-
- return out, nil
-}
-
-func peerToNode(nopID uint32, p peer) (kcr.CapabilitiesRegistryNodeParams, error) {
- peerIDB, err := peerIDToB(p.PeerID)
- if err != nil {
- return kcr.CapabilitiesRegistryNodeParams{}, fmt.Errorf("failed to convert peerID: %w", err)
- }
-
- sig := strings.TrimPrefix(p.Signer, "0x")
- signerB, err := hex.DecodeString(sig)
- if err != nil {
- return kcr.CapabilitiesRegistryNodeParams{}, fmt.Errorf("failed to convert signer: %w", err)
- }
-
- keyStr := strings.TrimPrefix(p.EncryptionPublicKey, "0x")
- encKey, err := hex.DecodeString(keyStr)
- if err != nil {
- return kcr.CapabilitiesRegistryNodeParams{}, fmt.Errorf("failed to convert encryptionPublicKey: %w", err)
- }
-
- var sigb [32]byte
- var encKeyB [32]byte
- copy(sigb[:], signerB)
- copy(encKeyB[:], encKey)
-
- return kcr.CapabilitiesRegistryNodeParams{
- NodeOperatorId: nopID,
- P2pId: peerIDB,
- Signer: sigb,
- EncryptionPublicKey: encKeyB,
- }, nil
-}
-
-// newCapabilityConfig returns a new capability config with the default config set as empty.
-// Override the empty default config with functional options.
-func newCapabilityConfig(opts ...func(*values.Map)) *capabilitiespb.CapabilityConfig {
- dc := values.EmptyMap()
- for _, opt := range opts {
- opt(dc)
- }
-
- return &capabilitiespb.CapabilityConfig{
- DefaultConfig: values.ProtoMap(dc),
- }
-}
-
// withDefaultConfig returns a function that sets the default config for a capability by merging
// the provided map with the existing default config. This is a shallow merge.
func withDefaultConfig(m map[string]any) func(*values.Map) {
@@ -163,7 +90,7 @@ func (c *deployAndInitializeCapabilitiesRegistryCommand) Run(args []string) {
// create flags for all of the env vars then set the env vars to normalize the interface
// this is a bit of a hack but it's the easiest way to make this work
ethUrl := fs.String("ethurl", "", "URL of the Ethereum node")
- chainID := fs.Int64("chainid", 11155111, "chain ID of the Ethereum network to deploy to")
+ chainID := fs.Int64("chainid", 1337, "chain ID of the Ethereum network to deploy to")
accountKey := fs.String("accountkey", "", "private key of the account to deploy from")
capabilityRegistryAddress := fs.String("craddress", "", "address of the capability registry")
@@ -179,6 +106,7 @@ func (c *deployAndInitializeCapabilitiesRegistryCommand) Run(args []string) {
os.Setenv("ETH_URL", *ethUrl)
os.Setenv("ETH_CHAIN_ID", fmt.Sprintf("%d", *chainID))
os.Setenv("ACCOUNT_KEY", *accountKey)
+ os.Setenv("INSECURE_SKIP_VERIFY", "true")
env := helpers.SetupEnv(false)
@@ -288,7 +216,7 @@ func (c *deployAndInitializeCapabilitiesRegistryCommand) Run(args []string) {
nopID := recLog.NodeOperatorId
nodes := []kcr.CapabilitiesRegistryNodeParams{}
- for _, wfPeer := range workflowDonPeers {
+ for _, wfPeer := range hardcodedWorkflowDonPeers {
n, innerErr := peerToNode(nopID, wfPeer)
if innerErr != nil {
panic(innerErr)
@@ -306,7 +234,7 @@ func (c *deployAndInitializeCapabilitiesRegistryCommand) Run(args []string) {
helpers.ConfirmTXMined(ctx, env.Ec, tx, env.ChainID)
// workflow DON
- ps, err := peers(workflowDonPeers)
+ ps, err := peers(hardcodedWorkflowDonPeers)
if err != nil {
panic(err)
}
diff --git a/core/scripts/keystone/src/06_deploy_workflows_cmd.go b/core/scripts/keystone/src/06_deploy_workflows_cmd.go
deleted file mode 100644
index 0ca8e5d4a7b..00000000000
--- a/core/scripts/keystone/src/06_deploy_workflows_cmd.go
+++ /dev/null
@@ -1,71 +0,0 @@
-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
deleted file mode 100644
index cccedaf9e70..00000000000
--- a/core/scripts/keystone/src/07_delete_workflows_cmd.go
+++ /dev/null
@@ -1,74 +0,0 @@
-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_capabilities_registry_helpers.go b/core/scripts/keystone/src/88_capabilities_registry_helpers.go
new file mode 100644
index 00000000000..5494375aa4f
--- /dev/null
+++ b/core/scripts/keystone/src/88_capabilities_registry_helpers.go
@@ -0,0 +1,578 @@
+package src
+
+import (
+ "bytes"
+ "context"
+ "encoding/hex"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "log"
+ "strings"
+ "time"
+
+ "github.com/ethereum/go-ethereum"
+ "github.com/ethereum/go-ethereum/accounts/abi"
+ "github.com/ethereum/go-ethereum/accounts/abi/bind"
+ gethCommon "github.com/ethereum/go-ethereum/common"
+ gethTypes "github.com/ethereum/go-ethereum/core/types"
+ ragetypes "github.com/smartcontractkit/libocr/ragep2p/types"
+ "google.golang.org/protobuf/proto"
+ "google.golang.org/protobuf/types/known/durationpb"
+
+ capabilitiespb "github.com/smartcontractkit/chainlink-common/pkg/capabilities/pb"
+ "github.com/smartcontractkit/chainlink-common/pkg/values"
+ helpers "github.com/smartcontractkit/chainlink/core/scripts/common"
+ evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client"
+ evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types"
+ kcr "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry_1_1_0"
+)
+
+type CapabilityRegistryProvisioner struct {
+ reg kcr.CapabilitiesRegistryInterface
+ env helpers.Environment
+}
+
+func NewCapabilityRegistryProvisioner(reg kcr.CapabilitiesRegistryInterface, env helpers.Environment) *CapabilityRegistryProvisioner {
+ return &CapabilityRegistryProvisioner{reg: reg, env: env}
+}
+
+func extractRevertReason(errData string, a abi.ABI) (string, string, error) {
+ data, err := hex.DecodeString(errData[2:])
+ if err != nil {
+ return "", "", err
+ }
+
+ for errName, abiError := range a.Errors {
+ if bytes.Equal(data[:4], abiError.ID.Bytes()[:4]) {
+ // Found a matching error
+ v, err := abiError.Unpack(data)
+ if err != nil {
+ return "", "", err
+ }
+ b, err := json.Marshal(v)
+ if err != nil {
+ return "", "", err
+ }
+ return errName, string(b), nil
+ }
+ }
+ return "", "", errors.New("revert Reason could not be found for given abistring")
+}
+
+func (c *CapabilityRegistryProvisioner) testCallContract(method string, args ...interface{}) error {
+ abi := evmtypes.MustGetABI(kcr.CapabilitiesRegistryABI)
+ data, err := abi.Pack(method, args...)
+ helpers.PanicErr(err)
+ cAddress := c.reg.Address()
+ gasPrice, err := c.env.Ec.SuggestGasPrice(context.Background())
+ helpers.PanicErr(err)
+
+ msg := ethereum.CallMsg{
+ From: c.env.Owner.From,
+ To: &cAddress,
+ Data: data,
+ Gas: 10_000_000,
+ GasPrice: gasPrice,
+ }
+ _, err = c.env.Ec.CallContract(context.Background(), msg, nil)
+ if err != nil {
+ if err.Error() == "execution reverted" {
+ rpcError, ierr := evmclient.ExtractRPCError(err)
+ if ierr != nil {
+ return ierr
+ }
+
+ reason, abiErr, ierr := extractRevertReason(rpcError.Data.(string), abi)
+ if ierr != nil {
+ return ierr
+ }
+
+ e := fmt.Errorf("failed to call %s: reason: %s reasonargs: %s", method, reason, abiErr)
+ return e
+ }
+
+ return err
+ }
+
+ return nil
+}
+
+// AddCapabilities takes a capability set and provisions it in the registry.
+func (c *CapabilityRegistryProvisioner) AddCapabilities(ctx context.Context, capSet CapabilitySet) {
+ fmt.Printf("Adding capabilities to registry: %s\n", capSet.IDs())
+ tx, err := c.reg.AddCapabilities(c.env.Owner, capSet.Capabilities())
+
+ helpers.PanicErr(err)
+ helpers.ConfirmTXMined(ctx, c.env.Ec, tx, c.env.ChainID)
+}
+
+// AddNodeOperator takes a node operator and provisions it in the registry.
+//
+// A node operator is a group of nodes that are all controlled by the same entity. The admin address is the
+// address that controls the node operator.
+//
+// The name is a human-readable name for the node operator.
+//
+// The node operator is then added to the registry, and the registry will issue an ID for the node operator.
+// The ID is then used when adding nodes to the registry such that the registry knows which nodes belong to which
+// node operator.
+func (c *CapabilityRegistryProvisioner) AddNodeOperator(ctx context.Context, nop *NodeOperator) {
+ fmt.Printf("Adding NodeOperator to registry: %s\n", nop.Name)
+ nop.BindToRegistry(c.reg)
+
+ nops, err := c.reg.GetNodeOperators(&bind.CallOpts{})
+ if err != nil {
+ log.Printf("failed to GetNodeOperators: %s", err)
+ }
+ for _, n := range nops {
+ if n.Admin == nop.Admin {
+ log.Printf("NodeOperator with admin address %s already exists", n.Admin.Hex())
+ return
+ }
+ }
+
+ tx, err := c.reg.AddNodeOperators(c.env.Owner, []kcr.CapabilitiesRegistryNodeOperator{
+ {
+ Admin: nop.Admin,
+ Name: nop.Name,
+ },
+ })
+ if err != nil {
+ log.Printf("failed to AddNodeOperators: %s", err)
+ }
+
+ receipt := helpers.ConfirmTXMined(ctx, c.env.Ec, tx, c.env.ChainID)
+ nop.SetCapabilityRegistryIssuedID(receipt)
+}
+
+// AddNodes takes a node operators nodes, along with a capability set, then configures the registry such that
+// each node is assigned the same capability set. The registry will then know that each node supports each of the
+// capabilities in the set.
+//
+// This is a simplified version of the actual implementation, which is more flexible. The actual implementation
+// allows for the ability to add different capability sets to different nodes, _and_ lets you add nodes from different
+// node operators to the same capability set. This is not yet implemented here.
+//
+// Note that the registry must already have the capability set added via `AddCapabilities`, you cannot
+// add capabilities that the registry is not yet aware of.
+//
+// Note that in terms of the provisioning process, this is not the last step. A capability is only active once
+// there is a DON servicing it. This is done via `AddDON`.
+func (c *CapabilityRegistryProvisioner) AddNodes(ctx context.Context, nop *NodeOperator, donNames ...string) {
+ fmt.Printf("Adding nodes to registry for NodeOperator %s with DONs: %v\n", nop.Name, donNames)
+ var params []kcr.CapabilitiesRegistryNodeParams
+ for _, donName := range donNames {
+ don, exists := nop.DONs[donName]
+ if !exists {
+ log.Fatalf("DON with name %s does not exist in NodeOperator %s", donName, nop.Name)
+ }
+ capSet := don.CapabilitySet
+ for i, peer := range don.Peers {
+ node, innerErr := peerToNode(nop.id, peer)
+ if innerErr != nil {
+ panic(innerErr)
+ }
+ node.HashedCapabilityIds = capSet.HashedIDs(c.reg)
+ node.EncryptionPublicKey = [32]byte{2: byte(i + 1)}
+ fmt.Printf("Adding node %s to registry with capabilities: %s\n", peer.PeerID, capSet.IDs())
+ params = append(params, node)
+ }
+ }
+
+ err := c.testCallContract("addNodes", params)
+ PanicErr(err)
+
+ tx, err := c.reg.AddNodes(c.env.Owner, params)
+ if err != nil {
+ log.Printf("failed to AddNodes: %s", err)
+ }
+ helpers.ConfirmTXMined(ctx, c.env.Ec, tx, c.env.ChainID)
+}
+
+// AddDON takes a node operator then provisions a DON with the given capabilities.
+//
+// A DON is a group of nodes that all support the same capability set. This set can be a subset of the
+// capabilities that the nodes support. In other words, each node within the node set can support
+// a different, possibly overlapping, set of capabilities, but a DON is a subgroup of those nodes that all support
+// the same set of capabilities.
+//
+// A node can belong to multiple DONs, but it must belong to one and only one workflow DON.
+//
+// A DON can be a capability DON or a workflow DON, or both.
+//
+// When you want to add solely a workflow DON, you should set `acceptsWorkflows` to true and
+// `isPublic` to false.
+// This means that the DON can service workflow requests and will not service external capability requests.
+//
+// If you want to add solely a capability DON, you should set `acceptsWorkflows` to false and `isPublic` to true. This means that the DON
+// will service external capability requests and reject workflow requests.
+//
+// If you want to add a DON that services both capabilities and workflows, you should set both `acceptsWorkflows` and `isPublic` to true.
+//
+// Another important distinction is that DON can comprise of nodes from different node operators, but for now, we're keeping it simple and restricting it to a single node operator. We also hard code F to 1.
+func (c *CapabilityRegistryProvisioner) AddDON(ctx context.Context, nop *NodeOperator, donName string, isPublic bool, acceptsWorkflows bool) {
+ fmt.Printf("Adding DON %s to registry for NodeOperator %s with isPublic: %t and acceptsWorkflows: %t\n", donName, nop.Name, isPublic, acceptsWorkflows)
+ don, exists := nop.DONs[donName]
+ if !exists {
+ log.Fatalf("DON with name %s does not exist in NodeOperator %s", donName, nop.Name)
+ }
+ configs := don.CapabilitySet.Configs(c.reg)
+
+ err := c.testCallContract("addDON", don.MustGetPeerIDs(), configs, isPublic, acceptsWorkflows, don.F)
+ PanicErr(err)
+
+ tx, err := c.reg.AddDON(c.env.Owner, don.MustGetPeerIDs(), configs, isPublic, acceptsWorkflows, don.F)
+
+ if err != nil {
+ log.Printf("failed to AddDON: %s", err)
+ }
+ helpers.ConfirmTXMined(ctx, c.env.Ec, tx, c.env.ChainID)
+}
+
+/*
+ *
+ * Capabilities
+ *
+ *
+ */
+const ( // Taken from https://github.com/smartcontractkit/chainlink/blob/29117850e9be1be1993dbf8f21cf13cbb6af9d24/core/capabilities/integration_tests/keystone_contracts_setup.go#L43
+ CapabilityTypeTrigger = uint8(0)
+ CapabilityTypeAction = uint8(1)
+ CapabilityTypeConsensus = uint8(2)
+ CapabilityTypeTarget = uint8(3)
+)
+
+type CapabillityProvisioner interface {
+ Config() kcr.CapabilitiesRegistryCapabilityConfiguration
+ Capability() kcr.CapabilitiesRegistryCapability
+ BindToRegistry(reg kcr.CapabilitiesRegistryInterface)
+ GetHashedCID() [32]byte
+}
+
+type baseCapability struct {
+ registry kcr.CapabilitiesRegistryInterface
+ capability kcr.CapabilitiesRegistryCapability
+}
+
+func (b *baseCapability) BindToRegistry(reg kcr.CapabilitiesRegistryInterface) {
+ b.registry = reg
+}
+
+func (b *baseCapability) GetHashedCID() [32]byte {
+ if b.registry == nil {
+ panic(errors.New("registry not bound to capability, cannot get hashed capability ID"))
+ }
+
+ return mustHashCapabilityID(b.registry, b.capability)
+}
+
+func (b *baseCapability) GetID() string {
+ return fmt.Sprintf("%s@%s", b.capability.LabelledName, b.capability.Version)
+}
+
+func (b *baseCapability) config(config *capabilitiespb.CapabilityConfig) kcr.CapabilitiesRegistryCapabilityConfiguration {
+ configBytes, err := proto.Marshal(config)
+ if err != nil {
+ panic(err)
+ }
+
+ return kcr.CapabilitiesRegistryCapabilityConfiguration{
+ Config: configBytes,
+ CapabilityId: b.GetHashedCID(),
+ }
+}
+
+func (b *baseCapability) Capability() kcr.CapabilitiesRegistryCapability {
+ return b.capability
+}
+
+type ConsensusCapability struct {
+ baseCapability
+}
+
+var _ CapabillityProvisioner = &ConsensusCapability{}
+
+func (c *ConsensusCapability) Config() kcr.CapabilitiesRegistryCapabilityConfiguration {
+ // Note that this is hard-coded for now, we'll want to support more flexible configurations in the future
+ // for configuring consensus once it has more configuration options
+ config := &capabilitiespb.CapabilityConfig{
+ DefaultConfig: values.Proto(values.EmptyMap()).GetMapValue(),
+ }
+
+ return c.config(config)
+}
+
+// NewOCR3V1ConsensusCapability returns a new ConsensusCapability for OCR3
+func NewOCR3V1ConsensusCapability() *ConsensusCapability {
+ return &ConsensusCapability{
+ baseCapability{
+ capability: kcr.CapabilitiesRegistryCapability{
+ LabelledName: "offchain_reporting",
+ Version: "1.0.0",
+ CapabilityType: CapabilityTypeConsensus,
+ },
+ },
+ }
+}
+
+type TargetCapability struct {
+ baseCapability
+}
+
+var _ CapabillityProvisioner = &TargetCapability{}
+
+func (t *TargetCapability) Config() kcr.CapabilitiesRegistryCapabilityConfiguration {
+ // Note that this is hard-coded for now, we'll want to support more flexible configurations in the future
+ // for configuring the target. This configuration is also specific to the write target
+ config := &capabilitiespb.CapabilityConfig{
+ DefaultConfig: values.Proto(values.EmptyMap()).GetMapValue(),
+ RemoteConfig: &capabilitiespb.CapabilityConfig_RemoteTargetConfig{
+ RemoteTargetConfig: &capabilitiespb.RemoteTargetConfig{
+ RequestHashExcludedAttributes: []string{"signed_report.Signatures"},
+ },
+ },
+ }
+
+ return t.config(config)
+}
+
+func NewEthereumGethTestnetV1WriteCapability() *TargetCapability {
+ return &TargetCapability{
+ baseCapability{
+ capability: kcr.CapabilitiesRegistryCapability{
+ LabelledName: "write_geth-testnet",
+ Version: "1.0.0",
+ CapabilityType: CapabilityTypeTarget,
+ },
+ },
+ }
+}
+
+type TriggerCapability struct {
+ baseCapability
+}
+
+var _ CapabillityProvisioner = &TriggerCapability{}
+
+func (t *TriggerCapability) Config() kcr.CapabilitiesRegistryCapabilityConfiguration {
+ // Note that this is hard-coded for now, we'll want to support more flexible configurations in the future
+ // for configuring the trigger. This configuration is also possibly specific to the streams trigger.
+ 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),
+ MinResponsesToAggregate: uint32(1) + 1, // We've hardcoded F + 1 here
+ },
+ },
+ }
+
+ return t.config(config)
+}
+
+func NewStreamsTriggerV1Capability() *TriggerCapability {
+ return &TriggerCapability{
+ baseCapability{
+ capability: kcr.CapabilitiesRegistryCapability{
+ LabelledName: "streams-trigger",
+ Version: "1.1.0",
+ CapabilityType: CapabilityTypeTrigger,
+ },
+ },
+ }
+}
+
+func mustHashCapabilityID(reg kcr.CapabilitiesRegistryInterface, capability kcr.CapabilitiesRegistryCapability) [32]byte {
+ hashedCapabilityID, err := reg.GetHashedCapabilityId(&bind.CallOpts{}, capability.LabelledName, capability.Version)
+ if err != nil {
+ panic(err)
+ }
+ return hashedCapabilityID
+}
+
+/*
+ *
+ * Capability Sets
+ *
+ *
+ */
+type CapabilitySet []CapabillityProvisioner
+
+func NewCapabilitySet(capabilities ...CapabillityProvisioner) CapabilitySet {
+ if len(capabilities) == 0 {
+ log.Fatalf("No capabilities provided to NewCapabilitySet")
+ }
+
+ return capabilities
+}
+
+func MergeCapabilitySets(sets ...CapabilitySet) CapabilitySet {
+ var merged CapabilitySet
+ for _, set := range sets {
+ merged = append(merged, set...)
+ }
+
+ return merged
+}
+
+func (c *CapabilitySet) Capabilities() []kcr.CapabilitiesRegistryCapability {
+ definitions := make([]kcr.CapabilitiesRegistryCapability, 0, len(*c))
+ for _, cap := range *c {
+ definitions = append(definitions, cap.Capability())
+ }
+
+ return definitions
+}
+
+func (c *CapabilitySet) IDs() []string {
+ strings := make([]string, 0, len(*c))
+ for _, cap := range *c {
+ strings = append(strings, fmt.Sprintf("%s@%s", cap.Capability().LabelledName, cap.Capability().Version))
+ }
+
+ return strings
+}
+
+func (c *CapabilitySet) HashedIDs(reg kcr.CapabilitiesRegistryInterface) [][32]byte {
+ ids := make([][32]byte, 0, len(*c))
+ for _, cap := range *c {
+ cap.BindToRegistry(reg)
+ ids = append(ids, cap.GetHashedCID())
+ }
+
+ return ids
+}
+
+func (c *CapabilitySet) Configs(reg kcr.CapabilitiesRegistryInterface) []kcr.CapabilitiesRegistryCapabilityConfiguration {
+ configs := make([]kcr.CapabilitiesRegistryCapabilityConfiguration, 0, len(*c))
+ for _, cap := range *c {
+ cap.BindToRegistry(reg)
+ configs = append(configs, cap.Config())
+ }
+
+ return configs
+}
+
+/*
+ *
+ * Node Operator
+ *
+ *
+ */
+
+// DON represents a Decentralized Oracle Network with a name, peers, and associated capabilities.
+type DON struct {
+ F uint8
+ Name string
+ Peers []peer
+ CapabilitySet CapabilitySet
+}
+
+// MustGetPeerIDs retrieves the peer IDs for the DON. It panics if any error occurs.
+func (d *DON) MustGetPeerIDs() [][32]byte {
+ ps, err := peers(d.Peers)
+ if err != nil {
+ panic(fmt.Errorf("failed to get peer IDs for DON %s: %w", d.Name, err))
+ }
+ return ps
+}
+
+// NodeOperator represents a node operator with administrative details and multiple DONs.
+type NodeOperator struct {
+ Admin gethCommon.Address
+ Name string
+ DONs map[string]DON
+
+ reg kcr.CapabilitiesRegistryInterface
+ // This ID is generated by the registry when the NodeOperator is added
+ id uint32
+}
+
+// NewNodeOperator creates a new NodeOperator with the provided admin address, name, and DONs.
+func NewNodeOperator(admin gethCommon.Address, name string, dons map[string]DON) *NodeOperator {
+ return &NodeOperator{
+ Admin: admin,
+ Name: name,
+ DONs: dons,
+ }
+}
+
+func (n *NodeOperator) BindToRegistry(reg kcr.CapabilitiesRegistryInterface) {
+ n.reg = reg
+}
+
+func (n *NodeOperator) SetCapabilityRegistryIssuedID(receipt *gethTypes.Receipt) uint32 {
+ if n.reg == nil {
+ panic(errors.New("registry not bound to node operator, cannot set ID"))
+ }
+ // We'll need more complex handling for multiple node operators
+ // since we'll need to handle log ordering
+ recLog, err := n.reg.ParseNodeOperatorAdded(*receipt.Logs[0])
+ if err != nil {
+ panic(err)
+ }
+
+ n.id = recLog.NodeOperatorId
+ return n.id
+}
+
+func peerIDToB(peerID string) ([32]byte, error) {
+ var peerIDB ragetypes.PeerID
+ err := peerIDB.UnmarshalText([]byte(peerID))
+ if err != nil {
+ return [32]byte{}, err
+ }
+
+ return peerIDB, nil
+}
+
+func peers(ps []peer) ([][32]byte, error) {
+ out := [][32]byte{}
+ for _, p := range ps {
+ b, err := peerIDToB(p.PeerID)
+ if err != nil {
+ return nil, err
+ }
+
+ out = append(out, b)
+ }
+
+ return out, nil
+}
+
+func peerToNode(nopID uint32, p peer) (kcr.CapabilitiesRegistryNodeParams, error) {
+ peerIDB, err := peerIDToB(p.PeerID)
+ if err != nil {
+ return kcr.CapabilitiesRegistryNodeParams{}, fmt.Errorf("failed to convert peerID: %w", err)
+ }
+
+ sig := strings.TrimPrefix(p.Signer, "0x")
+ signerB, err := hex.DecodeString(sig)
+ if err != nil {
+ return kcr.CapabilitiesRegistryNodeParams{}, fmt.Errorf("failed to convert signer: %w", err)
+ }
+
+ var sigb [32]byte
+ copy(sigb[:], signerB)
+
+ return kcr.CapabilitiesRegistryNodeParams{
+ NodeOperatorId: nopID,
+ P2pId: peerIDB,
+ Signer: sigb,
+ }, nil
+}
+
+// newCapabilityConfig returns a new capability config with the default config set as empty.
+// Override the empty default config with functional options.
+func newCapabilityConfig(opts ...func(*values.Map)) *capabilitiespb.CapabilityConfig {
+ dc := values.EmptyMap()
+ for _, opt := range opts {
+ opt(dc)
+ }
+
+ return &capabilitiespb.CapabilityConfig{
+ DefaultConfig: values.ProtoMap(dc),
+ }
+}
diff --git a/core/scripts/keystone/src/88_contracts_helpers.go b/core/scripts/keystone/src/88_contracts_helpers.go
new file mode 100644
index 00000000000..59bfeb68201
--- /dev/null
+++ b/core/scripts/keystone/src/88_contracts_helpers.go
@@ -0,0 +1,192 @@
+package src
+
+import (
+ "context"
+
+ "encoding/json"
+ "fmt"
+ "os"
+ "path/filepath"
+
+ "github.com/ethereum/go-ethereum/common"
+
+ helpers "github.com/smartcontractkit/chainlink/core/scripts/common"
+ capabilities_registry "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry_1_1_0"
+ "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/forwarder"
+ "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/ocr3_capability"
+ verifierContract "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/llo-feeds/generated/verifier"
+ "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/llo-feeds/generated/verifier_proxy"
+)
+
+var ZeroAddress = common.Address{}
+
+type OnChainMetaSerialized struct {
+ OCR common.Address `json:"ocrContract"`
+ Forwarder common.Address `json:"forwarderContract"`
+ // The block number of the transaction that set the config on the OCR3 contract. We use this to replay blocks from this point on
+ // when we load the OCR3 job specs on the nodes.
+ SetConfigTxBlock uint64 `json:"setConfigTxBlock"`
+
+ CapabilitiesRegistry common.Address `json:"CapabilitiesRegistry"`
+ Verifier common.Address `json:"VerifierContract"`
+ VerifierProxy common.Address `json:"VerifierProxy"`
+ // Stores the address that has been initialized by the proxy, if any
+ InitializedVerifierAddress common.Address `json:"InitializedVerifierAddress"`
+}
+
+type onchainMeta struct {
+ OCR3 ocr3_capability.OCR3CapabilityInterface
+ Forwarder forwarder.KeystoneForwarderInterface
+ // The block number of the transaction that set the config on the OCR3 contract. We use this to replay blocks from this point on
+ // when we load the OCR3 job specs on the nodes.
+ SetConfigTxBlock uint64
+
+ CapabilitiesRegistry capabilities_registry.CapabilitiesRegistryInterface
+ Verifier verifierContract.VerifierInterface
+ VerifierProxy verifier_proxy.VerifierProxyInterface
+ InitializedVerifierAddress common.Address `json:"InitializedVerifierAddress"`
+}
+
+func WriteOnchainMeta(o *onchainMeta, artefactsDir string) {
+ ensureArtefactsDir(artefactsDir)
+
+ fmt.Println("Writing deployed contract addresses to file...")
+ serialzed := OnChainMetaSerialized{}
+
+ if o.OCR3 != nil {
+ serialzed.OCR = o.OCR3.Address()
+ }
+
+ if o.Forwarder != nil {
+ serialzed.Forwarder = o.Forwarder.Address()
+ }
+
+ serialzed.SetConfigTxBlock = o.SetConfigTxBlock
+ serialzed.InitializedVerifierAddress = o.InitializedVerifierAddress
+
+ if o.CapabilitiesRegistry != nil {
+ serialzed.CapabilitiesRegistry = o.CapabilitiesRegistry.Address()
+ }
+
+ if o.Verifier != nil {
+ serialzed.Verifier = o.Verifier.Address()
+ }
+
+ if o.VerifierProxy != nil {
+ serialzed.VerifierProxy = o.VerifierProxy.Address()
+ }
+
+ jsonBytes, err := json.Marshal(serialzed)
+ PanicErr(err)
+
+ err = os.WriteFile(deployedContractsFilePath(artefactsDir), jsonBytes, 0600)
+ PanicErr(err)
+}
+
+func LoadOnchainMeta(artefactsDir string, env helpers.Environment) *onchainMeta {
+ hydrated := &onchainMeta{}
+ if !ContractsAlreadyDeployed(artefactsDir) {
+ fmt.Printf("No deployed contracts file found at %s\n", deployedContractsFilePath(artefactsDir))
+ return hydrated
+ }
+
+ jsonBytes, err := os.ReadFile(deployedContractsFilePath(artefactsDir))
+ if err != nil {
+ fmt.Printf("Error reading deployed contracts file: %s\n", err)
+ return hydrated
+ }
+
+ var s OnChainMetaSerialized
+ err = json.Unmarshal(jsonBytes, &s)
+ if err != nil {
+ return hydrated
+ }
+
+ hydrated.SetConfigTxBlock = s.SetConfigTxBlock
+ if s.OCR != ZeroAddress {
+ if !contractExists(s.OCR, env) {
+ fmt.Printf("OCR contract at %s does not exist\n", s.OCR.Hex())
+ } else {
+ ocr3, e := ocr3_capability.NewOCR3Capability(s.OCR, env.Ec)
+ PanicErr(e)
+ hydrated.OCR3 = ocr3
+ }
+ }
+
+ if s.Forwarder != ZeroAddress {
+ if !contractExists(s.Forwarder, env) {
+ fmt.Printf("Forwarder contract at %s does not exist\n", s.Forwarder.Hex())
+ } else {
+ fwdr, e := forwarder.NewKeystoneForwarder(s.Forwarder, env.Ec)
+ PanicErr(e)
+ hydrated.Forwarder = fwdr
+ }
+ }
+
+ if s.CapabilitiesRegistry != ZeroAddress {
+ if !contractExists(s.CapabilitiesRegistry, env) {
+ fmt.Printf("CapabilityRegistry contract at %s does not exist\n", s.CapabilitiesRegistry.Hex())
+ } else {
+ cr, e := capabilities_registry.NewCapabilitiesRegistry(s.CapabilitiesRegistry, env.Ec)
+ PanicErr(e)
+ hydrated.CapabilitiesRegistry = cr
+ }
+ }
+
+ hydrated.InitializedVerifierAddress = s.InitializedVerifierAddress
+
+ if s.Verifier != ZeroAddress {
+ if !contractExists(s.Verifier, env) {
+ fmt.Printf("Verifier contract at %s does not exist\n", s.Verifier.Hex())
+ hydrated.InitializedVerifierAddress = ZeroAddress
+ } else {
+ verifier, e := verifierContract.NewVerifier(s.Verifier, env.Ec)
+ PanicErr(e)
+ hydrated.Verifier = verifier
+ }
+ }
+
+ if s.VerifierProxy != ZeroAddress {
+ if !contractExists(s.VerifierProxy, env) {
+ fmt.Printf("VerifierProxy contract at %s does not exist\n", s.VerifierProxy.Hex())
+ hydrated.InitializedVerifierAddress = ZeroAddress
+ } else {
+ verifierProxy, e := verifier_proxy.NewVerifierProxy(s.VerifierProxy, env.Ec)
+ PanicErr(e)
+ hydrated.VerifierProxy = verifierProxy
+ }
+ }
+
+ blkNum, err := env.Ec.BlockNumber(context.Background())
+ PanicErr(err)
+
+ if s.SetConfigTxBlock > blkNum {
+ fmt.Printf("Stale SetConfigTxBlock: %d, current block number: %d\n", s.SetConfigTxBlock, blkNum)
+ hydrated.SetConfigTxBlock = 0
+ }
+
+ return hydrated
+}
+
+func ContractsAlreadyDeployed(artefactsDir string) bool {
+ _, err := os.Stat(artefactsDir)
+ if err != nil {
+ return false
+ }
+
+ _, err = os.Stat(deployedContractsFilePath(artefactsDir))
+
+ return err == nil
+}
+
+func deployedContractsFilePath(artefactsDir string) string {
+ return filepath.Join(artefactsDir, deployedContractsJSON)
+}
+
+func contractExists(address common.Address, env helpers.Environment) bool {
+ byteCode, err := env.Ec.CodeAt(context.Background(), address, nil)
+ if err != nil {
+ return false
+ }
+ return len(byteCode) != 0
+}
diff --git a/core/scripts/keystone/src/88_gen_jobspecs.go b/core/scripts/keystone/src/88_gen_jobspecs.go
deleted file mode 100644
index e88833c9865..00000000000
--- a/core/scripts/keystone/src/88_gen_jobspecs.go
+++ /dev/null
@@ -1,91 +0,0 @@
-package src
-
-import (
- "fmt"
- "path/filepath"
- "strconv"
- "strings"
-
- helpers "github.com/smartcontractkit/chainlink/core/scripts/common"
- "github.com/smartcontractkit/chainlink/deployment/keystone/changeset"
-)
-
-type spec []string
-
-func (s spec) ToString() string {
- return strings.Join(s, "\n")
-}
-
-type hostSpec struct {
- spec spec
- host string
-}
-
-type donHostSpec struct {
- bootstrap hostSpec
- oracles []hostSpec
-}
-
-func genSpecs(
- pubkeysPath string,
- nodeListPath string,
- templatesDir string,
- chainID int64,
- p2pPort int64,
- ocrConfigContractAddress string,
-) donHostSpec {
- nodes := downloadNodeAPICredentials(nodeListPath)
- nca := downloadNodePubKeys(nodeListPath, chainID, pubkeysPath)
- bootstrapNode := nca[0]
-
- bootstrapSpecLines, err := readLines(filepath.Join(templatesDir, bootstrapSpecTemplate))
- helpers.PanicErr(err)
- bootHost := nodes[0].remoteURL.Hostname()
- bootstrapSpecLines = replacePlaceholders(
- bootstrapSpecLines,
- chainID, p2pPort,
- ocrConfigContractAddress, bootHost,
- bootstrapNode, bootstrapNode,
- )
- bootstrap := hostSpec{bootstrapSpecLines, bootHost}
-
- oracleSpecLinesTemplate, err := readLines(filepath.Join(templatesDir, oracleSpecTemplate))
- helpers.PanicErr(err)
- oracles := []hostSpec{}
- for i := 1; i < len(nodes); i++ {
- oracleSpecLines := oracleSpecLinesTemplate
- oracleSpecLines = replacePlaceholders(
- oracleSpecLines,
- chainID, p2pPort,
- ocrConfigContractAddress, bootHost,
- bootstrapNode, nca[i],
- )
- oracles = append(oracles, hostSpec{oracleSpecLines, nodes[i].remoteURL.Host})
- }
-
- return donHostSpec{
- bootstrap: bootstrap,
- oracles: oracles,
- }
-}
-
-func replacePlaceholders(
- lines []string,
-
- chainID, p2pPort int64,
- contractAddress, bootHost string,
- boot, node changeset.NodeKeys,
-) (output []string) {
- chainIDStr := strconv.FormatInt(chainID, 10)
- bootstrapper := fmt.Sprintf("%s@%s:%d", boot.P2PPeerID, bootHost, p2pPort)
- for _, l := range lines {
- l = strings.Replace(l, "{{ chain_id }}", chainIDStr, 1)
- l = strings.Replace(l, "{{ ocr_config_contract_address }}", contractAddress, 1)
- l = strings.Replace(l, "{{ transmitter_id }}", node.EthAddress, 1)
- l = strings.Replace(l, "{{ ocr_key_bundle_id }}", node.OCR2BundleID, 1)
- l = strings.Replace(l, "{{ aptos_key_bundle_id }}", node.AptosBundleID, 1)
- l = strings.Replace(l, "{{ bootstrapper_p2p_id }}", bootstrapper, 1)
- output = append(output, l)
- }
- return
-}
diff --git a/core/scripts/keystone/src/88_gen_jobspecs_test.go b/core/scripts/keystone/src/88_gen_jobspecs_test.go
deleted file mode 100644
index 7af11646c4e..00000000000
--- a/core/scripts/keystone/src/88_gen_jobspecs_test.go
+++ /dev/null
@@ -1,37 +0,0 @@
-package src
-
-import (
- "fmt"
- "testing"
-
- "github.com/gkampitakis/go-snaps/snaps"
-)
-
-func (d *donHostSpec) ToString() string {
- var result string
- result += "Bootstrap:\n"
- result += "Host: " + d.bootstrap.host + "\n"
- result += d.bootstrap.spec.ToString()
- result += "\n\nOracles:\n"
- for i, oracle := range d.oracles {
- if i != 0 {
- result += "--------------------------------\n"
- }
- result += fmt.Sprintf("Oracle %d:\n", i)
- result += "Host: " + oracle.host + "\n"
- result += oracle.spec.ToString()
- result += "\n\n"
- }
- return result
-}
-
-func TestGenSpecs(t *testing.T) {
- pubkeysPath := "./testdata/PublicKeys.json"
- nodeListPath := "./testdata/NodeList.txt"
- chainID := int64(11155111)
- p2pPort := int64(6690)
- contractAddress := "0xB29934624cAe3765E33115A9530a13f5aEC7fa8A"
-
- specs := genSpecs(pubkeysPath, nodeListPath, "../templates", chainID, p2pPort, contractAddress)
- snaps.MatchSnapshot(t, specs.ToString())
-}
diff --git a/core/scripts/keystone/src/88_gen_ocr3_config.go b/core/scripts/keystone/src/88_gen_ocr3_config.go
deleted file mode 100644
index 94217b07f4e..00000000000
--- a/core/scripts/keystone/src/88_gen_ocr3_config.go
+++ /dev/null
@@ -1,20 +0,0 @@
-package src
-
-import (
- helpers "github.com/smartcontractkit/chainlink/core/scripts/common"
- "github.com/smartcontractkit/chainlink/deployment"
- ksdeploy "github.com/smartcontractkit/chainlink/deployment/keystone/changeset"
-)
-
-func mustReadConfig(fileName string) (output ksdeploy.TopLevelConfigSource) {
- return mustParseJSON[ksdeploy.TopLevelConfigSource](fileName)
-}
-
-func generateOCR3Config(nodeList string, configFile string, chainID int64, pubKeysPath string) ksdeploy.OCR3OnchainConfig {
- topLevelCfg := mustReadConfig(configFile)
- cfg := topLevelCfg.OracleConfig
- nca := downloadNodePubKeys(nodeList, chainID, pubKeysPath)
- c, err := ksdeploy.GenerateOCR3Config(cfg, nca, deployment.XXXGenerateTestOCRSecrets())
- helpers.PanicErr(err)
- return c
-}
diff --git a/core/scripts/keystone/src/88_gen_ocr3_config_test.go b/core/scripts/keystone/src/88_gen_ocr3_config_test.go
deleted file mode 100644
index 10cdc07b204..00000000000
--- a/core/scripts/keystone/src/88_gen_ocr3_config_test.go
+++ /dev/null
@@ -1,31 +0,0 @@
-package src
-
-import (
- "errors"
- "testing"
-
- "github.com/gkampitakis/go-snaps/match"
- "github.com/gkampitakis/go-snaps/snaps"
-)
-
-func TestGenerateOCR3Config(t *testing.T) {
- // Generate OCR3 config
- 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
- s, ok := s.(string)
- if !ok {
- return nil, errors.New("offchain config is not a string")
- }
-
- // if the string is not empty
- if s == "" {
- return nil, errors.New("offchain config is empty")
- }
-
- return "", nil
- })
-
- snaps.MatchJSON(t, config, matchOffchainConfig)
-}
diff --git a/core/scripts/keystone/src/88_jobspecs_helpers.go b/core/scripts/keystone/src/88_jobspecs_helpers.go
new file mode 100644
index 00000000000..0e6cc3a043a
--- /dev/null
+++ b/core/scripts/keystone/src/88_jobspecs_helpers.go
@@ -0,0 +1,53 @@
+package src
+
+import (
+ "fmt"
+)
+
+type OCRSpec struct {
+ ContractID string
+}
+
+type BootSpec struct {
+ ContractID string
+}
+
+type WorkflowSpec struct {
+ WorkflowID string
+}
+
+type JobSpec struct {
+ ID string
+ Name string
+ BootstrapSpec BootSpec
+ OffChainReporting2OracleSpec OCRSpec
+ WorkflowSpec WorkflowSpec
+}
+
+func upsertJob(api *nodeAPI, jobSpecName string, jobSpecStr string) {
+ jobsResp := api.mustExec(api.methods.ListJobs)
+ jobs := mustJSON[[]JobSpec](jobsResp)
+ for _, job := range *jobs {
+ if job.Name == jobSpecName {
+ fmt.Printf("Job already exists: %s, replacing..\n", jobSpecName)
+ api.withArg(job.ID).mustExec(api.methods.DeleteJob)
+ break
+ }
+ }
+
+ fmt.Printf("Deploying jobspec: %s\n", jobSpecName)
+ _, err := api.withArg(jobSpecStr).exec(api.methods.CreateJob)
+ if err != nil {
+ panic(fmt.Sprintf("Failed to deploy job spec: %s Error: %s", jobSpecStr, err))
+ }
+}
+
+func clearJobs(api *nodeAPI) {
+ jobsResp := api.mustExec(api.methods.ListJobs)
+ jobs := mustJSON[[]JobSpec](jobsResp)
+ for _, job := range *jobs {
+ fmt.Printf("Deleting job: %s\n", job.Name)
+ api.withArg(job.ID).mustExec(api.methods.DeleteJob)
+ }
+ fmt.Println("All jobs have been deleted.")
+}
diff --git a/core/scripts/keystone/src/88_ocr_helpers.go b/core/scripts/keystone/src/88_ocr_helpers.go
new file mode 100644
index 00000000000..7cdfd72ca52
--- /dev/null
+++ b/core/scripts/keystone/src/88_ocr_helpers.go
@@ -0,0 +1,69 @@
+package src
+
+import (
+ "encoding/hex"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/smartcontractkit/libocr/offchainreporting2/types"
+
+ ksdeploy "github.com/smartcontractkit/chainlink/deployment/keystone/changeset"
+)
+
+func ocrConfToContractConfig(ocrConf ksdeploy.OCR3OnchainConfig, configCount uint32) types.ContractConfig {
+ cc := types.ContractConfig{
+ Signers: convertByteSliceToOnchainPublicKeys(ocrConf.Signers),
+ Transmitters: convertAddressesToAccounts(ocrConf.Transmitters),
+ F: ocrConf.F,
+ OnchainConfig: ocrConf.OnchainConfig,
+ OffchainConfigVersion: ocrConf.OffchainConfigVersion,
+ OffchainConfig: ocrConf.OffchainConfig,
+ ConfigCount: uint64(configCount),
+ }
+ return cc
+}
+
+func mercuryOCRConfigToContractConfig(ocrConf MercuryOCR2Config, configCount uint32) types.ContractConfig {
+ cc := types.ContractConfig{
+ Signers: convertAddressesToOnchainPublicKeys(ocrConf.Signers),
+ Transmitters: convertBytes32sToAccounts(ocrConf.Transmitters),
+ F: ocrConf.F,
+ OnchainConfig: ocrConf.OnchainConfig,
+ OffchainConfigVersion: ocrConf.OffchainConfigVersion,
+ OffchainConfig: ocrConf.OffchainConfig,
+ ConfigCount: uint64(configCount),
+ }
+
+ return cc
+}
+
+func convertAddressesToOnchainPublicKeys(addresses []common.Address) []types.OnchainPublicKey {
+ keys := make([]types.OnchainPublicKey, len(addresses))
+ for i, addr := range addresses {
+ keys[i] = types.OnchainPublicKey(addr.Bytes())
+ }
+ return keys
+}
+
+func convertAddressesToAccounts(addresses []common.Address) []types.Account {
+ accounts := make([]types.Account, len(addresses))
+ for i, addr := range addresses {
+ accounts[i] = types.Account(addr.Hex())
+ }
+ return accounts
+}
+
+func convertBytes32sToAccounts(bs [][32]byte) []types.Account {
+ accounts := make([]types.Account, len(bs))
+ for i, b := range bs {
+ accounts[i] = types.Account(hex.EncodeToString(b[:]))
+ }
+ return accounts
+}
+
+func convertByteSliceToOnchainPublicKeys(bs [][]byte) []types.OnchainPublicKey {
+ keys := make([]types.OnchainPublicKey, len(bs))
+ for i, b := range bs {
+ keys[i] = types.OnchainPublicKey(b)
+ }
+ return keys
+}
diff --git a/core/scripts/keystone/src/99_app.go b/core/scripts/keystone/src/99_app.go
index 6e59932aa71..29164959bec 100644
--- a/core/scripts/keystone/src/99_app.go
+++ b/core/scripts/keystone/src/99_app.go
@@ -1,31 +1,389 @@
package src
import (
+ "bytes"
+ "context"
+ "encoding/json"
+ "errors"
"flag"
"io"
+ "net/http"
+ "net/url"
+ "reflect"
+ "runtime"
+ "strings"
+ "sync"
+ "time"
"github.com/urfave/cli"
+ "go.uber.org/zap/zapcore"
helpers "github.com/smartcontractkit/chainlink/core/scripts/common"
+ "github.com/smartcontractkit/chainlink/v2/core/cmd"
clcmd "github.com/smartcontractkit/chainlink/v2/core/cmd"
+ "github.com/smartcontractkit/chainlink/v2/core/logger"
+ clsessions "github.com/smartcontractkit/chainlink/v2/core/sessions"
)
-func newApp(n *node, writer io.Writer) (*clcmd.Shell, *cli.App) {
+// Package-level cache and mutex
+var (
+ nodeAPICache = make(map[string]*nodeAPI)
+ cacheMutex = &sync.Mutex{}
+)
+
+func newApp(n NodeWithCreds, writer io.Writer) (*clcmd.Shell, *cli.App) {
+ loggingCfg := logger.Config{
+ LogLevel: zapcore.InfoLevel,
+ JsonConsole: true,
+ }
+ logger, closeLggr := loggingCfg.New()
+ u, err := url.Parse(n.RemoteURL.String())
+ PanicErr(err)
+
+ clientOpts := clcmd.ClientOpts{RemoteNodeURL: *u, InsecureSkipVerify: true}
+ sr := clsessions.SessionRequest{Email: n.APILogin, Password: n.APIPassword}
+
+ // Set the log level to error for the HTTP client, we don't care about
+ // the ssl warnings it emits for CRIB
+ logger.SetLogLevel(zapcore.ErrorLevel)
+ cookieAuth := cmd.NewSessionCookieAuthenticator(
+ clientOpts,
+ &cmd.MemoryCookieStore{},
+ logger,
+ )
+
+ http := NewRetryableAuthenticatedHTTPClient(logger, clientOpts, cookieAuth, sr)
+ // Set the log level back to info for the shell
+ logger.SetLogLevel(zapcore.InfoLevel)
client := &clcmd.Shell{
- Renderer: clcmd.RendererJSON{Writer: writer},
- AppFactory: clcmd.ChainlinkAppFactory{},
- KeyStoreAuthenticator: clcmd.TerminalKeyStoreAuthenticator{Prompter: n},
- FallbackAPIInitializer: clcmd.NewPromptingAPIInitializer(n),
- Runner: clcmd.ChainlinkRunner{},
- PromptingSessionRequestBuilder: clcmd.NewPromptingSessionRequestBuilder(n),
- ChangePasswordPrompter: clcmd.NewChangePasswordPrompter(),
- PasswordPrompter: clcmd.NewPasswordPrompter(),
+ Logger: logger,
+ Renderer: clcmd.RendererJSON{Writer: writer},
+ AppFactory: clcmd.ChainlinkAppFactory{},
+ Runner: clcmd.ChainlinkRunner{},
+ HTTP: http,
+
+ CloseLogger: closeLggr,
}
app := clcmd.NewApp(client)
- fs := flag.NewFlagSet("blah", flag.ContinueOnError)
- fs.String("remote-node-url", n.url.String(), "")
- helpers.PanicErr(app.Before(cli.NewContext(nil, fs, nil)))
- // overwrite renderer since it's set to stdout after Before() is called
- client.Renderer = clcmd.RendererJSON{Writer: writer}
return client, app
}
+
+type nodeAPI struct {
+ methods *cmd.Shell
+ app *cli.App
+ output *bytes.Buffer
+ fs *flag.FlagSet
+ clientMethod func(*cli.Context) error
+ logger logger.Logger
+}
+
+func newNodeAPI(n NodeWithCreds) *nodeAPI {
+ // Create a unique key for the cache
+ key := n.RemoteURL.String()
+
+ // Check if the nodeAPI exists in the cache
+ cacheMutex.Lock()
+ if api, exists := nodeAPICache[key]; exists {
+ cacheMutex.Unlock()
+ return api
+ }
+ cacheMutex.Unlock()
+
+ output := &bytes.Buffer{}
+ methods, app := newApp(n, output)
+
+ api := &nodeAPI{
+ output: output,
+ methods: methods,
+ app: app,
+ fs: flag.NewFlagSet("test", flag.ContinueOnError),
+ logger: methods.Logger.Named("NodeAPI"),
+ }
+
+ // Store the new nodeAPI in the cache
+ cacheMutex.Lock()
+ nodeAPICache[key] = api
+ cacheMutex.Unlock()
+
+ return api
+}
+
+func (c *nodeAPI) withArg(arg string) *nodeAPI {
+ err := c.fs.Parse([]string{arg})
+ helpers.PanicErr(err)
+
+ return c
+}
+
+func (c *nodeAPI) withArgs(args ...string) *nodeAPI {
+ err := c.fs.Parse(args)
+ helpers.PanicErr(err)
+
+ return c
+}
+
+func (c *nodeAPI) withFlags(clientMethod func(*cli.Context) error, applyFlags func(*flag.FlagSet)) *nodeAPI {
+ flagSetApplyFromAction(clientMethod, c.fs, "")
+ applyFlags(c.fs)
+
+ c.clientMethod = clientMethod
+
+ return c
+}
+
+func (c *nodeAPI) exec(clientMethod ...func(*cli.Context) error) ([]byte, error) {
+ if len(clientMethod) > 1 {
+ PanicErr(errors.New("Only one client method allowed"))
+ }
+
+ defer c.output.Reset()
+ defer func() {
+ c.fs = flag.NewFlagSet("test", flag.ContinueOnError)
+ c.clientMethod = nil
+ }()
+
+ if c.clientMethod == nil {
+ c.clientMethod = clientMethod[0]
+ }
+
+ retryCount := 3
+ for i := 0; i < retryCount; i++ {
+ c.logger.Tracew("Attempting API request", "attempt", i+1, "maxAttempts", retryCount)
+ c.output.Reset()
+ ctx := cli.NewContext(c.app, c.fs, nil)
+ err := c.clientMethod(ctx)
+
+ if err == nil {
+ c.logger.Tracew("API request completed successfully", "attempt", i+1)
+ return c.output.Bytes(), nil
+ }
+
+ if !strings.Contains(err.Error(), "invalid character '<' looking for beginning of value") {
+ c.logger.Tracew("API request failed with non-retriable error",
+ "attempt", i+1,
+ "err", err,
+ )
+ return nil, err
+ }
+
+ c.logger.Warnw("Encountered 504 gateway error during API request, retrying",
+ "attempt", i+1,
+ "maxAttempts", retryCount,
+ "err", err,
+ )
+
+ if i == retryCount-1 {
+ c.logger.Error("Failed to complete API request after all retry attempts")
+ return nil, err
+ }
+
+ c.logger.Tracew("Waiting before retry attempt",
+ "attempt", i+1,
+ "waitTime", "1s",
+ )
+ time.Sleep(3 * time.Second)
+ }
+
+ return nil, errors.New("API request failed after retries")
+}
+
+func (c *nodeAPI) mustExec(clientMethod ...func(*cli.Context) error) []byte {
+ bytes, err := c.exec(clientMethod...)
+ helpers.PanicErr(err)
+ return bytes
+}
+
+// flagSetApplyFromAction applies the flags from action to the flagSet.
+//
+// `parentCommand` will filter the app commands and only applies the flags if the command/subcommand has a parent with that name, if left empty no filtering is done
+//
+// Taken from: https://github.com/smartcontractkit/chainlink/blob/develop/core/cmd/shell_test.go#L590
+func flagSetApplyFromAction(action interface{}, flagSet *flag.FlagSet, parentCommand string) {
+ cliApp := cmd.Shell{}
+ app := cmd.NewApp(&cliApp)
+
+ foundName := parentCommand == ""
+ actionFuncName := getFuncName(action)
+
+ for _, command := range app.Commands {
+ flags := recursiveFindFlagsWithName(actionFuncName, command, parentCommand, foundName)
+
+ for _, flag := range flags {
+ flag.Apply(flagSet)
+ }
+ }
+}
+
+func recursiveFindFlagsWithName(actionFuncName string, command cli.Command, parent string, foundName bool) []cli.Flag {
+ if command.Action != nil {
+ if actionFuncName == getFuncName(command.Action) && foundName {
+ return command.Flags
+ }
+ }
+
+ for _, subcommand := range command.Subcommands {
+ if !foundName {
+ foundName = strings.EqualFold(subcommand.Name, parent)
+ }
+
+ found := recursiveFindFlagsWithName(actionFuncName, subcommand, parent, foundName)
+ if found != nil {
+ return found
+ }
+ }
+ return nil
+}
+
+func getFuncName(i interface{}) string {
+ return runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name()
+}
+
+func mustJSON[T any](bytes []byte) *T {
+ typedPayload := new(T)
+ err := json.Unmarshal(bytes, typedPayload)
+ if err != nil {
+ PanicErr(err)
+ }
+ return typedPayload
+}
+
+type retryableAuthenticatedHTTPClient struct {
+ client cmd.HTTPClient
+ logger logger.Logger
+}
+
+func NewRetryableAuthenticatedHTTPClient(lggr logger.Logger, clientOpts clcmd.ClientOpts, cookieAuth cmd.CookieAuthenticator, sessionRequest clsessions.SessionRequest) cmd.HTTPClient {
+ return &retryableAuthenticatedHTTPClient{
+ client: cmd.NewAuthenticatedHTTPClient(lggr, clientOpts, cookieAuth, sessionRequest),
+ logger: lggr.Named("RetryableAuthenticatedHTTPClient"),
+ }
+}
+
+func logBody(body io.Reader) (string, io.Reader) {
+ if body == nil {
+ return "", nil
+ }
+
+ var buf bytes.Buffer
+ tee := io.TeeReader(body, &buf)
+ bodyBytes, _ := io.ReadAll(tee)
+ return string(bodyBytes), bytes.NewReader(buf.Bytes())
+}
+
+func logResponse(logger logger.Logger, resp *http.Response) {
+ if resp == nil {
+ logger.Trace("Response was nil")
+ return
+ }
+
+ bodyBytes, err := io.ReadAll(resp.Body)
+ if err != nil {
+ logger.Errorw("Failed to read response body for logging", "err", err)
+ return
+ }
+ // Replace the body so it can be read again by the caller
+ resp.Body = io.NopCloser(bytes.NewReader(bodyBytes))
+
+ logger.Tracew("Response details",
+ "statusCode", resp.StatusCode,
+ "status", resp.Status,
+ "headers", resp.Header,
+ "body", string(bodyBytes),
+ )
+}
+
+func (h *retryableAuthenticatedHTTPClient) Get(ctx context.Context, path string, headers ...map[string]string) (*http.Response, error) {
+ h.logger.Tracew("Making GET request",
+ "path", path,
+ "headers", headers,
+ )
+ return h.doRequestWithRetry(ctx, func() (*http.Response, error) {
+ return h.client.Get(ctx, path, headers...)
+ })
+}
+
+func (h *retryableAuthenticatedHTTPClient) Post(ctx context.Context, path string, body io.Reader) (*http.Response, error) {
+ bodyStr, newBody := logBody(body)
+ h.logger.Tracew("Making POST request",
+ "path", path,
+ "body", bodyStr,
+ )
+ return h.doRequestWithRetry(ctx, func() (*http.Response, error) {
+ return h.client.Post(ctx, path, newBody)
+ })
+}
+
+func (h *retryableAuthenticatedHTTPClient) Put(ctx context.Context, path string, body io.Reader) (*http.Response, error) {
+ bodyStr, newBody := logBody(body)
+ h.logger.Tracew("Making PUT request",
+ "path", path,
+ "body", bodyStr,
+ )
+ return h.doRequestWithRetry(ctx, func() (*http.Response, error) {
+ return h.client.Put(ctx, path, newBody)
+ })
+}
+
+func (h *retryableAuthenticatedHTTPClient) Patch(ctx context.Context, path string, body io.Reader, headers ...map[string]string) (*http.Response, error) {
+ bodyStr, newBody := logBody(body)
+ h.logger.Tracew("Making PATCH request",
+ "path", path,
+ "headers", headers,
+ "body", bodyStr,
+ )
+ return h.doRequestWithRetry(ctx, func() (*http.Response, error) {
+ return h.client.Patch(ctx, path, newBody, headers...)
+ })
+}
+
+func (h *retryableAuthenticatedHTTPClient) Delete(ctx context.Context, path string) (*http.Response, error) {
+ h.logger.Tracew("Making DELETE request",
+ "path", path,
+ )
+ return h.doRequestWithRetry(ctx, func() (*http.Response, error) {
+ return h.client.Delete(ctx, path)
+ })
+}
+
+func (h *retryableAuthenticatedHTTPClient) doRequestWithRetry(_ context.Context, req func() (*http.Response, error)) (*http.Response, error) {
+ retryCount := 3
+ for i := 0; i < retryCount; i++ {
+ h.logger.Tracew("Attempting request", "attempt", i+1, "maxAttempts", retryCount)
+
+ response, err := req()
+ logResponse(h.logger, response)
+
+ if err == nil || !strings.Contains(err.Error(), "invalid character '<' looking for beginning of value") {
+ if err != nil {
+ h.logger.Warn("Request completed with error",
+ "attempt", i+1,
+ "err", err,
+ )
+ } else {
+ h.logger.Tracew("Request completed successfully",
+ "attempt", i+1,
+ "statusCode", response.StatusCode,
+ )
+ }
+ return response, err
+ }
+
+ h.logger.Warnw("Encountered 504 error during request, retrying",
+ "attempt", i+1,
+ "maxAttempts", retryCount,
+ "err", err,
+ )
+
+ if i == retryCount-1 {
+ h.logger.Error("Failed to complete request after all retry attempts")
+ return response, err
+ }
+
+ h.logger.Tracew("Waiting before retry attempt",
+ "attempt", i+1,
+ "waitTime", "1s",
+ )
+ time.Sleep(1 * time.Second)
+ }
+ return nil, errors.New("request failed after retries")
+}
diff --git a/core/scripts/keystone/src/99_crib_client.go b/core/scripts/keystone/src/99_crib_client.go
index ebf9f9ee955..1a38cafe9bb 100644
--- a/core/scripts/keystone/src/99_crib_client.go
+++ b/core/scripts/keystone/src/99_crib_client.go
@@ -4,7 +4,7 @@ package src
import (
"fmt"
- "net/url"
+ "sort"
"strings"
)
@@ -12,12 +12,24 @@ type CribClient struct {
k8sClient *K8sClient
}
-type CLNodeCredentials struct {
- URL *url.URL
- PodName string
- Username string
- Password string
- NodePassword string
+// SimpleURL lets us marshal a URL with only the fields we need.
+type SimpleURL struct {
+ Scheme string `json:"scheme"`
+ Host string `json:"host"`
+ Path string `json:"path"`
+}
+
+func (s SimpleURL) String() string {
+ return fmt.Sprintf("%s://%s%s", s.Scheme, s.Host, s.Path)
+}
+
+type NodeWithCreds struct {
+ URL SimpleURL
+ RemoteURL SimpleURL
+ ServiceName string
+ APILogin string
+ APIPassword string
+ KeystorePassword string
}
func NewCribClient() *CribClient {
@@ -27,35 +39,44 @@ func NewCribClient() *CribClient {
}
}
-func (m *CribClient) GetCLNodeCredentials() ([]CLNodeCredentials, error) {
- fmt.Println("Getting CL node pods with config maps...")
- pods, err := m.k8sClient.GetPodsWithConfigMap()
+func (m *CribClient) getCLNodes() ([]NodeWithCreds, error) {
+ fmt.Println("Getting CL node deployments with config maps...")
+ deployments, err := m.k8sClient.GetDeploymentsWithConfigMap()
if err != nil {
return nil, err
}
- clNodeCredentials := []CLNodeCredentials{}
+ nodes := []NodeWithCreds{}
- for _, pod := range pods {
- apiCredentials := pod.ConfigMap.Data["apicredentials"]
+ for _, deployment := range deployments {
+ apiCredentials := deployment.ConfigMap.Data["apicredentials"]
splitCreds := strings.Split(strings.TrimSpace(apiCredentials), "\n")
username := splitCreds[0]
password := splitCreds[1]
- nodePassword := pod.ConfigMap.Data["node-password"]
- url, err := url.Parse("https://" + pod.Host)
- if err != nil {
- return nil, err
+ keystorePassword := deployment.ConfigMap.Data["node-password"]
+ url := SimpleURL{
+ Scheme: "https",
+ Host: deployment.Host,
+ Path: "",
}
- clNodeCredential := CLNodeCredentials{
- URL: url,
- PodName: pod.Name,
- Username: username,
- Password: password,
- NodePassword: nodePassword,
+ node := NodeWithCreds{
+ // We dont handle both in-cluster and out-of-cluster deployments
+ // Hence why both URL and RemoteURL are the same
+ URL: url,
+ RemoteURL: url,
+ ServiceName: deployment.ServiceName,
+ APILogin: username,
+ APIPassword: password,
+ KeystorePassword: keystorePassword,
}
- clNodeCredentials = append(clNodeCredentials, clNodeCredential)
+ nodes = append(nodes, node)
}
- return clNodeCredentials, nil
+ // Sort nodes by URL
+ sort.Slice(nodes, func(i, j int) bool {
+ return nodes[i].URL.Host < nodes[j].URL.Host
+ })
+
+ return nodes, nil
}
diff --git a/core/scripts/keystone/src/99_fetch_keys.go b/core/scripts/keystone/src/99_fetch_keys.go
index 056769dc714..63e191a1234 100644
--- a/core/scripts/keystone/src/99_fetch_keys.go
+++ b/core/scripts/keystone/src/99_fetch_keys.go
@@ -1,230 +1,271 @@
package src
import (
- "bytes"
"encoding/json"
"errors"
- "flag"
"fmt"
"os"
- "sort"
"strings"
"github.com/urfave/cli"
helpers "github.com/smartcontractkit/chainlink/core/scripts/common"
- "github.com/smartcontractkit/chainlink/deployment/keystone/changeset"
ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big"
"github.com/smartcontractkit/chainlink/v2/core/cmd"
"github.com/smartcontractkit/chainlink/v2/core/web/presenters"
)
-func downloadNodePubKeys(nodeList string, chainID int64, pubKeysPath string) []changeset.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[[]changeset.NodeKeys](pubKeysPath)
- }
-
- nodes := downloadNodeAPICredentials(nodeList)
- nodesKeys := mustFetchNodesKeys(chainID, nodes)
+// NodeSet represents a set of nodes with associated metadata.
+// NodeKeys are indexed by the same order as Nodes.
+type NodeSet struct {
+ Name string
+ Prefix string
+ Nodes []NodeWithCreds
+ NodeKeys []NodeKeys
+}
- marshalledNodeKeys, err := json.MarshalIndent(nodesKeys, "", " ")
- if err != nil {
- panic(err)
- }
- err = os.WriteFile(pubKeysPath, marshalledNodeKeys, 0600)
- if err != nil {
- panic(err)
- }
- fmt.Println("Keystone OCR2 public keys have been saved to:", pubKeysPath)
+var (
+ WorkflowNodeSetName = "workflow"
+ WorkflowNodeSetPrefix = "ks-wf-"
+ StreamsTriggerNodeSetName = "streams-trigger"
+ StreamsTriggerNodeSetPrefix = "ks-str-trig-"
+)
- return nodesKeys
+// NodeSets holds the two NodeSets: Workflow and StreamsTrigger.
+type NodeSets struct {
+ Workflow NodeSet
+ StreamsTrigger NodeSet
}
-// 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.
-func downloadNodeAPICredentials(nodeListPath string) []*node {
- if _, err := os.Stat(nodeListPath); err == nil {
- fmt.Println("Loading existing node host list at:", nodeListPath)
- nodesList := mustReadNodesList(nodeListPath)
- return nodesList
+func downloadNodeSets(chainID int64, nodeSetPath string, nodeSetSize int) NodeSets {
+ if _, err := os.Stat(nodeSetPath); err == nil {
+ fmt.Println("Loading existing nodesets at:", nodeSetPath)
+ nodeSets := mustReadJSON[NodeSets](nodeSetPath)
+ return nodeSets
}
fmt.Println("Connecting to Kubernetes to fetch node credentials...")
crib := NewCribClient()
- clNodesWithCreds, err := crib.GetCLNodeCredentials()
-
- if err != nil {
- panic(err)
+ nodes, err := crib.getCLNodes()
+ PanicErr(err)
+
+ totalNodes := len(nodes)
+ // Workflow and StreamsTrigger nodeSets should have the same number of nodes
+ // hence we need at least 2 * nodeSetSize nodes
+ requiredNodes := nodeSetSize * 2
+ if totalNodes < requiredNodes {
+ panic(fmt.Errorf("not enough nodes to populate both nodeSets: required %d, got %d", requiredNodes, totalNodes))
}
- nodesList := clNodesWithCredsToNodes(clNodesWithCreds)
- err = writeNodesList(nodeListPath, nodesList)
- if err != nil {
- panic(err)
- }
- if len(nodesList) == 0 {
- panic("No nodes found")
+ nodeSets := NodeSets{
+ Workflow: NodeSet{
+ Name: WorkflowNodeSetName,
+ Prefix: WorkflowNodeSetPrefix,
+ Nodes: nodes[:nodeSetSize],
+ },
+ StreamsTrigger: NodeSet{
+ Name: StreamsTriggerNodeSetName,
+ Prefix: StreamsTriggerNodeSetPrefix,
+ Nodes: nodes[nodeSetSize : nodeSetSize*2],
+ },
}
- return nodesList
+
+ nodeSets.Workflow.NodeKeys = mustFetchNodeKeys(chainID, nodeSets.Workflow.Nodes, true)
+ nodeSets.StreamsTrigger.NodeKeys = mustFetchNodeKeys(chainID, nodeSets.StreamsTrigger.Nodes, false)
+ mustWriteJSON(nodeSetPath, nodeSets)
+
+ return nodeSets
}
-func clNodesWithCredsToNodes(clNodesWithCreds []CLNodeCredentials) []*node {
- nodes := []*node{}
- for _, cl := range clNodesWithCreds {
- n := node{
- url: cl.URL,
- password: cl.Password,
- login: cl.Username,
- }
- nodes = append(nodes, &n)
- }
+// NodeKeys represents the keys for a single node.
+// If there are multiple OCR2KBs or OCR2AptosKBs, only the first one is used.
+type NodeKeys struct {
+ AptosAccount string `json:"AptosAccount"`
+ EthAddress string `json:"EthAddress"`
+ P2PPeerID string `json:"P2PPeerID"`
+ CSAPublicKey string `json:"CSAPublicKey"`
+ OCR2KBTrimmed
+ OCR2AptosKBTrimmed
+}
- // sort nodes by URL
- sort.Slice(nodes, func(i, j int) bool {
- return nodes[i].url.String() < nodes[j].url.String()
- })
- return nodes
+// This is an OCR key bundle with the prefixes on each respective key
+// trimmed off
+type OCR2KBTrimmed struct {
+ OCR2BundleID string `json:"OCR2BundleID"` // used only in job spec
+ OCR2OnchainPublicKey string `json:"OCR2OnchainPublicKey"` // ocr2on_evm_
+ OCR2OffchainPublicKey string `json:"OCR2OffchainPublicKey"` // ocr2off_evm_
+ OCR2ConfigPublicKey string `json:"OCR2ConfigPublicKey"` // ocr2cfg_evm_
}
-type ocr2Bundle struct {
- ID string `json:"id"`
- ChainType string `json:"chainType"`
- OnchainPublicKey string `json:"onchainPublicKey"`
- OffchainPublicKey string `json:"offchainPublicKey"`
- ConfigPublicKey string `json:"configPublicKey"`
+// This is an Aptos key bundle with the prefixes on each respective key
+// trimmed off
+type OCR2AptosKBTrimmed struct {
+ AptosBundleID string `json:"AptosBundleID"`
+ AptosOnchainPublicKey string `json:"AptosOnchainPublicKey"` // ocr2on_aptos_
}
-func mustFetchNodesKeys(chainID int64, nodes []*node) (nca []changeset.NodeKeys) {
- for _, n := range nodes {
- 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()
+func mustFetchNodeKeys(chainID int64, nodes []NodeWithCreds, createAptosKeys bool) []NodeKeys {
+ nodeKeys := []NodeKeys{}
- err = client.ListETHKeys(&cli.Context{
- App: app,
- })
- helpers.PanicErr(err)
- var ethKeys []presenters.ETHKeyResource
- helpers.PanicErr(json.Unmarshal(output.Bytes(), ðKeys))
- ethAddress, err := findFirstGoodEthKeyAddress(chainID, ethKeys)
+ for _, n := range nodes {
+ api := newNodeAPI(n)
+ // Get eth key
+ fmt.Printf("Fetching ETH keys for node %s\n", n.ServiceName)
+ eKey := api.mustExec(api.methods.ListETHKeys)
+ ethKeys := mustJSON[[]presenters.ETHKeyResource](eKey)
+ ethAddress, err := findFirstGoodEthKeyAddress(chainID, *ethKeys)
helpers.PanicErr(err)
- output.Reset()
- keysClient := cmd.NewAptosKeysClient(client)
- err = keysClient.ListKeys(&cli.Context{
- App: app,
- })
- helpers.PanicErr(err)
- var aptosKeys []presenters.AptosKeyResource
- helpers.PanicErr(json.Unmarshal(output.Bytes(), &aptosKeys))
- if len(aptosKeys) != 1 {
- helpers.PanicErr(errors.New("node must have single aptos key"))
+ var aptosAccount string
+ if createAptosKeys {
+ aptosAccount = getOrCreateAptosKey(api)
}
- aptosAccount := aptosKeys[0].Account
- output.Reset()
- err = client.ListP2PKeys(&cli.Context{
- App: app,
- })
- helpers.PanicErr(err)
- var p2pKeys []presenters.P2PKeyResource
- helpers.PanicErr(json.Unmarshal(output.Bytes(), &p2pKeys))
- if len(p2pKeys) != 1 {
+ // Get p2p key
+ fmt.Printf("Fetching P2P key for node %s\n", n.ServiceName)
+ p2pKeys := api.mustExec(api.methods.ListP2PKeys)
+ p2pKey := mustJSON[[]presenters.P2PKeyResource](p2pKeys)
+ if len(*p2pKey) != 1 {
helpers.PanicErr(errors.New("node must have single p2p key"))
}
- peerID := strings.TrimPrefix(p2pKeys[0].PeerID, "p2p_")
- output.Reset()
+ peerID := strings.TrimPrefix((*p2pKey)[0].PeerID, "p2p_")
+
+ // Get OCR2 key bundles for both EVM and Aptos chains
+ bundles := api.mustExec(api.methods.ListOCR2KeyBundles)
+ ocr2Bundles := mustJSON[cmd.OCR2KeyBundlePresenters](bundles)
+
+ expectedBundleLen := 1
+
+ // evm key bundles
+ fmt.Printf("Fetching OCR2 EVM key bundles for node %s\n", n.ServiceName)
+ ocr2EvmBundles := getTrimmedEVMOCR2KBs(*ocr2Bundles)
+ evmBundleLen := len(ocr2EvmBundles)
+ if evmBundleLen < expectedBundleLen {
+ fmt.Printf("WARN: node has %d EVM OCR2 bundles when it should have at least %d, creating bundles...\n", evmBundleLen, expectedBundleLen)
+ for i := evmBundleLen; i < expectedBundleLen; i++ {
+ cBundle := api.withArg("evm").mustExec(api.methods.CreateOCR2KeyBundle)
+ createdBundle := mustJSON[cmd.OCR2KeyBundlePresenter](cBundle)
+ fmt.Printf("Created OCR2 EVM key bundle %s\n", string(cBundle))
+ ocr2EvmBundles = append(ocr2EvmBundles, trimmedOCR2KB(*createdBundle))
+ }
+ }
- chainType := "evm"
+ // aptos key bundles
+ var ocr2AptosBundles []OCR2AptosKBTrimmed
+ if createAptosKeys {
+ fmt.Printf("Fetching OCR2 Aptos key bundles for node %s\n", n.ServiceName)
+ ocr2AptosBundles = createAptosOCR2KB(ocr2Bundles, expectedBundleLen, api)
+ }
- var ocr2Bundles []ocr2Bundle
- err = client.ListOCR2KeyBundles(&cli.Context{
- App: app,
- })
+ fmt.Printf("Fetching CSA keys for node %s\n", n.ServiceName)
+ csaKeys := api.mustExec(api.methods.ListCSAKeys)
+ csaKeyResources := mustJSON[[]presenters.CSAKeyResource](csaKeys)
+ csaPubKey, err := findFirstCSAPublicKey(*csaKeyResources)
helpers.PanicErr(err)
- helpers.PanicErr(json.Unmarshal(output.Bytes(), &ocr2Bundles))
- ocr2BundleIndex := findOCR2Bundle(ocr2Bundles, chainType)
- output.Reset()
- if ocr2BundleIndex == -1 {
- fmt.Println("WARN: node does not have EVM OCR2 bundle, creating one")
- fs := flag.NewFlagSet("test", flag.ContinueOnError)
- err = fs.Parse([]string{chainType})
- helpers.PanicErr(err)
- ocr2CreateBundleCtx := cli.NewContext(app, fs, nil)
- err = client.CreateOCR2KeyBundle(ocr2CreateBundleCtx)
- helpers.PanicErr(err)
- output.Reset()
-
- err = client.ListOCR2KeyBundles(&cli.Context{
- App: app,
- })
- helpers.PanicErr(err)
- helpers.PanicErr(json.Unmarshal(output.Bytes(), &ocr2Bundles))
- ocr2BundleIndex = findOCR2Bundle(ocr2Bundles, chainType)
- output.Reset()
+
+ // We can handle multiple OCR bundles in the future
+ // but for now we only support a single bundle per node
+ keys := NodeKeys{
+ OCR2KBTrimmed: ocr2EvmBundles[0],
+ EthAddress: ethAddress,
+ AptosAccount: aptosAccount,
+ P2PPeerID: peerID,
+ CSAPublicKey: strings.TrimPrefix(csaPubKey, "csa_"),
}
+ if createAptosKeys {
+ keys.OCR2AptosKBTrimmed = ocr2AptosBundles[0]
+ }
+
+ nodeKeys = append(nodeKeys, keys)
+ }
- ocr2Bndl := ocr2Bundles[ocr2BundleIndex]
-
- aptosBundleIndex := findOCR2Bundle(ocr2Bundles, "aptos")
- if aptosBundleIndex == -1 {
- chainType2 := "aptos"
- fmt.Println("WARN: node does not have Aptos OCR2 bundle, creating one")
- fs := flag.NewFlagSet("test", flag.ContinueOnError)
- err = fs.Parse([]string{chainType2})
- helpers.PanicErr(err)
- ocr2CreateBundleCtx := cli.NewContext(app, fs, nil)
- err = client.CreateOCR2KeyBundle(ocr2CreateBundleCtx)
- helpers.PanicErr(err)
- output.Reset()
-
- err = client.ListOCR2KeyBundles(&cli.Context{
- App: app,
- })
- helpers.PanicErr(err)
- helpers.PanicErr(json.Unmarshal(output.Bytes(), &ocr2Bundles))
- aptosBundleIndex = findOCR2Bundle(ocr2Bundles, chainType2)
- output.Reset()
+ return nodeKeys
+}
+
+func trimmedOCR2KB(ocr2Bndl cmd.OCR2KeyBundlePresenter) OCR2KBTrimmed {
+ return OCR2KBTrimmed{
+ OCR2BundleID: ocr2Bndl.ID,
+ OCR2ConfigPublicKey: strings.TrimPrefix(ocr2Bndl.ConfigPublicKey, "ocr2cfg_evm_"),
+ OCR2OnchainPublicKey: strings.TrimPrefix(ocr2Bndl.OnchainPublicKey, "ocr2on_evm_"),
+ OCR2OffchainPublicKey: strings.TrimPrefix(ocr2Bndl.OffChainPublicKey, "ocr2off_evm_"),
+ }
+}
+
+func trimmedAptosOCR2KB(ocr2Bndl cmd.OCR2KeyBundlePresenter) OCR2AptosKBTrimmed {
+ return OCR2AptosKBTrimmed{
+ AptosBundleID: ocr2Bndl.ID,
+ AptosOnchainPublicKey: strings.TrimPrefix(ocr2Bndl.OnchainPublicKey, "ocr2on_aptos_"),
+ }
+}
+
+func createAptosOCR2KB(ocr2Bundles *cmd.OCR2KeyBundlePresenters, expectedBundleLen int, api *nodeAPI) []OCR2AptosKBTrimmed {
+ ocr2AptosBundles := getTrimmedAptosOCR2KBs(*ocr2Bundles)
+ aptosBundleLen := len(ocr2AptosBundles)
+
+ if aptosBundleLen < expectedBundleLen {
+ fmt.Printf("WARN: node has %d Aptos OCR2 bundles when it should have at least %d, creating bundles...\n", aptosBundleLen, expectedBundleLen)
+ for i := aptosBundleLen; i < expectedBundleLen; i++ {
+ cBundle := api.withArg("aptos").mustExec(api.methods.CreateOCR2KeyBundle)
+ createdBundle := mustJSON[cmd.OCR2KeyBundlePresenter](cBundle)
+ fmt.Println("Created OCR2 Aptos key bundle", string(cBundle))
+ ocr2AptosBundles = append(ocr2AptosBundles, trimmedAptosOCR2KB(*createdBundle))
}
+ }
- aptosBundle := ocr2Bundles[aptosBundleIndex]
+ return ocr2AptosBundles
+}
- err = client.ListCSAKeys(&cli.Context{
- App: app,
- })
+// getOrCreateAptosKey returns the Aptos account of the node.
+//
+// If the node has no Aptos keys, it creates one and returns the account.
+func getOrCreateAptosKey(api *nodeAPI) string {
+ api.output.Reset()
+ aKeysClient := cmd.NewAptosKeysClient(api.methods)
+ err := aKeysClient.ListKeys(&cli.Context{App: api.app})
+ helpers.PanicErr(err)
+ var aptosKeys []presenters.AptosKeyResource
+ helpers.PanicErr(json.Unmarshal(api.output.Bytes(), &aptosKeys))
+ if len(aptosKeys) == 0 {
+ api.output.Reset()
+ fmt.Printf("WARN: node has no aptos keys, creating one...\n")
+ err = aKeysClient.CreateKey(&cli.Context{App: api.app})
helpers.PanicErr(err)
- var csaKeys []presenters.CSAKeyResource
- helpers.PanicErr(json.Unmarshal(output.Bytes(), &csaKeys))
- csaPubKey, err := findFirstCSAPublicKey(csaKeys)
+ api.output.Reset()
+ err = aKeysClient.ListKeys(&cli.Context{App: api.app})
helpers.PanicErr(err)
- output.Reset()
-
- nc := changeset.NodeKeys{
- EthAddress: ethAddress,
- AptosAccount: aptosAccount,
- P2PPeerID: peerID,
- AptosBundleID: aptosBundle.ID,
- AptosOnchainPublicKey: strings.TrimPrefix(aptosBundle.OnchainPublicKey, fmt.Sprintf("ocr2on_%s_", "aptos")),
- OCR2BundleID: ocr2Bndl.ID,
- OCR2ConfigPublicKey: strings.TrimPrefix(ocr2Bndl.ConfigPublicKey, fmt.Sprintf("ocr2cfg_%s_", chainType)),
- OCR2OnchainPublicKey: strings.TrimPrefix(ocr2Bndl.OnchainPublicKey, fmt.Sprintf("ocr2on_%s_", chainType)),
- OCR2OffchainPublicKey: strings.TrimPrefix(ocr2Bndl.OffchainPublicKey, fmt.Sprintf("ocr2off_%s_", chainType)),
- CSAPublicKey: csaPubKey,
+ helpers.PanicErr(json.Unmarshal(api.output.Bytes(), &aptosKeys))
+ api.output.Reset()
+ }
+
+ if len(aptosKeys) != 1 {
+ fmt.Printf("Node has %d aptos keys\n", len(aptosKeys))
+ PanicErr(errors.New("node must have single aptos key"))
+ }
+
+ aptosAccount := aptosKeys[0].Account
+ api.output.Reset()
+
+ return aptosAccount
+}
+
+func getTrimmedAptosOCR2KBs(ocr2Bundles cmd.OCR2KeyBundlePresenters) []OCR2AptosKBTrimmed {
+ aptosBundles := []OCR2AptosKBTrimmed{}
+ for _, b := range ocr2Bundles {
+ if b.ChainType == "aptos" {
+ aptosBundles = append(aptosBundles, trimmedAptosOCR2KB(b))
}
+ }
+ return aptosBundles
+}
- nca = append(nca, nc)
+func getTrimmedEVMOCR2KBs(ocr2Bundles cmd.OCR2KeyBundlePresenters) []OCR2KBTrimmed {
+ evmBundles := []OCR2KBTrimmed{}
+ for _, b := range ocr2Bundles {
+ if b.ChainType == "evm" {
+ evmBundles = append(evmBundles, trimmedOCR2KB(b))
+ }
}
- return
+ return evmBundles
}
func findFirstCSAPublicKey(csaKeyResources []presenters.CSAKeyResource) (string, error) {
@@ -234,21 +275,9 @@ func findFirstCSAPublicKey(csaKeyResources []presenters.CSAKeyResource) (string,
return "", errors.New("did not find any CSA Key Resources")
}
-func findOCR2Bundle(ocr2Bundles []ocr2Bundle, chainType string) int {
- for i, b := range ocr2Bundles {
- if b.ChainType == chainType {
- return i
- }
- }
- return -1
-}
-
func findFirstGoodEthKeyAddress(chainID int64, ethKeys []presenters.ETHKeyResource) (string, error) {
for _, ethKey := range ethKeys {
if ethKey.EVMChainID.Equal(ubig.NewI(chainID)) && !ethKey.Disabled {
- if ethKey.EthBalance.IsZero() {
- fmt.Println("WARN: selected ETH address has zero balance", ethKey.Address)
- }
return ethKey.Address, nil
}
}
diff --git a/core/scripts/keystone/src/99_files.go b/core/scripts/keystone/src/99_files.go
index 08ba12e4194..1848ba8fae9 100644
--- a/core/scripts/keystone/src/99_files.go
+++ b/core/scripts/keystone/src/99_files.go
@@ -1,71 +1,54 @@
package src
import (
- "bufio"
"encoding/json"
"fmt"
"io"
"os"
-
- "github.com/smartcontractkit/chainlink/v2/core/utils"
)
const (
defaultArtefactsDir = "artefacts"
- defaultPublicKeys = ".cache/PublicKeys.json"
- defaultNodeList = ".cache/NodeList.txt"
+ defaultNodeSetsPath = ".cache/node_sets.json"
deployedContractsJSON = "deployed_contracts.json"
- bootstrapSpecTemplate = "bootstrap.toml"
- cribOverrideTemplate = "crib-overrides.yaml"
- oracleSpecTemplate = "oracle.toml"
)
-func writeLines(lines []string, path string) error {
- file, err := os.Create(path)
+func mustReadJSON[T any](fileName string) (output T) {
+ jsonFile, err := os.Open(fileName)
if err != nil {
- return err
- }
- wc := utils.NewDeferableWriteCloser(file)
- defer wc.Close()
-
- w := bufio.NewWriter(file)
- for _, line := range lines {
- fmt.Fprintln(w, line)
- }
- if err := w.Flush(); err != nil {
- return err
+ panic(fmt.Sprintf("failed to open file at %s: %v", fileName, err))
}
- return wc.Close()
-}
-
-func readLines(path string) ([]string, error) {
- file, err := os.Open(path)
+ defer jsonFile.Close()
+ bytes, err := io.ReadAll(jsonFile)
if err != nil {
- return nil, err
+ panic(fmt.Sprintf("failed to read file at %s: %v", fileName, err))
}
- defer file.Close()
-
- var lines []string
- scanner := bufio.NewScanner(file)
- for scanner.Scan() {
- lines = append(lines, scanner.Text())
+ err = json.Unmarshal(bytes, &output)
+ if err != nil {
+ panic(fmt.Sprintf("failed to unmarshal data: %v", err))
}
- return lines, scanner.Err()
+ return
}
-func mustParseJSON[T any](fileName string) (output T) {
- jsonFile, err := os.Open(fileName)
+func mustWriteJSON[T any](fileName string, data T) {
+ jsonFile, err := os.Create(fileName)
if err != nil {
- panic(err)
+ panic(fmt.Sprintf("failed to create file at %s: %v", fileName, err))
}
defer jsonFile.Close()
- bytes, err := io.ReadAll(jsonFile)
+ encoder := json.NewEncoder(jsonFile)
+ encoder.SetIndent("", " ")
+ err = encoder.Encode(data)
if err != nil {
- panic(err)
+ panic(fmt.Sprintf("failed to encode data: %v", err))
}
- err = json.Unmarshal(bytes, &output)
+}
+
+func ensureArtefactsDir(artefactsDir string) {
+ _, err := os.Stat(artefactsDir)
if err != nil {
- panic(err)
+ fmt.Println("Creating artefacts directory" + artefactsDir)
+ err = os.MkdirAll(artefactsDir, 0700)
+ PanicErr(err)
}
- return
}
diff --git a/core/scripts/keystone/src/99_files_test.go b/core/scripts/keystone/src/99_files_test.go
deleted file mode 100644
index 83ceb5cd9cc..00000000000
--- a/core/scripts/keystone/src/99_files_test.go
+++ /dev/null
@@ -1,36 +0,0 @@
-package src
-
-import (
- "path/filepath"
- "strings"
- "testing"
-
- "github.com/stretchr/testify/assert"
-)
-
-func Test_writeLines(t *testing.T) {
- type args struct {
- lines []string
- }
- tests := []struct {
- name string
- args args
- }{
- {
- name: "write read lines",
- args: args{
- lines: []string{"a", "b"},
- },
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- pth := filepath.Join(t.TempDir(), strings.ReplaceAll(tt.name, " ", "_"))
- err := writeLines(tt.args.lines, pth)
- assert.NoError(t, err)
- got, err := readLines(pth)
- assert.NoError(t, err)
- assert.Equal(t, tt.args.lines, got)
- })
- }
-}
diff --git a/core/scripts/keystone/src/99_k8s_client.go b/core/scripts/keystone/src/99_k8s_client.go
index 55a0ac82bcb..e4885e53a19 100644
--- a/core/scripts/keystone/src/99_k8s_client.go
+++ b/core/scripts/keystone/src/99_k8s_client.go
@@ -2,11 +2,13 @@ package src
import (
"context"
+ "errors"
"fmt"
"log"
"sort"
"strings"
+ apps "k8s.io/api/apps/v1"
v1 "k8s.io/api/core/v1"
networkingV1 "k8s.io/api/networking/v1"
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -63,32 +65,33 @@ func MustNewK8sClient() *K8sClient {
}
}
-type PodWithConfigMap struct {
- v1.Pod
- ConfigMap v1.ConfigMap
- Host string
+type DeploymentWithConfigMap struct {
+ apps.Deployment
+ ServiceName string
+ ConfigMap v1.ConfigMap
+ Host string
}
-func (m *K8sClient) GetPodsWithConfigMap() ([]PodWithConfigMap, error) {
- pods, err := m.ListPods("app=app")
+func (m *K8sClient) GetDeploymentsWithConfigMap() ([]DeploymentWithConfigMap, error) {
+ deployments, err := m.ListDeployments("app=app")
if err != nil {
return nil, err
}
- if len(pods.Items) == 0 {
- return nil, fmt.Errorf("no chainlink node crib pods found, is your crib cluster deployed?")
+ if len(deployments.Items) == 0 {
+ return nil, errors.New("no deployments found, is your nodeset deployed?")
}
- podsWithConfigMaps := []PodWithConfigMap{}
+ deploymentsWithConfigMaps := []DeploymentWithConfigMap{}
ingressList, err := m.ListIngresses()
if err != nil {
return nil, err
}
if len(ingressList.Items) == 0 {
- return nil, fmt.Errorf("no ingress found, is your crib cluster deployed?")
+ return nil, errors.New("no ingress found, is your nodeset deployed?")
}
- for _, pod := range pods.Items {
- for _, v := range pod.Spec.Volumes {
+ for _, deployment := range deployments.Items {
+ for _, v := range deployment.Spec.Template.Spec.Volumes {
if v.ConfigMap == nil {
continue
}
@@ -96,53 +99,48 @@ func (m *K8sClient) GetPodsWithConfigMap() ([]PodWithConfigMap, error) {
if err != nil {
return nil, err
}
- // - host: crib-henry-keystone-node2.main.stage.cldev.sh
- // http:
- // paths:
- // - backend:
- // service:
- // name: app-node-2
- // port:
- // number: 6688
- // path: /*
- // pathType: ImplementationSpecific
- instance := pod.Labels["instance"]
+ instance := deployment.Labels["instance"]
var host string
+ var serviceName string
for _, ingress := range ingressList.Items {
for _, rule := range ingress.Spec.Rules {
for _, path := range rule.HTTP.Paths {
if strings.Contains(path.Backend.Service.Name, instance) {
host = rule.Host
+ serviceName = path.Backend.Service.Name
}
}
}
}
if host == "" {
- return nil, fmt.Errorf("could not find host for pod %s", pod.Name)
+ return nil, fmt.Errorf("could not find host for deployment %s", deployment.Name)
}
- podWithConfigMap := PodWithConfigMap{
- Host: host,
- Pod: pod,
- ConfigMap: *cm,
+ deploymentWithConfigMap := DeploymentWithConfigMap{
+ Host: host,
+ ServiceName: serviceName,
+ Deployment: deployment,
+ ConfigMap: *cm,
}
- podsWithConfigMaps = append(podsWithConfigMaps, podWithConfigMap)
+ deploymentsWithConfigMaps = append(deploymentsWithConfigMaps, deploymentWithConfigMap)
}
}
- fmt.Printf("Found %d chainlink node crib pods\n", len(podsWithConfigMaps))
- return podsWithConfigMaps, nil
+ fmt.Printf("Found %d deployments with config maps\n", len(deploymentsWithConfigMaps))
+ return deploymentsWithConfigMaps, nil
}
-// ListPods lists pods for a namespace and selector
-func (m *K8sClient) ListPods(selector string) (*v1.PodList, error) {
- pods, err := m.ClientSet.CoreV1().Pods(m.namespace).List(context.Background(), metaV1.ListOptions{LabelSelector: selector})
- sort.Slice(pods.Items, func(i, j int) bool {
- return pods.Items[i].CreationTimestamp.Before(pods.Items[j].CreationTimestamp.DeepCopy())
+// ListDeployments lists deployments for a namespace
+func (m *K8sClient) ListDeployments(selector string) (*apps.DeploymentList, error) {
+ deployments, err := m.ClientSet.AppsV1().Deployments(m.namespace).List(context.Background(), metaV1.ListOptions{LabelSelector: selector})
+ if err != nil {
+ return nil, err
+ }
+ sort.Slice(deployments.Items, func(i, j int) bool {
+ return deployments.Items[i].CreationTimestamp.Before(deployments.Items[j].CreationTimestamp.DeepCopy())
})
-
- return pods.DeepCopy(), err
+ return deployments.DeepCopy(), nil
}
// Get a config map
diff --git a/core/scripts/keystone/src/99_nodes.go b/core/scripts/keystone/src/99_nodes.go
deleted file mode 100644
index 68d3621ce63..00000000000
--- a/core/scripts/keystone/src/99_nodes.go
+++ /dev/null
@@ -1,72 +0,0 @@
-package src
-
-import (
- "errors"
- "fmt"
- "net/url"
- "strings"
-
- helpers "github.com/smartcontractkit/chainlink/core/scripts/common"
-)
-
-type node struct {
- url *url.URL
- remoteURL *url.URL
- login string
- password string
-}
-
-func (n node) IsTerminal() bool {
- return false
-}
-
-func (n node) PasswordPrompt(p string) string {
- return n.password
-}
-
-func (n node) Prompt(p string) string {
- return n.login
-}
-
-func writeNodesList(path string, nodes []*node) error {
- fmt.Println("Writing nodes list to", path)
- var lines []string
- for _, n := range nodes {
- lines = append(lines, fmt.Sprintf("%s %s %s", n.url.String(), n.login, n.password))
- }
-
- return writeLines(lines, path)
-}
-
-func mustReadNodesList(path string) []*node {
- fmt.Println("Reading nodes list from", path)
- nodesList, err := readLines(path)
- helpers.PanicErr(err)
-
- var nodes []*node
- var hasBoot bool
- for _, r := range nodesList {
- rr := strings.TrimSpace(r)
- if len(rr) == 0 {
- continue
- }
- s := strings.Split(rr, " ")
- if len(s) != 4 {
- helpers.PanicErr(errors.New("wrong nodes list format"))
- }
- if strings.Contains(s[0], "boot") && hasBoot {
- helpers.PanicErr(errors.New("the single boot node must come first"))
- }
- hasBoot = true
- url, err := url.Parse(s[0])
- remoteURL, err := url.Parse(s[1])
- helpers.PanicErr(err)
- nodes = append(nodes, &node{
- url: url,
- remoteURL: remoteURL,
- login: s[2],
- password: s[3],
- })
- }
- return nodes
-}
diff --git a/core/scripts/keystone/src/__snapshots__/02_deploy_keystone_workflows_test.snap b/core/scripts/keystone/src/__snapshots__/02_deploy_keystone_workflows_test.snap
new file mode 100755
index 00000000000..8556ca9304c
--- /dev/null
+++ b/core/scripts/keystone/src/__snapshots__/02_deploy_keystone_workflows_test.snap
@@ -0,0 +1,57 @@
+
+[TestCreateKeystoneWorkflowJob - 1]
+
+type = "workflow"
+schemaVersion = 1
+name = "keystone_workflow"
+workflow = """
+name: "ccip_kiab1"
+owner: '0x1234567890abcdef1234567890abcdef12345678'
+triggers:
+ - id: streams-trigger@1.1.0
+ config:
+ maxFrequencyMs: 10000
+ feedIds:
+ - 'feed1'
+ - 'feed2'
+ - 'feed3'
+
+consensus:
+ - id: offchain_reporting@1.0.0
+ ref: ccip_feeds
+ inputs:
+ observations:
+ - $(trigger.outputs)
+ config:
+ report_id: '0001'
+ key_id: 'evm'
+ aggregation_method: data_feeds
+ aggregation_config:
+ feeds:
+ 'feed1':
+ deviation: '0.05'
+ heartbeat: 1800
+ 'feed2':
+ deviation: '0.05'
+ heartbeat: 1800
+ 'feed3':
+ deviation: '0.05'
+ heartbeat: 1800
+ encoder: EVM
+ encoder_config:
+ abi: "(bytes32 FeedID, uint224 Price, uint32 Timestamp)[] Reports"
+ abi: (bytes32 FeedID, uint224 Price, uint32 Timestamp)[] Reports
+
+targets:
+ - id: target_id
+ inputs:
+ signed_report: $(ccip_feeds.outputs)
+ config:
+ address: '0xabcdefabcdefabcdefabcdefabcdefabcdef'
+ deltaStage: 5s
+ schedule: oneAtATime
+
+"""
+workflowOwner = "0x1234567890abcdef1234567890abcdef12345678"
+
+---
diff --git a/core/scripts/keystone/src/__snapshots__/02_provision_crib_test.snap b/core/scripts/keystone/src/__snapshots__/02_provision_crib_test.snap
new file mode 100755
index 00000000000..06532bea727
--- /dev/null
+++ b/core/scripts/keystone/src/__snapshots__/02_provision_crib_test.snap
@@ -0,0 +1,415 @@
+
+[TestGeneratePostprovisionConfig - 1]
+helm:
+ values:
+ chainlink:
+ nodes:
+ 0-ks-wf-bt-node1:
+ image: ${runtime.images.app}
+ overridesToml: |
+ [Capabilities]
+ [Capabilities.Peering]
+ [Capabilities.Peering.V2]
+ Enabled = true
+ ListenAddresses = ['0.0.0.0:6691']
+
+ [Capabilities.ExternalRegistry]
+ Address = '0x0200000000000000000000000000000000000000'
+ NetworkID = 'evm'
+ ChainID = '1337'
+
+ [[EVM]]
+ ChainID = '1337'
+ Nodes = []
+ 0-ks-wf-node2:
+ image: ${runtime.images.app}
+ overridesToml: |
+ [Capabilities]
+ [Capabilities.Peering]
+ [Capabilities.Peering.V2]
+ Enabled = true
+ DefaultBootstrappers = ['12D3KooWFSmZaLFF1nu3mzxPKj43F89WgVDqkpvwFUHBfMHSqpVq@app-0-ks-wf-bt-node1:6691']
+ ListenAddresses = ['0.0.0.0:6691']
+
+ [Capabilities.ExternalRegistry]
+ Address = '0x0200000000000000000000000000000000000000'
+ NetworkID = 'evm'
+ ChainID = '1337'
+
+ [[EVM]]
+ ChainID = '1337'
+ Nodes = []
+
+ [EVM.Workflow]
+ FromAddress = '0x75cf1355cC4Eb358feaBb9e269a4DAEeB6721DBB'
+ ForwarderAddress = '0x0100000000000000000000000000000000000000'
+ 0-ks-wf-node3:
+ image: ${runtime.images.app}
+ overridesToml: |
+ [Capabilities]
+ [Capabilities.Peering]
+ [Capabilities.Peering.V2]
+ Enabled = true
+ DefaultBootstrappers = ['12D3KooWFSmZaLFF1nu3mzxPKj43F89WgVDqkpvwFUHBfMHSqpVq@app-0-ks-wf-bt-node1:6691']
+ ListenAddresses = ['0.0.0.0:6691']
+
+ [Capabilities.ExternalRegistry]
+ Address = '0x0200000000000000000000000000000000000000'
+ NetworkID = 'evm'
+ ChainID = '1337'
+
+ [[EVM]]
+ ChainID = '1337'
+ Nodes = []
+
+ [EVM.Workflow]
+ FromAddress = '0xc6dcE30f492CBD223b9946603192f22D86e783ca'
+ ForwarderAddress = '0x0100000000000000000000000000000000000000'
+ 0-ks-wf-node4:
+ image: ${runtime.images.app}
+ overridesToml: |
+ [Capabilities]
+ [Capabilities.Peering]
+ [Capabilities.Peering.V2]
+ Enabled = true
+ DefaultBootstrappers = ['12D3KooWFSmZaLFF1nu3mzxPKj43F89WgVDqkpvwFUHBfMHSqpVq@app-0-ks-wf-bt-node1:6691']
+ ListenAddresses = ['0.0.0.0:6691']
+
+ [Capabilities.ExternalRegistry]
+ Address = '0x0200000000000000000000000000000000000000'
+ NetworkID = 'evm'
+ ChainID = '1337'
+
+ [[EVM]]
+ ChainID = '1337'
+ Nodes = []
+
+ [EVM.Workflow]
+ FromAddress = '0x1289d00A6565Afcd6437B09548F6019EF49696d0'
+ ForwarderAddress = '0x0100000000000000000000000000000000000000'
+ 0-ks-wf-node5:
+ image: ${runtime.images.app}
+ overridesToml: |
+ [Capabilities]
+ [Capabilities.Peering]
+ [Capabilities.Peering.V2]
+ Enabled = true
+ DefaultBootstrappers = ['12D3KooWFSmZaLFF1nu3mzxPKj43F89WgVDqkpvwFUHBfMHSqpVq@app-0-ks-wf-bt-node1:6691']
+ ListenAddresses = ['0.0.0.0:6691']
+
+ [Capabilities.ExternalRegistry]
+ Address = '0x0200000000000000000000000000000000000000'
+ NetworkID = 'evm'
+ ChainID = '1337'
+
+ [[EVM]]
+ ChainID = '1337'
+ Nodes = []
+
+ [EVM.Workflow]
+ FromAddress = '0x4b92B0aaC39932B7302676F48e78FA91852DC0EE'
+ ForwarderAddress = '0x0100000000000000000000000000000000000000'
+ 1-ks-str-trig-bt-node1:
+ image: ${runtime.images.app}
+ overridesToml: |
+ [Capabilities]
+ [Capabilities.Peering]
+ [Capabilities.Peering.V2]
+ Enabled = true
+ DefaultBootstrappers = ['12D3KooWFSmZaLFF1nu3mzxPKj43F89WgVDqkpvwFUHBfMHSqpVq@app-0-ks-wf-bt-node1:6691']
+ ListenAddresses = ['0.0.0.0:6691']
+
+ [Capabilities.ExternalRegistry]
+ Address = '0x0200000000000000000000000000000000000000'
+ NetworkID = 'evm'
+ ChainID = '1337'
+
+ [[EVM]]
+ ChainID = '1337'
+ Nodes = []
+ 1-ks-str-trig-node2:
+ image: ${runtime.images.app}
+ overridesToml: |
+ [Capabilities]
+ [Capabilities.Peering]
+ [Capabilities.Peering.V2]
+ Enabled = true
+ DefaultBootstrappers = ['12D3KooWFSmZaLFF1nu3mzxPKj43F89WgVDqkpvwFUHBfMHSqpVq@app-0-ks-wf-bt-node1:6691']
+ ListenAddresses = ['0.0.0.0:6691']
+
+ [Capabilities.ExternalRegistry]
+ Address = '0x0200000000000000000000000000000000000000'
+ NetworkID = 'evm'
+ ChainID = '1337'
+
+ [[EVM]]
+ ChainID = '1337'
+ Nodes = []
+ 1-ks-str-trig-node3:
+ image: ${runtime.images.app}
+ overridesToml: |
+ [Capabilities]
+ [Capabilities.Peering]
+ [Capabilities.Peering.V2]
+ Enabled = true
+ DefaultBootstrappers = ['12D3KooWFSmZaLFF1nu3mzxPKj43F89WgVDqkpvwFUHBfMHSqpVq@app-0-ks-wf-bt-node1:6691']
+ ListenAddresses = ['0.0.0.0:6691']
+
+ [Capabilities.ExternalRegistry]
+ Address = '0x0200000000000000000000000000000000000000'
+ NetworkID = 'evm'
+ ChainID = '1337'
+
+ [[EVM]]
+ ChainID = '1337'
+ Nodes = []
+ 1-ks-str-trig-node4:
+ image: ${runtime.images.app}
+ overridesToml: |
+ [Capabilities]
+ [Capabilities.Peering]
+ [Capabilities.Peering.V2]
+ Enabled = true
+ DefaultBootstrappers = ['12D3KooWFSmZaLFF1nu3mzxPKj43F89WgVDqkpvwFUHBfMHSqpVq@app-0-ks-wf-bt-node1:6691']
+ ListenAddresses = ['0.0.0.0:6691']
+
+ [Capabilities.ExternalRegistry]
+ Address = '0x0200000000000000000000000000000000000000'
+ NetworkID = 'evm'
+ ChainID = '1337'
+
+ [[EVM]]
+ ChainID = '1337'
+ Nodes = []
+ 1-ks-str-trig-node5:
+ image: ${runtime.images.app}
+ overridesToml: |
+ [Capabilities]
+ [Capabilities.Peering]
+ [Capabilities.Peering.V2]
+ Enabled = true
+ DefaultBootstrappers = ['12D3KooWFSmZaLFF1nu3mzxPKj43F89WgVDqkpvwFUHBfMHSqpVq@app-0-ks-wf-bt-node1:6691']
+ ListenAddresses = ['0.0.0.0:6691']
+
+ [Capabilities.ExternalRegistry]
+ Address = '0x0200000000000000000000000000000000000000'
+ NetworkID = 'evm'
+ ChainID = '1337'
+
+ [[EVM]]
+ ChainID = '1337'
+ Nodes = []
+ ingress:
+ hosts:
+ - host: ${DEVSPACE_NAMESPACE}-0-ks-wf-bt-node1.${DEVSPACE_INGRESS_BASE_DOMAIN}
+ http:
+ paths:
+ - path: /
+ backend:
+ service:
+ name: app-0-ks-wf-bt-node1
+ port:
+ number: 6688
+ - host: ${DEVSPACE_NAMESPACE}-0-ks-wf-node2.${DEVSPACE_INGRESS_BASE_DOMAIN}
+ http:
+ paths:
+ - path: /
+ backend:
+ service:
+ name: app-0-ks-wf-node2
+ port:
+ number: 6688
+ - host: ${DEVSPACE_NAMESPACE}-0-ks-wf-node3.${DEVSPACE_INGRESS_BASE_DOMAIN}
+ http:
+ paths:
+ - path: /
+ backend:
+ service:
+ name: app-0-ks-wf-node3
+ port:
+ number: 6688
+ - host: ${DEVSPACE_NAMESPACE}-0-ks-wf-node4.${DEVSPACE_INGRESS_BASE_DOMAIN}
+ http:
+ paths:
+ - path: /
+ backend:
+ service:
+ name: app-0-ks-wf-node4
+ port:
+ number: 6688
+ - host: ${DEVSPACE_NAMESPACE}-0-ks-wf-node5.${DEVSPACE_INGRESS_BASE_DOMAIN}
+ http:
+ paths:
+ - path: /
+ backend:
+ service:
+ name: app-0-ks-wf-node5
+ port:
+ number: 6688
+ - host: ${DEVSPACE_NAMESPACE}-1-ks-str-trig-bt-node1.${DEVSPACE_INGRESS_BASE_DOMAIN}
+ http:
+ paths:
+ - path: /
+ backend:
+ service:
+ name: app-1-ks-str-trig-bt-node1
+ port:
+ number: 6688
+ - host: ${DEVSPACE_NAMESPACE}-1-ks-str-trig-node2.${DEVSPACE_INGRESS_BASE_DOMAIN}
+ http:
+ paths:
+ - path: /
+ backend:
+ service:
+ name: app-1-ks-str-trig-node2
+ port:
+ number: 6688
+ - host: ${DEVSPACE_NAMESPACE}-1-ks-str-trig-node3.${DEVSPACE_INGRESS_BASE_DOMAIN}
+ http:
+ paths:
+ - path: /
+ backend:
+ service:
+ name: app-1-ks-str-trig-node3
+ port:
+ number: 6688
+ - host: ${DEVSPACE_NAMESPACE}-1-ks-str-trig-node4.${DEVSPACE_INGRESS_BASE_DOMAIN}
+ http:
+ paths:
+ - path: /
+ backend:
+ service:
+ name: app-1-ks-str-trig-node4
+ port:
+ number: 6688
+ - host: ${DEVSPACE_NAMESPACE}-1-ks-str-trig-node5.${DEVSPACE_INGRESS_BASE_DOMAIN}
+ http:
+ paths:
+ - path: /
+ backend:
+ service:
+ name: app-1-ks-str-trig-node5
+ port:
+ number: 6688
+
+---
+
+[TestGeneratePreprovisionConfig - 1]
+helm:
+ values:
+ chainlink:
+ nodes:
+ 0-ks-wf-bt-node1:
+ image: ${runtime.images.app}
+ 0-ks-wf-node2:
+ image: ${runtime.images.app}
+ 0-ks-wf-node3:
+ image: ${runtime.images.app}
+ 0-ks-wf-node4:
+ image: ${runtime.images.app}
+ 0-ks-wf-node5:
+ image: ${runtime.images.app}
+ 1-ks-str-trig-bt-node1:
+ image: ${runtime.images.app}
+ 1-ks-str-trig-node2:
+ image: ${runtime.images.app}
+ 1-ks-str-trig-node3:
+ image: ${runtime.images.app}
+ 1-ks-str-trig-node4:
+ image: ${runtime.images.app}
+ 1-ks-str-trig-node5:
+ image: ${runtime.images.app}
+ ingress:
+ hosts:
+ - host: ${DEVSPACE_NAMESPACE}-0-ks-wf-bt-node1.${DEVSPACE_INGRESS_BASE_DOMAIN}
+ http:
+ paths:
+ - path: /
+ backend:
+ service:
+ name: app-0-ks-wf-bt-node1
+ port:
+ number: 6688
+ - host: ${DEVSPACE_NAMESPACE}-0-ks-wf-node2.${DEVSPACE_INGRESS_BASE_DOMAIN}
+ http:
+ paths:
+ - path: /
+ backend:
+ service:
+ name: app-0-ks-wf-node2
+ port:
+ number: 6688
+ - host: ${DEVSPACE_NAMESPACE}-0-ks-wf-node3.${DEVSPACE_INGRESS_BASE_DOMAIN}
+ http:
+ paths:
+ - path: /
+ backend:
+ service:
+ name: app-0-ks-wf-node3
+ port:
+ number: 6688
+ - host: ${DEVSPACE_NAMESPACE}-0-ks-wf-node4.${DEVSPACE_INGRESS_BASE_DOMAIN}
+ http:
+ paths:
+ - path: /
+ backend:
+ service:
+ name: app-0-ks-wf-node4
+ port:
+ number: 6688
+ - host: ${DEVSPACE_NAMESPACE}-0-ks-wf-node5.${DEVSPACE_INGRESS_BASE_DOMAIN}
+ http:
+ paths:
+ - path: /
+ backend:
+ service:
+ name: app-0-ks-wf-node5
+ port:
+ number: 6688
+ - host: ${DEVSPACE_NAMESPACE}-1-ks-str-trig-bt-node1.${DEVSPACE_INGRESS_BASE_DOMAIN}
+ http:
+ paths:
+ - path: /
+ backend:
+ service:
+ name: app-1-ks-str-trig-bt-node1
+ port:
+ number: 6688
+ - host: ${DEVSPACE_NAMESPACE}-1-ks-str-trig-node2.${DEVSPACE_INGRESS_BASE_DOMAIN}
+ http:
+ paths:
+ - path: /
+ backend:
+ service:
+ name: app-1-ks-str-trig-node2
+ port:
+ number: 6688
+ - host: ${DEVSPACE_NAMESPACE}-1-ks-str-trig-node3.${DEVSPACE_INGRESS_BASE_DOMAIN}
+ http:
+ paths:
+ - path: /
+ backend:
+ service:
+ name: app-1-ks-str-trig-node3
+ port:
+ number: 6688
+ - host: ${DEVSPACE_NAMESPACE}-1-ks-str-trig-node4.${DEVSPACE_INGRESS_BASE_DOMAIN}
+ http:
+ paths:
+ - path: /
+ backend:
+ service:
+ name: app-1-ks-str-trig-node4
+ port:
+ number: 6688
+ - host: ${DEVSPACE_NAMESPACE}-1-ks-str-trig-node5.${DEVSPACE_INGRESS_BASE_DOMAIN}
+ http:
+ paths:
+ - path: /
+ backend:
+ service:
+ name: app-1-ks-str-trig-node5
+ port:
+ number: 6688
+
+---
diff --git a/core/scripts/keystone/src/__snapshots__/02_provision_ocr3_capability_test.snap b/core/scripts/keystone/src/__snapshots__/02_provision_ocr3_capability_test.snap
new file mode 100755
index 00000000000..9d38f78899b
--- /dev/null
+++ b/core/scripts/keystone/src/__snapshots__/02_provision_ocr3_capability_test.snap
@@ -0,0 +1,65 @@
+
+[TestGenerateOCR3Config - 1]
+{
+ "F": 1,
+ "OffchainConfig": "",
+ "OffchainConfigVersion": 30,
+ "OnchainConfig": "0x",
+ "Signers": [
+ "011400321bc7af41a634375526006365a31bf32b4cfa7c0520004ca789105da974eec967758ad32b575741d6cb36c1bb3bcfd87b235502cc1753",
+ "0114005192c43a68efb7a698c0459ff8591a115da128ee052000169008927a60e6c03e99aac6fa268dabaf4d00e117419861d87836211267361b",
+ "011400ed613636925af2df6ed8332d95028eabcbe95a3f052000ce86b34de67249f92058f69e47961907ebbf8a71c12123f1d2a7cab4874f6365",
+ "01140053b5bbc0efa2e2d2770029bab5d5a647a260a72b052000f2cb4932d3ce8c10bf67c60d35372a5ff1578255e25c2a119c2dea70e919567a"
+ ],
+ "Transmitters": [
+ "0x75cf1355cC4Eb358feaBb9e269a4DAEeB6721DBB",
+ "0xc6dcE30f492CBD223b9946603192f22D86e783ca",
+ "0x1289d00A6565Afcd6437B09548F6019EF49696d0",
+ "0x4b92B0aaC39932B7302676F48e78FA91852DC0EE"
+ ]
+}
+---
+
+[TestGenSpecs - 1]
+
+type = "bootstrap"
+schemaVersion = 1
+name = "ocr3_bootstrap"
+contractID = "0xB29934624cAe3765E33115A9530a13f5aEC7fa8A"
+relay = "evm"
+
+[relayConfig]
+chainID = "1337"
+providerType = "ocr3-capability"
+
+
+
+type = "offchainreporting2"
+schemaVersion = 1
+name = "ocr3_oracle"
+contractID = "0xB29934624cAe3765E33115A9530a13f5aEC7fa8A"
+ocrKeyBundleID = "20ccdc97afdf467465590115e3da4e5eb591bf5f43808e81a5d0807cd889b3c7"
+p2pv2Bootstrappers = [
+ "12D3KooWFSmZaLFF1nu3mzxPKj43F89WgVDqkpvwFUHBfMHSqpVq@app-0-ks-wf-bt-node1:6690",
+]
+relay = "evm"
+pluginType = "plugin"
+transmitterID = "12D3KooWHhXyDmHB6D1UQosLXmhczw3zxB3DLYBuq9Unb4iCD4Sc"
+
+[relayConfig]
+chainID = "1337"
+
+[pluginConfig]
+command = "chainlink-ocr3-capability"
+ocrVersion = 3
+pluginName = "ocr-capability"
+providerType = "ocr3-capability"
+telemetryType = "plugin"
+
+[onchainSigningStrategy]
+strategyName = 'multi-chain'
+[onchainSigningStrategy.config]
+evm = "20ccdc97afdf467465590115e3da4e5eb591bf5f43808e81a5d0807cd889b3c7"
+aptos = "ac364cec9fe7d9ea1035fc511e5b2f30900caa6e65ac0501168005d05129e088"
+
+---
diff --git a/core/scripts/keystone/src/__snapshots__/02_provision_streams_trigger_capability_test.snap b/core/scripts/keystone/src/__snapshots__/02_provision_streams_trigger_capability_test.snap
new file mode 100755
index 00000000000..07ac61b7264
--- /dev/null
+++ b/core/scripts/keystone/src/__snapshots__/02_provision_streams_trigger_capability_test.snap
@@ -0,0 +1,50 @@
+
+[TestCreateMercuryV3Job - 1]
+
+type = "offchainreporting2"
+schemaVersion = 1
+name = "mercury-BTC/USD"
+p2pv2Bootstrappers = ["crib-henry-keystone-node1.main.stage.cldev.sh"]
+forwardingAllowed = false
+maxTaskDuration = "1s"
+contractID = "0x0700000000000000000000000000000000000000"
+feedID = "0x0100000000000000000000000000000000000000000000000000000000000000"
+contractConfigTrackerPollInterval = "1s"
+ocrKeyBundleID = "ocr_key_bundle_id"
+relay = "evm"
+pluginType = "mercury"
+transmitterID = "node_csa_key"
+observationSource = """
+ price [type=bridge name="bridge_name" timeout="50ms" requestData=""];
+
+ benchmark_price [type=jsonparse path="result,mid" index=0];
+ price -> benchmark_price;
+
+ bid_price [type=jsonparse path="result,bid" index=1];
+ price -> bid_price;
+
+ ask_price [type=jsonparse path="result,ask" index=2];
+ price -> ask_price;
+"""
+
+[relayConfig]
+enableTriggerCapability = true
+chainID = "123456"
+
+---
+
+[TestCreateMercuryBootstrapJob - 1]
+
+type = "bootstrap"
+relay = "evm"
+schemaVersion = 1
+name = "boot-BTC/USD"
+contractID = "0x0700000000000000000000000000000000000000"
+feedID = "0x0100000000000000000000000000000000000000000000000000000000000000"
+contractConfigTrackerPollInterval = "1s"
+
+[relayConfig]
+chainID = 123456
+enableTriggerCapability = true
+
+---
diff --git a/core/scripts/keystone/src/__snapshots__/03_gen_crib_cluster_overrides_cmd_test.snap b/core/scripts/keystone/src/__snapshots__/03_gen_crib_cluster_overrides_cmd_test.snap
deleted file mode 100755
index 08b79a9f4f9..00000000000
--- a/core/scripts/keystone/src/__snapshots__/03_gen_crib_cluster_overrides_cmd_test.snap
+++ /dev/null
@@ -1,44 +0,0 @@
-
-[TestGenerateCribConfig - 1]
-helm:
- values:
- chainlink:
- nodes:
- node1:
- image: ${runtime.images.app}
- overridesToml: |-
- [[EVM]]
- ChainID = '11155111'
- node2:
- image: ${runtime.images.app}
- overridesToml: |-
- [[EVM]]
- ChainID = '11155111'
- [EVM.Workflow]
- FromAddress = '0x8B60FDcc9CAC8ea476b31d17011CB204471431d9'
- ForwarderAddress = '0x1234567890abcdef'
- node3:
- image: ${runtime.images.app}
- overridesToml: |-
- [[EVM]]
- ChainID = '11155111'
- [EVM.Workflow]
- FromAddress = '0x6620F516F29979B214e2451498a057FDd3a0A85d'
- ForwarderAddress = '0x1234567890abcdef'
- node4:
- image: ${runtime.images.app}
- overridesToml: |-
- [[EVM]]
- ChainID = '11155111'
- [EVM.Workflow]
- FromAddress = '0xFeB61E22FCf4F9740c9D96b05199F195bd61A7c2'
- ForwarderAddress = '0x1234567890abcdef'
- node5:
- image: ${runtime.images.app}
- overridesToml: |-
- [[EVM]]
- ChainID = '11155111'
- [EVM.Workflow]
- FromAddress = '0x882Fd04D78A7e7D386Dd5b550f19479E5494B0B2'
- ForwarderAddress = '0x1234567890abcdef'
----
diff --git a/core/scripts/keystone/src/__snapshots__/88_gen_jobspecs_test.snap b/core/scripts/keystone/src/__snapshots__/88_gen_jobspecs_test.snap
deleted file mode 100755
index c0c7c7d7e67..00000000000
--- a/core/scripts/keystone/src/__snapshots__/88_gen_jobspecs_test.snap
+++ /dev/null
@@ -1,140 +0,0 @@
-
-[TestGenSpecs - 1]
-Bootstrap:
-Host: crib-henry-keystone-node1.main.stage.cldev.sh
-type = "bootstrap"
-schemaVersion = 1
-name = "Keystone boot"
-contractID = "0xB29934624cAe3765E33115A9530a13f5aEC7fa8A"
-relay = "evm"
-
-[relayConfig]
-chainID = "11155111"
-providerType = "ocr3-capability"
-
-Oracles:
-Oracle 0:
-Host: crib-henry-keystone-node2.main.stage.cldev.sh
-type = "offchainreporting2"
-schemaVersion = 1
-name = "Keystone"
-contractID = "0xB29934624cAe3765E33115A9530a13f5aEC7fa8A"
-ocrKeyBundleID = "b3df4d8748b67731a1112e8b45a764941974f5590c93672eebbc4f3504dd10ed"
-p2pv2Bootstrappers = [
- "12D3KooWNmhKZL1XW4Vv3rNjLXzJ6mqcVerihdijjGYuexPrFUFZ@crib-henry-keystone-node1.main.stage.cldev.sh:6690",
-]
-relay = "evm"
-pluginType = "plugin"
-transmitterID = "0x8B60FDcc9CAC8ea476b31d17011CB204471431d9"
-
-[relayConfig]
-chainID = "11155111"
-
-[pluginConfig]
-command = "chainlink-ocr3-capability"
-ocrVersion = 3
-pluginName = "ocr-capability"
-providerType = "ocr3-capability"
-telemetryType = "plugin"
-
-[onchainSigningStrategy]
-strategyName = 'multi-chain'
-[onchainSigningStrategy.config]
-evm = "b3df4d8748b67731a1112e8b45a764941974f5590c93672eebbc4f3504dd10ed"
-aptos = "9bebfa953e7a7522746f72b4023308de36db626f3e0bcb9033407b8a183e8bfb"
-
---------------------------------
-Oracle 1:
-Host: crib-henry-keystone-node3.main.stage.cldev.sh
-type = "offchainreporting2"
-schemaVersion = 1
-name = "Keystone"
-contractID = "0xB29934624cAe3765E33115A9530a13f5aEC7fa8A"
-ocrKeyBundleID = "38459ae37f29f2c1fde0f25972a973322be8cada82acf43f464756836725be97"
-p2pv2Bootstrappers = [
- "12D3KooWNmhKZL1XW4Vv3rNjLXzJ6mqcVerihdijjGYuexPrFUFZ@crib-henry-keystone-node1.main.stage.cldev.sh:6690",
-]
-relay = "evm"
-pluginType = "plugin"
-transmitterID = "0x6620F516F29979B214e2451498a057FDd3a0A85d"
-
-[relayConfig]
-chainID = "11155111"
-
-[pluginConfig]
-command = "chainlink-ocr3-capability"
-ocrVersion = 3
-pluginName = "ocr-capability"
-providerType = "ocr3-capability"
-telemetryType = "plugin"
-
-[onchainSigningStrategy]
-strategyName = 'multi-chain'
-[onchainSigningStrategy.config]
-evm = "38459ae37f29f2c1fde0f25972a973322be8cada82acf43f464756836725be97"
-aptos = "9bebfa953e7a7522746f72b4023308de36db626f3e0bcb9033407b8a183e8bfc"
-
---------------------------------
-Oracle 2:
-Host: crib-henry-keystone-node4.main.stage.cldev.sh
-type = "offchainreporting2"
-schemaVersion = 1
-name = "Keystone"
-contractID = "0xB29934624cAe3765E33115A9530a13f5aEC7fa8A"
-ocrKeyBundleID = "b5dbc4c9da983cddde2e3226b85807eb7beaf818694a22576af4d80f352702ed"
-p2pv2Bootstrappers = [
- "12D3KooWNmhKZL1XW4Vv3rNjLXzJ6mqcVerihdijjGYuexPrFUFZ@crib-henry-keystone-node1.main.stage.cldev.sh:6690",
-]
-relay = "evm"
-pluginType = "plugin"
-transmitterID = "0xFeB61E22FCf4F9740c9D96b05199F195bd61A7c2"
-
-[relayConfig]
-chainID = "11155111"
-
-[pluginConfig]
-command = "chainlink-ocr3-capability"
-ocrVersion = 3
-pluginName = "ocr-capability"
-providerType = "ocr3-capability"
-telemetryType = "plugin"
-
-[onchainSigningStrategy]
-strategyName = 'multi-chain'
-[onchainSigningStrategy.config]
-evm = "b5dbc4c9da983cddde2e3226b85807eb7beaf818694a22576af4d80f352702ed"
-aptos = "9bebfa953e7a7522746f72b4023308de36db626f3e0bcb9033407b8a183e8bfd"
-
---------------------------------
-Oracle 3:
-Host: crib-henry-keystone-node5.main.stage.cldev.sh
-type = "offchainreporting2"
-schemaVersion = 1
-name = "Keystone"
-contractID = "0xB29934624cAe3765E33115A9530a13f5aEC7fa8A"
-ocrKeyBundleID = "260d5c1a618cdf5324509d7db95f5a117511864ebb9e1f709e8969339eb225af"
-p2pv2Bootstrappers = [
- "12D3KooWNmhKZL1XW4Vv3rNjLXzJ6mqcVerihdijjGYuexPrFUFZ@crib-henry-keystone-node1.main.stage.cldev.sh:6690",
-]
-relay = "evm"
-pluginType = "plugin"
-transmitterID = "0x882Fd04D78A7e7D386Dd5b550f19479E5494B0B2"
-
-[relayConfig]
-chainID = "11155111"
-
-[pluginConfig]
-command = "chainlink-ocr3-capability"
-ocrVersion = 3
-pluginName = "ocr-capability"
-providerType = "ocr3-capability"
-telemetryType = "plugin"
-
-[onchainSigningStrategy]
-strategyName = 'multi-chain'
-[onchainSigningStrategy.config]
-evm = "260d5c1a618cdf5324509d7db95f5a117511864ebb9e1f709e8969339eb225af"
-aptos = "9bebfa953e7a7522746f72b4023308de36db626f3e0bcb9033407b8a183e8bfe"
-
-
----
diff --git a/core/scripts/keystone/src/__snapshots__/88_gen_ocr3_config_test.snap b/core/scripts/keystone/src/__snapshots__/88_gen_ocr3_config_test.snap
deleted file mode 100755
index eac3cdaff4c..00000000000
--- a/core/scripts/keystone/src/__snapshots__/88_gen_ocr3_config_test.snap
+++ /dev/null
@@ -1,23 +0,0 @@
-
-[TestGenerateOCR3Config - 1]
-{
- "F": 1,
- "OffchainConfig": "",
- "OffchainConfigVersion": 30,
- "OnchainConfig": "0x",
- "Signers": [
- "011400a2402db8e549f094ea31e1c0edd77623f4ca5b12052000ea551e503b93a1c9ae26262b4db8f66db4cbe5ddcb6039e29d2665a634d48e4a",
- "0114004af19c802b244d1d085492c3946391c965e10519052000ea551e503b93a1c9ae26262b4db8f66db4cbe5ddcb6039e29d2665a634d48e4b",
- "01140061925685d2b80b121537341d063c4e57b2f9323c052000ea551e503b93a1c9ae26262b4db8f66db4cbe5ddcb6039e29d2665a634d48e4c",
- "011400fd97efd53fc20acc098fcd746c04d8d7540d97e0052000ea551e503b93a1c9ae26262b4db8f66db4cbe5ddcb6039e29d2665a634d48e4d",
- "011400a0b67dc5345a71d02b396147ae2cb75dda63cbe9052000ea551e503b93a1c9ae26262b4db8f66db4cbe5ddcb6039e29d2665a634d48e4e"
- ],
- "Transmitters": [
- "0xF4e7e516146c8567F8E8be0ED1f1A92798628d35",
- "0x8B60FDcc9CAC8ea476b31d17011CB204471431d9",
- "0x6620F516F29979B214e2451498a057FDd3a0A85d",
- "0xFeB61E22FCf4F9740c9D96b05199F195bd61A7c2",
- "0x882Fd04D78A7e7D386Dd5b550f19479E5494B0B2"
- ]
-}
----
diff --git a/core/scripts/keystone/src/external-adapter/.goreleaser.yaml b/core/scripts/keystone/src/external-adapter/.goreleaser.yaml
new file mode 100644
index 00000000000..524be367e09
--- /dev/null
+++ b/core/scripts/keystone/src/external-adapter/.goreleaser.yaml
@@ -0,0 +1,49 @@
+project_name: kiab-mock-external-adapter
+version: 2
+
+builds:
+ - targets:
+ - go_first_class
+ no_unique_dist_dir: true
+ binary: kiab-mock-external-adapter
+ env:
+ - CGO_ENABLED=0
+
+dockers:
+ - id: linux-arm64
+ use: buildx
+ goos: linux
+ goarch: arm64
+ image_templates:
+ - "{{ .Env.IMAGE }}"
+ build_flag_templates:
+ - --platform=linux/arm64
+
+ - id: linux-amd64
+ use: buildx
+ goos: linux
+ goarch: amd64
+ image_templates:
+ - "{{ .Env.IMAGE }}"
+ build_flag_templates:
+ - --platform=linux/amd64
+docker_manifests:
+ - name_template: '{{ .Env.IMAGE }}'
+ image_templates:
+ - '{{ .Env.IMAGE }}'
+archives:
+ - format: binary
+
+release:
+ disable: true
+changelog:
+ disable: true
+
+nightly:
+ version_template: "{{ .ProjectName }}-{{ .ShortCommit }}"
+
+snapshot:
+ version_template: "{{ .ProjectName }}-{{ .ShortCommit }}"
+
+partial:
+ by: target
diff --git a/core/scripts/keystone/src/external-adapter/99_external_adapter.go b/core/scripts/keystone/src/external-adapter/99_external_adapter.go
new file mode 100644
index 00000000000..8af035f30fd
--- /dev/null
+++ b/core/scripts/keystone/src/external-adapter/99_external_adapter.go
@@ -0,0 +1,154 @@
+package main
+
+import (
+ "fmt"
+ "math/rand"
+ "net"
+ "net/http"
+ "net/http/httptest"
+ "os"
+ "strconv"
+ "sync"
+ "time"
+)
+
+func PanicErr(err error) {
+ if err != nil {
+ panic(err)
+ }
+}
+
+// Price struct encapsulates bid, mid, ask values along with a mutex for synchronization
+type Price struct {
+ mu sync.RWMutex
+ Bid float64
+ Mid float64
+ Ask float64
+}
+
+// Update safely updates the price values within the specified bounds
+func (p *Price) Update(step, floor, ceiling float64) {
+ p.mu.Lock()
+ defer p.mu.Unlock()
+
+ p.Mid = adjustValue(p.Mid, step, floor, ceiling)
+ p.Bid = adjustValue(p.Mid, step, floor, p.Mid)
+ p.Ask = adjustValue(p.Mid, step, p.Mid, ceiling)
+}
+
+// GetSnapshot safely retrieves a copy of the current price values
+func (p *Price) GetSnapshot() (bid, mid, ask float64) {
+ p.mu.RLock()
+ defer p.mu.RUnlock()
+ return p.Bid, p.Mid, p.Ask
+}
+
+func main() {
+ // Get initial values from environment variables or use defaults
+ btcUsdInitialValue := getInitialValue("BTCUSD_INITIAL_VALUE", 1000.0)
+ linkInitialValue := getInitialValue("LINK_INITIAL_VALUE", 11.0)
+ nativeInitialValue := getInitialValue("NATIVE_INITIAL_VALUE", 2400.0)
+
+ pctBounds := 0.3
+
+ // Start external adapters on different ports
+ externalAdapter(btcUsdInitialValue, "4001", pctBounds)
+ externalAdapter(linkInitialValue, "4002", pctBounds)
+ externalAdapter(nativeInitialValue, "4003", pctBounds)
+
+ // Block main goroutine indefinitely
+ select {}
+}
+
+// getInitialValue retrieves the initial value from the environment or returns a default
+func getInitialValue(envVar string, defaultValue float64) float64 {
+ valueEnv := os.Getenv(envVar)
+ if valueEnv == "" {
+ fmt.Printf("%s not set, using default value: %.4f\n", envVar, defaultValue)
+ return defaultValue
+ }
+ fmt.Printf("%s set to %s\n", envVar, valueEnv)
+ val, err := strconv.ParseFloat(valueEnv, 64)
+ PanicErr(err)
+ return val
+}
+
+// externalAdapter sets up a mock external adapter server for a specific asset
+func externalAdapter(initialValue float64, port string, pctBounds float64) *httptest.Server {
+ // Create a custom listener on the specified port
+ listener, err := net.Listen("tcp", "0.0.0.0:"+port)
+ if err != nil {
+ panic(err)
+ }
+
+ // Initialize the Price struct
+ price := &Price{
+ Bid: initialValue,
+ Mid: initialValue,
+ Ask: initialValue,
+ }
+
+ step := initialValue * pctBounds / 10
+ ceiling := initialValue * (1 + pctBounds)
+ floor := initialValue * (1 - pctBounds)
+
+ // Perform initial adjustment to set bid and ask
+ price.Update(step, floor, ceiling)
+
+ // Start a goroutine to periodically update the price
+ go func() {
+ ticker := time.NewTicker(10 * time.Second)
+ defer ticker.Stop()
+ for range ticker.C {
+ price.Update(step, floor, ceiling)
+ fmt.Printf("Updated prices on port %s: bid=%.4f, mid=%.4f, ask=%.4f\n", port, price.Bid, price.Mid, price.Ask)
+ }
+ }()
+
+ handler := http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
+ bid, mid, ask := price.GetSnapshot()
+
+ res.Header().Set("Content-Type", "application/json")
+ res.WriteHeader(http.StatusOK)
+ resp := fmt.Sprintf(`{"result": {"bid": %.4f, "mid": %.4f, "ask": %.4f}}`, bid, mid, ask)
+ if _, err := res.Write([]byte(resp)); err != nil {
+ fmt.Printf("failed to write response: %v\n", err)
+ }
+ })
+
+ // Create and start the test server
+ ea := &httptest.Server{
+ Listener: listener,
+ Config: &http.Server{
+ Handler: handler,
+ ReadHeaderTimeout: 5 * time.Second,
+ },
+ }
+ ea.Start()
+
+ fmt.Printf("Mock external adapter started at %s\n", ea.URL)
+ fmt.Printf("Initial value: %.4f, Floor: %.4f, Ceiling: %.4f\n", initialValue, floor, ceiling)
+ return ea
+}
+
+// adjustValue takes a starting value and randomly shifts it up or down by a step.
+// It ensures that the value stays within the specified bounds.
+func adjustValue(start, step, floor, ceiling float64) float64 {
+ // Randomly choose to increase or decrease the value
+ // #nosec G404
+ if rand.Intn(2) == 0 {
+ step = -step
+ }
+
+ // Apply the step to the starting value
+ newValue := start + step
+
+ // Ensure the value is within the bounds
+ if newValue < floor {
+ newValue = floor
+ } else if newValue > ceiling {
+ newValue = ceiling
+ }
+
+ return newValue
+}
diff --git a/core/scripts/keystone/src/external-adapter/Dockerfile b/core/scripts/keystone/src/external-adapter/Dockerfile
new file mode 100644
index 00000000000..714d9397c34
--- /dev/null
+++ b/core/scripts/keystone/src/external-adapter/Dockerfile
@@ -0,0 +1,5 @@
+FROM scratch
+
+COPY ./kiab-mock-external-adapter /
+
+ENTRYPOINT ["/kiab-mock-external-adapter"]
diff --git a/core/scripts/keystone/src/testdata/NodeList.txt b/core/scripts/keystone/src/testdata/NodeList.txt
deleted file mode 100644
index 6fb65dded69..00000000000
--- a/core/scripts/keystone/src/testdata/NodeList.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-https://local-node1 https://crib-henry-keystone-node1.main.stage.cldev.sh notreal@fakeemail.ch fj293fbBnlQ!f9vNs
-https://local-node2 https://crib-henry-keystone-node2.main.stage.cldev.sh notreal@fakeemail.ch fj293fbBnlQ!f9vNs
-https://local-node3 https://crib-henry-keystone-node3.main.stage.cldev.sh notreal@fakeemail.ch fj293fbBnlQ!f9vNs
-https://local-node4 https://crib-henry-keystone-node4.main.stage.cldev.sh notreal@fakeemail.ch fj293fbBnlQ!f9vNs
-https://local-node5 https://crib-henry-keystone-node5.main.stage.cldev.sh notreal@fakeemail.ch fj293fbBnlQ!f9vNs
diff --git a/core/scripts/keystone/src/testdata/PublicKeys.json b/core/scripts/keystone/src/testdata/PublicKeys.json
deleted file mode 100644
index b29e8290895..00000000000
--- a/core/scripts/keystone/src/testdata/PublicKeys.json
+++ /dev/null
@@ -1,57 +0,0 @@
-[
- {
- "AptosBundleID": "9bebfa953e7a7522746f72b4023308de36db626f3e0bcb9033407b8a183e8bfa",
- "AptosOnchainPublicKey": "ea551e503b93a1c9ae26262b4db8f66db4cbe5ddcb6039e29d2665a634d48e4a",
- "EthAddress": "0xF4e7e516146c8567F8E8be0ED1f1A92798628d35",
- "P2PPeerID": "12D3KooWNmhKZL1XW4Vv3rNjLXzJ6mqcVerihdijjGYuexPrFUFZ",
- "OCR2BundleID": "2f92c96da20fbe39c89e59516e3a7473254523316887394e406527c72071d3db",
- "OCR2OnchainPublicKey": "a2402db8e549f094ea31e1c0edd77623f4ca5b12",
- "OCR2OffchainPublicKey": "3ca9918cd2787de8f9aff91f220f30a5cc54c394f73e173b12c93368bd7072ad",
- "OCR2ConfigPublicKey": "19904debd03994fe9ea411cda7a6b2f01f20a3fe803df0fed67aaf00cc99113f",
- "CSAPublicKey": "csa_dbae6965bad0b0fa95ecc34a602eee1c0c570ddc29b56502e400d18574b8c3df"
- },
- {
- "AptosBundleID": "9bebfa953e7a7522746f72b4023308de36db626f3e0bcb9033407b8a183e8bfb",
- "AptosOnchainPublicKey": "ea551e503b93a1c9ae26262b4db8f66db4cbe5ddcb6039e29d2665a634d48e4b",
- "EthAddress": "0x8B60FDcc9CAC8ea476b31d17011CB204471431d9",
- "P2PPeerID": "12D3KooWFUjV73ZYkAMhS2cVwte3kXDWD8Ybyx3u9CEDHNoeEhBH",
- "OCR2BundleID": "b3df4d8748b67731a1112e8b45a764941974f5590c93672eebbc4f3504dd10ed",
- "OCR2OnchainPublicKey": "4af19c802b244d1d085492c3946391c965e10519",
- "OCR2OffchainPublicKey": "365b9e1c3c945fc3f51afb25772f0a5a1f1547935a4b5dc89c012f590709fefe",
- "OCR2ConfigPublicKey": "15ff12569d11b8ff9f17f8999ea928d03a439f3fb116661cbc4669a0a3192775",
- "CSAPublicKey": "csa_c5cc655a9c19b69626519c4a72c44a94a3675daeba9c16cc23e010a7a6dac1be"
- },
- {
- "AptosBundleID": "9bebfa953e7a7522746f72b4023308de36db626f3e0bcb9033407b8a183e8bfc",
- "AptosOnchainPublicKey": "ea551e503b93a1c9ae26262b4db8f66db4cbe5ddcb6039e29d2665a634d48e4c",
- "EthAddress": "0x6620F516F29979B214e2451498a057FDd3a0A85d",
- "P2PPeerID": "12D3KooWRTtH2WWrztD87Do1kXePSmGjyU4r7mZVWThmqTGgdbUC",
- "OCR2BundleID": "38459ae37f29f2c1fde0f25972a973322be8cada82acf43f464756836725be97",
- "OCR2OnchainPublicKey": "61925685d2b80b121537341d063c4e57b2f9323c",
- "OCR2OffchainPublicKey": "7fe2dbd9f9fb96f7dbbe0410e32d435ad67dae6c91410189fe5664cf3057ef10",
- "OCR2ConfigPublicKey": "2f02fd80b362e1c7acf91680fd48c062718233acd595a6ae7cbe434e118e6a4f",
- "CSAPublicKey": "csa_7407fc90c70895c0fb2bdf385e2e4918364bec1f7a74bad7fdf696bffafbcab8"
- },
- {
- "AptosBundleID": "9bebfa953e7a7522746f72b4023308de36db626f3e0bcb9033407b8a183e8bfd",
- "AptosOnchainPublicKey": "ea551e503b93a1c9ae26262b4db8f66db4cbe5ddcb6039e29d2665a634d48e4d",
- "EthAddress": "0xFeB61E22FCf4F9740c9D96b05199F195bd61A7c2",
- "P2PPeerID": "12D3KooWMTZnZtcVK4EJsjkKsV9qXNoNRSjT62CZi3tKkXGaCsGh",
- "OCR2BundleID": "b5dbc4c9da983cddde2e3226b85807eb7beaf818694a22576af4d80f352702ed",
- "OCR2OnchainPublicKey": "fd97efd53fc20acc098fcd746c04d8d7540d97e0",
- "OCR2OffchainPublicKey": "91b393bb5e6bd6fd9de23845bcd0e0d9b0dd28a1d65d3cfb1fce9f91bd3d8c19",
- "OCR2ConfigPublicKey": "09eb53924ff8b33a08b4eae2f3819015314ce6e8864ac4f86e97caafd4181506",
- "CSAPublicKey": "csa_ef55caf17eefc2a9d547b5a3978d396bd237c73af99cd849a4758701122e3cba"
- },
- {
- "AptosBundleID": "9bebfa953e7a7522746f72b4023308de36db626f3e0bcb9033407b8a183e8bfe",
- "AptosOnchainPublicKey": "ea551e503b93a1c9ae26262b4db8f66db4cbe5ddcb6039e29d2665a634d48e4e",
- "EthAddress": "0x882Fd04D78A7e7D386Dd5b550f19479E5494B0B2",
- "P2PPeerID": "12D3KooWRsM9yordRQDhLgbErH8WMMGz1bC1J4hR5gAGvMWu8goN",
- "OCR2BundleID": "260d5c1a618cdf5324509d7db95f5a117511864ebb9e1f709e8969339eb225af",
- "OCR2OnchainPublicKey": "a0b67dc5345a71d02b396147ae2cb75dda63cbe9",
- "OCR2OffchainPublicKey": "4f42ef42e5cc351dbbd79c29ef33af25c0250cac84837c1ff997bc111199d07e",
- "OCR2ConfigPublicKey": "3b90249731beb9e4f598371f0b96c3babf47bcc62121ebc9c195e3c33e4fd708",
- "CSAPublicKey": "csa_1b874ac2d54b966cec5a8358678ca6f030261aabf3372ce9dbea2d4eb9cdab3d"
- }
-]
\ No newline at end of file
diff --git a/core/scripts/keystone/src/testdata/node_sets.json b/core/scripts/keystone/src/testdata/node_sets.json
new file mode 100644
index 00000000000..b5502a0ed53
--- /dev/null
+++ b/core/scripts/keystone/src/testdata/node_sets.json
@@ -0,0 +1,298 @@
+{
+ "Workflow": {
+ "Name": "workflow",
+ "Prefix": "ks-wf-",
+ "Nodes": [
+ {
+ "URL": {
+ "scheme": "https",
+ "host": "crib-local-0-ks-wf-bt-node1.local",
+ "path": ""
+ },
+ "RemoteURL": {
+ "scheme": "https",
+ "host": "crib-local-0-ks-wf-bt-node1.local",
+ "path": ""
+ },
+ "ServiceName": "app-0-ks-wf-bt-node1",
+ "APILogin": "notreal@fakeemail.ch",
+ "APIPassword": "fj293fbBnlQ!f9vNs",
+ "KeystorePassword": "T.tLHkcmwePT/p,]sYuntjwHKAsrhm#4eRs4LuKHwvHejWYAC2JP4M8HimwgmbaZ"
+ },
+ {
+ "URL": {
+ "scheme": "https",
+ "host": "crib-local-0-ks-wf-node2.local",
+ "path": ""
+ },
+ "RemoteURL": {
+ "scheme": "https",
+ "host": "crib-local-0-ks-wf-node2.local",
+ "path": ""
+ },
+ "ServiceName": "app-0-ks-wf-node2",
+ "APILogin": "notreal@fakeemail.ch",
+ "APIPassword": "fj293fbBnlQ!f9vNs",
+ "KeystorePassword": "T.tLHkcmwePT/p,]sYuntjwHKAsrhm#4eRs4LuKHwvHejWYAC2JP4M8HimwgmbaZ"
+ },
+ {
+ "URL": {
+ "scheme": "https",
+ "host": "crib-local-0-ks-wf-node3.local",
+ "path": ""
+ },
+ "RemoteURL": {
+ "scheme": "https",
+ "host": "crib-local-0-ks-wf-node3.local",
+ "path": ""
+ },
+ "ServiceName": "app-0-ks-wf-node3",
+ "APILogin": "notreal@fakeemail.ch",
+ "APIPassword": "fj293fbBnlQ!f9vNs",
+ "KeystorePassword": "T.tLHkcmwePT/p,]sYuntjwHKAsrhm#4eRs4LuKHwvHejWYAC2JP4M8HimwgmbaZ"
+ },
+ {
+ "URL": {
+ "scheme": "https",
+ "host": "crib-local-0-ks-wf-node4.local",
+ "path": ""
+ },
+ "RemoteURL": {
+ "scheme": "https",
+ "host": "crib-local-0-ks-wf-node4.local",
+ "path": ""
+ },
+ "ServiceName": "app-0-ks-wf-node4",
+ "APILogin": "notreal@fakeemail.ch",
+ "APIPassword": "fj293fbBnlQ!f9vNs",
+ "KeystorePassword": "T.tLHkcmwePT/p,]sYuntjwHKAsrhm#4eRs4LuKHwvHejWYAC2JP4M8HimwgmbaZ"
+ },
+ {
+ "URL": {
+ "scheme": "https",
+ "host": "crib-local-0-ks-wf-node5.local",
+ "path": ""
+ },
+ "RemoteURL": {
+ "scheme": "https",
+ "host": "crib-local-0-ks-wf-node5.local",
+ "path": ""
+ },
+ "ServiceName": "app-0-ks-wf-node5",
+ "APILogin": "notreal@fakeemail.ch",
+ "APIPassword": "fj293fbBnlQ!f9vNs",
+ "KeystorePassword": "T.tLHkcmwePT/p,]sYuntjwHKAsrhm#4eRs4LuKHwvHejWYAC2JP4M8HimwgmbaZ"
+ }
+ ],
+ "NodeKeys": [
+ {
+ "AptosAccount": "38476e214b5c60e642019b38db9f06ce6c5a9bcb987d2bfbbbe750195aa7e964",
+ "EthAddress": "0x568C859E34F210a23847acE0D4960dB74f359dC4",
+ "P2PPeerID": "12D3KooWFSmZaLFF1nu3mzxPKj43F89WgVDqkpvwFUHBfMHSqpVq",
+ "CSAPublicKey": "981d781740ff79bb181a4c70390bd54e936f2d9211f5b20c708205b481a8efcc",
+ "OCR2BundleID": "782e4d14d0f53071ab7a45b9085eb99beed3350e7ab72d5edd4429169e5c87ef",
+ "OCR2OnchainPublicKey": "357ddc6c0fc6510ec67edbb9a63819dcb47f1506",
+ "OCR2OffchainPublicKey": "822488a7e4583eed41e5ab142dd6be721c2cc0f217ceee0912ff2db2a24e404c",
+ "OCR2ConfigPublicKey": "c92ae7ff9b1cef97bb875917456bc6b83df5f5a76ad00c914869c7068748f31a",
+ "AptosBundleID": "d4acd2c80860fd1e49363f08426e7e5efa7fcd57356a8aba408732e975d3e9a6",
+ "AptosOnchainPublicKey": "48b37d91fd2c2c784759021d421e7e6f98078b4343cf8cab378394aa357a49a2"
+ },
+ {
+ "AptosAccount": "bd4d7e53622621af04a0100db7720508c41f3dd5fe9e97dd57eb9673d82a385d",
+ "EthAddress": "0x75cf1355cC4Eb358feaBb9e269a4DAEeB6721DBB",
+ "P2PPeerID": "12D3KooWHhXyDmHB6D1UQosLXmhczw3zxB3DLYBuq9Unb4iCD4Sc",
+ "CSAPublicKey": "6a4723752c843c8d91e542af5373b3d123eca05b570a6e910f5d2f28737a26f6",
+ "OCR2BundleID": "20ccdc97afdf467465590115e3da4e5eb591bf5f43808e81a5d0807cd889b3c7",
+ "OCR2OnchainPublicKey": "321bc7af41a634375526006365a31bf32b4cfa7c",
+ "OCR2OffchainPublicKey": "a2b7f0b85be445e2c7317bdff74c41acd9c67b5a35cda94ae31da8a9ef886db2",
+ "OCR2ConfigPublicKey": "faa4cfefb226ae8e86480e019bd5bbd6405c26e22dcea40d2c6f01e583213e21",
+ "AptosBundleID": "ac364cec9fe7d9ea1035fc511e5b2f30900caa6e65ac0501168005d05129e088",
+ "AptosOnchainPublicKey": "4ca789105da974eec967758ad32b575741d6cb36c1bb3bcfd87b235502cc1753"
+ },
+ {
+ "AptosAccount": "9543634f4a73e4cfb284816b32d7ec6b7ac8d07b841f2c1f714750186cc28a5a",
+ "EthAddress": "0xc6dcE30f492CBD223b9946603192f22D86e783ca",
+ "P2PPeerID": "12D3KooWEWK8e627u6S5NuwXTgGLakGpn1vzQjyjp6Regu1pcpFC",
+ "CSAPublicKey": "a715467dd87ea210d685b82a32f30c781031df00c2c974dc5fad9159a7ba240c",
+ "OCR2BundleID": "c734a4bf01aabe8152b7d0df0b18111ce9b3fe1ef1bca1d6a580967c8e4afc2d",
+ "OCR2OnchainPublicKey": "5192c43a68efb7a698c0459ff8591a115da128ee",
+ "OCR2OffchainPublicKey": "5e68d07f82ea0bf7054560775c2919bc955dd7fa73b2a36391a4dc27cbb18fdb",
+ "OCR2ConfigPublicKey": "ebd66285a029f443277091bc4b191b13e21a9b806ce379584411277a265c8e5c",
+ "AptosBundleID": "f1dfc3d44ee349b4349f33ce4c0ec3716142e9be3ae3ba9276c616556f6430bb",
+ "AptosOnchainPublicKey": "169008927a60e6c03e99aac6fa268dabaf4d00e117419861d87836211267361b"
+ },
+ {
+ "AptosAccount": "bcd6fdce3fdcd060fed58fe13be522dc3fb0cff138b0f4f4460392f5e6d88728",
+ "EthAddress": "0x1289d00A6565Afcd6437B09548F6019EF49696d0",
+ "P2PPeerID": "12D3KooW9uJ981ocDxTJrPVxMEzPcS14WTJSU1YWH5otcpZSqkUd",
+ "CSAPublicKey": "9db8641f2067bfdf476e375060a0bd97c21da46d9f54c6ff4f990c6aef882478",
+ "OCR2BundleID": "129377e1aea4f628b2a3274e528a131175ace13e7cc062b048a34f5b4cf7b512",
+ "OCR2OnchainPublicKey": "ed613636925af2df6ed8332d95028eabcbe95a3f",
+ "OCR2OffchainPublicKey": "4eb3e2f1d324804d0adf5169bc187425d3e665c29cddf13bd57ec40ee207ce75",
+ "OCR2ConfigPublicKey": "effd7d3535e1b6596068085b3e19f9577a536aeacbdeea318cbd870ec678334d",
+ "AptosBundleID": "2e39d555ec0d1e8795167d72d2a53faa5c537762c144f8a569c601f6bcc95d1d",
+ "AptosOnchainPublicKey": "ce86b34de67249f92058f69e47961907ebbf8a71c12123f1d2a7cab4874f6365"
+ },
+ {
+ "AptosAccount": "6549063d427778024fc4230154753c1a30eac88a7a8eab1d36014a3db48c39b3",
+ "EthAddress": "0x4b92B0aaC39932B7302676F48e78FA91852DC0EE",
+ "P2PPeerID": "12D3KooWJJC2KgoP1oih7cky9B1wL12d5CBqWFKpdfQgfujmHGyz",
+ "CSAPublicKey": "8c8b473cc37664a21d548477cd268013256d1d70cd9a137bdfd99da7612a93e0",
+ "OCR2BundleID": "053f21bfd2bbdb65261308af2d0be48593229d644d8b9e3e5dbe36f85399ae6c",
+ "OCR2OnchainPublicKey": "53b5bbc0efa2e2d2770029bab5d5a647a260a72b",
+ "OCR2OffchainPublicKey": "eac02c66802acd9cd998b9b45c52b5b36837bfb829b2838cade040e0155c774a",
+ "OCR2ConfigPublicKey": "43b9d0c7cace05fd17426dad4386857025a71eb08205690dff5f76224e9c7f5c",
+ "AptosBundleID": "3de7ab03a5b6b7fcfd196c6101d9302c5e6a5221ebd82b1fd9afa9a6bc9b0445",
+ "AptosOnchainPublicKey": "f2cb4932d3ce8c10bf67c60d35372a5ff1578255e25c2a119c2dea70e919567a"
+ }
+ ]
+ },
+ "StreamsTrigger": {
+ "Name": "streams-trigger",
+ "Prefix": "ks-str-trig-",
+ "Nodes": [
+ {
+ "URL": {
+ "scheme": "https",
+ "host": "crib-local-1-ks-str-trig-bt-node1.local",
+ "path": ""
+ },
+ "RemoteURL": {
+ "scheme": "https",
+ "host": "crib-local-1-ks-str-trig-bt-node1.local",
+ "path": ""
+ },
+ "ServiceName": "app-1-ks-str-trig-bt-node1",
+ "APILogin": "notreal@fakeemail.ch",
+ "APIPassword": "fj293fbBnlQ!f9vNs",
+ "KeystorePassword": "T.tLHkcmwePT/p,]sYuntjwHKAsrhm#4eRs4LuKHwvHejWYAC2JP4M8HimwgmbaZ"
+ },
+ {
+ "URL": {
+ "scheme": "https",
+ "host": "crib-local-1-ks-str-trig-node2.local",
+ "path": ""
+ },
+ "RemoteURL": {
+ "scheme": "https",
+ "host": "crib-local-1-ks-str-trig-node2.local",
+ "path": ""
+ },
+ "ServiceName": "app-1-ks-str-trig-node2",
+ "APILogin": "notreal@fakeemail.ch",
+ "APIPassword": "fj293fbBnlQ!f9vNs",
+ "KeystorePassword": "T.tLHkcmwePT/p,]sYuntjwHKAsrhm#4eRs4LuKHwvHejWYAC2JP4M8HimwgmbaZ"
+ },
+ {
+ "URL": {
+ "scheme": "https",
+ "host": "crib-local-1-ks-str-trig-node3.local",
+ "path": ""
+ },
+ "RemoteURL": {
+ "scheme": "https",
+ "host": "crib-local-1-ks-str-trig-node3.local",
+ "path": ""
+ },
+ "ServiceName": "app-1-ks-str-trig-node3",
+ "APILogin": "notreal@fakeemail.ch",
+ "APIPassword": "fj293fbBnlQ!f9vNs",
+ "KeystorePassword": "T.tLHkcmwePT/p,]sYuntjwHKAsrhm#4eRs4LuKHwvHejWYAC2JP4M8HimwgmbaZ"
+ },
+ {
+ "URL": {
+ "scheme": "https",
+ "host": "crib-local-1-ks-str-trig-node4.local",
+ "path": ""
+ },
+ "RemoteURL": {
+ "scheme": "https",
+ "host": "crib-local-1-ks-str-trig-node4.local",
+ "path": ""
+ },
+ "ServiceName": "app-1-ks-str-trig-node4",
+ "APILogin": "notreal@fakeemail.ch",
+ "APIPassword": "fj293fbBnlQ!f9vNs",
+ "KeystorePassword": "T.tLHkcmwePT/p,]sYuntjwHKAsrhm#4eRs4LuKHwvHejWYAC2JP4M8HimwgmbaZ"
+ },
+ {
+ "URL": {
+ "scheme": "https",
+ "host": "crib-local-1-ks-str-trig-node5.local",
+ "path": ""
+ },
+ "RemoteURL": {
+ "scheme": "https",
+ "host": "crib-local-1-ks-str-trig-node5.local",
+ "path": ""
+ },
+ "ServiceName": "app-1-ks-str-trig-node5",
+ "APILogin": "notreal@fakeemail.ch",
+ "APIPassword": "fj293fbBnlQ!f9vNs",
+ "KeystorePassword": "T.tLHkcmwePT/p,]sYuntjwHKAsrhm#4eRs4LuKHwvHejWYAC2JP4M8HimwgmbaZ"
+ }
+ ],
+ "NodeKeys": [
+ {
+ "AptosAccount": "",
+ "EthAddress": "0xCE59C33dc0807F194ba2566e093398dc5e459840",
+ "P2PPeerID": "12D3KooWGQkR76gPL7Qt1aYYHtPdU65zG4h36efLAERMwudHqGK3",
+ "CSAPublicKey": "b3dcb60fcf807453c95628a1e7970b077b5e2bbefb0b159841c28dc1776574de",
+ "OCR2BundleID": "ca2e13222b556f34ae5bc9cea72e6e7ce8221bf76e2d8b3d91505facdbbd71d3",
+ "OCR2OnchainPublicKey": "a4938b0552e830b45d481cca9893f319259d365c",
+ "OCR2OffchainPublicKey": "221fe1f972f01da727dbd6842daf4d322f7cab5a7e93816688be7a52f4088b86",
+ "OCR2ConfigPublicKey": "1af18519dfc5a22db640f1f8095bafaaeb987ab4e3e7ec366dfaa92df9a6ee7b",
+ "AptosBundleID": "",
+ "AptosOnchainPublicKey": ""
+ },
+ {
+ "AptosAccount": "",
+ "EthAddress": "0x898D0206d3b3156b92bD499eDFBAfc476543A21F",
+ "P2PPeerID": "12D3KooWDN2jTmtrpZMpjFFuQdzxHyUecBE3zPLG4goaWG7H2iDa",
+ "CSAPublicKey": "bdf13ff3944d59a3e1ea5888f86c0bbfe5eb33e2140188516592bf245d080320",
+ "OCR2BundleID": "3b5d75124ef0f02efd46c08da4b67d36154eed680d7dafd360d976430fe11a7b",
+ "OCR2OnchainPublicKey": "3815f48818db64aa8d7b225e229a328826f3d1de",
+ "OCR2OffchainPublicKey": "ca6f4c20c00fb7af411060cfc226d61d11ce5e3532ebbd15786fe53c32244de3",
+ "OCR2ConfigPublicKey": "7b4e462c24d9076a8822bd4e2bdbd834fcc7898aabd9862dbcdb7df6686d2b41",
+ "AptosBundleID": "",
+ "AptosOnchainPublicKey": ""
+ },
+ {
+ "AptosAccount": "",
+ "EthAddress": "0xb26dD9CD1Fc4Df2F84170960E2a36ed4a5ac6bB7",
+ "P2PPeerID": "12D3KooWJTedkdgDmkAms4pEKDnXX7CXshkxwEcK6hWki519YEqF",
+ "CSAPublicKey": "e95ded4fc733eac43292dc24d8630101cf0c3f40c3764233a6321077eacd0b90",
+ "OCR2BundleID": "742b2a8a90e59aeb8bb35313d4078ef3f950a9e42a157b7ee9e9abd8c7d97d94",
+ "OCR2OnchainPublicKey": "57b41043e9a2b21329be48ccf72943af20b322ff",
+ "OCR2OffchainPublicKey": "0d90fc04c4c0439c184a06478ec1fed7cedfb799b303a6d68c046d90f077b5bd",
+ "OCR2ConfigPublicKey": "a73c070b60c9a175ac34cfd5c6c7884e73b5c8d43417be3f00bc43ac0fb67f39",
+ "AptosBundleID": "",
+ "AptosOnchainPublicKey": ""
+ },
+ {
+ "AptosAccount": "",
+ "EthAddress": "0x50b1bB407F0Ecd71416BfA8a1d703F6115112676",
+ "P2PPeerID": "12D3KooWS1i3x2r34vYCfYrz2ddWUVYtFGNaZvGNNxqzL4Rysd3V",
+ "CSAPublicKey": "46b50be4d72b03f1601ade056bc754f194d6418283065970d470f6f3243f0705",
+ "OCR2BundleID": "1232eb7cdb4145ec8b543b76f17fe59c69aa6df31c827a7553aea3a3d340c637",
+ "OCR2OnchainPublicKey": "dad1e5d6824d7b64df57e9ca3342e4caf66b2c91",
+ "OCR2OffchainPublicKey": "8a7e9833bf8a55435c82866dbe5f9a9bac63b9a93c8c55664dffe653ab4145a2",
+ "OCR2ConfigPublicKey": "48ce76ee5ddd8003ebbd10485a092f8bd237f0f855aca8aba5ccac78b593e62d",
+ "AptosBundleID": "",
+ "AptosOnchainPublicKey": ""
+ },
+ {
+ "AptosAccount": "",
+ "EthAddress": "0xa2340108BE2c563bB89462b464aCF3f88cCd1584",
+ "P2PPeerID": "12D3KooWLZFWAhTejyR7WwwQndgNGGiW3XcGKK6nNtWbhdgCG1rC",
+ "CSAPublicKey": "0837cd5a8544664eaf04f68347bdba4cb7ac6af34488f0a26c65b03fe223d5af",
+ "OCR2BundleID": "2fcbac5dd48e995772d85c47d2744b0df7b74b71d17001f283318cae43b96add",
+ "OCR2OnchainPublicKey": "469d3c0c484c6846be1176920f1cbdc8abb6f638",
+ "OCR2OffchainPublicKey": "21aa97506b74e3bfcbe6eb87f2a6add07898fecbddbcec2447832dc343395499",
+ "OCR2ConfigPublicKey": "a6b7e8ca4faf6122165928d82354de3f9334cdb47af058f6a983d11473c21b5f",
+ "AptosBundleID": "",
+ "AptosOnchainPublicKey": ""
+ }
+ ]
+ }
+}
diff --git a/core/scripts/keystone/templates/bootstrap.toml b/core/scripts/keystone/templates/bootstrap.toml
deleted file mode 100644
index cdd9065caba..00000000000
--- a/core/scripts/keystone/templates/bootstrap.toml
+++ /dev/null
@@ -1,9 +0,0 @@
-type = "bootstrap"
-schemaVersion = 1
-name = "Keystone boot"
-contractID = "{{ ocr_config_contract_address }}"
-relay = "evm"
-
-[relayConfig]
-chainID = "{{ chain_id }}"
-providerType = "ocr3-capability"
diff --git a/core/scripts/keystone/templates/crib-overrides.yaml b/core/scripts/keystone/templates/crib-overrides.yaml
deleted file mode 100644
index baeaa5fa1d9..00000000000
--- a/core/scripts/keystone/templates/crib-overrides.yaml
+++ /dev/null
@@ -1,41 +0,0 @@
-helm:
- values:
- chainlink:
- nodes:
- node1:
- image: ${runtime.images.app}
- overridesToml: |-
- [[EVM]]
- ChainID = '11155111'
- node2:
- image: ${runtime.images.app}
- overridesToml: |-
- [[EVM]]
- ChainID = '11155111'
- [EVM.Workflow]
- FromAddress = '{{ node_2_address }}'
- ForwarderAddress = '{{ forwarder_address }}'
- node3:
- image: ${runtime.images.app}
- overridesToml: |-
- [[EVM]]
- ChainID = '11155111'
- [EVM.Workflow]
- FromAddress = '{{ node_3_address }}'
- ForwarderAddress = '{{ forwarder_address }}'
- node4:
- image: ${runtime.images.app}
- overridesToml: |-
- [[EVM]]
- ChainID = '11155111'
- [EVM.Workflow]
- FromAddress = '{{ node_4_address }}'
- ForwarderAddress = '{{ forwarder_address }}'
- node5:
- image: ${runtime.images.app}
- overridesToml: |-
- [[EVM]]
- ChainID = '11155111'
- [EVM.Workflow]
- FromAddress = '{{ node_5_address }}'
- ForwarderAddress = '{{ forwarder_address }}'
diff --git a/core/scripts/keystone/templates/oracle.toml b/core/scripts/keystone/templates/oracle.toml
deleted file mode 100644
index 053baa2223b..00000000000
--- a/core/scripts/keystone/templates/oracle.toml
+++ /dev/null
@@ -1,27 +0,0 @@
-type = "offchainreporting2"
-schemaVersion = 1
-name = "Keystone"
-contractID = "{{ ocr_config_contract_address }}"
-ocrKeyBundleID = "{{ ocr_key_bundle_id }}"
-p2pv2Bootstrappers = [
- "{{ bootstrapper_p2p_id }}",
-]
-relay = "evm"
-pluginType = "plugin"
-transmitterID = "{{ transmitter_id }}"
-
-[relayConfig]
-chainID = "{{ chain_id }}"
-
-[pluginConfig]
-command = "chainlink-ocr3-capability"
-ocrVersion = 3
-pluginName = "ocr-capability"
-providerType = "ocr3-capability"
-telemetryType = "plugin"
-
-[onchainSigningStrategy]
-strategyName = 'multi-chain'
-[onchainSigningStrategy.config]
-evm = "{{ ocr_key_bundle_id }}"
-aptos = "{{ aptos_key_bundle_id }}"
diff --git a/core/services/job/models.go b/core/services/job/models.go
index 26d563c7ac8..63e521c5b3b 100644
--- a/core/services/job/models.go
+++ b/core/services/job/models.go
@@ -935,7 +935,7 @@ func (w *WorkflowSpec) SDKSpec(ctx context.Context) (sdk.WorkflowSpec, error) {
}
spec, rawSpec, cid, err := workflowSpecFactory.Spec(ctx, w.Workflow, w.Config)
if err != nil {
- return sdk.WorkflowSpec{}, err
+ return sdk.WorkflowSpec{}, fmt.Errorf("spec factory failed: %w", err)
}
w.sdkWorkflow = &spec
w.rawSpec = rawSpec
diff --git a/deployment/go.mod b/deployment/go.mod
index 2daefd78f11..872c3bd7815 100644
--- a/deployment/go.mod
+++ b/deployment/go.mod
@@ -9,7 +9,7 @@ replace github.com/smartcontractkit/chainlink/v2 => ../
// Using a separate inline `require` here to avoid surrounding line changes
// creating potential merge conflicts.
-require github.com/smartcontractkit/chainlink/v2 v2.0.0-20241206210521-125d98cdaf66
+require github.com/smartcontractkit/chainlink/v2 v2.0.0-20241212011003-de1a8f5e5b42
require (
github.com/Khan/genqlient v0.7.0
diff --git a/integration-tests/go.mod b/integration-tests/go.mod
index cc161d6b92a..f79a1e66a0d 100644
--- a/integration-tests/go.mod
+++ b/integration-tests/go.mod
@@ -12,8 +12,8 @@ replace github.com/smartcontractkit/chainlink/deployment => ../deployment
// Using a separate `require` here to avoid surrounding line changes
// creating potential merge conflicts.
require (
- github.com/smartcontractkit/chainlink/deployment v0.0.0-20241206210521-125d98cdaf66
- github.com/smartcontractkit/chainlink/v2 v2.0.0-20241206210521-125d98cdaf66
+ github.com/smartcontractkit/chainlink/deployment v0.0.0-20241212011003-de1a8f5e5b42
+ github.com/smartcontractkit/chainlink/v2 v2.0.0-20241212011003-de1a8f5e5b42
)
require (
diff --git a/integration-tests/load/go.mod b/integration-tests/load/go.mod
index fbf204e160d..ab7a58df9e9 100644
--- a/integration-tests/load/go.mod
+++ b/integration-tests/load/go.mod
@@ -14,9 +14,9 @@ replace github.com/smartcontractkit/chainlink/integration-tests => ../
// Using a separate `require` here to avoid surrounding line changes
// creating potential merge conflicts.
require (
- github.com/smartcontractkit/chainlink/deployment v0.0.0-20241206210521-125d98cdaf66
- github.com/smartcontractkit/chainlink/integration-tests v0.0.0-20241206210521-125d98cdaf66
- github.com/smartcontractkit/chainlink/v2 v2.0.0-20241206210521-125d98cdaf66
+ github.com/smartcontractkit/chainlink/deployment v0.0.0-20241212011003-de1a8f5e5b42
+ github.com/smartcontractkit/chainlink/integration-tests v0.0.0-20241212011003-de1a8f5e5b42
+ github.com/smartcontractkit/chainlink/v2 v2.0.0-20241212011003-de1a8f5e5b42
)
require (
diff --git a/shell.nix b/shell.nix
index 456bbd8a9c1..9860ae78cc5 100644
--- a/shell.nix
+++ b/shell.nix
@@ -111,11 +111,10 @@ in
echo "GORELEASER_KEY must be set in CRIB environments. You can find it in our 1p vault under 'goreleaser-pro-license'."
exit 1
fi
- ${if stdenv.isDarwin then "source ./nix-darwin-shell-hook.sh" else ""}
+ ${if stdenv.isDarwin then "source $(git rev-parse --show-toplevel)/nix-darwin-shell-hook.sh" else ""}
''}
'';
- GOROOT = "${go}/share/go";
PGDATA = "db";
CL_DATABASE_URL = "postgresql://chainlink:chainlink@localhost:5432/chainlink_test?sslmode=disable";
}
diff --git a/tools/goreleaser-config/go.mod b/tools/goreleaser-config/go.mod
index f46423b660d..d0e66514869 100644
--- a/tools/goreleaser-config/go.mod
+++ b/tools/goreleaser-config/go.mod
@@ -1,6 +1,6 @@
module github.com/smartcontractkit/chainlink/tools/goreleaser-config
-go 1.23.0
+go 1.22.8
require (
github.com/goreleaser/goreleaser-pro/v2 v2.3.2-pro
From 98adf6d1efaf9b8b15c889706fd6d604047275b1 Mon Sep 17 00:00:00 2001
From: Cedric
Date: Mon, 13 Jan 2025 11:33:05 +0000
Subject: [PATCH 35/91] [CRE-47] Add safeurl to protect against SSRF (#15885)
---
core/scripts/go.mod | 1 +
core/scripts/go.sum | 2 +
core/services/gateway/network/httpclient.go | 43 +++++-
.../gateway/network/httpclient_test.go | 143 ++++++++++++++++--
deployment/go.mod | 1 +
deployment/go.sum | 2 +
go.mod | 1 +
go.sum | 2 +
integration-tests/go.mod | 1 +
integration-tests/go.sum | 2 +
integration-tests/load/go.mod | 1 +
integration-tests/load/go.sum | 2 +
12 files changed, 182 insertions(+), 19 deletions(-)
diff --git a/core/scripts/go.mod b/core/scripts/go.mod
index 897962a6454..ed17147d67e 100644
--- a/core/scripts/go.mod
+++ b/core/scripts/go.mod
@@ -131,6 +131,7 @@ require (
github.com/docker/distribution v2.8.2+incompatible // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/dominikbraun/graph v0.23.0 // indirect
+ github.com/doyensec/safeurl v0.2.1 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/dvsekhvalnov/jose2go v1.7.0 // indirect
github.com/emicklei/go-restful/v3 v3.12.1 // indirect
diff --git a/core/scripts/go.sum b/core/scripts/go.sum
index 7cb29867b3a..5af14a2eaa9 100644
--- a/core/scripts/go.sum
+++ b/core/scripts/go.sum
@@ -370,6 +370,8 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/dominikbraun/graph v0.23.0 h1:TdZB4pPqCLFxYhdyMFb1TBdFxp8XLcJfTTBQucVPgCo=
github.com/dominikbraun/graph v0.23.0/go.mod h1:yOjYyogZLY1LSG9E33JWZJiq5k83Qy2C6POAuiViluc=
+github.com/doyensec/safeurl v0.2.1 h1:DY15JorEfQsnpBWhBkVQIkaif2jfxCC14PIuGDsjDVs=
+github.com/doyensec/safeurl v0.2.1/go.mod h1:wzSXqC/6Z410qHz23jtBWT+wQ8yTxcY0p8bZH/4EZIg=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
diff --git a/core/services/gateway/network/httpclient.go b/core/services/gateway/network/httpclient.go
index 52130c8d069..18f34118300 100644
--- a/core/services/gateway/network/httpclient.go
+++ b/core/services/gateway/network/httpclient.go
@@ -8,6 +8,8 @@ import (
"strings"
"time"
+ "github.com/doyensec/safeurl"
+
"github.com/smartcontractkit/chainlink-common/pkg/logger"
)
@@ -19,6 +21,28 @@ type HTTPClient interface {
type HTTPClientConfig struct {
MaxResponseBytes uint32
DefaultTimeout time.Duration
+ BlockedIPs []string
+ BlockedIPsCIDR []string
+ AllowedPorts []int
+ AllowedSchemes []string
+}
+
+var (
+ defaultAllowedPorts = []int{80, 443}
+ defaultAllowedSchemes = []string{"http", "https"}
+)
+
+func (c *HTTPClientConfig) ApplyDefaults() {
+ if len(c.AllowedPorts) == 0 {
+ c.AllowedPorts = defaultAllowedPorts
+ }
+
+ if len(c.AllowedSchemes) == 0 {
+ c.AllowedSchemes = defaultAllowedSchemes
+ }
+
+ // safeurl automatically blocks internal IPs so no need
+ // to set defaults here.
}
type HTTPRequest struct {
@@ -35,7 +59,7 @@ type HTTPResponse struct {
}
type httpClient struct {
- client *http.Client
+ client *safeurl.WrappedClient
config HTTPClientConfig
lggr logger.Logger
}
@@ -43,13 +67,20 @@ type httpClient struct {
// NewHTTPClient creates a new NewHTTPClient
// As of now, the client does not support TLS configuration but may be extended in the future
func NewHTTPClient(config HTTPClientConfig, lggr logger.Logger) (HTTPClient, error) {
+ config.ApplyDefaults()
+ safeConfig := safeurl.
+ GetConfigBuilder().
+ SetTimeout(config.DefaultTimeout).
+ SetAllowedPorts(config.AllowedPorts...).
+ SetAllowedSchemes(config.AllowedSchemes...).
+ SetBlockedIPs(config.BlockedIPs...).
+ SetBlockedIPsCIDR(config.BlockedIPsCIDR...).
+ Build()
+
return &httpClient{
config: config,
- client: &http.Client{
- Timeout: config.DefaultTimeout,
- Transport: http.DefaultTransport,
- },
- lggr: lggr,
+ client: safeurl.Client(safeConfig),
+ lggr: lggr,
}, nil
}
diff --git a/core/services/gateway/network/httpclient_test.go b/core/services/gateway/network/httpclient_test.go
index 2f4cc448ef5..f6e769066a7 100644
--- a/core/services/gateway/network/httpclient_test.go
+++ b/core/services/gateway/network/httpclient_test.go
@@ -1,16 +1,18 @@
-package network_test
+package network
import (
"context"
"net/http"
"net/http/httptest"
+ "net/url"
+ "strconv"
"testing"
"time"
+ "github.com/doyensec/safeurl"
"github.com/stretchr/testify/require"
"github.com/smartcontractkit/chainlink-common/pkg/logger"
- "github.com/smartcontractkit/chainlink/v2/core/services/gateway/network"
)
func TestHTTPClient_Send(t *testing.T) {
@@ -18,20 +20,18 @@ func TestHTTPClient_Send(t *testing.T) {
// Setup the test environment
lggr := logger.Test(t)
- config := network.HTTPClientConfig{
+ config := HTTPClientConfig{
MaxResponseBytes: 1024,
DefaultTimeout: 5 * time.Second,
}
- client, err := network.NewHTTPClient(config, lggr)
- require.NoError(t, err)
// Define test cases
tests := []struct {
name string
setupServer func() *httptest.Server
- request network.HTTPRequest
+ request HTTPRequest
expectedError error
- expectedResp *network.HTTPResponse
+ expectedResp *HTTPResponse
}{
{
name: "successful request",
@@ -42,7 +42,7 @@ func TestHTTPClient_Send(t *testing.T) {
require.NoError(t, err2)
}))
},
- request: network.HTTPRequest{
+ request: HTTPRequest{
Method: "GET",
URL: "/",
Headers: map[string]string{},
@@ -50,7 +50,7 @@ func TestHTTPClient_Send(t *testing.T) {
Timeout: 2 * time.Second,
},
expectedError: nil,
- expectedResp: &network.HTTPResponse{
+ expectedResp: &HTTPResponse{
StatusCode: http.StatusOK,
Headers: map[string]string{"Content-Length": "7"},
Body: []byte("success"),
@@ -66,7 +66,7 @@ func TestHTTPClient_Send(t *testing.T) {
require.NoError(t, err2)
}))
},
- request: network.HTTPRequest{
+ request: HTTPRequest{
Method: "GET",
URL: "/",
Headers: map[string]string{},
@@ -85,7 +85,7 @@ func TestHTTPClient_Send(t *testing.T) {
require.NoError(t, err2)
}))
},
- request: network.HTTPRequest{
+ request: HTTPRequest{
Method: "GET",
URL: "/",
Headers: map[string]string{},
@@ -93,7 +93,7 @@ func TestHTTPClient_Send(t *testing.T) {
Timeout: 2 * time.Second,
},
expectedError: nil,
- expectedResp: &network.HTTPResponse{
+ expectedResp: &HTTPResponse{
StatusCode: http.StatusInternalServerError,
Headers: map[string]string{"Content-Length": "5"},
Body: []byte("error"),
@@ -108,7 +108,7 @@ func TestHTTPClient_Send(t *testing.T) {
require.NoError(t, err2)
}))
},
- request: network.HTTPRequest{
+ request: HTTPRequest{
Method: "GET",
URL: "/",
Headers: map[string]string{},
@@ -126,6 +126,26 @@ func TestHTTPClient_Send(t *testing.T) {
server := tt.setupServer()
defer server.Close()
+ u, err := url.Parse(server.URL)
+ require.NoError(t, err)
+
+ hostname, port := u.Hostname(), u.Port()
+ portInt, err := strconv.ParseInt(port, 10, 32)
+ require.NoError(t, err)
+
+ safeConfig := safeurl.
+ GetConfigBuilder().
+ SetTimeout(config.DefaultTimeout).
+ SetAllowedIPs(hostname).
+ SetAllowedPorts(int(portInt)).
+ Build()
+
+ client := &httpClient{
+ config: config,
+ client: safeurl.Client(safeConfig),
+ lggr: lggr,
+ }
+
tt.request.URL = server.URL + tt.request.URL
resp, err := client.Send(context.Background(), tt.request)
@@ -145,3 +165,100 @@ func TestHTTPClient_Send(t *testing.T) {
})
}
}
+
+func TestHTTPClient_BlocksUnallowed(t *testing.T) {
+ t.Parallel()
+
+ // Setup the test environment
+ lggr := logger.Test(t)
+ config := HTTPClientConfig{
+ MaxResponseBytes: 1024,
+ DefaultTimeout: 5 * time.Second,
+ }
+
+ client, err := NewHTTPClient(config, lggr)
+ require.NoError(t, err)
+
+ // Define test cases
+ tests := []struct {
+ name string
+ request HTTPRequest
+ expectedError string
+ }{
+ {
+ name: "blocked port",
+ request: HTTPRequest{
+ Method: "GET",
+ URL: "http://127.0.0.1:8080",
+ Headers: map[string]string{},
+ Body: nil,
+ Timeout: 2 * time.Second,
+ },
+ expectedError: "port: 8080 not found in allowlist",
+ },
+ {
+ name: "blocked scheme",
+ request: HTTPRequest{
+ Method: "GET",
+ URL: "file://127.0.0.1",
+ Headers: map[string]string{},
+ Body: nil,
+ Timeout: 2 * time.Second,
+ },
+ expectedError: "scheme: file not found in allowlist",
+ },
+ {
+ name: "explicitly blocked IP",
+ request: HTTPRequest{
+ Method: "GET",
+ URL: "http://169.254.0.1",
+ Headers: map[string]string{},
+ Body: nil,
+ Timeout: 2 * time.Second,
+ },
+ expectedError: "ip: 169.254.0.1 not found in allowlist",
+ },
+ {
+ name: "explicitly blocked IP - internal network",
+ request: HTTPRequest{
+ Method: "GET",
+ URL: "http://169.254.0.1/endpoint",
+ Headers: map[string]string{},
+ Body: nil,
+ Timeout: 2 * time.Second,
+ },
+ expectedError: "ip: 169.254.0.1 not found in allowlist",
+ },
+ {
+ name: "explicitly blocked IP - localhost",
+ request: HTTPRequest{
+ Method: "GET",
+ URL: "http://127.0.0.1/endpoint",
+ Headers: map[string]string{},
+ Body: nil,
+ Timeout: 2 * time.Second,
+ },
+ expectedError: "ip: 127.0.0.1 not found in allowlist",
+ },
+ {
+ name: "explicitly blocked IP - current network",
+ request: HTTPRequest{
+ Method: "GET",
+ URL: "http://0.0.0.0/endpoint",
+ Headers: map[string]string{},
+ Body: nil,
+ Timeout: 2 * time.Second,
+ },
+ expectedError: "ip: 0.0.0.0 not found in allowlist",
+ },
+ }
+
+ // Execute test cases
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ _, err := client.Send(context.Background(), tt.request)
+ require.Error(t, err)
+ require.ErrorContains(t, err, tt.expectedError)
+ })
+ }
+}
diff --git a/deployment/go.mod b/deployment/go.mod
index 872c3bd7815..111539fc2b5 100644
--- a/deployment/go.mod
+++ b/deployment/go.mod
@@ -177,6 +177,7 @@ require (
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/dominikbraun/graph v0.23.0 // indirect
+ github.com/doyensec/safeurl v0.2.1 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/dvsekhvalnov/jose2go v1.7.0 // indirect
github.com/edsrzf/mmap-go v1.1.0 // indirect
diff --git a/deployment/go.sum b/deployment/go.sum
index 5e7d0ad9ced..fbb668cfe93 100644
--- a/deployment/go.sum
+++ b/deployment/go.sum
@@ -460,6 +460,8 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/dominikbraun/graph v0.23.0 h1:TdZB4pPqCLFxYhdyMFb1TBdFxp8XLcJfTTBQucVPgCo=
github.com/dominikbraun/graph v0.23.0/go.mod h1:yOjYyogZLY1LSG9E33JWZJiq5k83Qy2C6POAuiViluc=
+github.com/doyensec/safeurl v0.2.1 h1:DY15JorEfQsnpBWhBkVQIkaif2jfxCC14PIuGDsjDVs=
+github.com/doyensec/safeurl v0.2.1/go.mod h1:wzSXqC/6Z410qHz23jtBWT+wQ8yTxcY0p8bZH/4EZIg=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
diff --git a/go.mod b/go.mod
index 3d4c747034e..49dee03521f 100644
--- a/go.mod
+++ b/go.mod
@@ -19,6 +19,7 @@ require (
github.com/danielkov/gin-helmet v0.0.0-20171108135313-1387e224435e
github.com/deckarep/golang-set/v2 v2.6.0
github.com/dominikbraun/graph v0.23.0
+ github.com/doyensec/safeurl v0.2.1
github.com/esote/minmaxheap v1.0.0
github.com/ethereum/go-ethereum v1.14.11
github.com/fatih/color v1.17.0
diff --git a/go.sum b/go.sum
index 44fb07ef951..47caf1ff41c 100644
--- a/go.sum
+++ b/go.sum
@@ -358,6 +358,8 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/dominikbraun/graph v0.23.0 h1:TdZB4pPqCLFxYhdyMFb1TBdFxp8XLcJfTTBQucVPgCo=
github.com/dominikbraun/graph v0.23.0/go.mod h1:yOjYyogZLY1LSG9E33JWZJiq5k83Qy2C6POAuiViluc=
+github.com/doyensec/safeurl v0.2.1 h1:DY15JorEfQsnpBWhBkVQIkaif2jfxCC14PIuGDsjDVs=
+github.com/doyensec/safeurl v0.2.1/go.mod h1:wzSXqC/6Z410qHz23jtBWT+wQ8yTxcY0p8bZH/4EZIg=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
diff --git a/integration-tests/go.mod b/integration-tests/go.mod
index f79a1e66a0d..88eeb3e7fe6 100644
--- a/integration-tests/go.mod
+++ b/integration-tests/go.mod
@@ -196,6 +196,7 @@ require (
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/dominikbraun/graph v0.23.0 // indirect
+ github.com/doyensec/safeurl v0.2.1 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/dvsekhvalnov/jose2go v1.7.0 // indirect
github.com/edsrzf/mmap-go v1.1.0 // indirect
diff --git a/integration-tests/go.sum b/integration-tests/go.sum
index 52c0922d743..d50ef55c53c 100644
--- a/integration-tests/go.sum
+++ b/integration-tests/go.sum
@@ -464,6 +464,8 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/dominikbraun/graph v0.23.0 h1:TdZB4pPqCLFxYhdyMFb1TBdFxp8XLcJfTTBQucVPgCo=
github.com/dominikbraun/graph v0.23.0/go.mod h1:yOjYyogZLY1LSG9E33JWZJiq5k83Qy2C6POAuiViluc=
+github.com/doyensec/safeurl v0.2.1 h1:DY15JorEfQsnpBWhBkVQIkaif2jfxCC14PIuGDsjDVs=
+github.com/doyensec/safeurl v0.2.1/go.mod h1:wzSXqC/6Z410qHz23jtBWT+wQ8yTxcY0p8bZH/4EZIg=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
diff --git a/integration-tests/load/go.mod b/integration-tests/load/go.mod
index ab7a58df9e9..f6da8952689 100644
--- a/integration-tests/load/go.mod
+++ b/integration-tests/load/go.mod
@@ -166,6 +166,7 @@ require (
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/dominikbraun/graph v0.23.0 // indirect
+ github.com/doyensec/safeurl v0.2.1 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/dvsekhvalnov/jose2go v1.7.0 // indirect
github.com/edsrzf/mmap-go v1.1.0 // indirect
diff --git a/integration-tests/load/go.sum b/integration-tests/load/go.sum
index 0ed3bcbf368..4f490d077e3 100644
--- a/integration-tests/load/go.sum
+++ b/integration-tests/load/go.sum
@@ -456,6 +456,8 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/dominikbraun/graph v0.23.0 h1:TdZB4pPqCLFxYhdyMFb1TBdFxp8XLcJfTTBQucVPgCo=
github.com/dominikbraun/graph v0.23.0/go.mod h1:yOjYyogZLY1LSG9E33JWZJiq5k83Qy2C6POAuiViluc=
+github.com/doyensec/safeurl v0.2.1 h1:DY15JorEfQsnpBWhBkVQIkaif2jfxCC14PIuGDsjDVs=
+github.com/doyensec/safeurl v0.2.1/go.mod h1:wzSXqC/6Z410qHz23jtBWT+wQ8yTxcY0p8bZH/4EZIg=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
From 1251ee3a2c7bb6547b1b73a829da0fe04a49c5c5 Mon Sep 17 00:00:00 2001
From: george-dorin <120329946+george-dorin@users.noreply.github.com>
Date: Mon, 13 Jan 2025 19:28:02 +0200
Subject: [PATCH 36/91] Refactor secondary transmission (#15358)
* Refactor secondary transmission
* Add dual transmission ABI
* Update dual transmission ABI
* Update ABI
Add forwarder for secondary transmission
* Refactor dual transmitter
Add tests
* Add missing test file
* Add missing file
* Fix lint
* Fix lint
Fix test
* Pass txManagerOCR2 to ocr2FeedsDualTransmission
* Add dualTransmission meta validation
* Implement feedback
* Add ContractTransmitter helper function
* Add debug logging
* Add hint and refund validation
* rename file typo
---
core/services/job/job_orm_test.go | 93 ++++++++-
core/services/job/orm.go | 76 ++++++++
.../plugins/ccip/transmitter/transmitter.go | 6 +
core/services/ocrcommon/dual_transmitter.go | 134 +++++++++++++
core/services/ocrcommon/transmitter.go | 94 +++------
core/services/ocrcommon/transmitter_test.go | 1 +
.../relay/evm/contract_transmitter.go | 43 ++--
.../relay/evm/contract_transmitter_test.go | 4 +
.../relay/evm/dual_contract_transmitter.go | 184 ++++++++++++++++++
.../evm/dual_contract_transmitter_test.go | 164 ++++++++++++++++
core/services/relay/evm/evm.go | 31 ++-
11 files changed, 743 insertions(+), 87 deletions(-)
create mode 100644 core/services/ocrcommon/dual_transmitter.go
create mode 100644 core/services/relay/evm/dual_contract_transmitter.go
create mode 100644 core/services/relay/evm/dual_contract_transmitter_test.go
diff --git a/core/services/job/job_orm_test.go b/core/services/job/job_orm_test.go
index 7a310d6f791..6e7a0b2a034 100644
--- a/core/services/job/job_orm_test.go
+++ b/core/services/job/job_orm_test.go
@@ -2119,7 +2119,8 @@ func TestORM_CreateJob_OCR2_With_DualTransmission(t *testing.T) {
require.ErrorContains(t, jobORM.CreateJob(ctx, &jb), "invalid transmitter address in dual transmission config")
dtTransmitterAddress := cltest.MustGenerateRandomKey(t)
- completeDualTransmissionSpec := fmt.Sprintf(`
+
+ metaNotSliceDualTransmissionSpec := fmt.Sprintf(`
enableDualTransmission=true
[relayConfig.dualTransmission]
contractAddress = '0x613a38AC1659769640aaE063C651F48E0250454C'
@@ -2130,6 +2131,96 @@ func TestORM_CreateJob_OCR2_With_DualTransmission(t *testing.T) {
`,
dtTransmitterAddress.Address.String())
+ jb, err = ocr2validate.ValidatedOracleSpecToml(testutils.Context(t), config.OCR2(), config.Insecure(), baseJobSpec+metaNotSliceDualTransmissionSpec, nil)
+ require.NoError(t, err)
+ require.ErrorContains(t, jobORM.CreateJob(ctx, &jb), "dual transmission meta value key1 is not a slice")
+
+ hintNotValidDualTransmissionSpec := fmt.Sprintf(`
+ enableDualTransmission=true
+ [relayConfig.dualTransmission]
+ contractAddress = '0x613a38AC1659769640aaE063C651F48E0250454C'
+ transmitterAddress = '%s'
+ [relayConfig.dualTransmission.meta]
+ hint = ['some-invalid-hint']
+ key2 = ['val2','val3']
+ `,
+ dtTransmitterAddress.Address.String())
+
+ jb, err = ocr2validate.ValidatedOracleSpecToml(testutils.Context(t), config.OCR2(), config.Insecure(), baseJobSpec+hintNotValidDualTransmissionSpec, nil)
+ require.NoError(t, err)
+ require.ErrorContains(t, jobORM.CreateJob(ctx, &jb), "dual transmission meta.hint value some-invalid-hint should be one of the following [contract_address function_selector logs calldata default_logs]")
+
+ invalidRefundFormatDualTransmissionSpec := fmt.Sprintf(`
+ enableDualTransmission=true
+ [relayConfig.dualTransmission]
+ contractAddress = '0x613a38AC1659769640aaE063C651F48E0250454C'
+ transmitterAddress = '%s'
+ [relayConfig.dualTransmission.meta]
+ hint = ['calldata','logs']
+ refund = ['0x00']
+ `,
+ dtTransmitterAddress.Address.String())
+
+ jb, err = ocr2validate.ValidatedOracleSpecToml(testutils.Context(t), config.OCR2(), config.Insecure(), baseJobSpec+invalidRefundFormatDualTransmissionSpec, nil)
+ require.NoError(t, err)
+ require.ErrorContains(t, jobORM.CreateJob(ctx, &jb), "invalid dual transmission refund, format should be :")
+
+ invalidRefundAddressFormatDualTransmissionSpec := fmt.Sprintf(`
+ enableDualTransmission=true
+ [relayConfig.dualTransmission]
+ contractAddress = '0x613a38AC1659769640aaE063C651F48E0250454C'
+ transmitterAddress = '%s'
+ [relayConfig.dualTransmission.meta]
+ hint = ['calldata','logs']
+ refund = ['0x000:50']
+ `,
+ dtTransmitterAddress.Address.String())
+
+ jb, err = ocr2validate.ValidatedOracleSpecToml(testutils.Context(t), config.OCR2(), config.Insecure(), baseJobSpec+invalidRefundAddressFormatDualTransmissionSpec, nil)
+ require.NoError(t, err)
+ require.ErrorContains(t, jobORM.CreateJob(ctx, &jb), "invalid dual transmission refund address, 0x000 is not a valid address")
+
+ invalidRefundPercentFormatDualTransmissionSpec := fmt.Sprintf(`
+ enableDualTransmission=true
+ [relayConfig.dualTransmission]
+ contractAddress = '0x613a38AC1659769640aaE063C651F48E0250454C'
+ transmitterAddress = '%s'
+ [relayConfig.dualTransmission.meta]
+ hint = ['calldata','logs']
+ refund = ['0x0000000000000000000000000000000000000000:A']
+ `,
+ dtTransmitterAddress.Address.String())
+
+ jb, err = ocr2validate.ValidatedOracleSpecToml(testutils.Context(t), config.OCR2(), config.Insecure(), baseJobSpec+invalidRefundPercentFormatDualTransmissionSpec, nil)
+ require.NoError(t, err)
+ require.ErrorContains(t, jobORM.CreateJob(ctx, &jb), "invalid dual transmission refund percent, A is not a number")
+
+ invalidRefundPercentTotalFormatDualTransmissionSpec := fmt.Sprintf(`
+ enableDualTransmission=true
+ [relayConfig.dualTransmission]
+ contractAddress = '0x613a38AC1659769640aaE063C651F48E0250454C'
+ transmitterAddress = '%s'
+ [relayConfig.dualTransmission.meta]
+ hint = ['calldata','logs']
+ refund = ['0x0000000000000000000000000000000000000000:50','0x0000000000000000000000000000000000000001:50']
+ `,
+ dtTransmitterAddress.Address.String())
+
+ jb, err = ocr2validate.ValidatedOracleSpecToml(testutils.Context(t), config.OCR2(), config.Insecure(), baseJobSpec+invalidRefundPercentTotalFormatDualTransmissionSpec, nil)
+ require.NoError(t, err)
+ require.ErrorContains(t, jobORM.CreateJob(ctx, &jb), "invalid dual transmission refund percentages, total sum of percentages must be less than 100")
+
+ completeDualTransmissionSpec := fmt.Sprintf(`
+ enableDualTransmission=true
+ [relayConfig.dualTransmission]
+ contractAddress = '0x613a38AC1659769640aaE063C651F48E0250454C'
+ transmitterAddress = '%s'
+ [relayConfig.dualTransmission.meta]
+ key1 = ['val1']
+ key2 = ['val2','val3']
+ `,
+ dtTransmitterAddress.Address.String())
+
jb, err = ocr2validate.ValidatedOracleSpecToml(testutils.Context(t), config.OCR2(), config.Insecure(), baseJobSpec+completeDualTransmissionSpec, nil)
require.NoError(t, err)
diff --git a/core/services/job/orm.go b/core/services/job/orm.go
index fa64404ec3a..62cc2cd596a 100644
--- a/core/services/job/orm.go
+++ b/core/services/job/orm.go
@@ -7,6 +7,8 @@ import (
"fmt"
"reflect"
"slices"
+ "strconv"
+ "strings"
"time"
"github.com/ethereum/go-ethereum/common"
@@ -326,9 +328,19 @@ func (o *orm) CreateJob(ctx context.Context, jb *Job) error {
return errors.New("invalid transmitter address in dual transmission config")
}
+ rawMeta, ok := dualTransmissionConfig["meta"].(map[string]interface{})
+ if !ok {
+ return errors.New("invalid dual transmission meta")
+ }
+
+ if err = validateDualTransmissionMeta(rawMeta); err != nil {
+ return err
+ }
+
if err = validateKeyStoreMatchForRelay(ctx, jb.OCR2OracleSpec.Relay, tx.keyStore, dtTransmitterAddress); err != nil {
return errors.Wrap(err, "unknown dual transmission transmitterAddress")
}
+
}
specID, err := tx.insertOCR2OracleSpec(ctx, jb.OCR2OracleSpec)
@@ -1669,3 +1681,67 @@ func (r legacyGasStationServerSpecRow) toLegacyGasStationServerSpec() *LegacyGas
func (o *orm) loadJobSpecErrors(ctx context.Context, jb *Job) error {
return errors.Wrapf(o.ds.SelectContext(ctx, &jb.JobSpecErrors, `SELECT * FROM job_spec_errors WHERE job_id = $1`, jb.ID), "failed to load job spec errors for job %d", jb.ID)
}
+
+func validateDualTransmissionHint(vals []interface{}) error {
+ accepted := []string{"contract_address", "function_selector", "logs", "calldata", "default_logs"}
+ for _, v := range vals {
+ valString, ok := v.(string)
+ if !ok {
+ return errors.Errorf("dual transmission meta value %v is not a string", v)
+ }
+ if !slices.Contains(accepted, valString) {
+ return errors.Errorf("dual transmission meta.hint value %s should be one of the following %s", valString, accepted)
+ }
+ }
+ return nil
+}
+
+func validateDualTransmissionRefund(vals []interface{}) error {
+ totalRefund := 0
+ for _, v := range vals {
+ valString, ok := v.(string)
+ if !ok {
+ return errors.Errorf("dual transmission meta value %v is not a string", v)
+ }
+
+ s := strings.Split(valString, ":")
+ if len(s) != 2 {
+ return errors.New("invalid dual transmission refund, format should be :")
+ }
+ if !common.IsHexAddress(s[0]) {
+ return errors.Errorf("invalid dual transmission refund address, %s is not a valid address", s[0])
+ }
+ percent, err := strconv.Atoi(s[1])
+ if err != nil {
+ return errors.Errorf("invalid dual transmission refund percent, %s is not a number", s[1])
+ }
+ totalRefund += percent
+ }
+
+ if totalRefund >= 100 {
+ return errors.New("invalid dual transmission refund percentages, total sum of percentages must be less than 100")
+ }
+ return nil
+}
+
+func validateDualTransmissionMeta(meta map[string]interface{}) error {
+ for k, v := range meta {
+ metaFieldValues, ok := v.([]interface{})
+ if !ok {
+ return errors.Errorf("dual transmission meta value %s is not a slice", k)
+ }
+ if k == "hint" {
+ if err := validateDualTransmissionHint(metaFieldValues); err != nil {
+ return err
+ }
+ }
+
+ if k == "refund" {
+ if err := validateDualTransmissionRefund(metaFieldValues); err != nil {
+ return err
+ }
+ }
+ }
+
+ return nil
+}
diff --git a/core/services/ocr2/plugins/ccip/transmitter/transmitter.go b/core/services/ocr2/plugins/ccip/transmitter/transmitter.go
index 24123d03337..abb023a4251 100644
--- a/core/services/ocr2/plugins/ccip/transmitter/transmitter.go
+++ b/core/services/ocr2/plugins/ccip/transmitter/transmitter.go
@@ -26,6 +26,8 @@ type txManager interface {
type Transmitter interface {
CreateEthTransaction(ctx context.Context, toAddress common.Address, payload []byte, txMeta *txmgr.TxMeta) error
FromAddress(context.Context) common.Address
+
+ CreateSecondaryEthTransaction(context.Context, []byte, *txmgr.TxMeta) error
}
type transmitter struct {
@@ -141,3 +143,7 @@ func (t *transmitter) forwarderAddress() common.Address {
}
return t.effectiveTransmitterAddress
}
+
+func (t *transmitter) CreateSecondaryEthTransaction(ctx context.Context, bytes []byte, meta *txmgr.TxMeta) error {
+ return errors.New("trying to send a secondary transmission on a non dual transmitter")
+}
diff --git a/core/services/ocrcommon/dual_transmitter.go b/core/services/ocrcommon/dual_transmitter.go
new file mode 100644
index 00000000000..efc60978f19
--- /dev/null
+++ b/core/services/ocrcommon/dual_transmitter.go
@@ -0,0 +1,134 @@
+package ocrcommon
+
+import (
+ "context"
+ "math/big"
+ "net/url"
+ "slices"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/pkg/errors"
+
+ "github.com/smartcontractkit/chainlink/v2/common/txmgr/types"
+ "github.com/smartcontractkit/chainlink/v2/core/chains/evm/forwarders"
+ "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr"
+)
+
+type ocr2FeedsDualTransmission struct {
+ txm txManager
+ primaryFromAddresses []common.Address
+ gasLimit uint64
+ primaryEffectiveTransmitterAddress common.Address
+ strategy types.TxStrategy
+ checker txmgr.TransmitCheckerSpec
+ chainID *big.Int
+ keystore roundRobinKeystore
+
+ ocr2Aggregator common.Address
+ txManagerOCR2
+
+ secondaryContractAddress common.Address
+ secondaryFromAddress common.Address
+ secondaryMeta map[string][]string
+}
+
+func (t *ocr2FeedsDualTransmission) forwarderAddress(ctx context.Context, eoa, ocr2Aggregator common.Address) (common.Address, error) {
+ // If effectiveTransmitterAddress is in fromAddresses, then forwarders aren't set.
+ if slices.Contains(t.primaryFromAddresses, t.primaryEffectiveTransmitterAddress) {
+ return common.Address{}, nil
+ }
+
+ forwarderAddress, err := t.GetForwarderForEOAOCR2Feeds(ctx, eoa, ocr2Aggregator)
+ if err != nil {
+ return common.Address{}, err
+ }
+
+ // if forwarder address is in fromAddresses, then none of the forwarders are valid
+ if slices.Contains(t.primaryFromAddresses, forwarderAddress) {
+ forwarderAddress = common.Address{}
+ }
+
+ return forwarderAddress, nil
+}
+
+func (t *ocr2FeedsDualTransmission) CreateEthTransaction(ctx context.Context, toAddress common.Address, payload []byte, txMeta *txmgr.TxMeta) error {
+ roundRobinFromAddress, err := t.keystore.GetRoundRobinAddress(ctx, t.chainID, t.primaryFromAddresses...)
+ if err != nil {
+ return errors.Wrap(err, "skipped OCR transmission, error getting round-robin address")
+ }
+
+ forwarderAddress, err := t.forwarderAddress(ctx, roundRobinFromAddress, toAddress)
+ if err != nil {
+ return err
+ }
+
+ _, err = t.txm.CreateTransaction(ctx, txmgr.TxRequest{
+ FromAddress: roundRobinFromAddress,
+ ToAddress: toAddress,
+ EncodedPayload: payload,
+ FeeLimit: t.gasLimit,
+ ForwarderAddress: forwarderAddress,
+ Strategy: t.strategy,
+ Checker: t.checker,
+ Meta: txMeta,
+ })
+
+ return errors.Wrap(err, "skipped OCR transmission: skipped primary transmission")
+}
+
+func (t *ocr2FeedsDualTransmission) CreateSecondaryEthTransaction(ctx context.Context, payload []byte, txMeta *txmgr.TxMeta) error {
+ forwarderAddress, err := t.forwarderAddress(ctx, t.secondaryFromAddress, t.secondaryContractAddress)
+ if err != nil {
+ return err
+ }
+
+ if txMeta == nil {
+ txMeta = &txmgr.TxMeta{}
+ }
+
+ dualBroadcast := true
+ dualBroadcastParams := t.urlParams()
+
+ txMeta.DualBroadcast = &dualBroadcast
+ txMeta.DualBroadcastParams = &dualBroadcastParams
+
+ _, err = t.txm.CreateTransaction(ctx, txmgr.TxRequest{
+ FromAddress: t.secondaryFromAddress,
+ ToAddress: t.secondaryContractAddress,
+ EncodedPayload: payload,
+ ForwarderAddress: forwarderAddress,
+ FeeLimit: t.gasLimit,
+ Strategy: t.strategy,
+ Checker: t.checker,
+ Meta: txMeta,
+ })
+
+ return errors.Wrap(err, "skipped secondary transmission")
+}
+
+func (t *ocr2FeedsDualTransmission) FromAddress(ctx context.Context) common.Address {
+ roundRobinFromAddress, err := t.keystore.GetRoundRobinAddress(ctx, t.chainID, t.primaryFromAddresses...)
+ if err != nil {
+ return t.primaryEffectiveTransmitterAddress
+ }
+
+ forwarderAddress, err := t.GetForwarderForEOAOCR2Feeds(ctx, roundRobinFromAddress, t.ocr2Aggregator)
+ if errors.Is(err, forwarders.ErrForwarderForEOANotFound) {
+ // if there are no valid forwarders try to fallback to eoa
+ return roundRobinFromAddress
+ } else if err != nil {
+ return t.primaryEffectiveTransmitterAddress
+ }
+
+ return forwarderAddress
+}
+
+func (t *ocr2FeedsDualTransmission) urlParams() string {
+ values := url.Values{}
+ for k, v := range t.secondaryMeta {
+ for _, p := range v {
+ values.Add(k, p)
+ }
+ }
+ return values.Encode()
+}
diff --git a/core/services/ocrcommon/transmitter.go b/core/services/ocrcommon/transmitter.go
index 8121f3778d2..01200bbb7cb 100644
--- a/core/services/ocrcommon/transmitter.go
+++ b/core/services/ocrcommon/transmitter.go
@@ -2,10 +2,7 @@ package ocrcommon
import (
"context"
- errors2 "errors"
- "fmt"
"math/big"
- "net/url"
"slices"
"github.com/ethereum/go-ethereum/common"
@@ -28,6 +25,8 @@ type txManager interface {
type Transmitter interface {
CreateEthTransaction(ctx context.Context, toAddress common.Address, payload []byte, txMeta *txmgr.TxMeta) error
FromAddress(context.Context) common.Address
+
+ CreateSecondaryEthTransaction(context.Context, []byte, *txmgr.TxMeta) error
}
type transmitter struct {
@@ -99,7 +98,24 @@ func NewOCR2FeedsTransmitter(
return nil, errors.New("nil keystore provided to transmitter")
}
- baseTransmitter := &ocr2FeedsTransmitter{
+ if dualTransmissionConfig != nil {
+ return &ocr2FeedsDualTransmission{
+ ocr2Aggregator: ocr2Aggregator,
+ txm: txm,
+ txManagerOCR2: txm,
+ primaryFromAddresses: fromAddresses,
+ gasLimit: gasLimit,
+ primaryEffectiveTransmitterAddress: effectiveTransmitterAddress,
+ strategy: strategy,
+ checker: checker,
+ chainID: chainID,
+ keystore: keystore,
+ secondaryContractAddress: dualTransmissionConfig.ContractAddress,
+ secondaryFromAddress: dualTransmissionConfig.TransmitterAddress,
+ secondaryMeta: dualTransmissionConfig.Meta,
+ }, nil
+ }
+ return &ocr2FeedsTransmitter{
ocr2Aggregator: ocr2Aggregator,
txManagerOCR2: txm,
transmitter: transmitter{
@@ -112,17 +128,7 @@ func NewOCR2FeedsTransmitter(
chainID: chainID,
keystore: keystore,
},
- }
-
- if dualTransmissionConfig != nil {
- return &ocr2FeedsDualTransmission{
- transmitter: *baseTransmitter,
- secondaryContractAddress: dualTransmissionConfig.ContractAddress,
- secondaryFromAddress: dualTransmissionConfig.TransmitterAddress,
- secondaryMeta: dualTransmissionConfig.Meta,
- }, nil
- }
- return baseTransmitter, nil
+ }, nil
}
func (t *transmitter) CreateEthTransaction(ctx context.Context, toAddress common.Address, payload []byte, txMeta *txmgr.TxMeta) error {
@@ -144,6 +150,10 @@ func (t *transmitter) CreateEthTransaction(ctx context.Context, toAddress common
return errors.Wrap(err, "skipped OCR transmission")
}
+func (t *transmitter) CreateSecondaryEthTransaction(ctx context.Context, bytes []byte, meta *txmgr.TxMeta) error {
+ return errors.New("trying to send a secondary transmission on a non dual transmitter")
+}
+
func (t *transmitter) FromAddress(context.Context) common.Address {
return t.effectiveTransmitterAddress
}
@@ -219,56 +229,6 @@ func (t *ocr2FeedsTransmitter) forwarderAddress(ctx context.Context, eoa, ocr2Ag
return forwarderAddress, nil
}
-type ocr2FeedsDualTransmission struct {
- transmitter ocr2FeedsTransmitter
-
- secondaryContractAddress common.Address
- secondaryFromAddress common.Address
- secondaryMeta map[string][]string
-}
-
-func (t *ocr2FeedsDualTransmission) CreateEthTransaction(ctx context.Context, toAddress common.Address, payload []byte, txMeta *txmgr.TxMeta) error {
- // Primary transmission
- errPrimary := t.transmitter.CreateEthTransaction(ctx, toAddress, payload, txMeta)
- if errPrimary != nil {
- errPrimary = fmt.Errorf("skipped primary transmission: %w", errPrimary)
- }
-
- if txMeta == nil {
- txMeta = &txmgr.TxMeta{}
- }
-
- dualBroadcast := true
- dualBroadcastParams := t.urlParams()
-
- txMeta.DualBroadcast = &dualBroadcast
- txMeta.DualBroadcastParams = &dualBroadcastParams
-
- // Secondary transmission
- _, errSecondary := t.transmitter.txm.CreateTransaction(ctx, txmgr.TxRequest{
- FromAddress: t.secondaryFromAddress,
- ToAddress: t.secondaryContractAddress,
- EncodedPayload: payload,
- FeeLimit: t.transmitter.gasLimit,
- Strategy: t.transmitter.strategy,
- Checker: t.transmitter.checker,
- Meta: txMeta,
- })
-
- errSecondary = errors.Wrap(errSecondary, "skipped secondary transmission")
- return errors2.Join(errPrimary, errSecondary)
-}
-
-func (t *ocr2FeedsDualTransmission) FromAddress(ctx context.Context) common.Address {
- return t.transmitter.FromAddress(ctx)
-}
-
-func (t *ocr2FeedsDualTransmission) urlParams() string {
- values := url.Values{}
- for k, v := range t.secondaryMeta {
- for _, p := range v {
- values.Add(k, p)
- }
- }
- return values.Encode()
+func (t *ocr2FeedsTransmitter) CreateSecondaryEthTransaction(ctx context.Context, bytes []byte, meta *txmgr.TxMeta) error {
+ return errors.New("trying to send a secondary transmission on a non dual transmitter")
}
diff --git a/core/services/ocrcommon/transmitter_test.go b/core/services/ocrcommon/transmitter_test.go
index 5f434e59c62..bb91a87d517 100644
--- a/core/services/ocrcommon/transmitter_test.go
+++ b/core/services/ocrcommon/transmitter_test.go
@@ -241,6 +241,7 @@ func Test_DualTransmitter(t *testing.T) {
})).Twice().Return(txmgr.Tx{}, nil)
require.NoError(t, transmitter.CreateEthTransaction(testutils.Context(t), toAddress, payload, nil))
+ require.NoError(t, transmitter.CreateSecondaryEthTransaction(testutils.Context(t), payload, nil))
require.True(t, primaryTxConfirmed)
require.True(t, secondaryTxConfirmed)
diff --git a/core/services/relay/evm/contract_transmitter.go b/core/services/relay/evm/contract_transmitter.go
index aead9f6ca8a..248968ec053 100644
--- a/core/services/relay/evm/contract_transmitter.go
+++ b/core/services/relay/evm/contract_transmitter.go
@@ -34,6 +34,8 @@ var _ ContractTransmitter = &contractTransmitter{}
type Transmitter interface {
CreateEthTransaction(ctx context.Context, toAddress gethcommon.Address, payload []byte, txMeta *txmgr.TxMeta) error
FromAddress(context.Context) gethcommon.Address
+
+ CreateSecondaryEthTransaction(ctx context.Context, payload []byte, txMeta *txmgr.TxMeta) error
}
type ReportToEthMetadata func([]byte) (*txmgr.TxMeta, error)
@@ -42,28 +44,35 @@ func reportToEvmTxMetaNoop([]byte) (*txmgr.TxMeta, error) {
return nil, nil
}
-type OCRTransmitterOption func(transmitter *contractTransmitter)
+type transmitterOps struct {
+ reportToEvmTxMeta ReportToEthMetadata
+ excludeSigs bool
+ retention time.Duration
+ maxLogsKept uint64
+}
+
+type OCRTransmitterOption func(transmitter *transmitterOps)
func WithExcludeSignatures() OCRTransmitterOption {
- return func(ct *contractTransmitter) {
+ return func(ct *transmitterOps) {
ct.excludeSigs = true
}
}
func WithRetention(retention time.Duration) OCRTransmitterOption {
- return func(ct *contractTransmitter) {
+ return func(ct *transmitterOps) {
ct.retention = retention
}
}
func WithMaxLogsKept(maxLogsKept uint64) OCRTransmitterOption {
- return func(ct *contractTransmitter) {
+ return func(ct *transmitterOps) {
ct.maxLogsKept = maxLogsKept
}
}
func WithReportToEthMetadata(reportToEvmTxMeta ReportToEthMetadata) OCRTransmitterOption {
- return func(ct *contractTransmitter) {
+ return func(ct *transmitterOps) {
if reportToEvmTxMeta != nil {
ct.reportToEvmTxMeta = reportToEvmTxMeta
}
@@ -79,10 +88,7 @@ type contractTransmitter struct {
lp logpoller.LogPoller
lggr logger.Logger
// Options
- reportToEvmTxMeta ReportToEthMetadata
- excludeSigs bool
- retention time.Duration
- maxLogsKept uint64
+ transmitterOptions *transmitterOps
}
func transmitterFilterName(addr common.Address) string {
@@ -112,17 +118,19 @@ func NewOCRContractTransmitter(
lp: lp,
contractReader: caller,
lggr: logger.Named(lggr, "OCRContractTransmitter"),
- reportToEvmTxMeta: reportToEvmTxMetaNoop,
- excludeSigs: false,
- retention: 0,
- maxLogsKept: 0,
+ transmitterOptions: &transmitterOps{
+ reportToEvmTxMeta: reportToEvmTxMetaNoop,
+ excludeSigs: false,
+ retention: 0,
+ maxLogsKept: 0,
+ },
}
for _, opt := range opts {
- opt(newContractTransmitter)
+ opt(newContractTransmitter.transmitterOptions)
}
- err := lp.RegisterFilter(ctx, logpoller.Filter{Name: transmitterFilterName(address), EventSigs: []common.Hash{transmitted.ID}, Addresses: []common.Address{address}, Retention: newContractTransmitter.retention, MaxLogsKept: newContractTransmitter.maxLogsKept})
+ err := lp.RegisterFilter(ctx, logpoller.Filter{Name: transmitterFilterName(address), EventSigs: []common.Hash{transmitted.ID}, Addresses: []common.Address{address}, Retention: newContractTransmitter.transmitterOptions.retention, MaxLogsKept: newContractTransmitter.transmitterOptions.maxLogsKept})
if err != nil {
return nil, err
}
@@ -142,7 +150,7 @@ func (oc *contractTransmitter) Transmit(ctx context.Context, reportCtx ocrtypes.
if err != nil {
panic("eventTransmit(ev): error in SplitSignature")
}
- if !oc.excludeSigs {
+ if !oc.transmitterOptions.excludeSigs {
rs = append(rs, r)
ss = append(ss, s)
vs[i] = v
@@ -150,7 +158,7 @@ func (oc *contractTransmitter) Transmit(ctx context.Context, reportCtx ocrtypes.
}
rawReportCtx := evmutil.RawReportContext(reportCtx)
- txMeta, err := oc.reportToEvmTxMeta(report)
+ txMeta, err := oc.transmitterOptions.reportToEvmTxMeta(report)
if err != nil {
oc.lggr.Warnw("failed to generate tx metadata for report", "err", err)
}
@@ -163,6 +171,7 @@ func (oc *contractTransmitter) Transmit(ctx context.Context, reportCtx ocrtypes.
}
return errors.Wrap(oc.transmitter.CreateEthTransaction(ctx, oc.contractAddress, payload, txMeta), "failed to send Eth transaction")
+
}
type contractReader interface {
diff --git a/core/services/relay/evm/contract_transmitter_test.go b/core/services/relay/evm/contract_transmitter_test.go
index 5b9e1ae5981..6106389f326 100644
--- a/core/services/relay/evm/contract_transmitter_test.go
+++ b/core/services/relay/evm/contract_transmitter_test.go
@@ -34,6 +34,10 @@ type mockTransmitter struct {
lastPayload []byte
}
+func (m *mockTransmitter) CreateSecondaryEthTransaction(ctx context.Context, bytes []byte, meta *txmgr.TxMeta) error {
+ return nil
+}
+
func (m *mockTransmitter) CreateEthTransaction(ctx context.Context, toAddress gethcommon.Address, payload []byte, _ *txmgr.TxMeta) error {
m.lastPayload = payload
return nil
diff --git a/core/services/relay/evm/dual_contract_transmitter.go b/core/services/relay/evm/dual_contract_transmitter.go
new file mode 100644
index 00000000000..86d7d38be2e
--- /dev/null
+++ b/core/services/relay/evm/dual_contract_transmitter.go
@@ -0,0 +1,184 @@
+package evm
+
+import (
+ "context"
+ "database/sql"
+ "encoding/hex"
+ errors2 "errors"
+ "fmt"
+ "strings"
+ "sync"
+
+ "github.com/ethereum/go-ethereum/accounts/abi"
+ "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"
+)
+
+// TODO: Remove when new dual transmitter contracts are merged
+var dtABI = `[{"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":"transmitSecondary","outputs":[],"stateMutability":"nonpayable","type":"function"}]`
+
+var _ ContractTransmitter = (*dualContractTransmitter)(nil)
+
+type dualContractTransmitter struct {
+ contractAddress gethcommon.Address
+ contractABI abi.ABI
+ dualTransmissionABI abi.ABI
+ transmitter Transmitter
+ transmittedEventSig common.Hash
+ contractReader contractReader
+ lp logpoller.LogPoller
+ lggr logger.Logger
+ // Options
+ transmitterOptions *transmitterOps
+}
+
+var dualTransmissionABI = sync.OnceValue(func() abi.ABI {
+ dualTransmissionABI, err := abi.JSON(strings.NewReader(dtABI))
+ if err != nil {
+ panic(fmt.Errorf("failed to parse dualTransmission ABI: %w", err))
+ }
+ return dualTransmissionABI
+})
+
+func NewOCRDualContractTransmitter(
+ ctx context.Context,
+ address gethcommon.Address,
+ caller contractReader,
+ contractABI abi.ABI,
+ transmitter Transmitter,
+ lp logpoller.LogPoller,
+ lggr logger.Logger,
+ opts ...OCRTransmitterOption,
+) (*dualContractTransmitter, error) {
+ transmitted, ok := contractABI.Events["Transmitted"]
+ if !ok {
+ return nil, errors.New("invalid ABI, missing transmitted")
+ }
+
+ newContractTransmitter := &dualContractTransmitter{
+ contractAddress: address,
+ contractABI: contractABI,
+ dualTransmissionABI: dualTransmissionABI(),
+ transmitter: transmitter,
+ transmittedEventSig: transmitted.ID,
+ lp: lp,
+ contractReader: caller,
+ lggr: logger.Named(lggr, "OCRDualContractTransmitter"),
+ transmitterOptions: &transmitterOps{
+ reportToEvmTxMeta: reportToEvmTxMetaNoop,
+ excludeSigs: false,
+ retention: 0,
+ maxLogsKept: 0,
+ },
+ }
+
+ for _, opt := range opts {
+ opt(newContractTransmitter.transmitterOptions)
+ }
+
+ err := lp.RegisterFilter(ctx, logpoller.Filter{Name: transmitterFilterName(address), EventSigs: []common.Hash{transmitted.ID}, Addresses: []common.Address{address}, Retention: newContractTransmitter.transmitterOptions.retention, MaxLogsKept: newContractTransmitter.transmitterOptions.maxLogsKept})
+ if err != nil {
+ return nil, err
+ }
+ return newContractTransmitter, nil
+}
+
+// Transmit sends the report to the on-chain smart contract's Transmit method.
+func (oc *dualContractTransmitter) Transmit(ctx context.Context, reportCtx ocrtypes.ReportContext, report ocrtypes.Report, signatures []ocrtypes.AttributedOnchainSignature) error {
+ var rs [][32]byte
+ var ss [][32]byte
+ var vs [32]byte
+ if len(signatures) > 32 {
+ return errors.New("too many signatures, maximum is 32")
+ }
+ for i, as := range signatures {
+ r, s, v, err := evmutil.SplitSignature(as.Signature)
+ if err != nil {
+ panic("eventTransmit(ev): error in SplitSignature")
+ }
+ if !oc.transmitterOptions.excludeSigs {
+ rs = append(rs, r)
+ ss = append(ss, s)
+ vs[i] = v
+ }
+ }
+ rawReportCtx := evmutil.RawReportContext(reportCtx)
+
+ txMeta, err := oc.transmitterOptions.reportToEvmTxMeta(report)
+ if err != nil {
+ oc.lggr.Warnw("failed to generate tx metadata for report", "err", err)
+ }
+
+ oc.lggr.Debugw("Transmitting report", "report", hex.EncodeToString(report), "rawReportCtx", rawReportCtx, "contractAddress", oc.contractAddress, "txMeta", txMeta)
+
+ // Primary transmission
+ payload, err := oc.contractABI.Pack("transmit", rawReportCtx, []byte(report), rs, ss, vs)
+ if err != nil {
+ return errors.Wrap(err, "abi.Pack failed")
+ }
+
+ transactionErr := errors.Wrap(oc.transmitter.CreateEthTransaction(ctx, oc.contractAddress, payload, txMeta), "failed to send primary Eth transaction")
+
+ oc.lggr.Debugw("Created primary transaction", "error", transactionErr)
+
+ // Secondary transmission
+ secondaryPayload, err := oc.dualTransmissionABI.Pack("transmitSecondary", rawReportCtx, []byte(report), rs, ss, vs)
+ if err != nil {
+ return errors.Wrap(err, "transmitSecondary abi.Pack failed")
+ }
+
+ err = errors.Wrap(oc.transmitter.CreateSecondaryEthTransaction(ctx, secondaryPayload, txMeta), "failed to send secondary Eth transaction")
+ oc.lggr.Debugw("Created secondary transaction", "error", err)
+ return errors2.Join(transactionErr, err)
+}
+
+// LatestConfigDigestAndEpoch retrieves the latest config digest and epoch from the OCR2 contract.
+// It is plugin independent, in particular avoids use of the plugin specific generated evm wrappers
+// by using the evm client Call directly for functions/events that are part of OCR2Abstract.
+func (oc *dualContractTransmitter) LatestConfigDigestAndEpoch(ctx context.Context) (ocrtypes.ConfigDigest, uint32, error) {
+ latestConfigDigestAndEpoch, err := callContract(ctx, oc.contractAddress, oc.contractABI, "latestConfigDigestAndEpoch", nil, oc.contractReader)
+ if err != nil {
+ return ocrtypes.ConfigDigest{}, 0, err
+ }
+ // Panic on these conversions erroring, would mean a broken contract.
+ scanLogs := *abi.ConvertType(latestConfigDigestAndEpoch[0], new(bool)).(*bool)
+ configDigest := *abi.ConvertType(latestConfigDigestAndEpoch[1], new([32]byte)).(*[32]byte)
+ epoch := *abi.ConvertType(latestConfigDigestAndEpoch[2], new(uint32)).(*uint32)
+ if !scanLogs {
+ return configDigest, epoch, nil
+ }
+
+ // Otherwise, we have to scan for the logs.
+ latest, err := oc.lp.LatestLogByEventSigWithConfs(ctx, oc.transmittedEventSig, oc.contractAddress, 1)
+ if err != nil {
+ if errors.Is(err, sql.ErrNoRows) {
+ // No transmissions yet
+ return configDigest, 0, nil
+ }
+ return ocrtypes.ConfigDigest{}, 0, err
+ }
+ return parseTransmitted(latest.Data)
+}
+
+// FromAccount returns the account from which the transmitter invokes the contract
+func (oc *dualContractTransmitter) FromAccount(ctx context.Context) (ocrtypes.Account, error) {
+ return ocrtypes.Account(oc.transmitter.FromAddress(ctx).String()), nil
+}
+
+func (oc *dualContractTransmitter) Start(ctx context.Context) error { return nil }
+func (oc *dualContractTransmitter) Close() error { return nil }
+
+// Has no state/lifecycle so it's always healthy and ready
+func (oc *dualContractTransmitter) Ready() error { return nil }
+func (oc *dualContractTransmitter) HealthReport() map[string]error {
+ return map[string]error{oc.Name(): nil}
+}
+func (oc *dualContractTransmitter) Name() string { return oc.lggr.Name() }
diff --git a/core/services/relay/evm/dual_contract_transmitter_test.go b/core/services/relay/evm/dual_contract_transmitter_test.go
new file mode 100644
index 00000000000..a5110398159
--- /dev/null
+++ b/core/services/relay/evm/dual_contract_transmitter_test.go
@@ -0,0 +1,164 @@
+package evm
+
+import (
+ "context"
+ "encoding/hex"
+ "strings"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/accounts/abi"
+ gethcommon "github.com/ethereum/go-ethereum/common"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/mock"
+ "github.com/stretchr/testify/require"
+
+ "github.com/smartcontractkit/chainlink-common/pkg/utils/tests"
+ evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks"
+ "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller"
+ lpmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks"
+ "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr"
+ "github.com/smartcontractkit/chainlink/v2/core/internal/testutils"
+ "github.com/smartcontractkit/chainlink/v2/core/logger"
+
+ "github.com/smartcontractkit/libocr/gethwrappers2/ocr2aggregator"
+ "github.com/smartcontractkit/libocr/offchainreporting2plus/chains/evmutil"
+ "github.com/smartcontractkit/libocr/offchainreporting2plus/types"
+)
+
+var sampleAddressPrimary = testutils.NewAddress()
+
+type mockDualTransmitter struct {
+ lastPrimaryPayload []byte
+ lastSecondaryPayload []byte
+}
+
+func (*mockDualTransmitter) FromAddress(ctx context.Context) gethcommon.Address {
+ return sampleAddressPrimary
+}
+
+func (m *mockDualTransmitter) CreateEthTransaction(ctx context.Context, toAddress gethcommon.Address, payload []byte, _ *txmgr.TxMeta) error {
+ m.lastPrimaryPayload = payload
+ return nil
+}
+
+func (m *mockDualTransmitter) CreateSecondaryEthTransaction(ctx context.Context, payload []byte, _ *txmgr.TxMeta) error {
+ m.lastSecondaryPayload = payload
+ return nil
+}
+
+func TestDualContractTransmitter(t *testing.T) {
+ t.Parallel()
+
+ lggr := logger.TestLogger(t)
+ c := evmclimocks.NewClient(t)
+ lp := lpmocks.NewLogPoller(t)
+ ctx := testutils.Context(t)
+ // scanLogs = false
+ digestAndEpochDontScanLogs, _ := hex.DecodeString(
+ "0000000000000000000000000000000000000000000000000000000000000000" + // false
+ "000130da6b9315bd59af6b0a3f5463c0d0a39e92eaa34cbcbdbace7b3bfcc776" + // config digest
+ "0000000000000000000000000000000000000000000000000000000000000002") // epoch
+ c.On("CallContract", mock.Anything, mock.Anything, mock.Anything).Return(digestAndEpochDontScanLogs, nil).Once()
+ contractABI, _ := abi.JSON(strings.NewReader(ocr2aggregator.OCR2AggregatorMetaData.ABI))
+ lp.On("RegisterFilter", mock.Anything, mock.Anything).Return(nil)
+ reportToEvmTxMeta := func(b []byte) (*txmgr.TxMeta, error) {
+ return &txmgr.TxMeta{}, nil
+ }
+ ot, err := NewOCRDualContractTransmitter(ctx, gethcommon.Address{}, c, contractABI, &mockDualTransmitter{}, lp, lggr,
+ WithReportToEthMetadata(reportToEvmTxMeta))
+ require.NoError(t, err)
+ digest, epoch, err := ot.LatestConfigDigestAndEpoch(testutils.Context(t))
+ require.NoError(t, err)
+ assert.Equal(t, "000130da6b9315bd59af6b0a3f5463c0d0a39e92eaa34cbcbdbace7b3bfcc776", hex.EncodeToString(digest[:]))
+ assert.Equal(t, uint32(2), epoch)
+
+ // scanLogs = true
+ digestAndEpochScanLogs, _ := hex.DecodeString(
+ "0000000000000000000000000000000000000000000000000000000000000001" + // true
+ "000130da6b9315bd59af6b0a3f5463c0d0a39e92eaa34cbcbdbace7b3bfcc776" + // config digest
+ "0000000000000000000000000000000000000000000000000000000000000002") // epoch
+ c.On("CallContract", mock.Anything, mock.Anything, mock.Anything).Return(digestAndEpochScanLogs, nil).Once()
+ transmitted2, _ := hex.DecodeString(
+ "000130da6b9315bd59af6b0a3f5463c0d0a39e92eaa34cbcbdbace7b3bfcc777" + // config digest
+ "0000000000000000000000000000000000000000000000000000000000000002") // epoch
+ lp.On("LatestLogByEventSigWithConfs",
+ mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&logpoller.Log{
+ Data: transmitted2,
+ }, nil)
+ digest, epoch, err = ot.LatestConfigDigestAndEpoch(testutils.Context(t))
+ require.NoError(t, err)
+ assert.Equal(t, "000130da6b9315bd59af6b0a3f5463c0d0a39e92eaa34cbcbdbace7b3bfcc777", hex.EncodeToString(digest[:]))
+ assert.Equal(t, uint32(2), epoch)
+ from, err := ot.FromAccount(tests.Context(t))
+ require.NoError(t, err)
+ assert.Equal(t, sampleAddressPrimary.String(), string(from))
+}
+
+func Test_dualContractTransmitterNoSignatures_Transmit_SignaturesAreNotTransmitted(t *testing.T) {
+ t.Parallel()
+
+ transmitter := &mockDualTransmitter{}
+
+ ctx := context.Background()
+ reportCtx := types.ReportContext{}
+ report := types.Report{}
+ var signatures = oneSignature()
+
+ oc := createDualContractTransmitter(ctx, t, transmitter, WithExcludeSignatures())
+
+ err := oc.Transmit(ctx, reportCtx, report, signatures)
+ require.NoError(t, err)
+
+ var emptyRs [][32]byte
+ var emptySs [][32]byte
+ var emptyVs [32]byte
+ emptySignaturesPayloadPrimary, err := oc.contractABI.Pack("transmit", evmutil.RawReportContext(reportCtx), []byte(report), emptyRs, emptySs, emptyVs)
+ require.NoError(t, err)
+ emptySignaturesPayloadSecondary, err := oc.dualTransmissionABI.Pack("transmitSecondary", evmutil.RawReportContext(reportCtx), []byte(report), emptyRs, emptySs, emptyVs)
+ require.NoError(t, err)
+ require.Equal(t, transmitter.lastPrimaryPayload, emptySignaturesPayloadPrimary, "primary payload not equal")
+ require.Equal(t, transmitter.lastSecondaryPayload, emptySignaturesPayloadSecondary, "secondary payload not equal")
+}
+
+func Test_dualContractTransmitter_Transmit_SignaturesAreTransmitted(t *testing.T) {
+ t.Parallel()
+
+ transmitter := &mockDualTransmitter{}
+
+ ctx := context.Background()
+ reportCtx := types.ReportContext{}
+ report := types.Report{}
+ var signatures = oneSignature()
+
+ oc := createDualContractTransmitter(ctx, t, transmitter)
+
+ err := oc.Transmit(ctx, reportCtx, report, signatures)
+ require.NoError(t, err)
+
+ rs, ss, vs := signaturesAsPayload(t, signatures)
+ withSignaturesPayloadPrimary, err := oc.contractABI.Pack("transmit", evmutil.RawReportContext(reportCtx), []byte(report), rs, ss, vs)
+ require.NoError(t, err)
+ withSignaturesPayloadSecondary, err := oc.dualTransmissionABI.Pack("transmitSecondary", evmutil.RawReportContext(reportCtx), []byte(report), rs, ss, vs)
+ require.NoError(t, err)
+ require.Equal(t, transmitter.lastPrimaryPayload, withSignaturesPayloadPrimary, "primary payload not equal")
+ require.Equal(t, transmitter.lastSecondaryPayload, withSignaturesPayloadSecondary, "secondary payload not equal")
+}
+
+func createDualContractTransmitter(ctx context.Context, t *testing.T, transmitter Transmitter, ops ...OCRTransmitterOption) *dualContractTransmitter {
+ contractABI, err := abi.JSON(strings.NewReader(ocr2aggregator.OCR2AggregatorMetaData.ABI))
+ require.NoError(t, err)
+ lp := lpmocks.NewLogPoller(t)
+ lp.On("RegisterFilter", mock.Anything, mock.Anything).Return(nil)
+ contractTransmitter, err := NewOCRDualContractTransmitter(
+ ctx,
+ gethcommon.Address{},
+ evmclimocks.NewClient(t),
+ contractABI,
+ transmitter,
+ lp,
+ logger.TestLogger(t),
+ ops...,
+ )
+ require.NoError(t, err)
+ return contractTransmitter
+}
diff --git a/core/services/relay/evm/evm.go b/core/services/relay/evm/evm.go
index cf79d2aea59..0a418262988 100644
--- a/core/services/relay/evm/evm.go
+++ b/core/services/relay/evm/evm.go
@@ -938,6 +938,33 @@ func newOnChainContractTransmitter(ctx context.Context, lggr logger.Logger, rarg
)
}
+// newOnChainDualContractTransmitter creates a new dual contract transmitter.
+func newOnChainDualContractTransmitter(ctx context.Context, lggr logger.Logger, rargs commontypes.RelayArgs, ethKeystore keystore.Eth, configWatcher *configWatcher, opts configTransmitterOpts, transmissionContractABI abi.ABI, ocrTransmitterOpts ...OCRTransmitterOption) (*dualContractTransmitter, error) {
+ transmitter, err := generateTransmitterFrom(ctx, rargs, ethKeystore, configWatcher, opts)
+ if err != nil {
+ return nil, err
+ }
+
+ return NewOCRDualContractTransmitter(
+ ctx,
+ configWatcher.contractAddress,
+ configWatcher.chain.Client(),
+ transmissionContractABI,
+ transmitter,
+ configWatcher.chain.LogPoller(),
+ lggr,
+ ocrTransmitterOpts...,
+ )
+}
+
+func NewContractTransmitter(ctx context.Context, lggr logger.Logger, rargs commontypes.RelayArgs, ethKeystore keystore.Eth, configWatcher *configWatcher, opts configTransmitterOpts, transmissionContractABI abi.ABI, dualTransmission bool, ocrTransmitterOpts ...OCRTransmitterOption) (ContractTransmitter, error) {
+ if dualTransmission {
+ return newOnChainDualContractTransmitter(ctx, lggr, rargs, ethKeystore, configWatcher, opts, transmissionContractABI, ocrTransmitterOpts...)
+ }
+
+ return newOnChainContractTransmitter(ctx, lggr, rargs, ethKeystore, configWatcher, opts, transmissionContractABI, ocrTransmitterOpts...)
+}
+
func generateTransmitterFrom(ctx context.Context, rargs commontypes.RelayArgs, ethKeystore keystore.Eth, configWatcher *configWatcher, opts configTransmitterOpts) (Transmitter, error) {
var relayConfig types.RelayConfig
if err := json.Unmarshal(rargs.RelayConfig, &relayConfig); err != nil {
@@ -1075,7 +1102,7 @@ func (r *Relayer) NewMedianProvider(ctx context.Context, rargs commontypes.Relay
reportCodec := evmreportcodec.ReportCodec{}
- contractTransmitter, err := newOnChainContractTransmitter(ctx, lggr, rargs, r.ks.Eth(), configWatcher, configTransmitterOpts{}, OCR2AggregatorTransmissionContractABI)
+ ct, err := NewContractTransmitter(ctx, lggr, rargs, r.ks.Eth(), configWatcher, configTransmitterOpts{}, OCR2AggregatorTransmissionContractABI, relayConfig.EnableDualTransmission)
if err != nil {
return nil, err
}
@@ -1089,7 +1116,7 @@ func (r *Relayer) NewMedianProvider(ctx context.Context, rargs commontypes.Relay
lggr: lggr.Named("MedianProvider"),
configWatcher: configWatcher,
reportCodec: reportCodec,
- contractTransmitter: contractTransmitter,
+ contractTransmitter: ct,
medianContract: medianContract,
}
From cf7b1b047442a703c917f5306cb7369fb9663ddc Mon Sep 17 00:00:00 2001
From: Mateusz Sekara
Date: Mon, 13 Jan 2025 18:31:40 +0100
Subject: [PATCH 37/91] CCIP-4420 Mantle gas interceptor (#15900)
* Fixing gas estimator interceptor for Mantle
* Adding extra check for the address presence
---
.../interceptors/mantle/interceptor.go | 82 ----------------
.../interceptors/mantle/interceptor_test.go | 96 -------------------
core/services/relay/evm/evm.go | 8 +-
.../evm/interceptors/mantle/interceptor.go | 16 ++--
.../interceptors/mantle/interceptor_test.go | 11 ++-
5 files changed, 24 insertions(+), 189 deletions(-)
delete mode 100644 core/services/ocr2/plugins/ccip/estimatorconfig/interceptors/mantle/interceptor.go
delete mode 100644 core/services/ocr2/plugins/ccip/estimatorconfig/interceptors/mantle/interceptor_test.go
diff --git a/core/services/ocr2/plugins/ccip/estimatorconfig/interceptors/mantle/interceptor.go b/core/services/ocr2/plugins/ccip/estimatorconfig/interceptors/mantle/interceptor.go
deleted file mode 100644
index 359f32e13a5..00000000000
--- a/core/services/ocr2/plugins/ccip/estimatorconfig/interceptors/mantle/interceptor.go
+++ /dev/null
@@ -1,82 +0,0 @@
-package mantle
-
-import (
- "context"
- "fmt"
- "math/big"
- "strings"
- "time"
-
- "github.com/ethereum/go-ethereum"
- "github.com/ethereum/go-ethereum/accounts/abi"
- "github.com/ethereum/go-ethereum/common"
-
- evmClient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client"
-)
-
-const (
- // tokenRatio is not volatile and can be requested not often.
- tokenRatioUpdateInterval = 60 * time.Minute
- // tokenRatio fetches the tokenRatio used for Mantle's gas price calculation
- tokenRatioMethod = "tokenRatio"
- mantleTokenRatioAbiString = `[{"inputs":[],"name":"tokenRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]`
-)
-
-type Interceptor struct {
- client evmClient.Client
- tokenRatioCallData []byte
- tokenRatio *big.Int
- tokenRatioLastUpdate time.Time
-}
-
-func NewInterceptor(_ context.Context, client evmClient.Client) (*Interceptor, error) {
- // 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 Mantle; %w", tokenRatioMethod, err)
- }
- tokenRatioCallData, err := tokenRatioMethodAbi.Pack(tokenRatioMethod)
- if err != nil {
- return nil, fmt.Errorf("failed to parse GasPriceOracle %s() calldata for Mantle; %w", tokenRatioMethod, err)
- }
-
- return &Interceptor{
- client: client,
- tokenRatioCallData: tokenRatioCallData,
- }, nil
-}
-
-// ModifyGasPriceComponents returns modified gasPrice.
-func (i *Interceptor) ModifyGasPriceComponents(ctx context.Context, execGasPrice, daGasPrice *big.Int) (*big.Int, *big.Int, error) {
- if time.Since(i.tokenRatioLastUpdate) > tokenRatioUpdateInterval {
- mantleTokenRatio, err := i.getMantleTokenRatio(ctx)
- if err != nil {
- return nil, nil, err
- }
-
- i.tokenRatio, i.tokenRatioLastUpdate = mantleTokenRatio, time.Now()
- }
-
- // multiply daGasPrice and execGas price by tokenRatio
- newExecGasPrice := new(big.Int).Mul(execGasPrice, i.tokenRatio)
- newDAGasPrice := new(big.Int).Mul(daGasPrice, i.tokenRatio)
- return newExecGasPrice, newDAGasPrice, nil
-}
-
-// getMantleTokenRatio Requests and returns a token ratio value for the Mantle chain.
-func (i *Interceptor) getMantleTokenRatio(ctx context.Context) (*big.Int, error) {
- // FIXME it's removed from chainlink repo
- // precompile := common.HexToAddress(rollups.OPGasOracleAddress)
- precompile := common.Address{}
-
- tokenRatio, err := i.client.CallContract(ctx, ethereum.CallMsg{
- To: &precompile,
- Data: i.tokenRatioCallData,
- }, nil)
-
- if err != nil {
- return nil, fmt.Errorf("getMantleTokenRatio call failed: %w", err)
- }
-
- return new(big.Int).SetBytes(tokenRatio), nil
-}
diff --git a/core/services/ocr2/plugins/ccip/estimatorconfig/interceptors/mantle/interceptor_test.go b/core/services/ocr2/plugins/ccip/estimatorconfig/interceptors/mantle/interceptor_test.go
deleted file mode 100644
index 9134d996c27..00000000000
--- a/core/services/ocr2/plugins/ccip/estimatorconfig/interceptors/mantle/interceptor_test.go
+++ /dev/null
@@ -1,96 +0,0 @@
-package mantle
-
-import (
- "context"
- "math/big"
- "testing"
-
- "github.com/ethereum/go-ethereum"
- "github.com/ethereum/go-ethereum/common"
- "github.com/stretchr/testify/mock"
- "github.com/stretchr/testify/require"
-
- "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks"
-)
-
-func TestInterceptor(t *testing.T) {
- ethClient := mocks.NewClient(t)
- ctx := context.Background()
-
- tokenRatio := big.NewInt(10)
- interceptor, err := NewInterceptor(ctx, ethClient)
- require.NoError(t, err)
-
- // request token ratio
- ethClient.On("CallContract", ctx, mock.IsType(ethereum.CallMsg{}), mock.IsType(&big.Int{})).
- Return(common.BigToHash(tokenRatio).Bytes(), nil).Once()
-
- modExecGasPrice, modDAGasPrice, err := interceptor.ModifyGasPriceComponents(ctx, big.NewInt(1), big.NewInt(1))
- require.NoError(t, err)
- require.Equal(t, int64(10), modExecGasPrice.Int64())
- require.Equal(t, int64(10), modDAGasPrice.Int64())
-
- // second call won't invoke eth client
- modExecGasPrice, modDAGasPrice, err = interceptor.ModifyGasPriceComponents(ctx, big.NewInt(2), big.NewInt(1))
- require.NoError(t, err)
- require.Equal(t, int64(20), modExecGasPrice.Int64())
- require.Equal(t, int64(10), modDAGasPrice.Int64())
-}
-
-func TestModifyGasPriceComponents(t *testing.T) {
- testCases := map[string]struct {
- execGasPrice *big.Int
- daGasPrice *big.Int
- tokenRatio *big.Int
- resultExecGasPrice *big.Int
- resultDAGasPrice *big.Int
- }{
- "regular": {
- execGasPrice: big.NewInt(1000),
- daGasPrice: big.NewInt(100),
- resultExecGasPrice: big.NewInt(2000),
- resultDAGasPrice: big.NewInt(200),
- tokenRatio: big.NewInt(2),
- },
- "zero DAGasPrice": {
- execGasPrice: big.NewInt(1000),
- daGasPrice: big.NewInt(0),
- resultExecGasPrice: big.NewInt(5000),
- resultDAGasPrice: big.NewInt(0),
- tokenRatio: big.NewInt(5),
- },
- "zero ExecGasPrice": {
- execGasPrice: big.NewInt(0),
- daGasPrice: big.NewInt(10),
- resultExecGasPrice: big.NewInt(0),
- resultDAGasPrice: big.NewInt(50),
- tokenRatio: big.NewInt(5),
- },
- "zero token ratio": {
- execGasPrice: big.NewInt(15),
- daGasPrice: big.NewInt(10),
- resultExecGasPrice: big.NewInt(0),
- resultDAGasPrice: big.NewInt(0),
- tokenRatio: big.NewInt(0),
- },
- }
-
- for tcName, tc := range testCases {
- t.Run(tcName, func(t *testing.T) {
- ethClient := mocks.NewClient(t)
- ctx := context.Background()
-
- interceptor, err := NewInterceptor(ctx, ethClient)
- require.NoError(t, err)
-
- // request token ratio
- ethClient.On("CallContract", ctx, mock.IsType(ethereum.CallMsg{}), mock.IsType(&big.Int{})).
- Return(common.BigToHash(tc.tokenRatio).Bytes(), nil).Once()
-
- modExecGasPrice, modDAGasPrice, err := interceptor.ModifyGasPriceComponents(ctx, tc.execGasPrice, tc.daGasPrice)
- require.NoError(t, err)
- require.Equal(t, tc.resultExecGasPrice.Int64(), modExecGasPrice.Int64())
- require.Equal(t, tc.resultDAGasPrice.Int64(), modDAGasPrice.Int64())
- })
- }
-}
diff --git a/core/services/relay/evm/evm.go b/core/services/relay/evm/evm.go
index 0a418262988..ae77ce61e10 100644
--- a/core/services/relay/evm/evm.go
+++ b/core/services/relay/evm/evm.go
@@ -51,13 +51,13 @@ import (
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/ccipexec"
ccipconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/config"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/estimatorconfig"
- "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/estimatorconfig/interceptors/mantle"
cciptransmitter "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/transmitter"
lloconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/llo/config"
mercuryconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/mercury/config"
"github.com/smartcontractkit/chainlink/v2/core/services/ocrcommon"
"github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/codec"
"github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/functions"
+ "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/interceptors/mantle"
"github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury"
mercuryutils "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/utils"
reportcodecv1 "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/v1/reportcodec"
@@ -541,7 +541,8 @@ func (r *Relayer) NewCCIPCommitProvider(ctx context.Context, rargs commontypes.R
// to minimize misconfigure risk, might make sense to wire Mantle only when Commit + Mantle + IsSourceProvider
if r.chain.Config().EVM().ChainID().Uint64() == 5003 || r.chain.Config().EVM().ChainID().Uint64() == 5000 {
if commitPluginConfig.IsSourceProvider {
- mantleInterceptor, iErr := mantle.NewInterceptor(ctx, r.chain.Client())
+ oracleAddress := r.chain.Config().EVM().GasEstimator().DAOracle().OracleAddress()
+ mantleInterceptor, iErr := mantle.NewInterceptor(ctx, r.chain.Client(), oracleAddress)
if iErr != nil {
return nil, iErr
}
@@ -619,7 +620,8 @@ func (r *Relayer) NewCCIPExecProvider(ctx context.Context, rargs commontypes.Rel
// to minimize misconfigure risk, make sense to wire Mantle only when Exec + Mantle + !IsSourceProvider
if r.chain.Config().EVM().ChainID().Uint64() == 5003 || r.chain.Config().EVM().ChainID().Uint64() == 5000 {
if !execPluginConfig.IsSourceProvider {
- mantleInterceptor, iErr := mantle.NewInterceptor(ctx, r.chain.Client())
+ oracleAddress := r.chain.Config().EVM().GasEstimator().DAOracle().OracleAddress()
+ mantleInterceptor, iErr := mantle.NewInterceptor(ctx, r.chain.Client(), oracleAddress)
if iErr != nil {
return nil, iErr
}
diff --git a/core/services/relay/evm/interceptors/mantle/interceptor.go b/core/services/relay/evm/interceptors/mantle/interceptor.go
index c1520652d67..c6ace3e6cbc 100644
--- a/core/services/relay/evm/interceptors/mantle/interceptor.go
+++ b/core/services/relay/evm/interceptors/mantle/interceptor.go
@@ -2,6 +2,7 @@ package mantle
import (
"context"
+ "errors"
"fmt"
"math/big"
"strings"
@@ -9,9 +10,10 @@ import (
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
+ "github.com/ethereum/go-ethereum/common"
evmClient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client"
- "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils"
+ evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types"
)
const (
@@ -27,9 +29,13 @@ type Interceptor struct {
tokenRatioCallData []byte
tokenRatio *big.Int
tokenRatioLastUpdate time.Time
+ oracleAddress common.Address
}
-func NewInterceptor(_ context.Context, client evmClient.Client) (*Interceptor, error) {
+func NewInterceptor(_ context.Context, client evmClient.Client, address *evmtypes.EIP55Address) (*Interceptor, error) {
+ if address == nil {
+ return nil, errors.New("oracle address is missing")
+ }
// Encode calldata for tokenRatio method
tokenRatioMethodAbi, err := abi.JSON(strings.NewReader(mantleTokenRatioAbiString))
if err != nil {
@@ -43,6 +49,7 @@ func NewInterceptor(_ context.Context, client evmClient.Client) (*Interceptor, e
return &Interceptor{
client: client,
tokenRatioCallData: tokenRatioCallData,
+ oracleAddress: address.Address(),
}, nil
}
@@ -65,11 +72,8 @@ func (i *Interceptor) ModifyGasPriceComponents(ctx context.Context, execGasPrice
// getMantleTokenRatio Requests and returns a token ratio value for the Mantle chain.
func (i *Interceptor) getMantleTokenRatio(ctx context.Context) (*big.Int, error) {
- // FIXME it's removed from chainlink repo
- // precompile := common.HexToAddress(rollups.OPGasOracleAddress)
- precompile := utils.RandomAddress()
tokenRatio, err := i.client.CallContract(ctx, ethereum.CallMsg{
- To: &precompile,
+ To: &i.oracleAddress,
Data: i.tokenRatioCallData,
}, nil)
diff --git a/core/services/relay/evm/interceptors/mantle/interceptor_test.go b/core/services/relay/evm/interceptors/mantle/interceptor_test.go
index 9134d996c27..6fa485de487 100644
--- a/core/services/relay/evm/interceptors/mantle/interceptor_test.go
+++ b/core/services/relay/evm/interceptors/mantle/interceptor_test.go
@@ -11,14 +11,21 @@ import (
"github.com/stretchr/testify/require"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks"
+ "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types"
+ "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils"
)
+var randomAddress = types.MustEIP55Address(utils.RandomAddress().String())
+
func TestInterceptor(t *testing.T) {
ethClient := mocks.NewClient(t)
ctx := context.Background()
+ _, err := NewInterceptor(ctx, ethClient, nil)
+ require.Error(t, err)
+
tokenRatio := big.NewInt(10)
- interceptor, err := NewInterceptor(ctx, ethClient)
+ interceptor, err := NewInterceptor(ctx, ethClient, &randomAddress)
require.NoError(t, err)
// request token ratio
@@ -80,7 +87,7 @@ func TestModifyGasPriceComponents(t *testing.T) {
ethClient := mocks.NewClient(t)
ctx := context.Background()
- interceptor, err := NewInterceptor(ctx, ethClient)
+ interceptor, err := NewInterceptor(ctx, ethClient, &randomAddress)
require.NoError(t, err)
// request token ratio
From e0a5bb716cdf3a590760a76a578a7f790496ad73 Mon Sep 17 00:00:00 2001
From: Street <5597260+MStreet3@users.noreply.github.com>
Date: Mon, 13 Jan 2025 13:03:07 -0500
Subject: [PATCH 38/91] feat(keystone): add capabilities and add nops as mcms
proposals (#15887)
* feat(keystone/changeset): add capabilities and add nops as mcms proposals
* wip RegisterDons MCMS enabled
* chore(keystone): unit test adding capabilities mcms
* chore(keystone): add unit test register nops mcms
* fix(keystone): adds unit test on add nops
* chore(keystone): adds unit test for mcms add DONs
---
.../internal/append_node_capabilities.go | 2 +-
.../internal/capability_management.go | 55 ++---
.../keystone/changeset/internal/deploy.go | 117 +++++++++-
.../changeset/internal/deploy_test.go | 203 ++++++++++++++++++
.../internal/update_node_capabilities.go | 2 +-
5 files changed, 349 insertions(+), 30 deletions(-)
create mode 100644 deployment/keystone/changeset/internal/deploy_test.go
diff --git a/deployment/keystone/changeset/internal/append_node_capabilities.go b/deployment/keystone/changeset/internal/append_node_capabilities.go
index c6379fd24fd..a87688b5427 100644
--- a/deployment/keystone/changeset/internal/append_node_capabilities.go
+++ b/deployment/keystone/changeset/internal/append_node_capabilities.go
@@ -49,7 +49,7 @@ func AppendNodeCapabilitiesImpl(lggr logger.Logger, req *AppendNodeCapabilitiesR
for _, cap := range req.P2pToCapabilities {
capabilities = append(capabilities, cap...)
}
- op, err := AddCapabilities(lggr, req.ContractSet, req.Chain, capabilities, req.UseMCMS)
+ op, err := AddCapabilities(lggr, req.ContractSet.CapabilitiesRegistry, req.Chain, capabilities, req.UseMCMS)
if err != nil {
return nil, fmt.Errorf("failed to add capabilities: %w", err)
}
diff --git a/deployment/keystone/changeset/internal/capability_management.go b/deployment/keystone/changeset/internal/capability_management.go
index d85c3f0dfff..04f5a153482 100644
--- a/deployment/keystone/changeset/internal/capability_management.go
+++ b/deployment/keystone/changeset/internal/capability_management.go
@@ -13,44 +13,51 @@ import (
)
// AddCapabilities adds the capabilities to the registry
-func AddCapabilities(lggr logger.Logger, contractSet *ContractSet, chain deployment.Chain, capabilities []kcr.CapabilitiesRegistryCapability, useMCMS bool) (*timelock.BatchChainOperation, error) {
+func AddCapabilities(lggr logger.Logger, registry *kcr.CapabilitiesRegistry, chain deployment.Chain, capabilities []kcr.CapabilitiesRegistryCapability, useMCMS bool) (*timelock.BatchChainOperation, error) {
if len(capabilities) == 0 {
return nil, nil
}
- registry := contractSet.CapabilitiesRegistry
deduped, err := dedupCapabilities(registry, capabilities)
if err != nil {
return nil, fmt.Errorf("failed to dedup capabilities: %w", err)
}
- txOpts := chain.DeployerKey
+
if useMCMS {
- txOpts = deployment.SimTransactOpts()
+ return addCapabilitiesMCMSProposal(registry, deduped, chain)
}
- tx, err := registry.AddCapabilities(txOpts, deduped)
+
+ tx, err := registry.AddCapabilities(chain.DeployerKey, deduped)
if err != nil {
err = deployment.DecodeErr(kcr.CapabilitiesRegistryABI, err)
return nil, fmt.Errorf("failed to add capabilities: %w", err)
}
- var batch *timelock.BatchChainOperation
- if !useMCMS {
- _, err = chain.Confirm(tx)
- if err != nil {
- return nil, fmt.Errorf("failed to confirm AddCapabilities confirm transaction %s: %w", tx.Hash().String(), err)
- }
- lggr.Info("registered capabilities", "capabilities", deduped)
- } else {
- batch = &timelock.BatchChainOperation{
- ChainIdentifier: mcms.ChainIdentifier(chain.Selector),
- Batch: []mcms.Operation{
- {
- To: registry.Address(),
- Data: tx.Data(),
- Value: big.NewInt(0),
- },
- },
- }
+
+ _, err = chain.Confirm(tx)
+ if err != nil {
+ return nil, fmt.Errorf("failed to confirm AddCapabilities confirm transaction %s: %w", tx.Hash().String(), err)
}
- return batch, nil
+ lggr.Info("registered capabilities", "capabilities", deduped)
+
+ return nil, nil
+}
+
+func addCapabilitiesMCMSProposal(registry *kcr.CapabilitiesRegistry, caps []kcr.CapabilitiesRegistryCapability, regChain deployment.Chain) (*timelock.BatchChainOperation, error) {
+ tx, err := registry.AddCapabilities(deployment.SimTransactOpts(), caps)
+ if err != nil {
+ err = deployment.DecodeErr(kcr.CapabilitiesRegistryABI, err)
+ return nil, fmt.Errorf("failed to call AddNodeOperators: %w", err)
+ }
+
+ return &timelock.BatchChainOperation{
+ ChainIdentifier: mcms.ChainIdentifier(regChain.Selector),
+ Batch: []mcms.Operation{
+ {
+ To: registry.Address(),
+ Data: tx.Data(),
+ Value: big.NewInt(0),
+ },
+ },
+ }, nil
}
// CapabilityID returns a unique id for the capability
diff --git a/deployment/keystone/changeset/internal/deploy.go b/deployment/keystone/changeset/internal/deploy.go
index b52d269518d..e785b6dc161 100644
--- a/deployment/keystone/changeset/internal/deploy.go
+++ b/deployment/keystone/changeset/internal/deploy.go
@@ -416,10 +416,14 @@ type RegisterCapabilitiesRequest struct {
Env *deployment.Environment
RegistryChainSelector uint64
DonToCapabilities map[string][]capabilities_registry.CapabilitiesRegistryCapability
+
+ // if UseMCMS is true, a batch proposal is returned and no transaction is confirmed on chain.
+ UseMCMS bool
}
type RegisterCapabilitiesResponse struct {
DonToCapabilities map[string][]RegisteredCapability
+ Ops *timelock.BatchChainOperation
}
type RegisteredCapability struct {
@@ -492,11 +496,14 @@ func RegisterCapabilities(lggr logger.Logger, req RegisterCapabilitiesRequest) (
lggr.Warn("no new capabilities to register")
return &RegisterCapabilitiesResponse{}, nil
}
- // not using mcms; ignore proposals
- _, err = AddCapabilities(lggr, &contracts, registryChain, capabilities, false)
+
+ ops, err := AddCapabilities(lggr, contracts.CapabilitiesRegistry, registryChain, capabilities, req.UseMCMS)
if err != nil {
return nil, fmt.Errorf("failed to add capabilities: %w", err)
}
+
+ resp.Ops = ops
+
return resp, nil
}
@@ -504,10 +511,12 @@ type RegisterNOPSRequest struct {
Env *deployment.Environment
RegistryChainSelector uint64
Nops []capabilities_registry.CapabilitiesRegistryNodeOperator
+ UseMCMS bool
}
type RegisterNOPSResponse struct {
Nops []*capabilities_registry.CapabilitiesRegistryNodeOperatorAdded
+ Ops *timelock.BatchChainOperation
}
func RegisterNOPS(ctx context.Context, lggr logger.Logger, req RegisterNOPSRequest) (*RegisterNOPSResponse, error) {
@@ -545,11 +554,23 @@ func RegisterNOPS(ctx context.Context, lggr logger.Logger, req RegisterNOPSReque
lggr.Debug("no new node operators to register")
return resp, nil
}
+
+ if req.UseMCMS {
+ ops, err := addNOPsMCMSProposal(registry, nops, registryChain)
+ if err != nil {
+ return nil, fmt.Errorf("failed to generate proposal to add node operators: %w", err)
+ }
+
+ resp.Ops = ops
+ return resp, nil
+ }
+
tx, err := registry.AddNodeOperators(registryChain.DeployerKey, nops)
if err != nil {
err = deployment.DecodeErr(capabilities_registry.CapabilitiesRegistryABI, err)
return nil, fmt.Errorf("failed to call AddNodeOperators: %w", err)
}
+
// for some reason that i don't understand, the confirm must be called before the WaitMined or the latter will hang
// (at least for a simulated backend chain)
_, err = registryChain.Confirm(tx)
@@ -575,6 +596,25 @@ func RegisterNOPS(ctx context.Context, lggr logger.Logger, req RegisterNOPSReque
return resp, nil
}
+func addNOPsMCMSProposal(registry *capabilities_registry.CapabilitiesRegistry, nops []capabilities_registry.CapabilitiesRegistryNodeOperator, regChain deployment.Chain) (*timelock.BatchChainOperation, error) {
+ tx, err := registry.AddNodeOperators(deployment.SimTransactOpts(), nops)
+ if err != nil {
+ err = deployment.DecodeErr(capabilities_registry.CapabilitiesRegistryABI, err)
+ return nil, fmt.Errorf("failed to call AddNodeOperators: %w", err)
+ }
+
+ return &timelock.BatchChainOperation{
+ ChainIdentifier: mcms.ChainIdentifier(regChain.Selector),
+ Batch: []mcms.Operation{
+ {
+ To: registry.Address(),
+ Data: tx.Data(),
+ Value: big.NewInt(0),
+ },
+ },
+ }, nil
+}
+
func DefaultCapConfig(capType uint8, nNodes int) *capabilitiespb.CapabilityConfig {
switch capType {
// TODO: use the enum defined in ??
@@ -618,9 +658,11 @@ type RegisterNodesRequest struct {
DonToNodes map[string][]deployment.Node
DonToCapabilities map[string][]RegisteredCapability
Nops []*capabilities_registry.CapabilitiesRegistryNodeOperatorAdded
+ UseMCMS bool
}
type RegisterNodesResponse struct {
nodeIDToParams map[string]capabilities_registry.CapabilitiesRegistryNodeParams
+ Ops *timelock.BatchChainOperation
}
// registerNodes registers the nodes with the registry. it assumes that the deployer key in the Chain
@@ -725,6 +767,19 @@ func RegisterNodes(lggr logger.Logger, req *RegisterNodesRequest) (*RegisterNode
uniqueNodeParams = append(uniqueNodeParams, v)
}
lggr.Debugw("unique node params to add", "count", len(uniqueNodeParams), "params", uniqueNodeParams)
+
+ if req.UseMCMS {
+ ops, err := addNodesMCMSProposal(registry, uniqueNodeParams, registryChain)
+ if err != nil {
+ return nil, fmt.Errorf("failed to generate proposal to add nodes: %w", err)
+ }
+
+ return &RegisterNodesResponse{
+ nodeIDToParams: nodeIDToParams,
+ Ops: ops,
+ }, nil
+ }
+
tx, err := registry.AddNodes(registryChain.DeployerKey, uniqueNodeParams)
if err != nil {
err = deployment.DecodeErr(capabilities_registry.CapabilitiesRegistryABI, err)
@@ -758,11 +813,32 @@ func RegisterNodes(lggr logger.Logger, req *RegisterNodesRequest) (*RegisterNode
return nil, fmt.Errorf("failed to confirm AddNode confirm transaction %s: %w", tx.Hash().String(), err)
}
}
+
return &RegisterNodesResponse{
nodeIDToParams: nodeIDToParams,
}, nil
}
+// addNodesMCMSProposal generates a single call to AddNodes for all the node params at once.
+func addNodesMCMSProposal(registry *capabilities_registry.CapabilitiesRegistry, params []capabilities_registry.CapabilitiesRegistryNodeParams, regChain deployment.Chain) (*timelock.BatchChainOperation, error) {
+ tx, err := registry.AddNodes(deployment.SimTransactOpts(), params)
+ if err != nil {
+ err = deployment.DecodeErr(capabilities_registry.CapabilitiesRegistryABI, err)
+ return nil, fmt.Errorf("failed to simulate call to AddNodes: %w", err)
+ }
+
+ return &timelock.BatchChainOperation{
+ ChainIdentifier: mcms.ChainIdentifier(regChain.Selector),
+ Batch: []mcms.Operation{
+ {
+ To: registry.Address(),
+ Data: tx.Data(),
+ Value: big.NewInt(0),
+ },
+ },
+ }, nil
+}
+
type DONToRegister struct {
Name string
F uint8
@@ -776,10 +852,12 @@ type RegisterDonsRequest struct {
NodeIDToP2PID map[string][32]byte
DonToCapabilities map[string][]RegisteredCapability
DonsToRegister []DONToRegister
+ UseMCMS bool
}
type RegisterDonsResponse struct {
DonInfos map[string]capabilities_registry.CapabilitiesRegistryDONInfo
+ Ops *timelock.BatchChainOperation
}
func sortedHash(p2pids [][32]byte) string {
@@ -815,6 +893,7 @@ func RegisterDons(lggr logger.Logger, req RegisterDonsRequest) (*RegisterDonsRes
}
lggr.Infow("fetched existing DONs...", "len", len(donInfos), "lenByNodesHash", len(existingDONs))
+ mcmsOps := make([]mcms.Operation, 0, len(req.DonsToRegister))
for _, don := range req.DonsToRegister {
var p2pIds [][32]byte
for _, n := range don.Nodes {
@@ -832,10 +911,12 @@ func RegisterDons(lggr logger.Logger, req RegisterDonsRequest) (*RegisterDonsRes
p2pIdsToDon[p2pSortedHash] = don.Name
if _, ok := existingDONs[p2pSortedHash]; ok {
- lggr.Debugw("don already exists, ignoring", "don", don, "p2p sorted hash", p2pSortedHash)
+ lggr.Debugw("don already exists, ignoring", "don", don.Name, "p2p sorted hash", p2pSortedHash)
continue
}
+ lggr.Debugw("registering DON", "don", don.Name, "p2p sorted hash", p2pSortedHash)
+
caps, ok := req.DonToCapabilities[don.Name]
if !ok {
return nil, fmt.Errorf("capabilities not found for DON %s", don.Name)
@@ -858,11 +939,28 @@ func RegisterDons(lggr logger.Logger, req RegisterDonsRequest) (*RegisterDonsRes
})
}
- tx, err := registry.AddDON(registryChain.DeployerKey, p2pIds, cfgs, true, wfSupported, don.F)
+ txOpts := registryChain.DeployerKey
+ if req.UseMCMS {
+ txOpts = deployment.SimTransactOpts()
+ }
+
+ tx, err := registry.AddDON(txOpts, p2pIds, cfgs, true, wfSupported, don.F)
if err != nil {
err = deployment.DecodeErr(capabilities_registry.CapabilitiesRegistryABI, err)
return nil, fmt.Errorf("failed to call AddDON for don '%s' p2p2Id hash %s capability %v: %w", don.Name, p2pSortedHash, cfgs, err)
}
+
+ if req.UseMCMS {
+ lggr.Debugw("adding mcms op for DON", "don", don.Name)
+ op := mcms.Operation{
+ To: registry.Address(),
+ Data: tx.Data(),
+ Value: big.NewInt(0),
+ }
+ mcmsOps = append(mcmsOps, op)
+ continue
+ }
+
_, err = registryChain.Confirm(tx)
if err != nil {
return nil, fmt.Errorf("failed to confirm AddDON transaction %s for don %s: %w", tx.Hash().String(), don.Name, err)
@@ -870,6 +968,16 @@ func RegisterDons(lggr logger.Logger, req RegisterDonsRequest) (*RegisterDonsRes
lggr.Debugw("registered DON", "don", don.Name, "p2p sorted hash", p2pSortedHash, "cgs", cfgs, "wfSupported", wfSupported, "f", don.F)
addedDons++
}
+
+ if req.UseMCMS {
+ return &RegisterDonsResponse{
+ Ops: &timelock.BatchChainOperation{
+ ChainIdentifier: mcms.ChainIdentifier(registryChain.Selector),
+ Batch: mcmsOps,
+ },
+ }, nil
+ }
+
lggr.Debugf("Registered all DONs (new=%d), waiting for registry to update", addedDons)
// occasionally the registry does not return the expected number of DONS immediately after the txns above
@@ -906,6 +1014,7 @@ func RegisterDons(lggr logger.Logger, req RegisterDonsRequest) (*RegisterDonsRes
lggr.Debugw("adding don info to the response (keyed by DON name)", "don", donName)
resp.DonInfos[donName] = donInfos[i]
}
+
return &resp, nil
}
diff --git a/deployment/keystone/changeset/internal/deploy_test.go b/deployment/keystone/changeset/internal/deploy_test.go
new file mode 100644
index 00000000000..b8a98207ea0
--- /dev/null
+++ b/deployment/keystone/changeset/internal/deploy_test.go
@@ -0,0 +1,203 @@
+package internal_test
+
+import (
+ "context"
+ "testing"
+
+ "github.com/smartcontractkit/chainlink-common/pkg/logger"
+ "github.com/smartcontractkit/chainlink/deployment"
+ "github.com/smartcontractkit/chainlink/deployment/keystone/changeset/internal"
+ kstest "github.com/smartcontractkit/chainlink/deployment/keystone/changeset/internal/test"
+ kcr "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry_1_1_0"
+
+ "github.com/stretchr/testify/require"
+)
+
+func Test_RegisterNOPS(t *testing.T) {
+ var (
+ useMCMS bool
+ lggr = logger.Test(t)
+ setupResp = kstest.SetupTestRegistry(t, lggr, &kstest.SetupTestRegistryRequest{})
+ registry = setupResp.Registry
+ chain = setupResp.Chain
+ nops = make([]kcr.CapabilitiesRegistryNodeOperator, 0)
+ )
+ t.Run("success create add NOPs mcms proposal", func(t *testing.T) {
+ nops = append(nops, kcr.CapabilitiesRegistryNodeOperator{
+ Name: "test-nop",
+ })
+ useMCMS = true
+ env := &deployment.Environment{
+ Logger: lggr,
+ Chains: map[uint64]deployment.Chain{
+ chain.Selector: chain,
+ },
+ ExistingAddresses: deployment.NewMemoryAddressBookFromMap(map[uint64]map[string]deployment.TypeAndVersion{
+ chain.Selector: {
+ registry.Address().String(): deployment.TypeAndVersion{
+ Type: internal.CapabilitiesRegistry,
+ Version: deployment.Version1_0_0,
+ },
+ },
+ }),
+ }
+ resp, err := internal.RegisterNOPS(context.TODO(), lggr, internal.RegisterNOPSRequest{
+ Env: env,
+ RegistryChainSelector: chain.Selector,
+ Nops: nops,
+ UseMCMS: useMCMS,
+ })
+ require.NoError(t, err)
+ require.NotNil(t, resp.Ops)
+ require.Len(t, resp.Ops.Batch, 1)
+ })
+}
+
+func Test_AddCapabilities(t *testing.T) {
+ var (
+ useMCMS bool
+ lggr = logger.Test(t)
+ setupResp = kstest.SetupTestRegistry(t, lggr, &kstest.SetupTestRegistryRequest{})
+ registry = setupResp.Registry
+ chain = setupResp.Chain
+ capabilities = make([]kcr.CapabilitiesRegistryCapability, 0)
+ )
+
+ t.Run("successfully create mcms proposal", func(t *testing.T) {
+ useMCMS = true
+ capabilities = append(capabilities, kcr.CapabilitiesRegistryCapability{
+ LabelledName: "cap1",
+ Version: "1.0.0",
+ CapabilityType: 0,
+ })
+ ops, err := internal.AddCapabilities(lggr, registry, chain, capabilities, useMCMS)
+ require.NoError(t, err)
+ require.NotNil(t, ops)
+ require.Len(t, ops.Batch, 1)
+ })
+
+ t.Run("does nothing if no capabilities", func(t *testing.T) {
+ ops, err := internal.AddCapabilities(lggr, registry, chain, nil, useMCMS)
+ require.NoError(t, err)
+ require.Nil(t, ops)
+ })
+}
+
+func Test_RegisterNodes(t *testing.T) {
+ var (
+ useMCMS bool
+ lggr = logger.Test(t)
+ setupResp = kstest.SetupTestRegistry(t, lggr, &kstest.SetupTestRegistryRequest{})
+ registry = setupResp.Registry
+ chain = setupResp.Chain
+ )
+ t.Run("success create add nodes mcms proposal", func(t *testing.T) {
+ useMCMS = true
+ env := &deployment.Environment{
+ Logger: lggr,
+ Chains: map[uint64]deployment.Chain{
+ chain.Selector: chain,
+ },
+ ExistingAddresses: deployment.NewMemoryAddressBookFromMap(map[uint64]map[string]deployment.TypeAndVersion{
+ chain.Selector: {
+ registry.Address().String(): deployment.TypeAndVersion{
+ Type: internal.CapabilitiesRegistry,
+ Version: deployment.Version1_0_0,
+ },
+ },
+ }),
+ }
+ resp, err := internal.RegisterNodes(lggr, &internal.RegisterNodesRequest{
+ Env: env,
+ RegistryChainSelector: chain.Selector,
+ UseMCMS: useMCMS,
+ })
+ require.NoError(t, err)
+ require.NotNil(t, resp.Ops)
+ require.Len(t, resp.Ops.Batch, 1)
+ })
+}
+
+func Test_RegisterDons(t *testing.T) {
+ var (
+ useMCMS bool
+ lggr = logger.Test(t)
+ setupResp = kstest.SetupTestRegistry(t, lggr, &kstest.SetupTestRegistryRequest{})
+ registry = setupResp.Registry
+ chain = setupResp.Chain
+ )
+ t.Run("success create add DONs mcms proposal", func(t *testing.T) {
+ useMCMS = true
+ env := &deployment.Environment{
+ Logger: lggr,
+ Chains: map[uint64]deployment.Chain{
+ chain.Selector: chain,
+ },
+ ExistingAddresses: deployment.NewMemoryAddressBookFromMap(map[uint64]map[string]deployment.TypeAndVersion{
+ chain.Selector: {
+ registry.Address().String(): deployment.TypeAndVersion{
+ Type: internal.CapabilitiesRegistry,
+ Version: deployment.Version1_0_0,
+ },
+ },
+ }),
+ }
+ resp, err := internal.RegisterDons(lggr, internal.RegisterDonsRequest{
+ Env: env,
+ RegistryChainSelector: chain.Selector,
+ DonToCapabilities: map[string][]internal.RegisteredCapability{
+ "test-don": {},
+ },
+ DonsToRegister: []internal.DONToRegister{
+ {
+ Name: "test-don",
+ F: 2,
+ },
+ },
+ UseMCMS: useMCMS,
+ })
+ require.NoError(t, err)
+ require.NotNil(t, resp.Ops)
+ require.Len(t, resp.Ops.Batch, 1)
+ })
+
+ t.Run("success create add DONs mcms proposal with multiple DONs", func(t *testing.T) {
+ useMCMS = true
+ env := &deployment.Environment{
+ Logger: lggr,
+ Chains: map[uint64]deployment.Chain{
+ chain.Selector: chain,
+ },
+ ExistingAddresses: deployment.NewMemoryAddressBookFromMap(map[uint64]map[string]deployment.TypeAndVersion{
+ chain.Selector: {
+ registry.Address().String(): deployment.TypeAndVersion{
+ Type: internal.CapabilitiesRegistry,
+ Version: deployment.Version1_0_0,
+ },
+ },
+ }),
+ }
+ resp, err := internal.RegisterDons(lggr, internal.RegisterDonsRequest{
+ Env: env,
+ RegistryChainSelector: chain.Selector,
+ DonToCapabilities: map[string][]internal.RegisteredCapability{
+ "test-don-1": {},
+ "test-don-2": {},
+ },
+ DonsToRegister: []internal.DONToRegister{
+ {
+ Name: "test-don-1",
+ F: 2,
+ },
+ {
+ Name: "test-don-2",
+ F: 2,
+ },
+ },
+ UseMCMS: useMCMS,
+ })
+ require.NoError(t, err)
+ require.NotNil(t, resp.Ops)
+ require.Len(t, resp.Ops.Batch, 2)
+ })
+}
diff --git a/deployment/keystone/changeset/internal/update_node_capabilities.go b/deployment/keystone/changeset/internal/update_node_capabilities.go
index 23e3d66965c..34fb5346d5c 100644
--- a/deployment/keystone/changeset/internal/update_node_capabilities.go
+++ b/deployment/keystone/changeset/internal/update_node_capabilities.go
@@ -38,7 +38,7 @@ func UpdateNodeCapabilitiesImpl(lggr logger.Logger, req *UpdateNodeCapabilitiesI
for _, cap := range req.P2pToCapabilities {
capabilities = append(capabilities, cap...)
}
- op, err := AddCapabilities(lggr, req.ContractSet, req.Chain, capabilities, req.UseMCMS)
+ op, err := AddCapabilities(lggr, req.ContractSet.CapabilitiesRegistry, req.Chain, capabilities, req.UseMCMS)
if err != nil {
return nil, fmt.Errorf("failed to add capabilities: %w", err)
}
From d1a9f8be2f222ea30bdf7182aaa6428bfa605cf7 Mon Sep 17 00:00:00 2001
From: Nour Elrashidy
Date: Mon, 13 Jan 2025 19:04:48 +0100
Subject: [PATCH 39/91] Fix LBTC flakey test (#15906)
---
core/services/ocr2/plugins/ccip/tokendata/lbtc/lbtc_test.go | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/core/services/ocr2/plugins/ccip/tokendata/lbtc/lbtc_test.go b/core/services/ocr2/plugins/ccip/tokendata/lbtc/lbtc_test.go
index 375bb62aaeb..8a3f02c289e 100644
--- a/core/services/ocr2/plugins/ccip/tokendata/lbtc/lbtc_test.go
+++ b/core/services/ocr2/plugins/ccip/tokendata/lbtc/lbtc_test.go
@@ -285,7 +285,9 @@ func TestLBTCReader_rateLimiting(t *testing.T) {
},
}, 0)
- errorChan <- err
+ if err != nil {
+ errorChan <- err
+ }
}()
}
From 796357b17ca875ba80e157fc08b0da5db4ed1644 Mon Sep 17 00:00:00 2001
From: Graham Goh
Date: Tue, 14 Jan 2025 14:08:55 +1100
Subject: [PATCH 40/91] feat(tron): update operator ui (#15899)
context: https://github.com/smartcontractkit/operator-ui/pull/99
new version of operator ui supports creating tron chain config in the job distributor page.
JIRA: https://smartcontract-it.atlassian.net/browse/DPA-1333
---
.changeset/yellow-brooms-leave.md | 5 +++++
core/web/assets/index.html | 2 +-
core/web/assets/index.html.gz | Bin 420 -> 418 bytes
...61321d.js => main.5fed0c0f926fb5542ed6.js} | 4 ++--
....js.gz => main.5fed0c0f926fb5542ed6.js.gz} | Bin 1198171 -> 1198382 bytes
operator_ui/TAG | 2 +-
6 files changed, 9 insertions(+), 4 deletions(-)
create mode 100644 .changeset/yellow-brooms-leave.md
rename core/web/assets/{main.008c37495ba29761321d.js => main.5fed0c0f926fb5542ed6.js} (87%)
rename core/web/assets/{main.008c37495ba29761321d.js.gz => main.5fed0c0f926fb5542ed6.js.gz} (86%)
diff --git a/.changeset/yellow-brooms-leave.md b/.changeset/yellow-brooms-leave.md
new file mode 100644
index 00000000000..638751814bf
--- /dev/null
+++ b/.changeset/yellow-brooms-leave.md
@@ -0,0 +1,5 @@
+---
+"chainlink": minor
+---
+
+#updated feat:create tron chain config on operator ui
diff --git a/core/web/assets/index.html b/core/web/assets/index.html
index ae1aac9ede0..b878067d029 100644
--- a/core/web/assets/index.html
+++ b/core/web/assets/index.html
@@ -1 +1 @@
-Operator UI Chainlink You need to enable JavaScript to run this app.
\ No newline at end of file
+Operator UI Chainlink You need to enable JavaScript to run this app.
\ No newline at end of file
diff --git a/core/web/assets/index.html.gz b/core/web/assets/index.html.gz
index a65173c97c81ceb7da51eee8443c4d0395a6644d..56e0ad999c6f55527b5b01700b1cf597241ed4ac 100644
GIT binary patch
delta 407
zcmV;I0cifD1EK?eABzY8000000t1zjL2DZ^5QYDWD#)q!YU{?K3DV{eD5OvdZFA^x
zEX{hSl{AYq-o*dD?AkFD3Z)kbJ(%y!JdJ%5*voN5HAsq`J)a1nvk3*1AD~ner=Pxm
zTz}B!EN4jQ^hEePauKaOXTdB^KU0vw1)Rv^dU>oUlMuuqb@dR@y_*?A60!R-Vgccz
zo*u0^Nj6gcr3yx;>%c|)L6M*qf9>g|$tcDTKzU{~U+aAjiQ>_HHvqb=$7CZL80X$}wUc5dVujPxXra
z41$FqzJ2tZ>%Xw3hiR^!N7R4KYW`5CDZw7i%X-l+p+CGPbcwwBLZhgFZ9NvJmrJW!
zywe71MX4qM4YHcUYAEeD8P)MN^n&!~s
zSeo@tD`^&KyotYkS=%ub3Z)kbJ(%B{c^dmJu$TRaYLFB=dpZ$9XA=r2KR~G{PCtMD
zw7AjcBxgwI^hEeFauKaOXTdB^zfh3D1)Rv^;_$ejOhOPL7u7>Vx11S560!R_Vgccz
zo*%6_N;Xpdr3yx;>%c|)PLZG%e{Jct$tcDTKzU{~phNJxp`;Jfi+Uttizbpxm_WjbC41-d=5Bb$k7Bxn3=_},o=function(){},t.unstable_forceFrameRate=function(e){0>e||125M(o,n))void 0!==u&&0>M(u,o)?(e[r]=u,e[s]=n,r=s):(e[r]=o,e[a]=n,r=a);else if(void 0!==u&&0>M(u,n))e[r]=u,e[s]=n,r=s;else break a}}return t}return null}function M(e,t){var n=e.sortIndex-t.sortIndex;return 0!==n?n:e.id-t.id}var O=[],A=[],L=1,C=null,I=3,D=!1,N=!1,P=!1;function R(e){for(var t=x(A);null!==t;){if(null===t.callback)T(A);else if(t.startTime<=e)T(A),t.sortIndex=t.expirationTime,k(O,t);else break;t=x(A)}}function j(e){if(P=!1,R(e),!N){if(null!==x(O))N=!0,n(F);else{var t=x(A);null!==t&&r(j,t.startTime-e)}}}function F(e,n){N=!1,P&&(P=!1,i()),D=!0;var o=I;try{for(R(n),C=x(O);null!==C&&(!(C.expirationTime>n)||e&&!a());){var s=C.callback;if(null!==s){C.callback=null,I=C.priorityLevel;var u=s(C.expirationTime<=n);n=t.unstable_now(),"function"==typeof u?C.callback=u:C===x(O)&&T(O),R(n)}else T(O);C=x(O)}if(null!==C)var c=!0;else{var l=x(A);null!==l&&r(j,l.startTime-n),c=!1}return c}finally{C=null,I=o,D=!1}}function Y(e){switch(e){case 1:return -1;case 2:return 250;case 5:return 1073741823;case 4:return 1e4;default:return 5e3}}var B=o;t.unstable_ImmediatePriority=1,t.unstable_UserBlockingPriority=2,t.unstable_NormalPriority=3,t.unstable_IdlePriority=5,t.unstable_LowPriority=4,t.unstable_runWithPriority=function(e,t){switch(e){case 1:case 2:case 3:case 4:case 5:break;default:e=3}var n=I;I=e;try{return t()}finally{I=n}},t.unstable_next=function(e){switch(I){case 1:case 2:case 3:var t=3;break;default:t=I}var n=I;I=t;try{return e()}finally{I=n}},t.unstable_scheduleCallback=function(e,a,o){var s=t.unstable_now();if("object"==typeof o&&null!==o){var u=o.delay;u="number"==typeof u&&0s?(e.sortIndex=u,k(A,e),null===x(O)&&e===x(A)&&(P?i():P=!0,r(j,u-s))):(e.sortIndex=o,k(O,e),N||D||(N=!0,n(F))),e},t.unstable_cancelCallback=function(e){e.callback=null},t.unstable_wrapCallback=function(e){var t=I;return function(){var n=I;I=t;try{return e.apply(this,arguments)}finally{I=n}}},t.unstable_getCurrentPriorityLevel=function(){return I},t.unstable_shouldYield=function(){var e=t.unstable_now();R(e);var n=x(O);return n!==C&&null!==C&&null!==n&&null!==n.callback&&n.startTime<=e&&n.expirationTime>5==6?2:e>>4==14?3:e>>3==30?4:e>>6==2?-1:-2}function c(e,t,n){var r=t.length-1;if(r=0?(i>0&&(e.lastNeed=i-1),i):--r=0?(i>0&&(e.lastNeed=i-2),i):--r=0?(i>0&&(2===i?i=0:e.lastNeed=i-3),i):0}function l(e,t,n){if((192&t[0])!=128)return e.lastNeed=0,"�";if(e.lastNeed>1&&t.length>1){if((192&t[1])!=128)return e.lastNeed=1,"�";if(e.lastNeed>2&&t.length>2&&(192&t[2])!=128)return e.lastNeed=2,"�"}}function f(e){var t=this.lastTotal-this.lastNeed,n=l(this,e,t);return void 0!==n?n:this.lastNeed<=e.length?(e.copy(this.lastChar,t,0,this.lastNeed),this.lastChar.toString(this.encoding,0,this.lastTotal)):void(e.copy(this.lastChar,t,0,e.length),this.lastNeed-=e.length)}function d(e,t){var n=c(this,e,t);if(!this.lastNeed)return e.toString("utf8",t);this.lastTotal=n;var r=e.length-(n-this.lastNeed);return e.copy(this.lastChar,0,r),e.toString("utf8",t,r)}function h(e){var t=e&&e.length?this.write(e):"";return this.lastNeed?t+"�":t}function p(e,t){if((e.length-t)%2==0){var n=e.toString("utf16le",t);if(n){var r=n.charCodeAt(n.length-1);if(r>=55296&&r<=56319)return this.lastNeed=2,this.lastTotal=4,this.lastChar[0]=e[e.length-2],this.lastChar[1]=e[e.length-1],n.slice(0,-1)}return n}return this.lastNeed=1,this.lastTotal=2,this.lastChar[0]=e[e.length-1],e.toString("utf16le",t,e.length-1)}function b(e){var t=e&&e.length?this.write(e):"";if(this.lastNeed){var n=this.lastTotal-this.lastNeed;return t+this.lastChar.toString("utf16le",0,n)}return t}function m(e,t){var n=(e.length-t)%3;return 0===n?e.toString("base64",t):(this.lastNeed=3-n,this.lastTotal=3,1===n?this.lastChar[0]=e[e.length-1]:(this.lastChar[0]=e[e.length-2],this.lastChar[1]=e[e.length-1]),e.toString("base64",t,e.length-n))}function g(e){var t=e&&e.length?this.write(e):"";return this.lastNeed?t+this.lastChar.toString("base64",0,3-this.lastNeed):t}function v(e){return e.toString(this.encoding)}function y(e){return e&&e.length?this.write(e):""}t.s=s,s.prototype.write=function(e){var t,n;if(0===e.length)return"";if(this.lastNeed){if(void 0===(t=this.fillLast(e)))return"";n=this.lastNeed,this.lastNeed=0}else n=0;return n */ var r=n(48764),i=r.Buffer;function a(e,t){for(var n in e)t[n]=e[n]}function o(e,t,n){return i(e,t,n)}i.from&&i.alloc&&i.allocUnsafe&&i.allocUnsafeSlow?e.exports=r:(a(r,t),t.Buffer=o),o.prototype=Object.create(i.prototype),a(i,o),o.from=function(e,t,n){if("number"==typeof e)throw TypeError("Argument must not be a number");return i(e,t,n)},o.alloc=function(e,t,n){if("number"!=typeof e)throw TypeError("Argument must be a number");var r=i(e);return void 0!==t?"string"==typeof n?r.fill(t,n):r.fill(t):r.fill(0),r},o.allocUnsafe=function(e){if("number"!=typeof e)throw TypeError("Argument must be a number");return i(e)},o.allocUnsafeSlow=function(e){if("number"!=typeof e)throw TypeError("Argument must be a number");return r.SlowBuffer(e)}},93379(e,t,n){"use strict";var r,i,a=function(){return void 0===r&&(r=Boolean(window&&document&&document.all&&!window.atob)),r},o=(i={},function(e){if(void 0===i[e]){var t=document.querySelector(e);if(window.HTMLIFrameElement&&t instanceof window.HTMLIFrameElement)try{t=t.contentDocument.head}catch(n){t=null}i[e]=t}return i[e]}),s=[];function u(e){for(var t=-1,n=0;nAp});var r,i,a,o,s,u,c,l=n(67294),f=n.t(l,2),d=n(39814),h=n(5977),p=n(57209),b=n(32316),m=n(95880),g=n(17051),v=n(71381),y=n(81701),w=n(3022),_=n(60323),E=n(87591),S=n(25649),k=n(28902),x=n(71426),T=n(48884),M=n(94184),O=n.n(M),A=n(37703),L=n(73935),C=function(){if("undefined"!=typeof Map)return Map;function e(e,t){var n=-1;return e.some(function(e,r){return e[0]===t&&(n=r,!0)}),n}return function(){function t(){this.__entries__=[]}return Object.defineProperty(t.prototype,"size",{get:function(){return this.__entries__.length},enumerable:!0,configurable:!0}),t.prototype.get=function(t){var n=e(this.__entries__,t),r=this.__entries__[n];return r&&r[1]},t.prototype.set=function(t,n){var r=e(this.__entries__,t);~r?this.__entries__[r][1]=n:this.__entries__.push([t,n])},t.prototype.delete=function(t){var n=this.__entries__,r=e(n,t);~r&&n.splice(r,1)},t.prototype.has=function(t){return!!~e(this.__entries__,t)},t.prototype.clear=function(){this.__entries__.splice(0)},t.prototype.forEach=function(e,t){void 0===t&&(t=null);for(var n=0,r=this.__entries__;n0},e.prototype.connect_=function(){I&&!this.connected_&&(document.addEventListener("transitionend",this.onTransitionEnd_),window.addEventListener("resize",this.refresh),Y?(this.mutationsObserver_=new MutationObserver(this.refresh),this.mutationsObserver_.observe(document,{attributes:!0,childList:!0,characterData:!0,subtree:!0})):(document.addEventListener("DOMSubtreeModified",this.refresh),this.mutationEventsAdded_=!0),this.connected_=!0)},e.prototype.disconnect_=function(){I&&this.connected_&&(document.removeEventListener("transitionend",this.onTransitionEnd_),window.removeEventListener("resize",this.refresh),this.mutationsObserver_&&this.mutationsObserver_.disconnect(),this.mutationEventsAdded_&&document.removeEventListener("DOMSubtreeModified",this.refresh),this.mutationsObserver_=null,this.mutationEventsAdded_=!1,this.connected_=!1)},e.prototype.onTransitionEnd_=function(e){var t=e.propertyName,n=void 0===t?"":t;F.some(function(e){return!!~n.indexOf(e)})&&this.refresh()},e.getInstance=function(){return this.instance_||(this.instance_=new e),this.instance_},e.instance_=null,e}(),U=function(e,t){for(var n=0,r=Object.keys(t);n0},e}(),er="undefined"!=typeof WeakMap?new WeakMap:new C,ei=function(){function e(t){if(!(this instanceof e))throw TypeError("Cannot call a class as a function.");if(!arguments.length)throw TypeError("1 argument required, but only 0 present.");var n=B.getInstance(),r=new en(t,n,this);er.set(this,r)}return e}();["observe","unobserve","disconnect"].forEach(function(e){ei.prototype[e]=function(){var t;return(t=er.get(this))[e].apply(t,arguments)}});var ea=void 0!==D.ResizeObserver?D.ResizeObserver:ei;let eo=ea;var es=function(e){var t=[],n=null,r=function(){for(var r=arguments.length,i=Array(r),a=0;a=t||n<0||f&&r>=a}function g(){var e=eb();if(m(e))return v(e);s=setTimeout(g,b(e))}function v(e){return(s=void 0,d&&r)?h(e):(r=i=void 0,o)}function y(){void 0!==s&&clearTimeout(s),c=0,r=u=i=s=void 0}function w(){return void 0===s?o:v(eb())}function _(){var e=eb(),n=m(e);if(r=arguments,i=this,u=e,n){if(void 0===s)return p(u);if(f)return clearTimeout(s),s=setTimeout(g,t),h(u)}return void 0===s&&(s=setTimeout(g,t)),o}return t=ez(t)||0,ed(n)&&(l=!!n.leading,a=(f="maxWait"in n)?eW(ez(n.maxWait)||0,t):a,d="trailing"in n?!!n.trailing:d),_.cancel=y,_.flush=w,_}let eq=eV;var eZ="Expected a function";function eX(e,t,n){var r=!0,i=!0;if("function"!=typeof e)throw TypeError(eZ);return ed(n)&&(r="leading"in n?!!n.leading:r,i="trailing"in n?!!n.trailing:i),eq(e,t,{leading:r,maxWait:t,trailing:i})}let eJ=eX;var eQ={debounce:eq,throttle:eJ},e1=function(e){return eQ[e]},e0=function(e){return"function"==typeof e},e2=function(){return"undefined"==typeof window},e3=function(e){return e instanceof Element||e instanceof HTMLDocument};function e4(e){return(e4="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function e6(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}function e5(e,t){for(var n=0;ne.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&l.createElement(tG.Z,{variant:"indeterminate",classes:r}))};tK.propTypes={fetchCount:el().number.isRequired};let tV=(0,b.withStyles)(tW)(tK);var tq=n(5536);let tZ=n.p+"ba8bbf16ebf8e1d05bef.svg";function tX(){return(tX=Object.assign||function(e){for(var t=1;t120){for(var d=Math.floor(u/80),h=u%80,p=[],b=0;b0},name:{enumerable:!1},nodes:{enumerable:!1},source:{enumerable:!1},positions:{enumerable:!1},originalError:{enumerable:!1}}),null!=s&&s.stack)?(Object.defineProperty(nf(b),"stack",{value:s.stack,writable:!0,configurable:!0}),nl(b)):(Error.captureStackTrace?Error.captureStackTrace(nf(b),n):Object.defineProperty(nf(b),"stack",{value:Error().stack,writable:!0,configurable:!0}),b)}return ns(n,[{key:"toString",value:function(){return nw(this)}},{key:t4.YF,get:function(){return"Object"}}]),n}(nd(Error));function ny(e){return void 0===e||0===e.length?void 0:e}function nw(e){var t=e.message;if(e.nodes)for(var n=0,r=e.nodes;n",EOF:"",BANG:"!",DOLLAR:"$",AMP:"&",PAREN_L:"(",PAREN_R:")",SPREAD:"...",COLON:":",EQUALS:"=",AT:"@",BRACKET_L:"[",BRACKET_R:"]",BRACE_L:"{",PIPE:"|",BRACE_R:"}",NAME:"Name",INT:"Int",FLOAT:"Float",STRING:"String",BLOCK_STRING:"BlockString",COMMENT:"Comment"}),nx=n(10143),nT=Object.freeze({QUERY:"QUERY",MUTATION:"MUTATION",SUBSCRIPTION:"SUBSCRIPTION",FIELD:"FIELD",FRAGMENT_DEFINITION:"FRAGMENT_DEFINITION",FRAGMENT_SPREAD:"FRAGMENT_SPREAD",INLINE_FRAGMENT:"INLINE_FRAGMENT",VARIABLE_DEFINITION:"VARIABLE_DEFINITION",SCHEMA:"SCHEMA",SCALAR:"SCALAR",OBJECT:"OBJECT",FIELD_DEFINITION:"FIELD_DEFINITION",ARGUMENT_DEFINITION:"ARGUMENT_DEFINITION",INTERFACE:"INTERFACE",UNION:"UNION",ENUM:"ENUM",ENUM_VALUE:"ENUM_VALUE",INPUT_OBJECT:"INPUT_OBJECT",INPUT_FIELD_DEFINITION:"INPUT_FIELD_DEFINITION"}),nM=n(87392),nO=function(){function e(e){var t=new nS.WU(nk.SOF,0,0,0,0,null);this.source=e,this.lastToken=t,this.token=t,this.line=1,this.lineStart=0}var t=e.prototype;return t.advance=function(){return this.lastToken=this.token,this.token=this.lookahead()},t.lookahead=function(){var e,t=this.token;if(t.kind!==nk.EOF)do t=null!==(e=t.next)&&void 0!==e?e:t.next=nC(this,t);while(t.kind===nk.COMMENT)return t},e}();function nA(e){return e===nk.BANG||e===nk.DOLLAR||e===nk.AMP||e===nk.PAREN_L||e===nk.PAREN_R||e===nk.SPREAD||e===nk.COLON||e===nk.EQUALS||e===nk.AT||e===nk.BRACKET_L||e===nk.BRACKET_R||e===nk.BRACE_L||e===nk.PIPE||e===nk.BRACE_R}function nL(e){return isNaN(e)?nk.EOF:e<127?JSON.stringify(String.fromCharCode(e)):'"\\u'.concat(("00"+e.toString(16).toUpperCase()).slice(-4),'"')}function nC(e,t){for(var n=e.source,r=n.body,i=r.length,a=t.end;a31||9===a))return new nS.WU(nk.COMMENT,t,s,n,r,i,o.slice(t+1,s))}function nN(e,t,n,r,i,a){var o=e.body,s=n,u=t,c=!1;if(45===s&&(s=o.charCodeAt(++u)),48===s){if((s=o.charCodeAt(++u))>=48&&s<=57)throw n_(e,u,"Invalid number, unexpected digit after 0: ".concat(nL(s),"."))}else u=nP(e,u,s),s=o.charCodeAt(u);if(46===s&&(c=!0,s=o.charCodeAt(++u),u=nP(e,u,s),s=o.charCodeAt(u)),(69===s||101===s)&&(c=!0,(43===(s=o.charCodeAt(++u))||45===s)&&(s=o.charCodeAt(++u)),u=nP(e,u,s),s=o.charCodeAt(u)),46===s||nU(s))throw n_(e,u,"Invalid number, expected digit but got: ".concat(nL(s),"."));return new nS.WU(c?nk.FLOAT:nk.INT,t,u,r,i,a,o.slice(t,u))}function nP(e,t,n){var r=e.body,i=t,a=n;if(a>=48&&a<=57){do a=r.charCodeAt(++i);while(a>=48&&a<=57)return i}throw n_(e,i,"Invalid number, expected digit but got: ".concat(nL(a),"."))}function nR(e,t,n,r,i){for(var a=e.body,o=t+1,s=o,u=0,c="";o=48&&e<=57?e-48:e>=65&&e<=70?e-55:e>=97&&e<=102?e-87:-1}function nB(e,t,n,r,i){for(var a=e.body,o=a.length,s=t+1,u=0;s!==o&&!isNaN(u=a.charCodeAt(s))&&(95===u||u>=48&&u<=57||u>=65&&u<=90||u>=97&&u<=122);)++s;return new nS.WU(nk.NAME,t,s,n,r,i,a.slice(t,s))}function nU(e){return 95===e||e>=65&&e<=90||e>=97&&e<=122}function nH(e,t){return new n$(e,t).parseDocument()}var n$=function(){function e(e,t){var n=(0,nx.T)(e)?e:new nx.H(e);this._lexer=new nO(n),this._options=t}var t=e.prototype;return t.parseName=function(){var e=this.expectToken(nk.NAME);return{kind:nE.h.NAME,value:e.value,loc:this.loc(e)}},t.parseDocument=function(){var e=this._lexer.token;return{kind:nE.h.DOCUMENT,definitions:this.many(nk.SOF,this.parseDefinition,nk.EOF),loc:this.loc(e)}},t.parseDefinition=function(){if(this.peek(nk.NAME))switch(this._lexer.token.value){case"query":case"mutation":case"subscription":return this.parseOperationDefinition();case"fragment":return this.parseFragmentDefinition();case"schema":case"scalar":case"type":case"interface":case"union":case"enum":case"input":case"directive":return this.parseTypeSystemDefinition();case"extend":return this.parseTypeSystemExtension()}else if(this.peek(nk.BRACE_L))return this.parseOperationDefinition();else if(this.peekDescription())return this.parseTypeSystemDefinition();throw this.unexpected()},t.parseOperationDefinition=function(){var e,t=this._lexer.token;if(this.peek(nk.BRACE_L))return{kind:nE.h.OPERATION_DEFINITION,operation:"query",name:void 0,variableDefinitions:[],directives:[],selectionSet:this.parseSelectionSet(),loc:this.loc(t)};var n=this.parseOperationType();return this.peek(nk.NAME)&&(e=this.parseName()),{kind:nE.h.OPERATION_DEFINITION,operation:n,name:e,variableDefinitions:this.parseVariableDefinitions(),directives:this.parseDirectives(!1),selectionSet:this.parseSelectionSet(),loc:this.loc(t)}},t.parseOperationType=function(){var e=this.expectToken(nk.NAME);switch(e.value){case"query":return"query";case"mutation":return"mutation";case"subscription":return"subscription"}throw this.unexpected(e)},t.parseVariableDefinitions=function(){return this.optionalMany(nk.PAREN_L,this.parseVariableDefinition,nk.PAREN_R)},t.parseVariableDefinition=function(){var e=this._lexer.token;return{kind:nE.h.VARIABLE_DEFINITION,variable:this.parseVariable(),type:(this.expectToken(nk.COLON),this.parseTypeReference()),defaultValue:this.expectOptionalToken(nk.EQUALS)?this.parseValueLiteral(!0):void 0,directives:this.parseDirectives(!0),loc:this.loc(e)}},t.parseVariable=function(){var e=this._lexer.token;return this.expectToken(nk.DOLLAR),{kind:nE.h.VARIABLE,name:this.parseName(),loc:this.loc(e)}},t.parseSelectionSet=function(){var e=this._lexer.token;return{kind:nE.h.SELECTION_SET,selections:this.many(nk.BRACE_L,this.parseSelection,nk.BRACE_R),loc:this.loc(e)}},t.parseSelection=function(){return this.peek(nk.SPREAD)?this.parseFragment():this.parseField()},t.parseField=function(){var e,t,n=this._lexer.token,r=this.parseName();return this.expectOptionalToken(nk.COLON)?(e=r,t=this.parseName()):t=r,{kind:nE.h.FIELD,alias:e,name:t,arguments:this.parseArguments(!1),directives:this.parseDirectives(!1),selectionSet:this.peek(nk.BRACE_L)?this.parseSelectionSet():void 0,loc:this.loc(n)}},t.parseArguments=function(e){var t=e?this.parseConstArgument:this.parseArgument;return this.optionalMany(nk.PAREN_L,t,nk.PAREN_R)},t.parseArgument=function(){var e=this._lexer.token,t=this.parseName();return this.expectToken(nk.COLON),{kind:nE.h.ARGUMENT,name:t,value:this.parseValueLiteral(!1),loc:this.loc(e)}},t.parseConstArgument=function(){var e=this._lexer.token;return{kind:nE.h.ARGUMENT,name:this.parseName(),value:(this.expectToken(nk.COLON),this.parseValueLiteral(!0)),loc:this.loc(e)}},t.parseFragment=function(){var e=this._lexer.token;this.expectToken(nk.SPREAD);var t=this.expectOptionalKeyword("on");return!t&&this.peek(nk.NAME)?{kind:nE.h.FRAGMENT_SPREAD,name:this.parseFragmentName(),directives:this.parseDirectives(!1),loc:this.loc(e)}:{kind:nE.h.INLINE_FRAGMENT,typeCondition:t?this.parseNamedType():void 0,directives:this.parseDirectives(!1),selectionSet:this.parseSelectionSet(),loc:this.loc(e)}},t.parseFragmentDefinition=function(){var e,t=this._lexer.token;return(this.expectKeyword("fragment"),(null===(e=this._options)||void 0===e?void 0:e.experimentalFragmentVariables)===!0)?{kind:nE.h.FRAGMENT_DEFINITION,name:this.parseFragmentName(),variableDefinitions:this.parseVariableDefinitions(),typeCondition:(this.expectKeyword("on"),this.parseNamedType()),directives:this.parseDirectives(!1),selectionSet:this.parseSelectionSet(),loc:this.loc(t)}:{kind:nE.h.FRAGMENT_DEFINITION,name:this.parseFragmentName(),typeCondition:(this.expectKeyword("on"),this.parseNamedType()),directives:this.parseDirectives(!1),selectionSet:this.parseSelectionSet(),loc:this.loc(t)}},t.parseFragmentName=function(){if("on"===this._lexer.token.value)throw this.unexpected();return this.parseName()},t.parseValueLiteral=function(e){var t=this._lexer.token;switch(t.kind){case nk.BRACKET_L:return this.parseList(e);case nk.BRACE_L:return this.parseObject(e);case nk.INT:return this._lexer.advance(),{kind:nE.h.INT,value:t.value,loc:this.loc(t)};case nk.FLOAT:return this._lexer.advance(),{kind:nE.h.FLOAT,value:t.value,loc:this.loc(t)};case nk.STRING:case nk.BLOCK_STRING:return this.parseStringLiteral();case nk.NAME:switch(this._lexer.advance(),t.value){case"true":return{kind:nE.h.BOOLEAN,value:!0,loc:this.loc(t)};case"false":return{kind:nE.h.BOOLEAN,value:!1,loc:this.loc(t)};case"null":return{kind:nE.h.NULL,loc:this.loc(t)};default:return{kind:nE.h.ENUM,value:t.value,loc:this.loc(t)}}case nk.DOLLAR:if(!e)return this.parseVariable()}throw this.unexpected()},t.parseStringLiteral=function(){var e=this._lexer.token;return this._lexer.advance(),{kind:nE.h.STRING,value:e.value,block:e.kind===nk.BLOCK_STRING,loc:this.loc(e)}},t.parseList=function(e){var t=this,n=this._lexer.token,r=function(){return t.parseValueLiteral(e)};return{kind:nE.h.LIST,values:this.any(nk.BRACKET_L,r,nk.BRACKET_R),loc:this.loc(n)}},t.parseObject=function(e){var t=this,n=this._lexer.token,r=function(){return t.parseObjectField(e)};return{kind:nE.h.OBJECT,fields:this.any(nk.BRACE_L,r,nk.BRACE_R),loc:this.loc(n)}},t.parseObjectField=function(e){var t=this._lexer.token,n=this.parseName();return this.expectToken(nk.COLON),{kind:nE.h.OBJECT_FIELD,name:n,value:this.parseValueLiteral(e),loc:this.loc(t)}},t.parseDirectives=function(e){for(var t=[];this.peek(nk.AT);)t.push(this.parseDirective(e));return t},t.parseDirective=function(e){var t=this._lexer.token;return this.expectToken(nk.AT),{kind:nE.h.DIRECTIVE,name:this.parseName(),arguments:this.parseArguments(e),loc:this.loc(t)}},t.parseTypeReference=function(){var e,t=this._lexer.token;return(this.expectOptionalToken(nk.BRACKET_L)?(e=this.parseTypeReference(),this.expectToken(nk.BRACKET_R),e={kind:nE.h.LIST_TYPE,type:e,loc:this.loc(t)}):e=this.parseNamedType(),this.expectOptionalToken(nk.BANG))?{kind:nE.h.NON_NULL_TYPE,type:e,loc:this.loc(t)}:e},t.parseNamedType=function(){var e=this._lexer.token;return{kind:nE.h.NAMED_TYPE,name:this.parseName(),loc:this.loc(e)}},t.parseTypeSystemDefinition=function(){var e=this.peekDescription()?this._lexer.lookahead():this._lexer.token;if(e.kind===nk.NAME)switch(e.value){case"schema":return this.parseSchemaDefinition();case"scalar":return this.parseScalarTypeDefinition();case"type":return this.parseObjectTypeDefinition();case"interface":return this.parseInterfaceTypeDefinition();case"union":return this.parseUnionTypeDefinition();case"enum":return this.parseEnumTypeDefinition();case"input":return this.parseInputObjectTypeDefinition();case"directive":return this.parseDirectiveDefinition()}throw this.unexpected(e)},t.peekDescription=function(){return this.peek(nk.STRING)||this.peek(nk.BLOCK_STRING)},t.parseDescription=function(){if(this.peekDescription())return this.parseStringLiteral()},t.parseSchemaDefinition=function(){var e=this._lexer.token,t=this.parseDescription();this.expectKeyword("schema");var n=this.parseDirectives(!0),r=this.many(nk.BRACE_L,this.parseOperationTypeDefinition,nk.BRACE_R);return{kind:nE.h.SCHEMA_DEFINITION,description:t,directives:n,operationTypes:r,loc:this.loc(e)}},t.parseOperationTypeDefinition=function(){var e=this._lexer.token,t=this.parseOperationType();this.expectToken(nk.COLON);var n=this.parseNamedType();return{kind:nE.h.OPERATION_TYPE_DEFINITION,operation:t,type:n,loc:this.loc(e)}},t.parseScalarTypeDefinition=function(){var e=this._lexer.token,t=this.parseDescription();this.expectKeyword("scalar");var n=this.parseName(),r=this.parseDirectives(!0);return{kind:nE.h.SCALAR_TYPE_DEFINITION,description:t,name:n,directives:r,loc:this.loc(e)}},t.parseObjectTypeDefinition=function(){var e=this._lexer.token,t=this.parseDescription();this.expectKeyword("type");var n=this.parseName(),r=this.parseImplementsInterfaces(),i=this.parseDirectives(!0),a=this.parseFieldsDefinition();return{kind:nE.h.OBJECT_TYPE_DEFINITION,description:t,name:n,interfaces:r,directives:i,fields:a,loc:this.loc(e)}},t.parseImplementsInterfaces=function(){var e;if(!this.expectOptionalKeyword("implements"))return[];if((null===(e=this._options)||void 0===e?void 0:e.allowLegacySDLImplementsInterfaces)===!0){var t=[];this.expectOptionalToken(nk.AMP);do t.push(this.parseNamedType());while(this.expectOptionalToken(nk.AMP)||this.peek(nk.NAME))return t}return this.delimitedMany(nk.AMP,this.parseNamedType)},t.parseFieldsDefinition=function(){var e;return(null===(e=this._options)||void 0===e?void 0:e.allowLegacySDLEmptyFields)===!0&&this.peek(nk.BRACE_L)&&this._lexer.lookahead().kind===nk.BRACE_R?(this._lexer.advance(),this._lexer.advance(),[]):this.optionalMany(nk.BRACE_L,this.parseFieldDefinition,nk.BRACE_R)},t.parseFieldDefinition=function(){var e=this._lexer.token,t=this.parseDescription(),n=this.parseName(),r=this.parseArgumentDefs();this.expectToken(nk.COLON);var i=this.parseTypeReference(),a=this.parseDirectives(!0);return{kind:nE.h.FIELD_DEFINITION,description:t,name:n,arguments:r,type:i,directives:a,loc:this.loc(e)}},t.parseArgumentDefs=function(){return this.optionalMany(nk.PAREN_L,this.parseInputValueDef,nk.PAREN_R)},t.parseInputValueDef=function(){var e,t=this._lexer.token,n=this.parseDescription(),r=this.parseName();this.expectToken(nk.COLON);var i=this.parseTypeReference();this.expectOptionalToken(nk.EQUALS)&&(e=this.parseValueLiteral(!0));var a=this.parseDirectives(!0);return{kind:nE.h.INPUT_VALUE_DEFINITION,description:n,name:r,type:i,defaultValue:e,directives:a,loc:this.loc(t)}},t.parseInterfaceTypeDefinition=function(){var e=this._lexer.token,t=this.parseDescription();this.expectKeyword("interface");var n=this.parseName(),r=this.parseImplementsInterfaces(),i=this.parseDirectives(!0),a=this.parseFieldsDefinition();return{kind:nE.h.INTERFACE_TYPE_DEFINITION,description:t,name:n,interfaces:r,directives:i,fields:a,loc:this.loc(e)}},t.parseUnionTypeDefinition=function(){var e=this._lexer.token,t=this.parseDescription();this.expectKeyword("union");var n=this.parseName(),r=this.parseDirectives(!0),i=this.parseUnionMemberTypes();return{kind:nE.h.UNION_TYPE_DEFINITION,description:t,name:n,directives:r,types:i,loc:this.loc(e)}},t.parseUnionMemberTypes=function(){return this.expectOptionalToken(nk.EQUALS)?this.delimitedMany(nk.PIPE,this.parseNamedType):[]},t.parseEnumTypeDefinition=function(){var e=this._lexer.token,t=this.parseDescription();this.expectKeyword("enum");var n=this.parseName(),r=this.parseDirectives(!0),i=this.parseEnumValuesDefinition();return{kind:nE.h.ENUM_TYPE_DEFINITION,description:t,name:n,directives:r,values:i,loc:this.loc(e)}},t.parseEnumValuesDefinition=function(){return this.optionalMany(nk.BRACE_L,this.parseEnumValueDefinition,nk.BRACE_R)},t.parseEnumValueDefinition=function(){var e=this._lexer.token,t=this.parseDescription(),n=this.parseName(),r=this.parseDirectives(!0);return{kind:nE.h.ENUM_VALUE_DEFINITION,description:t,name:n,directives:r,loc:this.loc(e)}},t.parseInputObjectTypeDefinition=function(){var e=this._lexer.token,t=this.parseDescription();this.expectKeyword("input");var n=this.parseName(),r=this.parseDirectives(!0),i=this.parseInputFieldsDefinition();return{kind:nE.h.INPUT_OBJECT_TYPE_DEFINITION,description:t,name:n,directives:r,fields:i,loc:this.loc(e)}},t.parseInputFieldsDefinition=function(){return this.optionalMany(nk.BRACE_L,this.parseInputValueDef,nk.BRACE_R)},t.parseTypeSystemExtension=function(){var e=this._lexer.lookahead();if(e.kind===nk.NAME)switch(e.value){case"schema":return this.parseSchemaExtension();case"scalar":return this.parseScalarTypeExtension();case"type":return this.parseObjectTypeExtension();case"interface":return this.parseInterfaceTypeExtension();case"union":return this.parseUnionTypeExtension();case"enum":return this.parseEnumTypeExtension();case"input":return this.parseInputObjectTypeExtension()}throw this.unexpected(e)},t.parseSchemaExtension=function(){var e=this._lexer.token;this.expectKeyword("extend"),this.expectKeyword("schema");var t=this.parseDirectives(!0),n=this.optionalMany(nk.BRACE_L,this.parseOperationTypeDefinition,nk.BRACE_R);if(0===t.length&&0===n.length)throw this.unexpected();return{kind:nE.h.SCHEMA_EXTENSION,directives:t,operationTypes:n,loc:this.loc(e)}},t.parseScalarTypeExtension=function(){var e=this._lexer.token;this.expectKeyword("extend"),this.expectKeyword("scalar");var t=this.parseName(),n=this.parseDirectives(!0);if(0===n.length)throw this.unexpected();return{kind:nE.h.SCALAR_TYPE_EXTENSION,name:t,directives:n,loc:this.loc(e)}},t.parseObjectTypeExtension=function(){var e=this._lexer.token;this.expectKeyword("extend"),this.expectKeyword("type");var t=this.parseName(),n=this.parseImplementsInterfaces(),r=this.parseDirectives(!0),i=this.parseFieldsDefinition();if(0===n.length&&0===r.length&&0===i.length)throw this.unexpected();return{kind:nE.h.OBJECT_TYPE_EXTENSION,name:t,interfaces:n,directives:r,fields:i,loc:this.loc(e)}},t.parseInterfaceTypeExtension=function(){var e=this._lexer.token;this.expectKeyword("extend"),this.expectKeyword("interface");var t=this.parseName(),n=this.parseImplementsInterfaces(),r=this.parseDirectives(!0),i=this.parseFieldsDefinition();if(0===n.length&&0===r.length&&0===i.length)throw this.unexpected();return{kind:nE.h.INTERFACE_TYPE_EXTENSION,name:t,interfaces:n,directives:r,fields:i,loc:this.loc(e)}},t.parseUnionTypeExtension=function(){var e=this._lexer.token;this.expectKeyword("extend"),this.expectKeyword("union");var t=this.parseName(),n=this.parseDirectives(!0),r=this.parseUnionMemberTypes();if(0===n.length&&0===r.length)throw this.unexpected();return{kind:nE.h.UNION_TYPE_EXTENSION,name:t,directives:n,types:r,loc:this.loc(e)}},t.parseEnumTypeExtension=function(){var e=this._lexer.token;this.expectKeyword("extend"),this.expectKeyword("enum");var t=this.parseName(),n=this.parseDirectives(!0),r=this.parseEnumValuesDefinition();if(0===n.length&&0===r.length)throw this.unexpected();return{kind:nE.h.ENUM_TYPE_EXTENSION,name:t,directives:n,values:r,loc:this.loc(e)}},t.parseInputObjectTypeExtension=function(){var e=this._lexer.token;this.expectKeyword("extend"),this.expectKeyword("input");var t=this.parseName(),n=this.parseDirectives(!0),r=this.parseInputFieldsDefinition();if(0===n.length&&0===r.length)throw this.unexpected();return{kind:nE.h.INPUT_OBJECT_TYPE_EXTENSION,name:t,directives:n,fields:r,loc:this.loc(e)}},t.parseDirectiveDefinition=function(){var e=this._lexer.token,t=this.parseDescription();this.expectKeyword("directive"),this.expectToken(nk.AT);var n=this.parseName(),r=this.parseArgumentDefs(),i=this.expectOptionalKeyword("repeatable");this.expectKeyword("on");var a=this.parseDirectiveLocations();return{kind:nE.h.DIRECTIVE_DEFINITION,description:t,name:n,arguments:r,repeatable:i,locations:a,loc:this.loc(e)}},t.parseDirectiveLocations=function(){return this.delimitedMany(nk.PIPE,this.parseDirectiveLocation)},t.parseDirectiveLocation=function(){var e=this._lexer.token,t=this.parseName();if(void 0!==nT[t.value])return t;throw this.unexpected(e)},t.loc=function(e){var t;if((null===(t=this._options)||void 0===t?void 0:t.noLocation)!==!0)return new nS.Ye(e,this._lexer.lastToken,this._lexer.source)},t.peek=function(e){return this._lexer.token.kind===e},t.expectToken=function(e){var t=this._lexer.token;if(t.kind===e)return this._lexer.advance(),t;throw n_(this._lexer.source,t.start,"Expected ".concat(nG(e),", found ").concat(nz(t),"."))},t.expectOptionalToken=function(e){var t=this._lexer.token;if(t.kind===e)return this._lexer.advance(),t},t.expectKeyword=function(e){var t=this._lexer.token;if(t.kind===nk.NAME&&t.value===e)this._lexer.advance();else throw n_(this._lexer.source,t.start,'Expected "'.concat(e,'", found ').concat(nz(t),"."))},t.expectOptionalKeyword=function(e){var t=this._lexer.token;return t.kind===nk.NAME&&t.value===e&&(this._lexer.advance(),!0)},t.unexpected=function(e){var t=null!=e?e:this._lexer.token;return n_(this._lexer.source,t.start,"Unexpected ".concat(nz(t),"."))},t.any=function(e,t,n){this.expectToken(e);for(var r=[];!this.expectOptionalToken(n);)r.push(t.call(this));return r},t.optionalMany=function(e,t,n){if(this.expectOptionalToken(e)){var r=[];do r.push(t.call(this));while(!this.expectOptionalToken(n))return r}return[]},t.many=function(e,t,n){this.expectToken(e);var r=[];do r.push(t.call(this));while(!this.expectOptionalToken(n))return r},t.delimitedMany=function(e,t){this.expectOptionalToken(e);var n=[];do n.push(t.call(this));while(this.expectOptionalToken(e))return n},e}();function nz(e){var t=e.value;return nG(e.kind)+(null!=t?' "'.concat(t,'"'):"")}function nG(e){return nA(e)?'"'.concat(e,'"'):e}var nW=new Map,nK=new Map,nV=!0,nq=!1;function nZ(e){return e.replace(/[\s,]+/g," ").trim()}function nX(e){return nZ(e.source.body.substring(e.start,e.end))}function nJ(e){var t=new Set,n=[];return e.definitions.forEach(function(e){if("FragmentDefinition"===e.kind){var r=e.name.value,i=nX(e.loc),a=nK.get(r);a&&!a.has(i)?nV&&console.warn("Warning: fragment with name "+r+" already exists.\ngraphql-tag enforces all fragment names across your application to be unique; read more about\nthis in the docs: http://dev.apollodata.com/core/fragments.html#unique-names"):a||nK.set(r,a=new Set),a.add(i),t.has(i)||(t.add(i),n.push(e))}else n.push(e)}),(0,t0.pi)((0,t0.pi)({},e),{definitions:n})}function nQ(e){var t=new Set(e.definitions);t.forEach(function(e){e.loc&&delete e.loc,Object.keys(e).forEach(function(n){var r=e[n];r&&"object"==typeof r&&t.add(r)})});var n=e.loc;return n&&(delete n.startToken,delete n.endToken),e}function n1(e){var t=nZ(e);if(!nW.has(t)){var n=nH(e,{experimentalFragmentVariables:nq,allowLegacyFragmentVariables:nq});if(!n||"Document"!==n.kind)throw Error("Not a valid GraphQL document.");nW.set(t,nQ(nJ(n)))}return nW.get(t)}function n0(e){for(var t=[],n=1;n, or pass an ApolloClient instance in via options.'):(0,n9.kG)(!!n,32),n}var rp=n(10542),rb=n(53712),rm=n(21436),rg=Object.prototype.hasOwnProperty;function rv(e,t){return void 0===t&&(t=Object.create(null)),ry(rh(t.client),e).useQuery(t)}function ry(e,t){var n=(0,l.useRef)();n.current&&e===n.current.client&&t===n.current.query||(n.current=new rw(e,t,n.current));var r=n.current,i=(0,l.useState)(0),a=(i[0],i[1]);return r.forceUpdate=function(){a(function(e){return e+1})},r}var rw=function(){function e(e,t,n){this.client=e,this.query=t,this.ssrDisabledResult=(0,rp.J)({loading:!0,data:void 0,error:void 0,networkStatus:ru.I.loading}),this.skipStandbyResult=(0,rp.J)({loading:!1,data:void 0,error:void 0,networkStatus:ru.I.ready}),this.toQueryResultCache=new(n7.mr?WeakMap:Map),rd(t,r.Query);var i=n&&n.result,a=i&&i.data;a&&(this.previousData=a)}return e.prototype.forceUpdate=function(){__DEV__&&n9.kG.warn("Calling default no-op implementation of InternalState#forceUpdate")},e.prototype.executeQuery=function(e){var t,n=this;e.query&&Object.assign(this,{query:e.query}),this.watchQueryOptions=this.createWatchQueryOptions(this.queryHookOptions=e);var r=this.observable.reobserveAsConcast(this.getObsQueryOptions());return this.previousData=(null===(t=this.result)||void 0===t?void 0:t.data)||this.previousData,this.result=void 0,this.forceUpdate(),new Promise(function(e){var t;r.subscribe({next:function(e){t=e},error:function(){e(n.toQueryResult(n.observable.getCurrentResult()))},complete:function(){e(n.toQueryResult(t))}})})},e.prototype.useQuery=function(e){var t=this;this.renderPromises=(0,l.useContext)((0,ro.K)()).renderPromises,this.useOptions(e);var n=this.useObservableQuery(),r=rt((0,l.useCallback)(function(){if(t.renderPromises)return function(){};var e=function(){var e=t.result,r=n.getCurrentResult();!(e&&e.loading===r.loading&&e.networkStatus===r.networkStatus&&(0,ri.D)(e.data,r.data))&&t.setResult(r)},r=function(a){var o=n.last;i.unsubscribe();try{n.resetLastResults(),i=n.subscribe(e,r)}finally{n.last=o}if(!rg.call(a,"graphQLErrors"))throw a;var s=t.result;(!s||s&&s.loading||!(0,ri.D)(a,s.error))&&t.setResult({data:s&&s.data,error:a,loading:!1,networkStatus:ru.I.error})},i=n.subscribe(e,r);return function(){return setTimeout(function(){return i.unsubscribe()})}},[n,this.renderPromises,this.client.disableNetworkFetches,]),function(){return t.getCurrentResult()},function(){return t.getCurrentResult()});return this.unsafeHandlePartialRefetch(r),this.toQueryResult(r)},e.prototype.useOptions=function(t){var n,r=this.createWatchQueryOptions(this.queryHookOptions=t),i=this.watchQueryOptions;!(0,ri.D)(r,i)&&(this.watchQueryOptions=r,i&&this.observable&&(this.observable.reobserve(this.getObsQueryOptions()),this.previousData=(null===(n=this.result)||void 0===n?void 0:n.data)||this.previousData,this.result=void 0)),this.onCompleted=t.onCompleted||e.prototype.onCompleted,this.onError=t.onError||e.prototype.onError,(this.renderPromises||this.client.disableNetworkFetches)&&!1===this.queryHookOptions.ssr&&!this.queryHookOptions.skip?this.result=this.ssrDisabledResult:this.queryHookOptions.skip||"standby"===this.watchQueryOptions.fetchPolicy?this.result=this.skipStandbyResult:(this.result===this.ssrDisabledResult||this.result===this.skipStandbyResult)&&(this.result=void 0)},e.prototype.getObsQueryOptions=function(){var e=[],t=this.client.defaultOptions.watchQuery;return t&&e.push(t),this.queryHookOptions.defaultOptions&&e.push(this.queryHookOptions.defaultOptions),e.push((0,rb.o)(this.observable&&this.observable.options,this.watchQueryOptions)),e.reduce(ra.J)},e.prototype.createWatchQueryOptions=function(e){void 0===e&&(e={});var t,n=e.skip,r=Object.assign((e.ssr,e.onCompleted,e.onError,e.defaultOptions,(0,t0._T)(e,["skip","ssr","onCompleted","onError","defaultOptions"])),{query:this.query});if(this.renderPromises&&("network-only"===r.fetchPolicy||"cache-and-network"===r.fetchPolicy)&&(r.fetchPolicy="cache-first"),r.variables||(r.variables={}),n){var i=r.fetchPolicy,a=void 0===i?this.getDefaultFetchPolicy():i,o=r.initialFetchPolicy;Object.assign(r,{initialFetchPolicy:void 0===o?a:o,fetchPolicy:"standby"})}else r.fetchPolicy||(r.fetchPolicy=(null===(t=this.observable)||void 0===t?void 0:t.options.initialFetchPolicy)||this.getDefaultFetchPolicy());return r},e.prototype.getDefaultFetchPolicy=function(){var e,t;return(null===(e=this.queryHookOptions.defaultOptions)||void 0===e?void 0:e.fetchPolicy)||(null===(t=this.client.defaultOptions.watchQuery)||void 0===t?void 0:t.fetchPolicy)||"cache-first"},e.prototype.onCompleted=function(e){},e.prototype.onError=function(e){},e.prototype.useObservableQuery=function(){var e=this.observable=this.renderPromises&&this.renderPromises.getSSRObservable(this.watchQueryOptions)||this.observable||this.client.watchQuery(this.getObsQueryOptions());this.obsQueryFields=(0,l.useMemo)(function(){return{refetch:e.refetch.bind(e),reobserve:e.reobserve.bind(e),fetchMore:e.fetchMore.bind(e),updateQuery:e.updateQuery.bind(e),startPolling:e.startPolling.bind(e),stopPolling:e.stopPolling.bind(e),subscribeToMore:e.subscribeToMore.bind(e)}},[e]);var t=!(!1===this.queryHookOptions.ssr||this.queryHookOptions.skip);return this.renderPromises&&t&&(this.renderPromises.registerSSRObservable(e),e.getCurrentResult().loading&&this.renderPromises.addObservableQueryPromise(e)),e},e.prototype.setResult=function(e){var t=this.result;t&&t.data&&(this.previousData=t.data),this.result=e,this.forceUpdate(),this.handleErrorOrCompleted(e)},e.prototype.handleErrorOrCompleted=function(e){var t=this;if(!e.loading){var n=this.toApolloError(e);Promise.resolve().then(function(){n?t.onError(n):e.data&&t.onCompleted(e.data)}).catch(function(e){__DEV__&&n9.kG.warn(e)})}},e.prototype.toApolloError=function(e){return(0,rm.O)(e.errors)?new rs.cA({graphQLErrors:e.errors}):e.error},e.prototype.getCurrentResult=function(){return this.result||this.handleErrorOrCompleted(this.result=this.observable.getCurrentResult()),this.result},e.prototype.toQueryResult=function(e){var t=this.toQueryResultCache.get(e);if(t)return t;var n=e.data,r=(e.partial,(0,t0._T)(e,["data","partial"]));return this.toQueryResultCache.set(e,t=(0,t0.pi)((0,t0.pi)((0,t0.pi)({data:n},r),this.obsQueryFields),{client:this.client,observable:this.observable,variables:this.observable.variables,called:!this.queryHookOptions.skip,previousData:this.previousData})),!t.error&&(0,rm.O)(e.errors)&&(t.error=new rs.cA({graphQLErrors:e.errors})),t},e.prototype.unsafeHandlePartialRefetch=function(e){e.partial&&this.queryHookOptions.partialRefetch&&!e.loading&&(!e.data||0===Object.keys(e.data).length)&&"cache-only"!==this.observable.options.fetchPolicy&&(Object.assign(e,{loading:!0,networkStatus:ru.I.refetch}),this.observable.refetch())},e}();function r_(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&void 0!==arguments[0]?arguments[0]:{};return rv(iH,e)},iz=function(){var e=ij(),t=parseInt(e.get("page")||"1",10),n=parseInt(e.get("per")||"50",10),r=i$({variables:{offset:(t-1)*n,limit:n},fetchPolicy:"network-only"}),i=r.data,a=r.loading,o=r.error;return a?l.createElement(iR,null):o?l.createElement(iD,{error:o}):i?l.createElement(iI,{chains:i.chains.results,page:t,pageSize:n,total:i.chains.metadata.total}):null},iG=n(67932),iW=n(8126),iK="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e};function iV(e){if(iq())return Intl.DateTimeFormat.supportedLocalesOf(e)[0]}function iq(){return("undefined"==typeof Intl?"undefined":iK(Intl))==="object"&&"function"==typeof Intl.DateTimeFormat}var iZ="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},iX=function(){function e(e,t){for(var n=0;n=i.length)break;s=i[o++]}else{if((o=i.next()).done)break;s=o.value}var s,u=s;if((void 0===e?"undefined":iZ(e))!=="object")return;e=e[u]}return e}},{key:"put",value:function(){for(var e=arguments.length,t=Array(e),n=0;n=o.length)break;c=o[u++]}else{if((u=o.next()).done)break;c=u.value}var c,l=c;"object"!==iZ(a[l])&&(a[l]={}),a=a[l]}return a[i]=r}}]),e}();let i1=iQ;var i0=new i1;function i2(e,t){if(!iq())return function(e){return e.toString()};var n=i4(e),r=JSON.stringify(t),i=i0.get(String(n),r)||i0.put(String(n),r,new Intl.DateTimeFormat(n,t));return function(e){return i.format(e)}}var i3={};function i4(e){var t=e.toString();return i3[t]?i3[t]:i3[t]=iV(e)}var i6="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e};function i5(e){return i8(e)?e:new Date(e)}function i8(e){return e instanceof Date||i9(e)}function i9(e){return(void 0===e?"undefined":i6(e))==="object"&&"function"==typeof e.getTime}var i7=n(54087),ae=n.n(i7);function at(e,t){if(0===e.length)return 0;for(var n=0,r=e.length-1,i=void 0;n<=r;){var a=t(e[i=Math.floor((r+n)/2)]);if(0===a)return i;if(a<0){if((n=i+1)>r)return n}else if((r=i-1)=t.nextUpdateTime)aa(t,this.instances);else break}},scheduleNextTick:function(){var e=this;this.scheduledTick=ae()(function(){e.tick(),e.scheduleNextTick()})},start:function(){this.scheduleNextTick()},stop:function(){ae().cancel(this.scheduledTick)}};function ai(e){var t=an(e.getNextValue(),2),n=t[0],r=t[1];e.setValue(n),e.nextUpdateTime=r}function aa(e,t){ai(e),as(t,e),ao(t,e)}function ao(e,t){var n=au(e,t);e.splice(n,0,t)}function as(e,t){var n=e.indexOf(t);e.splice(n,1)}function au(e,t){var n=t.nextUpdateTime;return at(e,function(e){return e.nextUpdateTime===n?0:e.nextUpdateTime>n?1:-1})}var ac=(0,ec.oneOfType)([(0,ec.shape)({minTime:ec.number,formatAs:ec.string.isRequired}),(0,ec.shape)({test:ec.func,formatAs:ec.string.isRequired}),(0,ec.shape)({minTime:ec.number,format:ec.func.isRequired}),(0,ec.shape)({test:ec.func,format:ec.func.isRequired})]),al=(0,ec.oneOfType)([ec.string,(0,ec.shape)({steps:(0,ec.arrayOf)(ac).isRequired,labels:(0,ec.oneOfType)([ec.string,(0,ec.arrayOf)(ec.string)]).isRequired,round:ec.string})]),af=Object.assign||function(e){for(var t=1;t=0)&&Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]);return n}function ap(e){var t=e.date,n=e.future,r=e.timeStyle,i=e.round,a=e.minTimeLeft,o=e.tooltip,s=e.component,u=e.container,c=e.wrapperComponent,f=e.wrapperProps,d=e.locale,h=e.locales,p=e.formatVerboseDate,b=e.verboseDateFormat,m=e.updateInterval,g=e.tick,v=ah(e,["date","future","timeStyle","round","minTimeLeft","tooltip","component","container","wrapperComponent","wrapperProps","locale","locales","formatVerboseDate","verboseDateFormat","updateInterval","tick"]),y=(0,l.useMemo)(function(){return d&&(h=[d]),h.concat(iW.Z.getDefaultLocale())},[d,h]),w=(0,l.useMemo)(function(){return new iW.Z(y)},[y]);t=(0,l.useMemo)(function(){return i5(t)},[t]);var _=(0,l.useCallback)(function(){var e=Date.now(),o=void 0;if(n&&e>=t.getTime()&&(e=t.getTime(),o=!0),void 0!==a){var s=t.getTime()-1e3*a;e>s&&(e=s,o=!0)}var u=w.format(t,r,{getTimeToNextUpdate:!0,now:e,future:n,round:i}),c=ad(u,2),l=c[0],f=c[1];return f=o?ag:m||f||6e4,[l,e+f]},[t,n,r,m,i,a,w]),E=(0,l.useRef)();E.current=_;var S=(0,l.useMemo)(_,[]),k=ad(S,2),x=k[0],T=k[1],M=(0,l.useState)(x),O=ad(M,2),A=O[0],L=O[1],C=ad((0,l.useState)(),2),I=C[0],D=C[1],N=(0,l.useRef)();(0,l.useEffect)(function(){if(g)return N.current=ar.add({getNextValue:function(){return E.current()},setValue:L,nextUpdateTime:T}),function(){return N.current.stop()}},[g]),(0,l.useEffect)(function(){if(N.current)N.current.forceUpdate();else{var e=_(),t=ad(e,1)[0];L(t)}},[_]),(0,l.useEffect)(function(){D(!0)},[]);var P=(0,l.useMemo)(function(){if("undefined"!=typeof window)return i2(y,b)},[y,b]),R=(0,l.useMemo)(function(){if("undefined"!=typeof window)return p?p(t):P(t)},[t,p,P]),j=l.createElement(s,af({date:t,verboseDate:I?R:void 0,tooltip:o},v),A),F=c||u;return F?l.createElement(F,af({},f,{verboseDate:I?R:void 0}),j):j}ap.propTypes={date:el().oneOfType([el().instanceOf(Date),el().number]).isRequired,locale:el().string,locales:el().arrayOf(el().string),future:el().bool,timeStyle:al,round:el().string,minTimeLeft:el().number,component:el().elementType.isRequired,tooltip:el().bool.isRequired,formatVerboseDate:el().func,verboseDateFormat:el().object,updateInterval:el().oneOfType([el().number,el().arrayOf(el().shape({threshold:el().number,interval:el().number.isRequired}))]),tick:el().bool,wrapperComponent:el().func,wrapperProps:el().object},ap.defaultProps={locales:[],component:av,tooltip:!0,verboseDateFormat:{weekday:"long",day:"numeric",month:"long",year:"numeric",hour:"numeric",minute:"2-digit",second:"2-digit"},tick:!0},ap=l.memo(ap);let ab=ap;var am,ag=31536e9;function av(e){var t=e.date,n=e.verboseDate,r=e.tooltip,i=e.children,a=ah(e,["date","verboseDate","tooltip","children"]),o=(0,l.useMemo)(function(){return t.toISOString()},[t]);return l.createElement("time",af({},a,{dateTime:o,title:r?n:void 0}),i)}av.propTypes={date:el().instanceOf(Date).isRequired,verboseDate:el().string,tooltip:el().bool.isRequired,children:el().string.isRequired};var ay=n(30381),aw=n.n(ay),a_=n(31657);function aE(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function aS(e){for(var t=1;te.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0?new rs.cA({graphQLErrors:i}):void 0;if(u===s.current.mutationId&&!c.ignoreResults){var f={called:!0,loading:!1,data:r,error:l,client:a};s.current.isMounted&&!(0,ri.D)(s.current.result,f)&&o(s.current.result=f)}var d=e.onCompleted||(null===(n=s.current.options)||void 0===n?void 0:n.onCompleted);return null==d||d(t.data,c),t}).catch(function(t){if(u===s.current.mutationId&&s.current.isMounted){var n,r={loading:!1,error:t,data:void 0,called:!0,client:a};(0,ri.D)(s.current.result,r)||o(s.current.result=r)}var i=e.onError||(null===(n=s.current.options)||void 0===n?void 0:n.onError);if(i)return i(t,c),{data:void 0,errors:t};throw t})},[]),c=(0,l.useCallback)(function(){s.current.isMounted&&o({called:!1,loading:!1,client:n})},[]);return(0,l.useEffect)(function(){return s.current.isMounted=!0,function(){s.current.isMounted=!1}},[]),[u,(0,t0.pi)({reset:c},a)]}var os=n(59067),ou=n(28428),oc=n(11186),ol=n(78513);function of(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}var od=function(e){return(0,b.createStyles)({paper:{display:"flex",margin:"".concat(2.5*e.spacing.unit,"px 0"),padding:"".concat(3*e.spacing.unit,"px ").concat(3.5*e.spacing.unit,"px")},content:{flex:1,width:"100%"},actions:of({marginTop:-(1.5*e.spacing.unit),marginLeft:-(4*e.spacing.unit)},e.breakpoints.up("sm"),{marginLeft:0,marginRight:-(1.5*e.spacing.unit)}),itemBlock:{border:"1px solid rgba(224, 224, 224, 1)",borderRadius:e.shape.borderRadius,padding:2*e.spacing.unit,marginTop:e.spacing.unit},itemBlockText:{overflowWrap:"anywhere"}})},oh=(0,b.withStyles)(od)(function(e){var t=e.actions,n=e.children,r=e.classes;return l.createElement(ii.default,{className:r.paper},l.createElement("div",{className:r.content},n),t&&l.createElement("div",{className:r.actions},t))}),op=function(e){var t=e.title;return l.createElement(x.default,{variant:"subtitle2",gutterBottom:!0},t)},ob=function(e){var t=e.children,n=e.value;return l.createElement(x.default,{variant:"body1",noWrap:!0},t||n)},om=(0,b.withStyles)(od)(function(e){var t=e.children,n=e.classes,r=e.value;return l.createElement("div",{className:n.itemBlock},l.createElement(x.default,{variant:"body1",className:n.itemBlockText},t||r))});function og(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]-1}let sq=sV;function sZ(e,t){var n=this.__data__,r=sH(n,e);return r<0?(++this.size,n.push([e,t])):n[r][1]=t,this}let sX=sZ;function sJ(e){var t=-1,n=null==e?0:e.length;for(this.clear();++t-1&&e%1==0&&e-1&&e%1==0&&e<=cC}let cD=cI;var cN="[object Arguments]",cP="[object Array]",cR="[object Boolean]",cj="[object Date]",cF="[object Error]",cY="[object Function]",cB="[object Map]",cU="[object Number]",cH="[object Object]",c$="[object RegExp]",cz="[object Set]",cG="[object String]",cW="[object WeakMap]",cK="[object ArrayBuffer]",cV="[object DataView]",cq="[object Float64Array]",cZ="[object Int8Array]",cX="[object Int16Array]",cJ="[object Int32Array]",cQ="[object Uint8Array]",c1="[object Uint8ClampedArray]",c0="[object Uint16Array]",c2="[object Uint32Array]",c3={};function c4(e){return eD(e)&&cD(e.length)&&!!c3[eC(e)]}c3["[object Float32Array]"]=c3[cq]=c3[cZ]=c3[cX]=c3[cJ]=c3[cQ]=c3[c1]=c3[c0]=c3[c2]=!0,c3[cN]=c3[cP]=c3[cK]=c3[cR]=c3[cV]=c3[cj]=c3[cF]=c3[cY]=c3[cB]=c3[cU]=c3[cH]=c3[c$]=c3[cz]=c3[cG]=c3[cW]=!1;let c6=c4;function c5(e){return function(t){return e(t)}}let c8=c5;var c9=n(79730),c7=c9.Z&&c9.Z.isTypedArray,le=c7?c8(c7):c6;let lt=le;var ln=Object.prototype.hasOwnProperty;function lr(e,t){var n=cx(e),r=!n&&cS(e),i=!n&&!r&&(0,cT.Z)(e),a=!n&&!r&&!i&<(e),o=n||r||i||a,s=o?cb(e.length,String):[],u=s.length;for(var c in e)(t||ln.call(e,c))&&!(o&&("length"==c||i&&("offset"==c||"parent"==c)||a&&("buffer"==c||"byteLength"==c||"byteOffset"==c)||cL(c,u)))&&s.push(c);return s}let li=lr;var la=Object.prototype;function lo(e){var t=e&&e.constructor;return e===("function"==typeof t&&t.prototype||la)}let ls=lo;var lu=sT(Object.keys,Object);let lc=lu;var ll=Object.prototype.hasOwnProperty;function lf(e){if(!ls(e))return lc(e);var t=[];for(var n in Object(e))ll.call(e,n)&&"constructor"!=n&&t.push(n);return t}let ld=lf;function lh(e){return null!=e&&cD(e.length)&&!ur(e)}let lp=lh;function lb(e){return lp(e)?li(e):ld(e)}let lm=lb;function lg(e,t){return e&&ch(t,lm(t),e)}let lv=lg;function ly(e){var t=[];if(null!=e)for(var n in Object(e))t.push(n);return t}let lw=ly;var l_=Object.prototype.hasOwnProperty;function lE(e){if(!ed(e))return lw(e);var t=ls(e),n=[];for(var r in e)"constructor"==r&&(t||!l_.call(e,r))||n.push(r);return n}let lS=lE;function lk(e){return lp(e)?li(e,!0):lS(e)}let lx=lk;function lT(e,t){return e&&ch(t,lx(t),e)}let lM=lT;var lO=n(42896);function lA(e,t){var n=-1,r=e.length;for(t||(t=Array(r));++n=0||(i[n]=e[n]);return i}function hu(e){if(void 0===e)throw ReferenceError("this hasn't been initialised - super() hasn't been called");return e}var hc=function(e){return Array.isArray(e)&&0===e.length},hl=function(e){return"function"==typeof e},hf=function(e){return null!==e&&"object"==typeof e},hd=function(e){return String(Math.floor(Number(e)))===e},hh=function(e){return"[object String]"===Object.prototype.toString.call(e)},hp=function(e){return 0===l.Children.count(e)},hb=function(e){return hf(e)&&hl(e.then)};function hm(e,t,n,r){void 0===r&&(r=0);for(var i=d8(t);e&&r=0?[]:{}}}return(0===a?e:i)[o[a]]===n?e:(void 0===n?delete i[o[a]]:i[o[a]]=n,0===a&&void 0===n&&delete r[o[a]],r)}function hv(e,t,n,r){void 0===n&&(n=new WeakMap),void 0===r&&(r={});for(var i=0,a=Object.keys(e);i0?t.map(function(t){return x(t,hm(e,t))}):[Promise.resolve("DO_NOT_DELETE_YOU_WILL_BE_FIRED")]).then(function(e){return e.reduce(function(e,n,r){return"DO_NOT_DELETE_YOU_WILL_BE_FIRED"===n||n&&(e=hg(e,t[r],n)),e},{})})},[x]),M=(0,l.useCallback)(function(e){return Promise.all([T(e),h.validationSchema?k(e):{},h.validate?S(e):{}]).then(function(e){var t=e[0],n=e[1],r=e[2];return sk.all([t,n,r],{arrayMerge:hL})})},[h.validate,h.validationSchema,T,S,k]),O=hN(function(e){return void 0===e&&(e=_.values),E({type:"SET_ISVALIDATING",payload:!0}),M(e).then(function(e){return v.current&&(E({type:"SET_ISVALIDATING",payload:!1}),sd()(_.errors,e)||E({type:"SET_ERRORS",payload:e})),e})});(0,l.useEffect)(function(){o&&!0===v.current&&sd()(p.current,h.initialValues)&&O(p.current)},[o,O]);var A=(0,l.useCallback)(function(e){var t=e&&e.values?e.values:p.current,n=e&&e.errors?e.errors:b.current?b.current:h.initialErrors||{},r=e&&e.touched?e.touched:m.current?m.current:h.initialTouched||{},i=e&&e.status?e.status:g.current?g.current:h.initialStatus;p.current=t,b.current=n,m.current=r,g.current=i;var a=function(){E({type:"RESET_FORM",payload:{isSubmitting:!!e&&!!e.isSubmitting,errors:n,touched:r,status:i,values:t,isValidating:!!e&&!!e.isValidating,submitCount:e&&e.submitCount&&"number"==typeof e.submitCount?e.submitCount:0}})};if(h.onReset){var o=h.onReset(_.values,V);hb(o)?o.then(a):a()}else a()},[h.initialErrors,h.initialStatus,h.initialTouched]);(0,l.useEffect)(function(){!0===v.current&&!sd()(p.current,h.initialValues)&&(c&&(p.current=h.initialValues,A()),o&&O(p.current))},[c,h.initialValues,A,o,O]),(0,l.useEffect)(function(){c&&!0===v.current&&!sd()(b.current,h.initialErrors)&&(b.current=h.initialErrors||hS,E({type:"SET_ERRORS",payload:h.initialErrors||hS}))},[c,h.initialErrors]),(0,l.useEffect)(function(){c&&!0===v.current&&!sd()(m.current,h.initialTouched)&&(m.current=h.initialTouched||hk,E({type:"SET_TOUCHED",payload:h.initialTouched||hk}))},[c,h.initialTouched]),(0,l.useEffect)(function(){c&&!0===v.current&&!sd()(g.current,h.initialStatus)&&(g.current=h.initialStatus,E({type:"SET_STATUS",payload:h.initialStatus}))},[c,h.initialStatus,h.initialTouched]);var L=hN(function(e){if(y.current[e]&&hl(y.current[e].validate)){var t=hm(_.values,e),n=y.current[e].validate(t);return hb(n)?(E({type:"SET_ISVALIDATING",payload:!0}),n.then(function(e){return e}).then(function(t){E({type:"SET_FIELD_ERROR",payload:{field:e,value:t}}),E({type:"SET_ISVALIDATING",payload:!1})})):(E({type:"SET_FIELD_ERROR",payload:{field:e,value:n}}),Promise.resolve(n))}return h.validationSchema?(E({type:"SET_ISVALIDATING",payload:!0}),k(_.values,e).then(function(e){return e}).then(function(t){E({type:"SET_FIELD_ERROR",payload:{field:e,value:t[e]}}),E({type:"SET_ISVALIDATING",payload:!1})})):Promise.resolve()}),C=(0,l.useCallback)(function(e,t){var n=t.validate;y.current[e]={validate:n}},[]),I=(0,l.useCallback)(function(e){delete y.current[e]},[]),D=hN(function(e,t){return E({type:"SET_TOUCHED",payload:e}),(void 0===t?i:t)?O(_.values):Promise.resolve()}),N=(0,l.useCallback)(function(e){E({type:"SET_ERRORS",payload:e})},[]),P=hN(function(e,t){var r=hl(e)?e(_.values):e;return E({type:"SET_VALUES",payload:r}),(void 0===t?n:t)?O(r):Promise.resolve()}),R=(0,l.useCallback)(function(e,t){E({type:"SET_FIELD_ERROR",payload:{field:e,value:t}})},[]),j=hN(function(e,t,r){return E({type:"SET_FIELD_VALUE",payload:{field:e,value:t}}),(void 0===r?n:r)?O(hg(_.values,e,t)):Promise.resolve()}),F=(0,l.useCallback)(function(e,t){var n,r=t,i=e;if(!hh(e)){e.persist&&e.persist();var a=e.target?e.target:e.currentTarget,o=a.type,s=a.name,u=a.id,c=a.value,l=a.checked,f=(a.outerHTML,a.options),d=a.multiple;r=t||s||u,i=/number|range/.test(o)?(n=parseFloat(c),isNaN(n)?"":n):/checkbox/.test(o)?hI(hm(_.values,r),l,c):d?hC(f):c}r&&j(r,i)},[j,_.values]),Y=hN(function(e){if(hh(e))return function(t){return F(t,e)};F(e)}),B=hN(function(e,t,n){return void 0===t&&(t=!0),E({type:"SET_FIELD_TOUCHED",payload:{field:e,value:t}}),(void 0===n?i:n)?O(_.values):Promise.resolve()}),U=(0,l.useCallback)(function(e,t){e.persist&&e.persist();var n,r=e.target,i=r.name,a=r.id;r.outerHTML,B(t||i||a,!0)},[B]),H=hN(function(e){if(hh(e))return function(t){return U(t,e)};U(e)}),$=(0,l.useCallback)(function(e){hl(e)?E({type:"SET_FORMIK_STATE",payload:e}):E({type:"SET_FORMIK_STATE",payload:function(){return e}})},[]),z=(0,l.useCallback)(function(e){E({type:"SET_STATUS",payload:e})},[]),G=(0,l.useCallback)(function(e){E({type:"SET_ISSUBMITTING",payload:e})},[]),W=hN(function(){return E({type:"SUBMIT_ATTEMPT"}),O().then(function(e){var t,n=e instanceof Error;if(!n&&0===Object.keys(e).length){try{if(void 0===(t=q()))return}catch(r){throw r}return Promise.resolve(t).then(function(e){return v.current&&E({type:"SUBMIT_SUCCESS"}),e}).catch(function(e){if(v.current)throw E({type:"SUBMIT_FAILURE"}),e})}if(v.current&&(E({type:"SUBMIT_FAILURE"}),n))throw e})}),K=hN(function(e){e&&e.preventDefault&&hl(e.preventDefault)&&e.preventDefault(),e&&e.stopPropagation&&hl(e.stopPropagation)&&e.stopPropagation(),W().catch(function(e){console.warn("Warning: An unhandled error was caught from submitForm()",e)})}),V={resetForm:A,validateForm:O,validateField:L,setErrors:N,setFieldError:R,setFieldTouched:B,setFieldValue:j,setStatus:z,setSubmitting:G,setTouched:D,setValues:P,setFormikState:$,submitForm:W},q=hN(function(){return f(_.values,V)}),Z=hN(function(e){e&&e.preventDefault&&hl(e.preventDefault)&&e.preventDefault(),e&&e.stopPropagation&&hl(e.stopPropagation)&&e.stopPropagation(),A()}),X=(0,l.useCallback)(function(e){return{value:hm(_.values,e),error:hm(_.errors,e),touched:!!hm(_.touched,e),initialValue:hm(p.current,e),initialTouched:!!hm(m.current,e),initialError:hm(b.current,e)}},[_.errors,_.touched,_.values]),J=(0,l.useCallback)(function(e){return{setValue:function(t,n){return j(e,t,n)},setTouched:function(t,n){return B(e,t,n)},setError:function(t){return R(e,t)}}},[j,B,R]),Q=(0,l.useCallback)(function(e){var t=hf(e),n=t?e.name:e,r=hm(_.values,n),i={name:n,value:r,onChange:Y,onBlur:H};if(t){var a=e.type,o=e.value,s=e.as,u=e.multiple;"checkbox"===a?void 0===o?i.checked=!!r:(i.checked=!!(Array.isArray(r)&&~r.indexOf(o)),i.value=o):"radio"===a?(i.checked=r===o,i.value=o):"select"===s&&u&&(i.value=i.value||[],i.multiple=!0)}return i},[H,Y,_.values]),ee=(0,l.useMemo)(function(){return!sd()(p.current,_.values)},[p.current,_.values]),et=(0,l.useMemo)(function(){return void 0!==s?ee?_.errors&&0===Object.keys(_.errors).length:!1!==s&&hl(s)?s(h):s:_.errors&&0===Object.keys(_.errors).length},[s,ee,_.errors,h]);return ha({},_,{initialValues:p.current,initialErrors:b.current,initialTouched:m.current,initialStatus:g.current,handleBlur:H,handleChange:Y,handleReset:Z,handleSubmit:K,resetForm:A,setErrors:N,setFormikState:$,setFieldTouched:B,setFieldValue:j,setFieldError:R,setStatus:z,setSubmitting:G,setTouched:D,setValues:P,submitForm:W,validateForm:O,validateField:L,isValid:et,dirty:ee,unregisterField:I,registerField:C,getFieldProps:Q,getFieldMeta:X,getFieldHelpers:J,validateOnBlur:i,validateOnChange:n,validateOnMount:o})}function hT(e){var t=hx(e),n=e.component,r=e.children,i=e.render,a=e.innerRef;return(0,l.useImperativeHandle)(a,function(){return t}),(0,l.createElement)(hw,{value:t},n?(0,l.createElement)(n,t):i?i(t):r?hl(r)?r(t):hp(r)?null:l.Children.only(r):null)}function hM(e){var t={};if(e.inner){if(0===e.inner.length)return hg(t,e.path,e.message);for(var n=e.inner,r=Array.isArray(n),i=0,n=r?n:n[Symbol.iterator]();;){if(r){if(i>=n.length)break;a=n[i++]}else{if((i=n.next()).done)break;a=i.value}var a,o=a;hm(t,o.path)||(t=hg(t,o.path,o.message))}}return t}function hO(e,t,n,r){void 0===n&&(n=!1),void 0===r&&(r={});var i=hA(e);return t[n?"validateSync":"validate"](i,{abortEarly:!1,context:r})}function hA(e){var t=Array.isArray(e)?[]:{};for(var n in e)if(Object.prototype.hasOwnProperty.call(e,n)){var r=String(n);!0===Array.isArray(e[r])?t[r]=e[r].map(function(e){return!0===Array.isArray(e)||sR(e)?hA(e):""!==e?e:void 0}):sR(e[r])?t[r]=hA(e[r]):t[r]=""!==e[r]?e[r]:void 0}return t}function hL(e,t,n){var r=e.slice();return t.forEach(function(t,i){if(void 0===r[i]){var a=!1!==n.clone&&n.isMergeableObject(t);r[i]=a?sk(Array.isArray(t)?[]:{},t,n):t}else n.isMergeableObject(t)?r[i]=sk(e[i],t,n):-1===e.indexOf(t)&&r.push(t)}),r}function hC(e){return Array.from(e).filter(function(e){return e.selected}).map(function(e){return e.value})}function hI(e,t,n){if("boolean"==typeof e)return Boolean(t);var r=[],i=!1,a=-1;if(Array.isArray(e))r=e,i=(a=e.indexOf(n))>=0;else if(!n||"true"==n||"false"==n)return Boolean(t);return t&&n&&!i?r.concat(n):i?r.slice(0,a).concat(r.slice(a+1)):r}var hD="undefined"!=typeof window&&void 0!==window.document&&void 0!==window.document.createElement?l.useLayoutEffect:l.useEffect;function hN(e){var t=(0,l.useRef)(e);return hD(function(){t.current=e}),(0,l.useCallback)(function(){for(var e=arguments.length,n=Array(e),r=0;re?t:e},0);return Array.from(ha({},e,{length:t+1}))};(function(e){function t(t){var n;return(n=e.call(this,t)||this).updateArrayField=function(e,t,r){var i=n.props,a=i.name;(0,i.formik.setFormikState)(function(n){var i="function"==typeof r?r:e,o="function"==typeof t?t:e,s=hg(n.values,a,e(hm(n.values,a))),u=r?i(hm(n.errors,a)):void 0,c=t?o(hm(n.touched,a)):void 0;return hc(u)&&(u=void 0),hc(c)&&(c=void 0),ha({},n,{values:s,errors:r?hg(n.errors,a,u):n.errors,touched:t?hg(n.touched,a,c):n.touched})})},n.push=function(e){return n.updateArrayField(function(t){return[].concat(hU(t),[hi(e)])},!1,!1)},n.handlePush=function(e){return function(){return n.push(e)}},n.swap=function(e,t){return n.updateArrayField(function(n){return hF(n,e,t)},!0,!0)},n.handleSwap=function(e,t){return function(){return n.swap(e,t)}},n.move=function(e,t){return n.updateArrayField(function(n){return hj(n,e,t)},!0,!0)},n.handleMove=function(e,t){return function(){return n.move(e,t)}},n.insert=function(e,t){return n.updateArrayField(function(n){return hY(n,e,t)},function(t){return hY(t,e,null)},function(t){return hY(t,e,null)})},n.handleInsert=function(e,t){return function(){return n.insert(e,t)}},n.replace=function(e,t){return n.updateArrayField(function(n){return hB(n,e,t)},!1,!1)},n.handleReplace=function(e,t){return function(){return n.replace(e,t)}},n.unshift=function(e){var t=-1;return n.updateArrayField(function(n){var r=n?[e].concat(n):[e];return t<0&&(t=r.length),r},function(e){var n=e?[null].concat(e):[null];return t<0&&(t=n.length),n},function(e){var n=e?[null].concat(e):[null];return t<0&&(t=n.length),n}),t},n.handleUnshift=function(e){return function(){return n.unshift(e)}},n.handleRemove=function(e){return function(){return n.remove(e)}},n.handlePop=function(){return function(){return n.pop()}},n.remove=n.remove.bind(hu(n)),n.pop=n.pop.bind(hu(n)),n}ho(t,e);var n=t.prototype;return n.componentDidUpdate=function(e){this.props.validateOnChange&&this.props.formik.validateOnChange&&!sd()(hm(e.formik.values,e.name),hm(this.props.formik.values,this.props.name))&&this.props.formik.validateForm(this.props.formik.values)},n.remove=function(e){var t;return this.updateArrayField(function(n){var r=n?hU(n):[];return t||(t=r[e]),hl(r.splice)&&r.splice(e,1),r},!0,!0),t},n.pop=function(){var e;return this.updateArrayField(function(t){var n=t;return e||(e=n&&n.pop&&n.pop()),n},!0,!0),e},n.render=function(){var e={push:this.push,pop:this.pop,swap:this.swap,move:this.move,insert:this.insert,replace:this.replace,unshift:this.unshift,remove:this.remove,handlePush:this.handlePush,handlePop:this.handlePop,handleSwap:this.handleSwap,handleMove:this.handleMove,handleInsert:this.handleInsert,handleReplace:this.handleReplace,handleUnshift:this.handleUnshift,handleRemove:this.handleRemove},t=this.props,n=t.component,r=t.render,i=t.children,a=t.name,o=hs(t.formik,["validate","validationSchema"]),s=ha({},e,{form:o,name:a});return n?(0,l.createElement)(n,s):r?r(s):i?"function"==typeof i?i(s):hp(i)?null:l.Children.only(i):null},t})(l.Component).defaultProps={validateOnChange:!0},l.Component,l.Component;var hH=n(24802),h$=n(71209),hz=n(91750),hG=n(11970),hW=n(4689),hK=n(67598),hV=function(){return(hV=Object.assign||function(e){for(var t,n=1,r=arguments.length;nt.indexOf(r)&&(n[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var i=0,r=Object.getOwnPropertySymbols(e);it.indexOf(r[i])&&(n[r[i]]=e[r[i]]);return n}function hZ(e){var t=e.disabled,n=e.field,r=n.onBlur,i=hq(n,["onBlur"]),a=e.form,o=a.isSubmitting,s=a.touched,u=a.errors,c=e.onBlur,l=e.helperText,f=hq(e,["disabled","field","form","onBlur","helperText"]),d=hm(u,i.name),h=hm(s,i.name)&&!!d;return hV(hV({variant:f.variant,error:h,helperText:h?d:l,disabled:null!=t?t:o,onBlur:null!=c?c:function(e){r(null!=e?e:i.name)}},i),f)}function hX(e){var t=e.children,n=hq(e,["children"]);return(0,l.createElement)(iw.Z,hV({},hZ(n)),t)}function hJ(e){var t=e.disabled,n=e.field,r=n.onBlur,i=hq(n,["onBlur"]),a=e.form.isSubmitting,o=(e.type,e.onBlur),s=hq(e,["disabled","field","form","type","onBlur"]);return hV(hV({disabled:null!=t?t:a,onBlur:null!=o?o:function(e){r(null!=e?e:i.name)}},i),s)}function hQ(e){return(0,l.createElement)(hH.Z,hV({},hJ(e)))}function h1(e){var t,n=e.disabled,r=e.field,i=r.onBlur,a=hq(r,["onBlur"]),o=e.form.isSubmitting,s=(e.type,e.onBlur),u=hq(e,["disabled","field","form","type","onBlur"]);return hV(hV({disabled:null!=n?n:o,indeterminate:!Array.isArray(a.value)&&null==a.value,onBlur:null!=s?s:function(e){i(null!=e?e:a.name)}},a),u)}function h0(e){return(0,l.createElement)(h$.Z,hV({},h1(e)))}function h2(e){var t=e.Label,n=hq(e,["Label"]);return(0,l.createElement)(hz.Z,hV({control:(0,l.createElement)(h$.Z,hV({},h1(n)))},t))}function h3(e){var t=e.disabled,n=e.field,r=n.onBlur,i=hq(n,["onBlur"]),a=e.form.isSubmitting,o=e.onBlur,s=hq(e,["disabled","field","form","onBlur"]);return hV(hV({disabled:null!=t?t:a,onBlur:null!=o?o:function(e){r(null!=e?e:i.name)}},i),s)}function h4(e){return(0,l.createElement)(hG.default,hV({},h3(e)))}function h6(e){var t=e.field,n=t.onBlur,r=hq(t,["onBlur"]),i=(e.form,e.onBlur),a=hq(e,["field","form","onBlur"]);return hV(hV({onBlur:null!=i?i:function(e){n(null!=e?e:r.name)}},r),a)}function h5(e){return(0,l.createElement)(hW.Z,hV({},h6(e)))}function h8(e){var t=e.disabled,n=e.field,r=n.onBlur,i=hq(n,["onBlur"]),a=e.form.isSubmitting,o=e.onBlur,s=hq(e,["disabled","field","form","onBlur"]);return hV(hV({disabled:null!=t?t:a,onBlur:null!=o?o:function(e){r(null!=e?e:i.name)}},i),s)}function h9(e){return(0,l.createElement)(hK.default,hV({},h8(e)))}hX.displayName="FormikMaterialUITextField",hQ.displayName="FormikMaterialUISwitch",h0.displayName="FormikMaterialUICheckbox",h2.displayName="FormikMaterialUICheckboxWithLabel",h4.displayName="FormikMaterialUISelect",h5.displayName="FormikMaterialUIRadioGroup",h9.displayName="FormikMaterialUIInputBase";try{a=Map}catch(h7){}try{o=Set}catch(pe){}function pt(e,t,n){if(!e||"object"!=typeof e||"function"==typeof e)return e;if(e.nodeType&&"cloneNode"in e)return e.cloneNode(!0);if(e instanceof Date)return new Date(e.getTime());if(e instanceof RegExp)return RegExp(e);if(Array.isArray(e))return e.map(pn);if(a&&e instanceof a)return new Map(Array.from(e.entries()));if(o&&e instanceof o)return new Set(Array.from(e.values()));if(e instanceof Object){t.push(e);var r=Object.create(e);for(var i in n.push(r),e){var s=t.findIndex(function(t){return t===e[i]});r[i]=s>-1?n[s]:pt(e[i],t,n)}return r}return e}function pn(e){return pt(e,[],[])}let pr=Object.prototype.toString,pi=Error.prototype.toString,pa=RegExp.prototype.toString,po="undefined"!=typeof Symbol?Symbol.prototype.toString:()=>"",ps=/^Symbol\((.*)\)(.*)$/;function pu(e){if(e!=+e)return"NaN";let t=0===e&&1/e<0;return t?"-0":""+e}function pc(e,t=!1){if(null==e||!0===e||!1===e)return""+e;let n=typeof e;if("number"===n)return pu(e);if("string"===n)return t?`"${e}"`:e;if("function"===n)return"[Function "+(e.name||"anonymous")+"]";if("symbol"===n)return po.call(e).replace(ps,"Symbol($1)");let r=pr.call(e).slice(8,-1);return"Date"===r?isNaN(e.getTime())?""+e:e.toISOString(e):"Error"===r||e instanceof Error?"["+pi.call(e)+"]":"RegExp"===r?pa.call(e):null}function pl(e,t){let n=pc(e,t);return null!==n?n:JSON.stringify(e,function(e,n){let r=pc(this[e],t);return null!==r?r:n},2)}let pf={default:"${path} is invalid",required:"${path} is a required field",oneOf:"${path} must be one of the following values: ${values}",notOneOf:"${path} must not be one of the following values: ${values}",notType({path:e,type:t,value:n,originalValue:r}){let i=null!=r&&r!==n,a=`${e} must be a \`${t}\` type, but the final value was: \`${pl(n,!0)}\``+(i?` (cast from the value \`${pl(r,!0)}\`).`:".");return null===n&&(a+='\n If "null" is intended as an empty value be sure to mark the schema as `.nullable()`'),a},defined:"${path} must be defined"},pd={length:"${path} must be exactly ${length} characters",min:"${path} must be at least ${min} characters",max:"${path} must be at most ${max} characters",matches:'${path} must match the following: "${regex}"',email:"${path} must be a valid email",url:"${path} must be a valid URL",uuid:"${path} must be a valid UUID",trim:"${path} must be a trimmed string",lowercase:"${path} must be a lowercase string",uppercase:"${path} must be a upper case string"},ph={min:"${path} must be greater than or equal to ${min}",max:"${path} must be less than or equal to ${max}",lessThan:"${path} must be less than ${less}",moreThan:"${path} must be greater than ${more}",positive:"${path} must be a positive number",negative:"${path} must be a negative number",integer:"${path} must be an integer"},pp={min:"${path} field must be later than ${min}",max:"${path} field must be at earlier than ${max}"},pb={isValue:"${path} field must be ${value}"},pm={noUnknown:"${path} field has unspecified keys: ${unknown}"},pg={min:"${path} field must have at least ${min} items",max:"${path} field must have less than or equal to ${max} items",length:"${path} must be have ${length} items"};Object.assign(Object.create(null),{mixed:pf,string:pd,number:ph,date:pp,object:pm,array:pg,boolean:pb});var pv=n(18721),py=n.n(pv);let pw=e=>e&&e.__isYupSchema__;class p_{constructor(e,t){if(this.refs=e,this.refs=e,"function"==typeof t){this.fn=t;return}if(!py()(t,"is"))throw TypeError("`is:` is required for `when()` conditions");if(!t.then&&!t.otherwise)throw TypeError("either `then:` or `otherwise:` is required for `when()` conditions");let{is:n,then:r,otherwise:i}=t,a="function"==typeof n?n:(...e)=>e.every(e=>e===n);this.fn=function(...e){let t=e.pop(),n=e.pop(),o=a(...e)?r:i;if(o)return"function"==typeof o?o(n):n.concat(o.resolve(t))}}resolve(e,t){let n=this.refs.map(e=>e.getValue(null==t?void 0:t.value,null==t?void 0:t.parent,null==t?void 0:t.context)),r=this.fn.apply(e,n.concat(e,t));if(void 0===r||r===e)return e;if(!pw(r))throw TypeError("conditions must return a schema object");return r.resolve(t)}}let pE=p_;function pS(e){return null==e?[]:[].concat(e)}function pk(){return(pk=Object.assign||function(e){for(var t=1;tpl(t[n])):"function"==typeof e?e(t):e}static isError(e){return e&&"ValidationError"===e.name}constructor(e,t,n,r){super(),this.name="ValidationError",this.value=t,this.path=n,this.type=r,this.errors=[],this.inner=[],pS(e).forEach(e=>{pT.isError(e)?(this.errors.push(...e.errors),this.inner=this.inner.concat(e.inner.length?e.inner:e)):this.errors.push(e)}),this.message=this.errors.length>1?`${this.errors.length} errors occurred`:this.errors[0],Error.captureStackTrace&&Error.captureStackTrace(this,pT)}}let pM=e=>{let t=!1;return(...n)=>{t||(t=!0,e(...n))}};function pO(e,t){let{endEarly:n,tests:r,args:i,value:a,errors:o,sort:s,path:u}=e,c=pM(t),l=r.length,f=[];if(o=o||[],!l)return o.length?c(new pT(o,a,u)):c(null,a);for(let d=0;d=0||(i[n]=e[n]);return i}function pR(e){function t(t,n){let{value:r,path:i="",label:a,options:o,originalValue:s,sync:u}=t,c=pP(t,["value","path","label","options","originalValue","sync"]),{name:l,test:f,params:d,message:h}=e,{parent:p,context:b}=o;function m(e){return pD.isRef(e)?e.getValue(r,p,b):e}function g(e={}){let t=pL()(pN({value:r,originalValue:s,label:a,path:e.path||i},d,e.params),m),n=new pT(pT.formatError(e.message||h,t),r,t.path,e.type||l);return n.params=t,n}let v=pN({path:i,parent:p,type:l,createError:g,resolve:m,options:o,originalValue:s},c);if(!u){try{Promise.resolve(f.call(v,r,v)).then(e=>{pT.isError(e)?n(e):e?n(null,e):n(g())})}catch(y){n(y)}return}let w;try{var _;if(w=f.call(v,r,v),"function"==typeof(null==(_=w)?void 0:_.then))throw Error(`Validation test of type: "${v.type}" returned a Promise during a synchronous validate. This test will finish after the validate call has returned`)}catch(E){n(E);return}pT.isError(w)?n(w):w?n(null,w):n(g())}return t.OPTIONS=e,t}pD.prototype.__isYupRef=!0;let pj=e=>e.substr(0,e.length-1).substr(1);function pF(e,t,n,r=n){let i,a,o;return t?((0,pC.forEach)(t,(s,u,c)=>{let l=u?pj(s):s;if((e=e.resolve({context:r,parent:i,value:n})).innerType){let f=c?parseInt(l,10):0;if(n&&f>=n.length)throw Error(`Yup.reach cannot resolve an array item at index: ${s}, in the path: ${t}. because there is no value at that index. `);i=n,n=n&&n[f],e=e.innerType}if(!c){if(!e.fields||!e.fields[l])throw Error(`The schema does not contain the path: ${t}. (failed at: ${o} which is a type: "${e._type}")`);i=n,n=n&&n[l],e=e.fields[l]}a=l,o=u?"["+s+"]":"."+s}),{schema:e,parent:i,parentPath:a}):{parent:i,parentPath:t,schema:e}}class pY{constructor(){this.list=new Set,this.refs=new Map}get size(){return this.list.size+this.refs.size}describe(){let e=[];for(let t of this.list)e.push(t);for(let[,n]of this.refs)e.push(n.describe());return e}toArray(){return Array.from(this.list).concat(Array.from(this.refs.values()))}add(e){pD.isRef(e)?this.refs.set(e.key,e):this.list.add(e)}delete(e){pD.isRef(e)?this.refs.delete(e.key):this.list.delete(e)}has(e,t){if(this.list.has(e))return!0;let n,r=this.refs.values();for(;!(n=r.next()).done;)if(t(n.value)===e)return!0;return!1}clone(){let e=new pY;return e.list=new Set(this.list),e.refs=new Map(this.refs),e}merge(e,t){let n=this.clone();return e.list.forEach(e=>n.add(e)),e.refs.forEach(e=>n.add(e)),t.list.forEach(e=>n.delete(e)),t.refs.forEach(e=>n.delete(e)),n}}function pB(){return(pB=Object.assign||function(e){for(var t=1;t{this.typeError(pf.notType)}),this.type=(null==e?void 0:e.type)||"mixed",this.spec=pB({strip:!1,strict:!1,abortEarly:!0,recursive:!0,nullable:!1,presence:"optional"},null==e?void 0:e.spec)}get _type(){return this.type}_typeCheck(e){return!0}clone(e){if(this._mutate)return e&&Object.assign(this.spec,e),this;let t=Object.create(Object.getPrototypeOf(this));return t.type=this.type,t._typeError=this._typeError,t._whitelistError=this._whitelistError,t._blacklistError=this._blacklistError,t._whitelist=this._whitelist.clone(),t._blacklist=this._blacklist.clone(),t.exclusiveTests=pB({},this.exclusiveTests),t.deps=[...this.deps],t.conditions=[...this.conditions],t.tests=[...this.tests],t.transforms=[...this.transforms],t.spec=pn(pB({},this.spec,e)),t}label(e){var t=this.clone();return t.spec.label=e,t}meta(...e){if(0===e.length)return this.spec.meta;let t=this.clone();return t.spec.meta=Object.assign(t.spec.meta||{},e[0]),t}withMutation(e){let t=this._mutate;this._mutate=!0;let n=e(this);return this._mutate=t,n}concat(e){if(!e||e===this)return this;if(e.type!==this.type&&"mixed"!==this.type)throw TypeError(`You cannot \`concat()\` schema's of different types: ${this.type} and ${e.type}`);let t=this,n=e.clone(),r=pB({},t.spec,n.spec);return n.spec=r,n._typeError||(n._typeError=t._typeError),n._whitelistError||(n._whitelistError=t._whitelistError),n._blacklistError||(n._blacklistError=t._blacklistError),n._whitelist=t._whitelist.merge(e._whitelist,e._blacklist),n._blacklist=t._blacklist.merge(e._blacklist,e._whitelist),n.tests=t.tests,n.exclusiveTests=t.exclusiveTests,n.withMutation(t=>{e.tests.forEach(e=>{t.test(e.OPTIONS)})}),n}isType(e){return!!this.spec.nullable&&null===e||this._typeCheck(e)}resolve(e){let t=this;if(t.conditions.length){let n=t.conditions;(t=t.clone()).conditions=[],t=(t=n.reduce((t,n)=>n.resolve(t,e),t)).resolve(e)}return t}cast(e,t={}){let n=this.resolve(pB({value:e},t)),r=n._cast(e,t);if(void 0!==e&&!1!==t.assert&&!0!==n.isType(r)){let i=pl(e),a=pl(r);throw TypeError(`The value of ${t.path||"field"} could not be cast to a value that satisfies the schema type: "${n._type}".
+ */ Object.defineProperty(t,"__esModule",{value:!0}),"undefined"==typeof window||"function"!=typeof MessageChannel){var n,r,i,a,o,s=null,u=null,c=function(){if(null!==s)try{var e=t.unstable_now();s(!0,e),s=null}catch(n){throw setTimeout(c,0),n}},l=Date.now();t.unstable_now=function(){return Date.now()-l},n=function(e){null!==s?setTimeout(n,0,e):(s=e,setTimeout(c,0))},r=function(e,t){u=setTimeout(e,t)},i=function(){clearTimeout(u)},a=function(){return!1},o=t.unstable_forceFrameRate=function(){}}else{var f=window.performance,d=window.Date,h=window.setTimeout,p=window.clearTimeout;if("undefined"!=typeof console){var b=window.cancelAnimationFrame;"function"!=typeof window.requestAnimationFrame&&console.error("This browser doesn't support requestAnimationFrame. Make sure that you load a polyfill in older browsers. https://fb.me/react-polyfills"),"function"!=typeof b&&console.error("This browser doesn't support cancelAnimationFrame. Make sure that you load a polyfill in older browsers. https://fb.me/react-polyfills")}if("object"==typeof f&&"function"==typeof f.now)t.unstable_now=function(){return f.now()};else{var m=d.now();t.unstable_now=function(){return d.now()-m}}var g=!1,v=null,y=-1,w=5,_=0;a=function(){return t.unstable_now()>=_},o=function(){},t.unstable_forceFrameRate=function(e){0>e||125M(o,n))void 0!==u&&0>M(u,o)?(e[r]=u,e[s]=n,r=s):(e[r]=o,e[a]=n,r=a);else if(void 0!==u&&0>M(u,n))e[r]=u,e[s]=n,r=s;else break a}}return t}return null}function M(e,t){var n=e.sortIndex-t.sortIndex;return 0!==n?n:e.id-t.id}var O=[],A=[],L=1,C=null,I=3,D=!1,N=!1,P=!1;function R(e){for(var t=x(A);null!==t;){if(null===t.callback)T(A);else if(t.startTime<=e)T(A),t.sortIndex=t.expirationTime,k(O,t);else break;t=x(A)}}function j(e){if(P=!1,R(e),!N){if(null!==x(O))N=!0,n(F);else{var t=x(A);null!==t&&r(j,t.startTime-e)}}}function F(e,n){N=!1,P&&(P=!1,i()),D=!0;var o=I;try{for(R(n),C=x(O);null!==C&&(!(C.expirationTime>n)||e&&!a());){var s=C.callback;if(null!==s){C.callback=null,I=C.priorityLevel;var u=s(C.expirationTime<=n);n=t.unstable_now(),"function"==typeof u?C.callback=u:C===x(O)&&T(O),R(n)}else T(O);C=x(O)}if(null!==C)var c=!0;else{var l=x(A);null!==l&&r(j,l.startTime-n),c=!1}return c}finally{C=null,I=o,D=!1}}function Y(e){switch(e){case 1:return -1;case 2:return 250;case 5:return 1073741823;case 4:return 1e4;default:return 5e3}}var B=o;t.unstable_ImmediatePriority=1,t.unstable_UserBlockingPriority=2,t.unstable_NormalPriority=3,t.unstable_IdlePriority=5,t.unstable_LowPriority=4,t.unstable_runWithPriority=function(e,t){switch(e){case 1:case 2:case 3:case 4:case 5:break;default:e=3}var n=I;I=e;try{return t()}finally{I=n}},t.unstable_next=function(e){switch(I){case 1:case 2:case 3:var t=3;break;default:t=I}var n=I;I=t;try{return e()}finally{I=n}},t.unstable_scheduleCallback=function(e,a,o){var s=t.unstable_now();if("object"==typeof o&&null!==o){var u=o.delay;u="number"==typeof u&&0s?(e.sortIndex=u,k(A,e),null===x(O)&&e===x(A)&&(P?i():P=!0,r(j,u-s))):(e.sortIndex=o,k(O,e),N||D||(N=!0,n(F))),e},t.unstable_cancelCallback=function(e){e.callback=null},t.unstable_wrapCallback=function(e){var t=I;return function(){var n=I;I=t;try{return e.apply(this,arguments)}finally{I=n}}},t.unstable_getCurrentPriorityLevel=function(){return I},t.unstable_shouldYield=function(){var e=t.unstable_now();R(e);var n=x(O);return n!==C&&null!==C&&null!==n&&null!==n.callback&&n.startTime<=e&&n.expirationTime>5==6?2:e>>4==14?3:e>>3==30?4:e>>6==2?-1:-2}function c(e,t,n){var r=t.length-1;if(r=0?(i>0&&(e.lastNeed=i-1),i):--r=0?(i>0&&(e.lastNeed=i-2),i):--r=0?(i>0&&(2===i?i=0:e.lastNeed=i-3),i):0}function l(e,t,n){if((192&t[0])!=128)return e.lastNeed=0,"�";if(e.lastNeed>1&&t.length>1){if((192&t[1])!=128)return e.lastNeed=1,"�";if(e.lastNeed>2&&t.length>2&&(192&t[2])!=128)return e.lastNeed=2,"�"}}function f(e){var t=this.lastTotal-this.lastNeed,n=l(this,e,t);return void 0!==n?n:this.lastNeed<=e.length?(e.copy(this.lastChar,t,0,this.lastNeed),this.lastChar.toString(this.encoding,0,this.lastTotal)):void(e.copy(this.lastChar,t,0,e.length),this.lastNeed-=e.length)}function d(e,t){var n=c(this,e,t);if(!this.lastNeed)return e.toString("utf8",t);this.lastTotal=n;var r=e.length-(n-this.lastNeed);return e.copy(this.lastChar,0,r),e.toString("utf8",t,r)}function h(e){var t=e&&e.length?this.write(e):"";return this.lastNeed?t+"�":t}function p(e,t){if((e.length-t)%2==0){var n=e.toString("utf16le",t);if(n){var r=n.charCodeAt(n.length-1);if(r>=55296&&r<=56319)return this.lastNeed=2,this.lastTotal=4,this.lastChar[0]=e[e.length-2],this.lastChar[1]=e[e.length-1],n.slice(0,-1)}return n}return this.lastNeed=1,this.lastTotal=2,this.lastChar[0]=e[e.length-1],e.toString("utf16le",t,e.length-1)}function b(e){var t=e&&e.length?this.write(e):"";if(this.lastNeed){var n=this.lastTotal-this.lastNeed;return t+this.lastChar.toString("utf16le",0,n)}return t}function m(e,t){var n=(e.length-t)%3;return 0===n?e.toString("base64",t):(this.lastNeed=3-n,this.lastTotal=3,1===n?this.lastChar[0]=e[e.length-1]:(this.lastChar[0]=e[e.length-2],this.lastChar[1]=e[e.length-1]),e.toString("base64",t,e.length-n))}function g(e){var t=e&&e.length?this.write(e):"";return this.lastNeed?t+this.lastChar.toString("base64",0,3-this.lastNeed):t}function v(e){return e.toString(this.encoding)}function y(e){return e&&e.length?this.write(e):""}t.s=s,s.prototype.write=function(e){var t,n;if(0===e.length)return"";if(this.lastNeed){if(void 0===(t=this.fillLast(e)))return"";n=this.lastNeed,this.lastNeed=0}else n=0;return n */ var r=n(48764),i=r.Buffer;function a(e,t){for(var n in e)t[n]=e[n]}function o(e,t,n){return i(e,t,n)}i.from&&i.alloc&&i.allocUnsafe&&i.allocUnsafeSlow?e.exports=r:(a(r,t),t.Buffer=o),o.prototype=Object.create(i.prototype),a(i,o),o.from=function(e,t,n){if("number"==typeof e)throw TypeError("Argument must not be a number");return i(e,t,n)},o.alloc=function(e,t,n){if("number"!=typeof e)throw TypeError("Argument must be a number");var r=i(e);return void 0!==t?"string"==typeof n?r.fill(t,n):r.fill(t):r.fill(0),r},o.allocUnsafe=function(e){if("number"!=typeof e)throw TypeError("Argument must be a number");return i(e)},o.allocUnsafeSlow=function(e){if("number"!=typeof e)throw TypeError("Argument must be a number");return r.SlowBuffer(e)}},93379(e,t,n){"use strict";var r,i,a=function(){return void 0===r&&(r=Boolean(window&&document&&document.all&&!window.atob)),r},o=(i={},function(e){if(void 0===i[e]){var t=document.querySelector(e);if(window.HTMLIFrameElement&&t instanceof window.HTMLIFrameElement)try{t=t.contentDocument.head}catch(n){t=null}i[e]=t}return i[e]}),s=[];function u(e){for(var t=-1,n=0;nAk});var r,i,a,o,s,u,c,l=n(67294),f=n.t(l,2),d=n(39814),h=n(5977),p=n(57209),b=n(32316),m=n(95880),g=n(17051),v=n(71381),y=n(81701),w=n(3022),_=n(60323),E=n(87591),S=n(25649),k=n(28902),x=n(71426),T=n(48884),M=n(94184),O=n.n(M),A=n(37703),L=n(73935),C=function(){if("undefined"!=typeof Map)return Map;function e(e,t){var n=-1;return e.some(function(e,r){return e[0]===t&&(n=r,!0)}),n}return function(){function t(){this.__entries__=[]}return Object.defineProperty(t.prototype,"size",{get:function(){return this.__entries__.length},enumerable:!0,configurable:!0}),t.prototype.get=function(t){var n=e(this.__entries__,t),r=this.__entries__[n];return r&&r[1]},t.prototype.set=function(t,n){var r=e(this.__entries__,t);~r?this.__entries__[r][1]=n:this.__entries__.push([t,n])},t.prototype.delete=function(t){var n=this.__entries__,r=e(n,t);~r&&n.splice(r,1)},t.prototype.has=function(t){return!!~e(this.__entries__,t)},t.prototype.clear=function(){this.__entries__.splice(0)},t.prototype.forEach=function(e,t){void 0===t&&(t=null);for(var n=0,r=this.__entries__;n0},e.prototype.connect_=function(){I&&!this.connected_&&(document.addEventListener("transitionend",this.onTransitionEnd_),window.addEventListener("resize",this.refresh),Y?(this.mutationsObserver_=new MutationObserver(this.refresh),this.mutationsObserver_.observe(document,{attributes:!0,childList:!0,characterData:!0,subtree:!0})):(document.addEventListener("DOMSubtreeModified",this.refresh),this.mutationEventsAdded_=!0),this.connected_=!0)},e.prototype.disconnect_=function(){I&&this.connected_&&(document.removeEventListener("transitionend",this.onTransitionEnd_),window.removeEventListener("resize",this.refresh),this.mutationsObserver_&&this.mutationsObserver_.disconnect(),this.mutationEventsAdded_&&document.removeEventListener("DOMSubtreeModified",this.refresh),this.mutationsObserver_=null,this.mutationEventsAdded_=!1,this.connected_=!1)},e.prototype.onTransitionEnd_=function(e){var t=e.propertyName,n=void 0===t?"":t;F.some(function(e){return!!~n.indexOf(e)})&&this.refresh()},e.getInstance=function(){return this.instance_||(this.instance_=new e),this.instance_},e.instance_=null,e}(),U=function(e,t){for(var n=0,r=Object.keys(t);n0},e}(),er="undefined"!=typeof WeakMap?new WeakMap:new C,ei=function(){function e(t){if(!(this instanceof e))throw TypeError("Cannot call a class as a function.");if(!arguments.length)throw TypeError("1 argument required, but only 0 present.");var n=B.getInstance(),r=new en(t,n,this);er.set(this,r)}return e}();["observe","unobserve","disconnect"].forEach(function(e){ei.prototype[e]=function(){var t;return(t=er.get(this))[e].apply(t,arguments)}});var ea=void 0!==D.ResizeObserver?D.ResizeObserver:ei;let eo=ea;var es=function(e){var t=[],n=null,r=function(){for(var r=arguments.length,i=Array(r),a=0;a=t||n<0||f&&r>=a}function g(){var e=eb();if(m(e))return v(e);s=setTimeout(g,b(e))}function v(e){return(s=void 0,d&&r)?h(e):(r=i=void 0,o)}function y(){void 0!==s&&clearTimeout(s),c=0,r=u=i=s=void 0}function w(){return void 0===s?o:v(eb())}function _(){var e=eb(),n=m(e);if(r=arguments,i=this,u=e,n){if(void 0===s)return p(u);if(f)return clearTimeout(s),s=setTimeout(g,t),h(u)}return void 0===s&&(s=setTimeout(g,t)),o}return t=ez(t)||0,ed(n)&&(l=!!n.leading,a=(f="maxWait"in n)?eW(ez(n.maxWait)||0,t):a,d="trailing"in n?!!n.trailing:d),_.cancel=y,_.flush=w,_}let eq=eV;var eZ="Expected a function";function eX(e,t,n){var r=!0,i=!0;if("function"!=typeof e)throw TypeError(eZ);return ed(n)&&(r="leading"in n?!!n.leading:r,i="trailing"in n?!!n.trailing:i),eq(e,t,{leading:r,maxWait:t,trailing:i})}let eJ=eX;var eQ={debounce:eq,throttle:eJ},e1=function(e){return eQ[e]},e0=function(e){return"function"==typeof e},e2=function(){return"undefined"==typeof window},e3=function(e){return e instanceof Element||e instanceof HTMLDocument};function e4(e){return(e4="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function e6(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}function e5(e,t){for(var n=0;ne.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&l.createElement(tG.Z,{variant:"indeterminate",classes:r}))};tK.propTypes={fetchCount:el().number.isRequired};let tV=(0,b.withStyles)(tW)(tK);var tq=n(5536);let tZ=n.p+"ba8bbf16ebf8e1d05bef.svg";function tX(){return(tX=Object.assign||function(e){for(var t=1;t120){for(var d=Math.floor(u/80),h=u%80,p=[],b=0;b0},name:{enumerable:!1},nodes:{enumerable:!1},source:{enumerable:!1},positions:{enumerable:!1},originalError:{enumerable:!1}}),null!=s&&s.stack)?(Object.defineProperty(nf(b),"stack",{value:s.stack,writable:!0,configurable:!0}),nl(b)):(Error.captureStackTrace?Error.captureStackTrace(nf(b),n):Object.defineProperty(nf(b),"stack",{value:Error().stack,writable:!0,configurable:!0}),b)}return ns(n,[{key:"toString",value:function(){return nw(this)}},{key:t4.YF,get:function(){return"Object"}}]),n}(nd(Error));function ny(e){return void 0===e||0===e.length?void 0:e}function nw(e){var t=e.message;if(e.nodes)for(var n=0,r=e.nodes;n",EOF:"",BANG:"!",DOLLAR:"$",AMP:"&",PAREN_L:"(",PAREN_R:")",SPREAD:"...",COLON:":",EQUALS:"=",AT:"@",BRACKET_L:"[",BRACKET_R:"]",BRACE_L:"{",PIPE:"|",BRACE_R:"}",NAME:"Name",INT:"Int",FLOAT:"Float",STRING:"String",BLOCK_STRING:"BlockString",COMMENT:"Comment"}),nx=n(10143),nT=Object.freeze({QUERY:"QUERY",MUTATION:"MUTATION",SUBSCRIPTION:"SUBSCRIPTION",FIELD:"FIELD",FRAGMENT_DEFINITION:"FRAGMENT_DEFINITION",FRAGMENT_SPREAD:"FRAGMENT_SPREAD",INLINE_FRAGMENT:"INLINE_FRAGMENT",VARIABLE_DEFINITION:"VARIABLE_DEFINITION",SCHEMA:"SCHEMA",SCALAR:"SCALAR",OBJECT:"OBJECT",FIELD_DEFINITION:"FIELD_DEFINITION",ARGUMENT_DEFINITION:"ARGUMENT_DEFINITION",INTERFACE:"INTERFACE",UNION:"UNION",ENUM:"ENUM",ENUM_VALUE:"ENUM_VALUE",INPUT_OBJECT:"INPUT_OBJECT",INPUT_FIELD_DEFINITION:"INPUT_FIELD_DEFINITION"}),nM=n(87392),nO=function(){function e(e){var t=new nS.WU(nk.SOF,0,0,0,0,null);this.source=e,this.lastToken=t,this.token=t,this.line=1,this.lineStart=0}var t=e.prototype;return t.advance=function(){return this.lastToken=this.token,this.token=this.lookahead()},t.lookahead=function(){var e,t=this.token;if(t.kind!==nk.EOF)do t=null!==(e=t.next)&&void 0!==e?e:t.next=nC(this,t);while(t.kind===nk.COMMENT)return t},e}();function nA(e){return e===nk.BANG||e===nk.DOLLAR||e===nk.AMP||e===nk.PAREN_L||e===nk.PAREN_R||e===nk.SPREAD||e===nk.COLON||e===nk.EQUALS||e===nk.AT||e===nk.BRACKET_L||e===nk.BRACKET_R||e===nk.BRACE_L||e===nk.PIPE||e===nk.BRACE_R}function nL(e){return isNaN(e)?nk.EOF:e<127?JSON.stringify(String.fromCharCode(e)):'"\\u'.concat(("00"+e.toString(16).toUpperCase()).slice(-4),'"')}function nC(e,t){for(var n=e.source,r=n.body,i=r.length,a=t.end;a31||9===a))return new nS.WU(nk.COMMENT,t,s,n,r,i,o.slice(t+1,s))}function nN(e,t,n,r,i,a){var o=e.body,s=n,u=t,c=!1;if(45===s&&(s=o.charCodeAt(++u)),48===s){if((s=o.charCodeAt(++u))>=48&&s<=57)throw n_(e,u,"Invalid number, unexpected digit after 0: ".concat(nL(s),"."))}else u=nP(e,u,s),s=o.charCodeAt(u);if(46===s&&(c=!0,s=o.charCodeAt(++u),u=nP(e,u,s),s=o.charCodeAt(u)),(69===s||101===s)&&(c=!0,(43===(s=o.charCodeAt(++u))||45===s)&&(s=o.charCodeAt(++u)),u=nP(e,u,s),s=o.charCodeAt(u)),46===s||nU(s))throw n_(e,u,"Invalid number, expected digit but got: ".concat(nL(s),"."));return new nS.WU(c?nk.FLOAT:nk.INT,t,u,r,i,a,o.slice(t,u))}function nP(e,t,n){var r=e.body,i=t,a=n;if(a>=48&&a<=57){do a=r.charCodeAt(++i);while(a>=48&&a<=57)return i}throw n_(e,i,"Invalid number, expected digit but got: ".concat(nL(a),"."))}function nR(e,t,n,r,i){for(var a=e.body,o=t+1,s=o,u=0,c="";o=48&&e<=57?e-48:e>=65&&e<=70?e-55:e>=97&&e<=102?e-87:-1}function nB(e,t,n,r,i){for(var a=e.body,o=a.length,s=t+1,u=0;s!==o&&!isNaN(u=a.charCodeAt(s))&&(95===u||u>=48&&u<=57||u>=65&&u<=90||u>=97&&u<=122);)++s;return new nS.WU(nk.NAME,t,s,n,r,i,a.slice(t,s))}function nU(e){return 95===e||e>=65&&e<=90||e>=97&&e<=122}function nH(e,t){return new n$(e,t).parseDocument()}var n$=function(){function e(e,t){var n=(0,nx.T)(e)?e:new nx.H(e);this._lexer=new nO(n),this._options=t}var t=e.prototype;return t.parseName=function(){var e=this.expectToken(nk.NAME);return{kind:nE.h.NAME,value:e.value,loc:this.loc(e)}},t.parseDocument=function(){var e=this._lexer.token;return{kind:nE.h.DOCUMENT,definitions:this.many(nk.SOF,this.parseDefinition,nk.EOF),loc:this.loc(e)}},t.parseDefinition=function(){if(this.peek(nk.NAME))switch(this._lexer.token.value){case"query":case"mutation":case"subscription":return this.parseOperationDefinition();case"fragment":return this.parseFragmentDefinition();case"schema":case"scalar":case"type":case"interface":case"union":case"enum":case"input":case"directive":return this.parseTypeSystemDefinition();case"extend":return this.parseTypeSystemExtension()}else if(this.peek(nk.BRACE_L))return this.parseOperationDefinition();else if(this.peekDescription())return this.parseTypeSystemDefinition();throw this.unexpected()},t.parseOperationDefinition=function(){var e,t=this._lexer.token;if(this.peek(nk.BRACE_L))return{kind:nE.h.OPERATION_DEFINITION,operation:"query",name:void 0,variableDefinitions:[],directives:[],selectionSet:this.parseSelectionSet(),loc:this.loc(t)};var n=this.parseOperationType();return this.peek(nk.NAME)&&(e=this.parseName()),{kind:nE.h.OPERATION_DEFINITION,operation:n,name:e,variableDefinitions:this.parseVariableDefinitions(),directives:this.parseDirectives(!1),selectionSet:this.parseSelectionSet(),loc:this.loc(t)}},t.parseOperationType=function(){var e=this.expectToken(nk.NAME);switch(e.value){case"query":return"query";case"mutation":return"mutation";case"subscription":return"subscription"}throw this.unexpected(e)},t.parseVariableDefinitions=function(){return this.optionalMany(nk.PAREN_L,this.parseVariableDefinition,nk.PAREN_R)},t.parseVariableDefinition=function(){var e=this._lexer.token;return{kind:nE.h.VARIABLE_DEFINITION,variable:this.parseVariable(),type:(this.expectToken(nk.COLON),this.parseTypeReference()),defaultValue:this.expectOptionalToken(nk.EQUALS)?this.parseValueLiteral(!0):void 0,directives:this.parseDirectives(!0),loc:this.loc(e)}},t.parseVariable=function(){var e=this._lexer.token;return this.expectToken(nk.DOLLAR),{kind:nE.h.VARIABLE,name:this.parseName(),loc:this.loc(e)}},t.parseSelectionSet=function(){var e=this._lexer.token;return{kind:nE.h.SELECTION_SET,selections:this.many(nk.BRACE_L,this.parseSelection,nk.BRACE_R),loc:this.loc(e)}},t.parseSelection=function(){return this.peek(nk.SPREAD)?this.parseFragment():this.parseField()},t.parseField=function(){var e,t,n=this._lexer.token,r=this.parseName();return this.expectOptionalToken(nk.COLON)?(e=r,t=this.parseName()):t=r,{kind:nE.h.FIELD,alias:e,name:t,arguments:this.parseArguments(!1),directives:this.parseDirectives(!1),selectionSet:this.peek(nk.BRACE_L)?this.parseSelectionSet():void 0,loc:this.loc(n)}},t.parseArguments=function(e){var t=e?this.parseConstArgument:this.parseArgument;return this.optionalMany(nk.PAREN_L,t,nk.PAREN_R)},t.parseArgument=function(){var e=this._lexer.token,t=this.parseName();return this.expectToken(nk.COLON),{kind:nE.h.ARGUMENT,name:t,value:this.parseValueLiteral(!1),loc:this.loc(e)}},t.parseConstArgument=function(){var e=this._lexer.token;return{kind:nE.h.ARGUMENT,name:this.parseName(),value:(this.expectToken(nk.COLON),this.parseValueLiteral(!0)),loc:this.loc(e)}},t.parseFragment=function(){var e=this._lexer.token;this.expectToken(nk.SPREAD);var t=this.expectOptionalKeyword("on");return!t&&this.peek(nk.NAME)?{kind:nE.h.FRAGMENT_SPREAD,name:this.parseFragmentName(),directives:this.parseDirectives(!1),loc:this.loc(e)}:{kind:nE.h.INLINE_FRAGMENT,typeCondition:t?this.parseNamedType():void 0,directives:this.parseDirectives(!1),selectionSet:this.parseSelectionSet(),loc:this.loc(e)}},t.parseFragmentDefinition=function(){var e,t=this._lexer.token;return(this.expectKeyword("fragment"),(null===(e=this._options)||void 0===e?void 0:e.experimentalFragmentVariables)===!0)?{kind:nE.h.FRAGMENT_DEFINITION,name:this.parseFragmentName(),variableDefinitions:this.parseVariableDefinitions(),typeCondition:(this.expectKeyword("on"),this.parseNamedType()),directives:this.parseDirectives(!1),selectionSet:this.parseSelectionSet(),loc:this.loc(t)}:{kind:nE.h.FRAGMENT_DEFINITION,name:this.parseFragmentName(),typeCondition:(this.expectKeyword("on"),this.parseNamedType()),directives:this.parseDirectives(!1),selectionSet:this.parseSelectionSet(),loc:this.loc(t)}},t.parseFragmentName=function(){if("on"===this._lexer.token.value)throw this.unexpected();return this.parseName()},t.parseValueLiteral=function(e){var t=this._lexer.token;switch(t.kind){case nk.BRACKET_L:return this.parseList(e);case nk.BRACE_L:return this.parseObject(e);case nk.INT:return this._lexer.advance(),{kind:nE.h.INT,value:t.value,loc:this.loc(t)};case nk.FLOAT:return this._lexer.advance(),{kind:nE.h.FLOAT,value:t.value,loc:this.loc(t)};case nk.STRING:case nk.BLOCK_STRING:return this.parseStringLiteral();case nk.NAME:switch(this._lexer.advance(),t.value){case"true":return{kind:nE.h.BOOLEAN,value:!0,loc:this.loc(t)};case"false":return{kind:nE.h.BOOLEAN,value:!1,loc:this.loc(t)};case"null":return{kind:nE.h.NULL,loc:this.loc(t)};default:return{kind:nE.h.ENUM,value:t.value,loc:this.loc(t)}}case nk.DOLLAR:if(!e)return this.parseVariable()}throw this.unexpected()},t.parseStringLiteral=function(){var e=this._lexer.token;return this._lexer.advance(),{kind:nE.h.STRING,value:e.value,block:e.kind===nk.BLOCK_STRING,loc:this.loc(e)}},t.parseList=function(e){var t=this,n=this._lexer.token,r=function(){return t.parseValueLiteral(e)};return{kind:nE.h.LIST,values:this.any(nk.BRACKET_L,r,nk.BRACKET_R),loc:this.loc(n)}},t.parseObject=function(e){var t=this,n=this._lexer.token,r=function(){return t.parseObjectField(e)};return{kind:nE.h.OBJECT,fields:this.any(nk.BRACE_L,r,nk.BRACE_R),loc:this.loc(n)}},t.parseObjectField=function(e){var t=this._lexer.token,n=this.parseName();return this.expectToken(nk.COLON),{kind:nE.h.OBJECT_FIELD,name:n,value:this.parseValueLiteral(e),loc:this.loc(t)}},t.parseDirectives=function(e){for(var t=[];this.peek(nk.AT);)t.push(this.parseDirective(e));return t},t.parseDirective=function(e){var t=this._lexer.token;return this.expectToken(nk.AT),{kind:nE.h.DIRECTIVE,name:this.parseName(),arguments:this.parseArguments(e),loc:this.loc(t)}},t.parseTypeReference=function(){var e,t=this._lexer.token;return(this.expectOptionalToken(nk.BRACKET_L)?(e=this.parseTypeReference(),this.expectToken(nk.BRACKET_R),e={kind:nE.h.LIST_TYPE,type:e,loc:this.loc(t)}):e=this.parseNamedType(),this.expectOptionalToken(nk.BANG))?{kind:nE.h.NON_NULL_TYPE,type:e,loc:this.loc(t)}:e},t.parseNamedType=function(){var e=this._lexer.token;return{kind:nE.h.NAMED_TYPE,name:this.parseName(),loc:this.loc(e)}},t.parseTypeSystemDefinition=function(){var e=this.peekDescription()?this._lexer.lookahead():this._lexer.token;if(e.kind===nk.NAME)switch(e.value){case"schema":return this.parseSchemaDefinition();case"scalar":return this.parseScalarTypeDefinition();case"type":return this.parseObjectTypeDefinition();case"interface":return this.parseInterfaceTypeDefinition();case"union":return this.parseUnionTypeDefinition();case"enum":return this.parseEnumTypeDefinition();case"input":return this.parseInputObjectTypeDefinition();case"directive":return this.parseDirectiveDefinition()}throw this.unexpected(e)},t.peekDescription=function(){return this.peek(nk.STRING)||this.peek(nk.BLOCK_STRING)},t.parseDescription=function(){if(this.peekDescription())return this.parseStringLiteral()},t.parseSchemaDefinition=function(){var e=this._lexer.token,t=this.parseDescription();this.expectKeyword("schema");var n=this.parseDirectives(!0),r=this.many(nk.BRACE_L,this.parseOperationTypeDefinition,nk.BRACE_R);return{kind:nE.h.SCHEMA_DEFINITION,description:t,directives:n,operationTypes:r,loc:this.loc(e)}},t.parseOperationTypeDefinition=function(){var e=this._lexer.token,t=this.parseOperationType();this.expectToken(nk.COLON);var n=this.parseNamedType();return{kind:nE.h.OPERATION_TYPE_DEFINITION,operation:t,type:n,loc:this.loc(e)}},t.parseScalarTypeDefinition=function(){var e=this._lexer.token,t=this.parseDescription();this.expectKeyword("scalar");var n=this.parseName(),r=this.parseDirectives(!0);return{kind:nE.h.SCALAR_TYPE_DEFINITION,description:t,name:n,directives:r,loc:this.loc(e)}},t.parseObjectTypeDefinition=function(){var e=this._lexer.token,t=this.parseDescription();this.expectKeyword("type");var n=this.parseName(),r=this.parseImplementsInterfaces(),i=this.parseDirectives(!0),a=this.parseFieldsDefinition();return{kind:nE.h.OBJECT_TYPE_DEFINITION,description:t,name:n,interfaces:r,directives:i,fields:a,loc:this.loc(e)}},t.parseImplementsInterfaces=function(){var e;if(!this.expectOptionalKeyword("implements"))return[];if((null===(e=this._options)||void 0===e?void 0:e.allowLegacySDLImplementsInterfaces)===!0){var t=[];this.expectOptionalToken(nk.AMP);do t.push(this.parseNamedType());while(this.expectOptionalToken(nk.AMP)||this.peek(nk.NAME))return t}return this.delimitedMany(nk.AMP,this.parseNamedType)},t.parseFieldsDefinition=function(){var e;return(null===(e=this._options)||void 0===e?void 0:e.allowLegacySDLEmptyFields)===!0&&this.peek(nk.BRACE_L)&&this._lexer.lookahead().kind===nk.BRACE_R?(this._lexer.advance(),this._lexer.advance(),[]):this.optionalMany(nk.BRACE_L,this.parseFieldDefinition,nk.BRACE_R)},t.parseFieldDefinition=function(){var e=this._lexer.token,t=this.parseDescription(),n=this.parseName(),r=this.parseArgumentDefs();this.expectToken(nk.COLON);var i=this.parseTypeReference(),a=this.parseDirectives(!0);return{kind:nE.h.FIELD_DEFINITION,description:t,name:n,arguments:r,type:i,directives:a,loc:this.loc(e)}},t.parseArgumentDefs=function(){return this.optionalMany(nk.PAREN_L,this.parseInputValueDef,nk.PAREN_R)},t.parseInputValueDef=function(){var e,t=this._lexer.token,n=this.parseDescription(),r=this.parseName();this.expectToken(nk.COLON);var i=this.parseTypeReference();this.expectOptionalToken(nk.EQUALS)&&(e=this.parseValueLiteral(!0));var a=this.parseDirectives(!0);return{kind:nE.h.INPUT_VALUE_DEFINITION,description:n,name:r,type:i,defaultValue:e,directives:a,loc:this.loc(t)}},t.parseInterfaceTypeDefinition=function(){var e=this._lexer.token,t=this.parseDescription();this.expectKeyword("interface");var n=this.parseName(),r=this.parseImplementsInterfaces(),i=this.parseDirectives(!0),a=this.parseFieldsDefinition();return{kind:nE.h.INTERFACE_TYPE_DEFINITION,description:t,name:n,interfaces:r,directives:i,fields:a,loc:this.loc(e)}},t.parseUnionTypeDefinition=function(){var e=this._lexer.token,t=this.parseDescription();this.expectKeyword("union");var n=this.parseName(),r=this.parseDirectives(!0),i=this.parseUnionMemberTypes();return{kind:nE.h.UNION_TYPE_DEFINITION,description:t,name:n,directives:r,types:i,loc:this.loc(e)}},t.parseUnionMemberTypes=function(){return this.expectOptionalToken(nk.EQUALS)?this.delimitedMany(nk.PIPE,this.parseNamedType):[]},t.parseEnumTypeDefinition=function(){var e=this._lexer.token,t=this.parseDescription();this.expectKeyword("enum");var n=this.parseName(),r=this.parseDirectives(!0),i=this.parseEnumValuesDefinition();return{kind:nE.h.ENUM_TYPE_DEFINITION,description:t,name:n,directives:r,values:i,loc:this.loc(e)}},t.parseEnumValuesDefinition=function(){return this.optionalMany(nk.BRACE_L,this.parseEnumValueDefinition,nk.BRACE_R)},t.parseEnumValueDefinition=function(){var e=this._lexer.token,t=this.parseDescription(),n=this.parseName(),r=this.parseDirectives(!0);return{kind:nE.h.ENUM_VALUE_DEFINITION,description:t,name:n,directives:r,loc:this.loc(e)}},t.parseInputObjectTypeDefinition=function(){var e=this._lexer.token,t=this.parseDescription();this.expectKeyword("input");var n=this.parseName(),r=this.parseDirectives(!0),i=this.parseInputFieldsDefinition();return{kind:nE.h.INPUT_OBJECT_TYPE_DEFINITION,description:t,name:n,directives:r,fields:i,loc:this.loc(e)}},t.parseInputFieldsDefinition=function(){return this.optionalMany(nk.BRACE_L,this.parseInputValueDef,nk.BRACE_R)},t.parseTypeSystemExtension=function(){var e=this._lexer.lookahead();if(e.kind===nk.NAME)switch(e.value){case"schema":return this.parseSchemaExtension();case"scalar":return this.parseScalarTypeExtension();case"type":return this.parseObjectTypeExtension();case"interface":return this.parseInterfaceTypeExtension();case"union":return this.parseUnionTypeExtension();case"enum":return this.parseEnumTypeExtension();case"input":return this.parseInputObjectTypeExtension()}throw this.unexpected(e)},t.parseSchemaExtension=function(){var e=this._lexer.token;this.expectKeyword("extend"),this.expectKeyword("schema");var t=this.parseDirectives(!0),n=this.optionalMany(nk.BRACE_L,this.parseOperationTypeDefinition,nk.BRACE_R);if(0===t.length&&0===n.length)throw this.unexpected();return{kind:nE.h.SCHEMA_EXTENSION,directives:t,operationTypes:n,loc:this.loc(e)}},t.parseScalarTypeExtension=function(){var e=this._lexer.token;this.expectKeyword("extend"),this.expectKeyword("scalar");var t=this.parseName(),n=this.parseDirectives(!0);if(0===n.length)throw this.unexpected();return{kind:nE.h.SCALAR_TYPE_EXTENSION,name:t,directives:n,loc:this.loc(e)}},t.parseObjectTypeExtension=function(){var e=this._lexer.token;this.expectKeyword("extend"),this.expectKeyword("type");var t=this.parseName(),n=this.parseImplementsInterfaces(),r=this.parseDirectives(!0),i=this.parseFieldsDefinition();if(0===n.length&&0===r.length&&0===i.length)throw this.unexpected();return{kind:nE.h.OBJECT_TYPE_EXTENSION,name:t,interfaces:n,directives:r,fields:i,loc:this.loc(e)}},t.parseInterfaceTypeExtension=function(){var e=this._lexer.token;this.expectKeyword("extend"),this.expectKeyword("interface");var t=this.parseName(),n=this.parseImplementsInterfaces(),r=this.parseDirectives(!0),i=this.parseFieldsDefinition();if(0===n.length&&0===r.length&&0===i.length)throw this.unexpected();return{kind:nE.h.INTERFACE_TYPE_EXTENSION,name:t,interfaces:n,directives:r,fields:i,loc:this.loc(e)}},t.parseUnionTypeExtension=function(){var e=this._lexer.token;this.expectKeyword("extend"),this.expectKeyword("union");var t=this.parseName(),n=this.parseDirectives(!0),r=this.parseUnionMemberTypes();if(0===n.length&&0===r.length)throw this.unexpected();return{kind:nE.h.UNION_TYPE_EXTENSION,name:t,directives:n,types:r,loc:this.loc(e)}},t.parseEnumTypeExtension=function(){var e=this._lexer.token;this.expectKeyword("extend"),this.expectKeyword("enum");var t=this.parseName(),n=this.parseDirectives(!0),r=this.parseEnumValuesDefinition();if(0===n.length&&0===r.length)throw this.unexpected();return{kind:nE.h.ENUM_TYPE_EXTENSION,name:t,directives:n,values:r,loc:this.loc(e)}},t.parseInputObjectTypeExtension=function(){var e=this._lexer.token;this.expectKeyword("extend"),this.expectKeyword("input");var t=this.parseName(),n=this.parseDirectives(!0),r=this.parseInputFieldsDefinition();if(0===n.length&&0===r.length)throw this.unexpected();return{kind:nE.h.INPUT_OBJECT_TYPE_EXTENSION,name:t,directives:n,fields:r,loc:this.loc(e)}},t.parseDirectiveDefinition=function(){var e=this._lexer.token,t=this.parseDescription();this.expectKeyword("directive"),this.expectToken(nk.AT);var n=this.parseName(),r=this.parseArgumentDefs(),i=this.expectOptionalKeyword("repeatable");this.expectKeyword("on");var a=this.parseDirectiveLocations();return{kind:nE.h.DIRECTIVE_DEFINITION,description:t,name:n,arguments:r,repeatable:i,locations:a,loc:this.loc(e)}},t.parseDirectiveLocations=function(){return this.delimitedMany(nk.PIPE,this.parseDirectiveLocation)},t.parseDirectiveLocation=function(){var e=this._lexer.token,t=this.parseName();if(void 0!==nT[t.value])return t;throw this.unexpected(e)},t.loc=function(e){var t;if((null===(t=this._options)||void 0===t?void 0:t.noLocation)!==!0)return new nS.Ye(e,this._lexer.lastToken,this._lexer.source)},t.peek=function(e){return this._lexer.token.kind===e},t.expectToken=function(e){var t=this._lexer.token;if(t.kind===e)return this._lexer.advance(),t;throw n_(this._lexer.source,t.start,"Expected ".concat(nG(e),", found ").concat(nz(t),"."))},t.expectOptionalToken=function(e){var t=this._lexer.token;if(t.kind===e)return this._lexer.advance(),t},t.expectKeyword=function(e){var t=this._lexer.token;if(t.kind===nk.NAME&&t.value===e)this._lexer.advance();else throw n_(this._lexer.source,t.start,'Expected "'.concat(e,'", found ').concat(nz(t),"."))},t.expectOptionalKeyword=function(e){var t=this._lexer.token;return t.kind===nk.NAME&&t.value===e&&(this._lexer.advance(),!0)},t.unexpected=function(e){var t=null!=e?e:this._lexer.token;return n_(this._lexer.source,t.start,"Unexpected ".concat(nz(t),"."))},t.any=function(e,t,n){this.expectToken(e);for(var r=[];!this.expectOptionalToken(n);)r.push(t.call(this));return r},t.optionalMany=function(e,t,n){if(this.expectOptionalToken(e)){var r=[];do r.push(t.call(this));while(!this.expectOptionalToken(n))return r}return[]},t.many=function(e,t,n){this.expectToken(e);var r=[];do r.push(t.call(this));while(!this.expectOptionalToken(n))return r},t.delimitedMany=function(e,t){this.expectOptionalToken(e);var n=[];do n.push(t.call(this));while(this.expectOptionalToken(e))return n},e}();function nz(e){var t=e.value;return nG(e.kind)+(null!=t?' "'.concat(t,'"'):"")}function nG(e){return nA(e)?'"'.concat(e,'"'):e}var nW=new Map,nK=new Map,nV=!0,nq=!1;function nZ(e){return e.replace(/[\s,]+/g," ").trim()}function nX(e){return nZ(e.source.body.substring(e.start,e.end))}function nJ(e){var t=new Set,n=[];return e.definitions.forEach(function(e){if("FragmentDefinition"===e.kind){var r=e.name.value,i=nX(e.loc),a=nK.get(r);a&&!a.has(i)?nV&&console.warn("Warning: fragment with name "+r+" already exists.\ngraphql-tag enforces all fragment names across your application to be unique; read more about\nthis in the docs: http://dev.apollodata.com/core/fragments.html#unique-names"):a||nK.set(r,a=new Set),a.add(i),t.has(i)||(t.add(i),n.push(e))}else n.push(e)}),(0,t0.pi)((0,t0.pi)({},e),{definitions:n})}function nQ(e){var t=new Set(e.definitions);t.forEach(function(e){e.loc&&delete e.loc,Object.keys(e).forEach(function(n){var r=e[n];r&&"object"==typeof r&&t.add(r)})});var n=e.loc;return n&&(delete n.startToken,delete n.endToken),e}function n1(e){var t=nZ(e);if(!nW.has(t)){var n=nH(e,{experimentalFragmentVariables:nq,allowLegacyFragmentVariables:nq});if(!n||"Document"!==n.kind)throw Error("Not a valid GraphQL document.");nW.set(t,nQ(nJ(n)))}return nW.get(t)}function n0(e){for(var t=[],n=1;n, or pass an ApolloClient instance in via options.'):(0,n9.kG)(!!n,32),n}var rp=n(10542),rb=n(53712),rm=n(21436),rg=Object.prototype.hasOwnProperty;function rv(e,t){return void 0===t&&(t=Object.create(null)),ry(rh(t.client),e).useQuery(t)}function ry(e,t){var n=(0,l.useRef)();n.current&&e===n.current.client&&t===n.current.query||(n.current=new rw(e,t,n.current));var r=n.current,i=(0,l.useState)(0),a=(i[0],i[1]);return r.forceUpdate=function(){a(function(e){return e+1})},r}var rw=function(){function e(e,t,n){this.client=e,this.query=t,this.ssrDisabledResult=(0,rp.J)({loading:!0,data:void 0,error:void 0,networkStatus:ru.I.loading}),this.skipStandbyResult=(0,rp.J)({loading:!1,data:void 0,error:void 0,networkStatus:ru.I.ready}),this.toQueryResultCache=new(n7.mr?WeakMap:Map),rd(t,r.Query);var i=n&&n.result,a=i&&i.data;a&&(this.previousData=a)}return e.prototype.forceUpdate=function(){__DEV__&&n9.kG.warn("Calling default no-op implementation of InternalState#forceUpdate")},e.prototype.executeQuery=function(e){var t,n=this;e.query&&Object.assign(this,{query:e.query}),this.watchQueryOptions=this.createWatchQueryOptions(this.queryHookOptions=e);var r=this.observable.reobserveAsConcast(this.getObsQueryOptions());return this.previousData=(null===(t=this.result)||void 0===t?void 0:t.data)||this.previousData,this.result=void 0,this.forceUpdate(),new Promise(function(e){var t;r.subscribe({next:function(e){t=e},error:function(){e(n.toQueryResult(n.observable.getCurrentResult()))},complete:function(){e(n.toQueryResult(t))}})})},e.prototype.useQuery=function(e){var t=this;this.renderPromises=(0,l.useContext)((0,ro.K)()).renderPromises,this.useOptions(e);var n=this.useObservableQuery(),r=rt((0,l.useCallback)(function(){if(t.renderPromises)return function(){};var e=function(){var e=t.result,r=n.getCurrentResult();!(e&&e.loading===r.loading&&e.networkStatus===r.networkStatus&&(0,ri.D)(e.data,r.data))&&t.setResult(r)},r=function(a){var o=n.last;i.unsubscribe();try{n.resetLastResults(),i=n.subscribe(e,r)}finally{n.last=o}if(!rg.call(a,"graphQLErrors"))throw a;var s=t.result;(!s||s&&s.loading||!(0,ri.D)(a,s.error))&&t.setResult({data:s&&s.data,error:a,loading:!1,networkStatus:ru.I.error})},i=n.subscribe(e,r);return function(){return setTimeout(function(){return i.unsubscribe()})}},[n,this.renderPromises,this.client.disableNetworkFetches,]),function(){return t.getCurrentResult()},function(){return t.getCurrentResult()});return this.unsafeHandlePartialRefetch(r),this.toQueryResult(r)},e.prototype.useOptions=function(t){var n,r=this.createWatchQueryOptions(this.queryHookOptions=t),i=this.watchQueryOptions;!(0,ri.D)(r,i)&&(this.watchQueryOptions=r,i&&this.observable&&(this.observable.reobserve(this.getObsQueryOptions()),this.previousData=(null===(n=this.result)||void 0===n?void 0:n.data)||this.previousData,this.result=void 0)),this.onCompleted=t.onCompleted||e.prototype.onCompleted,this.onError=t.onError||e.prototype.onError,(this.renderPromises||this.client.disableNetworkFetches)&&!1===this.queryHookOptions.ssr&&!this.queryHookOptions.skip?this.result=this.ssrDisabledResult:this.queryHookOptions.skip||"standby"===this.watchQueryOptions.fetchPolicy?this.result=this.skipStandbyResult:(this.result===this.ssrDisabledResult||this.result===this.skipStandbyResult)&&(this.result=void 0)},e.prototype.getObsQueryOptions=function(){var e=[],t=this.client.defaultOptions.watchQuery;return t&&e.push(t),this.queryHookOptions.defaultOptions&&e.push(this.queryHookOptions.defaultOptions),e.push((0,rb.o)(this.observable&&this.observable.options,this.watchQueryOptions)),e.reduce(ra.J)},e.prototype.createWatchQueryOptions=function(e){void 0===e&&(e={});var t,n=e.skip,r=Object.assign((e.ssr,e.onCompleted,e.onError,e.defaultOptions,(0,t0._T)(e,["skip","ssr","onCompleted","onError","defaultOptions"])),{query:this.query});if(this.renderPromises&&("network-only"===r.fetchPolicy||"cache-and-network"===r.fetchPolicy)&&(r.fetchPolicy="cache-first"),r.variables||(r.variables={}),n){var i=r.fetchPolicy,a=void 0===i?this.getDefaultFetchPolicy():i,o=r.initialFetchPolicy;Object.assign(r,{initialFetchPolicy:void 0===o?a:o,fetchPolicy:"standby"})}else r.fetchPolicy||(r.fetchPolicy=(null===(t=this.observable)||void 0===t?void 0:t.options.initialFetchPolicy)||this.getDefaultFetchPolicy());return r},e.prototype.getDefaultFetchPolicy=function(){var e,t;return(null===(e=this.queryHookOptions.defaultOptions)||void 0===e?void 0:e.fetchPolicy)||(null===(t=this.client.defaultOptions.watchQuery)||void 0===t?void 0:t.fetchPolicy)||"cache-first"},e.prototype.onCompleted=function(e){},e.prototype.onError=function(e){},e.prototype.useObservableQuery=function(){var e=this.observable=this.renderPromises&&this.renderPromises.getSSRObservable(this.watchQueryOptions)||this.observable||this.client.watchQuery(this.getObsQueryOptions());this.obsQueryFields=(0,l.useMemo)(function(){return{refetch:e.refetch.bind(e),reobserve:e.reobserve.bind(e),fetchMore:e.fetchMore.bind(e),updateQuery:e.updateQuery.bind(e),startPolling:e.startPolling.bind(e),stopPolling:e.stopPolling.bind(e),subscribeToMore:e.subscribeToMore.bind(e)}},[e]);var t=!(!1===this.queryHookOptions.ssr||this.queryHookOptions.skip);return this.renderPromises&&t&&(this.renderPromises.registerSSRObservable(e),e.getCurrentResult().loading&&this.renderPromises.addObservableQueryPromise(e)),e},e.prototype.setResult=function(e){var t=this.result;t&&t.data&&(this.previousData=t.data),this.result=e,this.forceUpdate(),this.handleErrorOrCompleted(e)},e.prototype.handleErrorOrCompleted=function(e){var t=this;if(!e.loading){var n=this.toApolloError(e);Promise.resolve().then(function(){n?t.onError(n):e.data&&t.onCompleted(e.data)}).catch(function(e){__DEV__&&n9.kG.warn(e)})}},e.prototype.toApolloError=function(e){return(0,rm.O)(e.errors)?new rs.cA({graphQLErrors:e.errors}):e.error},e.prototype.getCurrentResult=function(){return this.result||this.handleErrorOrCompleted(this.result=this.observable.getCurrentResult()),this.result},e.prototype.toQueryResult=function(e){var t=this.toQueryResultCache.get(e);if(t)return t;var n=e.data,r=(e.partial,(0,t0._T)(e,["data","partial"]));return this.toQueryResultCache.set(e,t=(0,t0.pi)((0,t0.pi)((0,t0.pi)({data:n},r),this.obsQueryFields),{client:this.client,observable:this.observable,variables:this.observable.variables,called:!this.queryHookOptions.skip,previousData:this.previousData})),!t.error&&(0,rm.O)(e.errors)&&(t.error=new rs.cA({graphQLErrors:e.errors})),t},e.prototype.unsafeHandlePartialRefetch=function(e){e.partial&&this.queryHookOptions.partialRefetch&&!e.loading&&(!e.data||0===Object.keys(e.data).length)&&"cache-only"!==this.observable.options.fetchPolicy&&(Object.assign(e,{loading:!0,networkStatus:ru.I.refetch}),this.observable.refetch())},e}();function r_(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&void 0!==arguments[0]?arguments[0]:{};return rv(iH,e)},iz=function(){var e=ij(),t=parseInt(e.get("page")||"1",10),n=parseInt(e.get("per")||"50",10),r=i$({variables:{offset:(t-1)*n,limit:n},fetchPolicy:"network-only"}),i=r.data,a=r.loading,o=r.error;return a?l.createElement(iR,null):o?l.createElement(iD,{error:o}):i?l.createElement(iI,{chains:i.chains.results,page:t,pageSize:n,total:i.chains.metadata.total}):null},iG=n(67932),iW=n(8126),iK="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e};function iV(e){if(iq())return Intl.DateTimeFormat.supportedLocalesOf(e)[0]}function iq(){return("undefined"==typeof Intl?"undefined":iK(Intl))==="object"&&"function"==typeof Intl.DateTimeFormat}var iZ="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},iX=function(){function e(e,t){for(var n=0;n=i.length)break;s=i[o++]}else{if((o=i.next()).done)break;s=o.value}var s,u=s;if((void 0===e?"undefined":iZ(e))!=="object")return;e=e[u]}return e}},{key:"put",value:function(){for(var e=arguments.length,t=Array(e),n=0;n=o.length)break;c=o[u++]}else{if((u=o.next()).done)break;c=u.value}var c,l=c;"object"!==iZ(a[l])&&(a[l]={}),a=a[l]}return a[i]=r}}]),e}();let i1=iQ;var i0=new i1;function i2(e,t){if(!iq())return function(e){return e.toString()};var n=i4(e),r=JSON.stringify(t),i=i0.get(String(n),r)||i0.put(String(n),r,new Intl.DateTimeFormat(n,t));return function(e){return i.format(e)}}var i3={};function i4(e){var t=e.toString();return i3[t]?i3[t]:i3[t]=iV(e)}var i6="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e};function i5(e){return i8(e)?e:new Date(e)}function i8(e){return e instanceof Date||i9(e)}function i9(e){return(void 0===e?"undefined":i6(e))==="object"&&"function"==typeof e.getTime}var i7=n(54087),ae=n.n(i7);function at(e,t){if(0===e.length)return 0;for(var n=0,r=e.length-1,i=void 0;n<=r;){var a=t(e[i=Math.floor((r+n)/2)]);if(0===a)return i;if(a<0){if((n=i+1)>r)return n}else if((r=i-1)=t.nextUpdateTime)aa(t,this.instances);else break}},scheduleNextTick:function(){var e=this;this.scheduledTick=ae()(function(){e.tick(),e.scheduleNextTick()})},start:function(){this.scheduleNextTick()},stop:function(){ae().cancel(this.scheduledTick)}};function ai(e){var t=an(e.getNextValue(),2),n=t[0],r=t[1];e.setValue(n),e.nextUpdateTime=r}function aa(e,t){ai(e),as(t,e),ao(t,e)}function ao(e,t){var n=au(e,t);e.splice(n,0,t)}function as(e,t){var n=e.indexOf(t);e.splice(n,1)}function au(e,t){var n=t.nextUpdateTime;return at(e,function(e){return e.nextUpdateTime===n?0:e.nextUpdateTime>n?1:-1})}var ac=(0,ec.oneOfType)([(0,ec.shape)({minTime:ec.number,formatAs:ec.string.isRequired}),(0,ec.shape)({test:ec.func,formatAs:ec.string.isRequired}),(0,ec.shape)({minTime:ec.number,format:ec.func.isRequired}),(0,ec.shape)({test:ec.func,format:ec.func.isRequired})]),al=(0,ec.oneOfType)([ec.string,(0,ec.shape)({steps:(0,ec.arrayOf)(ac).isRequired,labels:(0,ec.oneOfType)([ec.string,(0,ec.arrayOf)(ec.string)]).isRequired,round:ec.string})]),af=Object.assign||function(e){for(var t=1;t=0)&&Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]);return n}function ap(e){var t=e.date,n=e.future,r=e.timeStyle,i=e.round,a=e.minTimeLeft,o=e.tooltip,s=e.component,u=e.container,c=e.wrapperComponent,f=e.wrapperProps,d=e.locale,h=e.locales,p=e.formatVerboseDate,b=e.verboseDateFormat,m=e.updateInterval,g=e.tick,v=ah(e,["date","future","timeStyle","round","minTimeLeft","tooltip","component","container","wrapperComponent","wrapperProps","locale","locales","formatVerboseDate","verboseDateFormat","updateInterval","tick"]),y=(0,l.useMemo)(function(){return d&&(h=[d]),h.concat(iW.Z.getDefaultLocale())},[d,h]),w=(0,l.useMemo)(function(){return new iW.Z(y)},[y]);t=(0,l.useMemo)(function(){return i5(t)},[t]);var _=(0,l.useCallback)(function(){var e=Date.now(),o=void 0;if(n&&e>=t.getTime()&&(e=t.getTime(),o=!0),void 0!==a){var s=t.getTime()-1e3*a;e>s&&(e=s,o=!0)}var u=w.format(t,r,{getTimeToNextUpdate:!0,now:e,future:n,round:i}),c=ad(u,2),l=c[0],f=c[1];return f=o?ag:m||f||6e4,[l,e+f]},[t,n,r,m,i,a,w]),E=(0,l.useRef)();E.current=_;var S=(0,l.useMemo)(_,[]),k=ad(S,2),x=k[0],T=k[1],M=(0,l.useState)(x),O=ad(M,2),A=O[0],L=O[1],C=ad((0,l.useState)(),2),I=C[0],D=C[1],N=(0,l.useRef)();(0,l.useEffect)(function(){if(g)return N.current=ar.add({getNextValue:function(){return E.current()},setValue:L,nextUpdateTime:T}),function(){return N.current.stop()}},[g]),(0,l.useEffect)(function(){if(N.current)N.current.forceUpdate();else{var e=_(),t=ad(e,1)[0];L(t)}},[_]),(0,l.useEffect)(function(){D(!0)},[]);var P=(0,l.useMemo)(function(){if("undefined"!=typeof window)return i2(y,b)},[y,b]),R=(0,l.useMemo)(function(){if("undefined"!=typeof window)return p?p(t):P(t)},[t,p,P]),j=l.createElement(s,af({date:t,verboseDate:I?R:void 0,tooltip:o},v),A),F=c||u;return F?l.createElement(F,af({},f,{verboseDate:I?R:void 0}),j):j}ap.propTypes={date:el().oneOfType([el().instanceOf(Date),el().number]).isRequired,locale:el().string,locales:el().arrayOf(el().string),future:el().bool,timeStyle:al,round:el().string,minTimeLeft:el().number,component:el().elementType.isRequired,tooltip:el().bool.isRequired,formatVerboseDate:el().func,verboseDateFormat:el().object,updateInterval:el().oneOfType([el().number,el().arrayOf(el().shape({threshold:el().number,interval:el().number.isRequired}))]),tick:el().bool,wrapperComponent:el().func,wrapperProps:el().object},ap.defaultProps={locales:[],component:av,tooltip:!0,verboseDateFormat:{weekday:"long",day:"numeric",month:"long",year:"numeric",hour:"numeric",minute:"2-digit",second:"2-digit"},tick:!0},ap=l.memo(ap);let ab=ap;var am,ag=31536e9;function av(e){var t=e.date,n=e.verboseDate,r=e.tooltip,i=e.children,a=ah(e,["date","verboseDate","tooltip","children"]),o=(0,l.useMemo)(function(){return t.toISOString()},[t]);return l.createElement("time",af({},a,{dateTime:o,title:r?n:void 0}),i)}av.propTypes={date:el().instanceOf(Date).isRequired,verboseDate:el().string,tooltip:el().bool.isRequired,children:el().string.isRequired};var ay=n(30381),aw=n.n(ay),a_=n(31657);function aE(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function aS(e){for(var t=1;te.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0?new rs.cA({graphQLErrors:i}):void 0;if(u===s.current.mutationId&&!c.ignoreResults){var f={called:!0,loading:!1,data:r,error:l,client:a};s.current.isMounted&&!(0,ri.D)(s.current.result,f)&&o(s.current.result=f)}var d=e.onCompleted||(null===(n=s.current.options)||void 0===n?void 0:n.onCompleted);return null==d||d(t.data,c),t}).catch(function(t){if(u===s.current.mutationId&&s.current.isMounted){var n,r={loading:!1,error:t,data:void 0,called:!0,client:a};(0,ri.D)(s.current.result,r)||o(s.current.result=r)}var i=e.onError||(null===(n=s.current.options)||void 0===n?void 0:n.onError);if(i)return i(t,c),{data:void 0,errors:t};throw t})},[]),c=(0,l.useCallback)(function(){s.current.isMounted&&o({called:!1,loading:!1,client:n})},[]);return(0,l.useEffect)(function(){return s.current.isMounted=!0,function(){s.current.isMounted=!1}},[]),[u,(0,t0.pi)({reset:c},a)]}var a4=n(59067),a6=n(28428),a5=n(11186),a8=n(78513);function a9(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}var a7=function(e){return(0,b.createStyles)({paper:{display:"flex",margin:"".concat(2.5*e.spacing.unit,"px 0"),padding:"".concat(3*e.spacing.unit,"px ").concat(3.5*e.spacing.unit,"px")},content:{flex:1,width:"100%"},actions:a9({marginTop:-(1.5*e.spacing.unit),marginLeft:-(4*e.spacing.unit)},e.breakpoints.up("sm"),{marginLeft:0,marginRight:-(1.5*e.spacing.unit)}),itemBlock:{border:"1px solid rgba(224, 224, 224, 1)",borderRadius:e.shape.borderRadius,padding:2*e.spacing.unit,marginTop:e.spacing.unit},itemBlockText:{overflowWrap:"anywhere"}})},oe=(0,b.withStyles)(a7)(function(e){var t=e.actions,n=e.children,r=e.classes;return l.createElement(ii.default,{className:r.paper},l.createElement("div",{className:r.content},n),t&&l.createElement("div",{className:r.actions},t))}),ot=function(e){var t=e.title;return l.createElement(x.default,{variant:"subtitle2",gutterBottom:!0},t)},on=function(e){var t=e.children,n=e.value;return l.createElement(x.default,{variant:"body1",noWrap:!0},t||n)},or=(0,b.withStyles)(a7)(function(e){var t=e.children,n=e.classes,r=e.value;return l.createElement("div",{className:n.itemBlock},l.createElement(x.default,{variant:"body1",className:n.itemBlockText},t||r))});function oi(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]-1}let sF=sj;function sY(e,t){var n=this.__data__,r=sC(n,e);return r<0?(++this.size,n.push([e,t])):n[r][1]=t,this}let sB=sY;function sU(e){var t=-1,n=null==e?0:e.length;for(this.clear();++t-1&&e%1==0&&e-1&&e%1==0&&e<=cw}let cE=c_;var cS="[object Arguments]",ck="[object Array]",cx="[object Boolean]",cT="[object Date]",cM="[object Error]",cO="[object Function]",cA="[object Map]",cL="[object Number]",cC="[object Object]",cI="[object RegExp]",cD="[object Set]",cN="[object String]",cP="[object WeakMap]",cR="[object ArrayBuffer]",cj="[object DataView]",cF="[object Float64Array]",cY="[object Int8Array]",cB="[object Int16Array]",cU="[object Int32Array]",cH="[object Uint8Array]",c$="[object Uint8ClampedArray]",cz="[object Uint16Array]",cG="[object Uint32Array]",cW={};function cK(e){return eD(e)&&cE(e.length)&&!!cW[eC(e)]}cW["[object Float32Array]"]=cW[cF]=cW[cY]=cW[cB]=cW[cU]=cW[cH]=cW[c$]=cW[cz]=cW[cG]=!0,cW[cS]=cW[ck]=cW[cR]=cW[cx]=cW[cj]=cW[cT]=cW[cM]=cW[cO]=cW[cA]=cW[cL]=cW[cC]=cW[cI]=cW[cD]=cW[cN]=cW[cP]=!1;let cV=cK;function cq(e){return function(t){return e(t)}}let cZ=cq;var cX=n(79730),cJ=cX.Z&&cX.Z.isTypedArray,cQ=cJ?cZ(cJ):cV;let c1=cQ;var c0=Object.prototype.hasOwnProperty;function c2(e,t){var n=cp(e),r=!n&&cd(e),i=!n&&!r&&(0,cb.Z)(e),a=!n&&!r&&!i&&c1(e),o=n||r||i||a,s=o?ci(e.length,String):[],u=s.length;for(var c in e)(t||c0.call(e,c))&&!(o&&("length"==c||i&&("offset"==c||"parent"==c)||a&&("buffer"==c||"byteLength"==c||"byteOffset"==c)||cy(c,u)))&&s.push(c);return s}let c3=c2;var c4=Object.prototype;function c6(e){var t=e&&e.constructor;return e===("function"==typeof t&&t.prototype||c4)}let c5=c6;var c8=sb(Object.keys,Object);let c9=c8;var c7=Object.prototype.hasOwnProperty;function le(e){if(!c5(e))return c9(e);var t=[];for(var n in Object(e))c7.call(e,n)&&"constructor"!=n&&t.push(n);return t}let lt=le;function ln(e){return null!=e&&cE(e.length)&&!s2(e)}let lr=ln;function li(e){return lr(e)?c3(e):lt(e)}let la=li;function lo(e,t){return e&&cn(t,la(t),e)}let ls=lo;function lu(e){var t=[];if(null!=e)for(var n in Object(e))t.push(n);return t}let lc=lu;var ll=Object.prototype.hasOwnProperty;function lf(e){if(!ed(e))return lc(e);var t=c5(e),n=[];for(var r in e)"constructor"==r&&(t||!ll.call(e,r))||n.push(r);return n}let ld=lf;function lh(e){return lr(e)?c3(e,!0):ld(e)}let lp=lh;function lb(e,t){return e&&cn(t,lp(t),e)}let lm=lb;var lg=n(42896);function lv(e,t){var n=-1,r=e.length;for(t||(t=Array(r));++n=0||(i[n]=e[n]);return i}function d8(e){if(void 0===e)throw ReferenceError("this hasn't been initialised - super() hasn't been called");return e}var d9=function(e){return Array.isArray(e)&&0===e.length},d7=function(e){return"function"==typeof e},he=function(e){return null!==e&&"object"==typeof e},ht=function(e){return String(Math.floor(Number(e)))===e},hn=function(e){return"[object String]"===Object.prototype.toString.call(e)},hr=function(e){return 0===l.Children.count(e)},hi=function(e){return he(e)&&d7(e.then)};function ha(e,t,n,r){void 0===r&&(r=0);for(var i=dZ(t);e&&r=0?[]:{}}}return(0===a?e:i)[o[a]]===n?e:(void 0===n?delete i[o[a]]:i[o[a]]=n,0===a&&void 0===n&&delete r[o[a]],r)}function hs(e,t,n,r){void 0===n&&(n=new WeakMap),void 0===r&&(r={});for(var i=0,a=Object.keys(e);i0?t.map(function(t){return x(t,ha(e,t))}):[Promise.resolve("DO_NOT_DELETE_YOU_WILL_BE_FIRED")]).then(function(e){return e.reduce(function(e,n,r){return"DO_NOT_DELETE_YOU_WILL_BE_FIRED"===n||n&&(e=ho(e,t[r],n)),e},{})})},[x]),M=(0,l.useCallback)(function(e){return Promise.all([T(e),h.validationSchema?k(e):{},h.validate?S(e):{}]).then(function(e){var t=e[0],n=e[1],r=e[2];return sh.all([t,n,r],{arrayMerge:hy})})},[h.validate,h.validationSchema,T,S,k]),O=hS(function(e){return void 0===e&&(e=_.values),E({type:"SET_ISVALIDATING",payload:!0}),M(e).then(function(e){return v.current&&(E({type:"SET_ISVALIDATING",payload:!1}),st()(_.errors,e)||E({type:"SET_ERRORS",payload:e})),e})});(0,l.useEffect)(function(){o&&!0===v.current&&st()(p.current,h.initialValues)&&O(p.current)},[o,O]);var A=(0,l.useCallback)(function(e){var t=e&&e.values?e.values:p.current,n=e&&e.errors?e.errors:b.current?b.current:h.initialErrors||{},r=e&&e.touched?e.touched:m.current?m.current:h.initialTouched||{},i=e&&e.status?e.status:g.current?g.current:h.initialStatus;p.current=t,b.current=n,m.current=r,g.current=i;var a=function(){E({type:"RESET_FORM",payload:{isSubmitting:!!e&&!!e.isSubmitting,errors:n,touched:r,status:i,values:t,isValidating:!!e&&!!e.isValidating,submitCount:e&&e.submitCount&&"number"==typeof e.submitCount?e.submitCount:0}})};if(h.onReset){var o=h.onReset(_.values,V);hi(o)?o.then(a):a()}else a()},[h.initialErrors,h.initialStatus,h.initialTouched]);(0,l.useEffect)(function(){!0===v.current&&!st()(p.current,h.initialValues)&&(c&&(p.current=h.initialValues,A()),o&&O(p.current))},[c,h.initialValues,A,o,O]),(0,l.useEffect)(function(){c&&!0===v.current&&!st()(b.current,h.initialErrors)&&(b.current=h.initialErrors||hd,E({type:"SET_ERRORS",payload:h.initialErrors||hd}))},[c,h.initialErrors]),(0,l.useEffect)(function(){c&&!0===v.current&&!st()(m.current,h.initialTouched)&&(m.current=h.initialTouched||hh,E({type:"SET_TOUCHED",payload:h.initialTouched||hh}))},[c,h.initialTouched]),(0,l.useEffect)(function(){c&&!0===v.current&&!st()(g.current,h.initialStatus)&&(g.current=h.initialStatus,E({type:"SET_STATUS",payload:h.initialStatus}))},[c,h.initialStatus,h.initialTouched]);var L=hS(function(e){if(y.current[e]&&d7(y.current[e].validate)){var t=ha(_.values,e),n=y.current[e].validate(t);return hi(n)?(E({type:"SET_ISVALIDATING",payload:!0}),n.then(function(e){return e}).then(function(t){E({type:"SET_FIELD_ERROR",payload:{field:e,value:t}}),E({type:"SET_ISVALIDATING",payload:!1})})):(E({type:"SET_FIELD_ERROR",payload:{field:e,value:n}}),Promise.resolve(n))}return h.validationSchema?(E({type:"SET_ISVALIDATING",payload:!0}),k(_.values,e).then(function(e){return e}).then(function(t){E({type:"SET_FIELD_ERROR",payload:{field:e,value:t[e]}}),E({type:"SET_ISVALIDATING",payload:!1})})):Promise.resolve()}),C=(0,l.useCallback)(function(e,t){var n=t.validate;y.current[e]={validate:n}},[]),I=(0,l.useCallback)(function(e){delete y.current[e]},[]),D=hS(function(e,t){return E({type:"SET_TOUCHED",payload:e}),(void 0===t?i:t)?O(_.values):Promise.resolve()}),N=(0,l.useCallback)(function(e){E({type:"SET_ERRORS",payload:e})},[]),P=hS(function(e,t){var r=d7(e)?e(_.values):e;return E({type:"SET_VALUES",payload:r}),(void 0===t?n:t)?O(r):Promise.resolve()}),R=(0,l.useCallback)(function(e,t){E({type:"SET_FIELD_ERROR",payload:{field:e,value:t}})},[]),j=hS(function(e,t,r){return E({type:"SET_FIELD_VALUE",payload:{field:e,value:t}}),(void 0===r?n:r)?O(ho(_.values,e,t)):Promise.resolve()}),F=(0,l.useCallback)(function(e,t){var n,r=t,i=e;if(!hn(e)){e.persist&&e.persist();var a=e.target?e.target:e.currentTarget,o=a.type,s=a.name,u=a.id,c=a.value,l=a.checked,f=(a.outerHTML,a.options),d=a.multiple;r=t||s||u,i=/number|range/.test(o)?(n=parseFloat(c),isNaN(n)?"":n):/checkbox/.test(o)?h_(ha(_.values,r),l,c):d?hw(f):c}r&&j(r,i)},[j,_.values]),Y=hS(function(e){if(hn(e))return function(t){return F(t,e)};F(e)}),B=hS(function(e,t,n){return void 0===t&&(t=!0),E({type:"SET_FIELD_TOUCHED",payload:{field:e,value:t}}),(void 0===n?i:n)?O(_.values):Promise.resolve()}),U=(0,l.useCallback)(function(e,t){e.persist&&e.persist();var n,r=e.target,i=r.name,a=r.id;r.outerHTML,B(t||i||a,!0)},[B]),H=hS(function(e){if(hn(e))return function(t){return U(t,e)};U(e)}),$=(0,l.useCallback)(function(e){d7(e)?E({type:"SET_FORMIK_STATE",payload:e}):E({type:"SET_FORMIK_STATE",payload:function(){return e}})},[]),z=(0,l.useCallback)(function(e){E({type:"SET_STATUS",payload:e})},[]),G=(0,l.useCallback)(function(e){E({type:"SET_ISSUBMITTING",payload:e})},[]),W=hS(function(){return E({type:"SUBMIT_ATTEMPT"}),O().then(function(e){var t,n=e instanceof Error;if(!n&&0===Object.keys(e).length){try{if(void 0===(t=q()))return}catch(r){throw r}return Promise.resolve(t).then(function(e){return v.current&&E({type:"SUBMIT_SUCCESS"}),e}).catch(function(e){if(v.current)throw E({type:"SUBMIT_FAILURE"}),e})}if(v.current&&(E({type:"SUBMIT_FAILURE"}),n))throw e})}),K=hS(function(e){e&&e.preventDefault&&d7(e.preventDefault)&&e.preventDefault(),e&&e.stopPropagation&&d7(e.stopPropagation)&&e.stopPropagation(),W().catch(function(e){console.warn("Warning: An unhandled error was caught from submitForm()",e)})}),V={resetForm:A,validateForm:O,validateField:L,setErrors:N,setFieldError:R,setFieldTouched:B,setFieldValue:j,setStatus:z,setSubmitting:G,setTouched:D,setValues:P,setFormikState:$,submitForm:W},q=hS(function(){return f(_.values,V)}),Z=hS(function(e){e&&e.preventDefault&&d7(e.preventDefault)&&e.preventDefault(),e&&e.stopPropagation&&d7(e.stopPropagation)&&e.stopPropagation(),A()}),X=(0,l.useCallback)(function(e){return{value:ha(_.values,e),error:ha(_.errors,e),touched:!!ha(_.touched,e),initialValue:ha(p.current,e),initialTouched:!!ha(m.current,e),initialError:ha(b.current,e)}},[_.errors,_.touched,_.values]),J=(0,l.useCallback)(function(e){return{setValue:function(t,n){return j(e,t,n)},setTouched:function(t,n){return B(e,t,n)},setError:function(t){return R(e,t)}}},[j,B,R]),Q=(0,l.useCallback)(function(e){var t=he(e),n=t?e.name:e,r=ha(_.values,n),i={name:n,value:r,onChange:Y,onBlur:H};if(t){var a=e.type,o=e.value,s=e.as,u=e.multiple;"checkbox"===a?void 0===o?i.checked=!!r:(i.checked=!!(Array.isArray(r)&&~r.indexOf(o)),i.value=o):"radio"===a?(i.checked=r===o,i.value=o):"select"===s&&u&&(i.value=i.value||[],i.multiple=!0)}return i},[H,Y,_.values]),ee=(0,l.useMemo)(function(){return!st()(p.current,_.values)},[p.current,_.values]),et=(0,l.useMemo)(function(){return void 0!==s?ee?_.errors&&0===Object.keys(_.errors).length:!1!==s&&d7(s)?s(h):s:_.errors&&0===Object.keys(_.errors).length},[s,ee,_.errors,h]);return d4({},_,{initialValues:p.current,initialErrors:b.current,initialTouched:m.current,initialStatus:g.current,handleBlur:H,handleChange:Y,handleReset:Z,handleSubmit:K,resetForm:A,setErrors:N,setFormikState:$,setFieldTouched:B,setFieldValue:j,setFieldError:R,setStatus:z,setSubmitting:G,setTouched:D,setValues:P,submitForm:W,validateForm:O,validateField:L,isValid:et,dirty:ee,unregisterField:I,registerField:C,getFieldProps:Q,getFieldMeta:X,getFieldHelpers:J,validateOnBlur:i,validateOnChange:n,validateOnMount:o})}function hb(e){var t=hp(e),n=e.component,r=e.children,i=e.render,a=e.innerRef;return(0,l.useImperativeHandle)(a,function(){return t}),(0,l.createElement)(hc,{value:t},n?(0,l.createElement)(n,t):i?i(t):r?d7(r)?r(t):hr(r)?null:l.Children.only(r):null)}function hm(e){var t={};if(e.inner){if(0===e.inner.length)return ho(t,e.path,e.message);for(var n=e.inner,r=Array.isArray(n),i=0,n=r?n:n[Symbol.iterator]();;){if(r){if(i>=n.length)break;a=n[i++]}else{if((i=n.next()).done)break;a=i.value}var a,o=a;ha(t,o.path)||(t=ho(t,o.path,o.message))}}return t}function hg(e,t,n,r){void 0===n&&(n=!1),void 0===r&&(r={});var i=hv(e);return t[n?"validateSync":"validate"](i,{abortEarly:!1,context:r})}function hv(e){var t=Array.isArray(e)?[]:{};for(var n in e)if(Object.prototype.hasOwnProperty.call(e,n)){var r=String(n);!0===Array.isArray(e[r])?t[r]=e[r].map(function(e){return!0===Array.isArray(e)||sx(e)?hv(e):""!==e?e:void 0}):sx(e[r])?t[r]=hv(e[r]):t[r]=""!==e[r]?e[r]:void 0}return t}function hy(e,t,n){var r=e.slice();return t.forEach(function(t,i){if(void 0===r[i]){var a=!1!==n.clone&&n.isMergeableObject(t);r[i]=a?sh(Array.isArray(t)?[]:{},t,n):t}else n.isMergeableObject(t)?r[i]=sh(e[i],t,n):-1===e.indexOf(t)&&r.push(t)}),r}function hw(e){return Array.from(e).filter(function(e){return e.selected}).map(function(e){return e.value})}function h_(e,t,n){if("boolean"==typeof e)return Boolean(t);var r=[],i=!1,a=-1;if(Array.isArray(e))r=e,i=(a=e.indexOf(n))>=0;else if(!n||"true"==n||"false"==n)return Boolean(t);return t&&n&&!i?r.concat(n):i?r.slice(0,a).concat(r.slice(a+1)):r}var hE="undefined"!=typeof window&&void 0!==window.document&&void 0!==window.document.createElement?l.useLayoutEffect:l.useEffect;function hS(e){var t=(0,l.useRef)(e);return hE(function(){t.current=e}),(0,l.useCallback)(function(){for(var e=arguments.length,n=Array(e),r=0;re?t:e},0);return Array.from(d4({},e,{length:t+1}))};(function(e){function t(t){var n;return(n=e.call(this,t)||this).updateArrayField=function(e,t,r){var i=n.props,a=i.name;(0,i.formik.setFormikState)(function(n){var i="function"==typeof r?r:e,o="function"==typeof t?t:e,s=ho(n.values,a,e(ha(n.values,a))),u=r?i(ha(n.errors,a)):void 0,c=t?o(ha(n.touched,a)):void 0;return d9(u)&&(u=void 0),d9(c)&&(c=void 0),d4({},n,{values:s,errors:r?ho(n.errors,a,u):n.errors,touched:t?ho(n.touched,a,c):n.touched})})},n.push=function(e){return n.updateArrayField(function(t){return[].concat(hL(t),[d3(e)])},!1,!1)},n.handlePush=function(e){return function(){return n.push(e)}},n.swap=function(e,t){return n.updateArrayField(function(n){return hM(n,e,t)},!0,!0)},n.handleSwap=function(e,t){return function(){return n.swap(e,t)}},n.move=function(e,t){return n.updateArrayField(function(n){return hT(n,e,t)},!0,!0)},n.handleMove=function(e,t){return function(){return n.move(e,t)}},n.insert=function(e,t){return n.updateArrayField(function(n){return hO(n,e,t)},function(t){return hO(t,e,null)},function(t){return hO(t,e,null)})},n.handleInsert=function(e,t){return function(){return n.insert(e,t)}},n.replace=function(e,t){return n.updateArrayField(function(n){return hA(n,e,t)},!1,!1)},n.handleReplace=function(e,t){return function(){return n.replace(e,t)}},n.unshift=function(e){var t=-1;return n.updateArrayField(function(n){var r=n?[e].concat(n):[e];return t<0&&(t=r.length),r},function(e){var n=e?[null].concat(e):[null];return t<0&&(t=n.length),n},function(e){var n=e?[null].concat(e):[null];return t<0&&(t=n.length),n}),t},n.handleUnshift=function(e){return function(){return n.unshift(e)}},n.handleRemove=function(e){return function(){return n.remove(e)}},n.handlePop=function(){return function(){return n.pop()}},n.remove=n.remove.bind(d8(n)),n.pop=n.pop.bind(d8(n)),n}d6(t,e);var n=t.prototype;return n.componentDidUpdate=function(e){this.props.validateOnChange&&this.props.formik.validateOnChange&&!st()(ha(e.formik.values,e.name),ha(this.props.formik.values,this.props.name))&&this.props.formik.validateForm(this.props.formik.values)},n.remove=function(e){var t;return this.updateArrayField(function(n){var r=n?hL(n):[];return t||(t=r[e]),d7(r.splice)&&r.splice(e,1),r},!0,!0),t},n.pop=function(){var e;return this.updateArrayField(function(t){var n=t;return e||(e=n&&n.pop&&n.pop()),n},!0,!0),e},n.render=function(){var e={push:this.push,pop:this.pop,swap:this.swap,move:this.move,insert:this.insert,replace:this.replace,unshift:this.unshift,remove:this.remove,handlePush:this.handlePush,handlePop:this.handlePop,handleSwap:this.handleSwap,handleMove:this.handleMove,handleInsert:this.handleInsert,handleReplace:this.handleReplace,handleUnshift:this.handleUnshift,handleRemove:this.handleRemove},t=this.props,n=t.component,r=t.render,i=t.children,a=t.name,o=d5(t.formik,["validate","validationSchema"]),s=d4({},e,{form:o,name:a});return n?(0,l.createElement)(n,s):r?r(s):i?"function"==typeof i?i(s):hr(i)?null:l.Children.only(i):null},t})(l.Component).defaultProps={validateOnChange:!0},l.Component,l.Component;var hC=n(24802),hI=n(71209),hD=n(91750),hN=n(11970),hP=n(4689),hR=n(67598),hj=function(){return(hj=Object.assign||function(e){for(var t,n=1,r=arguments.length;nt.indexOf(r)&&(n[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var i=0,r=Object.getOwnPropertySymbols(e);it.indexOf(r[i])&&(n[r[i]]=e[r[i]]);return n}function hY(e){var t=e.disabled,n=e.field,r=n.onBlur,i=hF(n,["onBlur"]),a=e.form,o=a.isSubmitting,s=a.touched,u=a.errors,c=e.onBlur,l=e.helperText,f=hF(e,["disabled","field","form","onBlur","helperText"]),d=ha(u,i.name),h=ha(s,i.name)&&!!d;return hj(hj({variant:f.variant,error:h,helperText:h?d:l,disabled:null!=t?t:o,onBlur:null!=c?c:function(e){r(null!=e?e:i.name)}},i),f)}function hB(e){var t=e.children,n=hF(e,["children"]);return(0,l.createElement)(iw.Z,hj({},hY(n)),t)}function hU(e){var t=e.disabled,n=e.field,r=n.onBlur,i=hF(n,["onBlur"]),a=e.form.isSubmitting,o=(e.type,e.onBlur),s=hF(e,["disabled","field","form","type","onBlur"]);return hj(hj({disabled:null!=t?t:a,onBlur:null!=o?o:function(e){r(null!=e?e:i.name)}},i),s)}function hH(e){return(0,l.createElement)(hC.Z,hj({},hU(e)))}function h$(e){var t,n=e.disabled,r=e.field,i=r.onBlur,a=hF(r,["onBlur"]),o=e.form.isSubmitting,s=(e.type,e.onBlur),u=hF(e,["disabled","field","form","type","onBlur"]);return hj(hj({disabled:null!=n?n:o,indeterminate:!Array.isArray(a.value)&&null==a.value,onBlur:null!=s?s:function(e){i(null!=e?e:a.name)}},a),u)}function hz(e){return(0,l.createElement)(hI.Z,hj({},h$(e)))}function hG(e){var t=e.Label,n=hF(e,["Label"]);return(0,l.createElement)(hD.Z,hj({control:(0,l.createElement)(hI.Z,hj({},h$(n)))},t))}function hW(e){var t=e.disabled,n=e.field,r=n.onBlur,i=hF(n,["onBlur"]),a=e.form.isSubmitting,o=e.onBlur,s=hF(e,["disabled","field","form","onBlur"]);return hj(hj({disabled:null!=t?t:a,onBlur:null!=o?o:function(e){r(null!=e?e:i.name)}},i),s)}function hK(e){return(0,l.createElement)(hN.default,hj({},hW(e)))}function hV(e){var t=e.field,n=t.onBlur,r=hF(t,["onBlur"]),i=(e.form,e.onBlur),a=hF(e,["field","form","onBlur"]);return hj(hj({onBlur:null!=i?i:function(e){n(null!=e?e:r.name)}},r),a)}function hq(e){return(0,l.createElement)(hP.Z,hj({},hV(e)))}function hZ(e){var t=e.disabled,n=e.field,r=n.onBlur,i=hF(n,["onBlur"]),a=e.form.isSubmitting,o=e.onBlur,s=hF(e,["disabled","field","form","onBlur"]);return hj(hj({disabled:null!=t?t:a,onBlur:null!=o?o:function(e){r(null!=e?e:i.name)}},i),s)}function hX(e){return(0,l.createElement)(hR.default,hj({},hZ(e)))}hB.displayName="FormikMaterialUITextField",hH.displayName="FormikMaterialUISwitch",hz.displayName="FormikMaterialUICheckbox",hG.displayName="FormikMaterialUICheckboxWithLabel",hK.displayName="FormikMaterialUISelect",hq.displayName="FormikMaterialUIRadioGroup",hX.displayName="FormikMaterialUIInputBase";try{a=Map}catch(hJ){}try{o=Set}catch(hQ){}function h1(e,t,n){if(!e||"object"!=typeof e||"function"==typeof e)return e;if(e.nodeType&&"cloneNode"in e)return e.cloneNode(!0);if(e instanceof Date)return new Date(e.getTime());if(e instanceof RegExp)return RegExp(e);if(Array.isArray(e))return e.map(h0);if(a&&e instanceof a)return new Map(Array.from(e.entries()));if(o&&e instanceof o)return new Set(Array.from(e.values()));if(e instanceof Object){t.push(e);var r=Object.create(e);for(var i in n.push(r),e){var s=t.findIndex(function(t){return t===e[i]});r[i]=s>-1?n[s]:h1(e[i],t,n)}return r}return e}function h0(e){return h1(e,[],[])}let h2=Object.prototype.toString,h3=Error.prototype.toString,h4=RegExp.prototype.toString,h6="undefined"!=typeof Symbol?Symbol.prototype.toString:()=>"",h5=/^Symbol\((.*)\)(.*)$/;function h8(e){if(e!=+e)return"NaN";let t=0===e&&1/e<0;return t?"-0":""+e}function h9(e,t=!1){if(null==e||!0===e||!1===e)return""+e;let n=typeof e;if("number"===n)return h8(e);if("string"===n)return t?`"${e}"`:e;if("function"===n)return"[Function "+(e.name||"anonymous")+"]";if("symbol"===n)return h6.call(e).replace(h5,"Symbol($1)");let r=h2.call(e).slice(8,-1);return"Date"===r?isNaN(e.getTime())?""+e:e.toISOString(e):"Error"===r||e instanceof Error?"["+h3.call(e)+"]":"RegExp"===r?h4.call(e):null}function h7(e,t){let n=h9(e,t);return null!==n?n:JSON.stringify(e,function(e,n){let r=h9(this[e],t);return null!==r?r:n},2)}let pe={default:"${path} is invalid",required:"${path} is a required field",oneOf:"${path} must be one of the following values: ${values}",notOneOf:"${path} must not be one of the following values: ${values}",notType({path:e,type:t,value:n,originalValue:r}){let i=null!=r&&r!==n,a=`${e} must be a \`${t}\` type, but the final value was: \`${h7(n,!0)}\``+(i?` (cast from the value \`${h7(r,!0)}\`).`:".");return null===n&&(a+='\n If "null" is intended as an empty value be sure to mark the schema as `.nullable()`'),a},defined:"${path} must be defined"},pt={length:"${path} must be exactly ${length} characters",min:"${path} must be at least ${min} characters",max:"${path} must be at most ${max} characters",matches:'${path} must match the following: "${regex}"',email:"${path} must be a valid email",url:"${path} must be a valid URL",uuid:"${path} must be a valid UUID",trim:"${path} must be a trimmed string",lowercase:"${path} must be a lowercase string",uppercase:"${path} must be a upper case string"},pn={min:"${path} must be greater than or equal to ${min}",max:"${path} must be less than or equal to ${max}",lessThan:"${path} must be less than ${less}",moreThan:"${path} must be greater than ${more}",positive:"${path} must be a positive number",negative:"${path} must be a negative number",integer:"${path} must be an integer"},pr={min:"${path} field must be later than ${min}",max:"${path} field must be at earlier than ${max}"},pi={isValue:"${path} field must be ${value}"},pa={noUnknown:"${path} field has unspecified keys: ${unknown}"},po={min:"${path} field must have at least ${min} items",max:"${path} field must have less than or equal to ${max} items",length:"${path} must be have ${length} items"};Object.assign(Object.create(null),{mixed:pe,string:pt,number:pn,date:pr,object:pa,array:po,boolean:pi});var ps=n(18721),pu=n.n(ps);let pc=e=>e&&e.__isYupSchema__;class pl{constructor(e,t){if(this.refs=e,this.refs=e,"function"==typeof t){this.fn=t;return}if(!pu()(t,"is"))throw TypeError("`is:` is required for `when()` conditions");if(!t.then&&!t.otherwise)throw TypeError("either `then:` or `otherwise:` is required for `when()` conditions");let{is:n,then:r,otherwise:i}=t,a="function"==typeof n?n:(...e)=>e.every(e=>e===n);this.fn=function(...e){let t=e.pop(),n=e.pop(),o=a(...e)?r:i;if(o)return"function"==typeof o?o(n):n.concat(o.resolve(t))}}resolve(e,t){let n=this.refs.map(e=>e.getValue(null==t?void 0:t.value,null==t?void 0:t.parent,null==t?void 0:t.context)),r=this.fn.apply(e,n.concat(e,t));if(void 0===r||r===e)return e;if(!pc(r))throw TypeError("conditions must return a schema object");return r.resolve(t)}}let pf=pl;function pd(e){return null==e?[]:[].concat(e)}function ph(){return(ph=Object.assign||function(e){for(var t=1;th7(t[n])):"function"==typeof e?e(t):e}static isError(e){return e&&"ValidationError"===e.name}constructor(e,t,n,r){super(),this.name="ValidationError",this.value=t,this.path=n,this.type=r,this.errors=[],this.inner=[],pd(e).forEach(e=>{pb.isError(e)?(this.errors.push(...e.errors),this.inner=this.inner.concat(e.inner.length?e.inner:e)):this.errors.push(e)}),this.message=this.errors.length>1?`${this.errors.length} errors occurred`:this.errors[0],Error.captureStackTrace&&Error.captureStackTrace(this,pb)}}let pm=e=>{let t=!1;return(...n)=>{t||(t=!0,e(...n))}};function pg(e,t){let{endEarly:n,tests:r,args:i,value:a,errors:o,sort:s,path:u}=e,c=pm(t),l=r.length,f=[];if(o=o||[],!l)return o.length?c(new pb(o,a,u)):c(null,a);for(let d=0;d=0||(i[n]=e[n]);return i}function px(e){function t(t,n){let{value:r,path:i="",label:a,options:o,originalValue:s,sync:u}=t,c=pk(t,["value","path","label","options","originalValue","sync"]),{name:l,test:f,params:d,message:h}=e,{parent:p,context:b}=o;function m(e){return pE.isRef(e)?e.getValue(r,p,b):e}function g(e={}){let t=py()(pS({value:r,originalValue:s,label:a,path:e.path||i},d,e.params),m),n=new pb(pb.formatError(e.message||h,t),r,t.path,e.type||l);return n.params=t,n}let v=pS({path:i,parent:p,type:l,createError:g,resolve:m,options:o,originalValue:s},c);if(!u){try{Promise.resolve(f.call(v,r,v)).then(e=>{pb.isError(e)?n(e):e?n(null,e):n(g())})}catch(y){n(y)}return}let w;try{var _;if(w=f.call(v,r,v),"function"==typeof(null==(_=w)?void 0:_.then))throw Error(`Validation test of type: "${v.type}" returned a Promise during a synchronous validate. This test will finish after the validate call has returned`)}catch(E){n(E);return}pb.isError(w)?n(w):w?n(null,w):n(g())}return t.OPTIONS=e,t}pE.prototype.__isYupRef=!0;let pT=e=>e.substr(0,e.length-1).substr(1);function pM(e,t,n,r=n){let i,a,o;return t?((0,pw.forEach)(t,(s,u,c)=>{let l=u?pT(s):s;if((e=e.resolve({context:r,parent:i,value:n})).innerType){let f=c?parseInt(l,10):0;if(n&&f>=n.length)throw Error(`Yup.reach cannot resolve an array item at index: ${s}, in the path: ${t}. because there is no value at that index. `);i=n,n=n&&n[f],e=e.innerType}if(!c){if(!e.fields||!e.fields[l])throw Error(`The schema does not contain the path: ${t}. (failed at: ${o} which is a type: "${e._type}")`);i=n,n=n&&n[l],e=e.fields[l]}a=l,o=u?"["+s+"]":"."+s}),{schema:e,parent:i,parentPath:a}):{parent:i,parentPath:t,schema:e}}class pO{constructor(){this.list=new Set,this.refs=new Map}get size(){return this.list.size+this.refs.size}describe(){let e=[];for(let t of this.list)e.push(t);for(let[,n]of this.refs)e.push(n.describe());return e}toArray(){return Array.from(this.list).concat(Array.from(this.refs.values()))}add(e){pE.isRef(e)?this.refs.set(e.key,e):this.list.add(e)}delete(e){pE.isRef(e)?this.refs.delete(e.key):this.list.delete(e)}has(e,t){if(this.list.has(e))return!0;let n,r=this.refs.values();for(;!(n=r.next()).done;)if(t(n.value)===e)return!0;return!1}clone(){let e=new pO;return e.list=new Set(this.list),e.refs=new Map(this.refs),e}merge(e,t){let n=this.clone();return e.list.forEach(e=>n.add(e)),e.refs.forEach(e=>n.add(e)),t.list.forEach(e=>n.delete(e)),t.refs.forEach(e=>n.delete(e)),n}}function pA(){return(pA=Object.assign||function(e){for(var t=1;t{this.typeError(pe.notType)}),this.type=(null==e?void 0:e.type)||"mixed",this.spec=pA({strip:!1,strict:!1,abortEarly:!0,recursive:!0,nullable:!1,presence:"optional"},null==e?void 0:e.spec)}get _type(){return this.type}_typeCheck(e){return!0}clone(e){if(this._mutate)return e&&Object.assign(this.spec,e),this;let t=Object.create(Object.getPrototypeOf(this));return t.type=this.type,t._typeError=this._typeError,t._whitelistError=this._whitelistError,t._blacklistError=this._blacklistError,t._whitelist=this._whitelist.clone(),t._blacklist=this._blacklist.clone(),t.exclusiveTests=pA({},this.exclusiveTests),t.deps=[...this.deps],t.conditions=[...this.conditions],t.tests=[...this.tests],t.transforms=[...this.transforms],t.spec=h0(pA({},this.spec,e)),t}label(e){var t=this.clone();return t.spec.label=e,t}meta(...e){if(0===e.length)return this.spec.meta;let t=this.clone();return t.spec.meta=Object.assign(t.spec.meta||{},e[0]),t}withMutation(e){let t=this._mutate;this._mutate=!0;let n=e(this);return this._mutate=t,n}concat(e){if(!e||e===this)return this;if(e.type!==this.type&&"mixed"!==this.type)throw TypeError(`You cannot \`concat()\` schema's of different types: ${this.type} and ${e.type}`);let t=this,n=e.clone(),r=pA({},t.spec,n.spec);return n.spec=r,n._typeError||(n._typeError=t._typeError),n._whitelistError||(n._whitelistError=t._whitelistError),n._blacklistError||(n._blacklistError=t._blacklistError),n._whitelist=t._whitelist.merge(e._whitelist,e._blacklist),n._blacklist=t._blacklist.merge(e._blacklist,e._whitelist),n.tests=t.tests,n.exclusiveTests=t.exclusiveTests,n.withMutation(t=>{e.tests.forEach(e=>{t.test(e.OPTIONS)})}),n}isType(e){return!!this.spec.nullable&&null===e||this._typeCheck(e)}resolve(e){let t=this;if(t.conditions.length){let n=t.conditions;(t=t.clone()).conditions=[],t=(t=n.reduce((t,n)=>n.resolve(t,e),t)).resolve(e)}return t}cast(e,t={}){let n=this.resolve(pA({value:e},t)),r=n._cast(e,t);if(void 0!==e&&!1!==t.assert&&!0!==n.isType(r)){let i=h7(e),a=h7(r);throw TypeError(`The value of ${t.path||"field"} could not be cast to a value that satisfies the schema type: "${n._type}".
attempted value: ${i}
-`+(a!==i?`result of cast: ${a}`:""))}return r}_cast(e,t){let n=void 0===e?e:this.transforms.reduce((t,n)=>n.call(this,t,e,this),e);return void 0===n&&(n=this.getDefault()),n}_validate(e,t={},n){let{sync:r,path:i,from:a=[],originalValue:o=e,strict:s=this.spec.strict,abortEarly:u=this.spec.abortEarly}=t,c=e;s||(c=this._cast(c,pB({assert:!1},t)));let l={value:c,path:i,options:t,originalValue:o,schema:this,label:this.spec.label,sync:r,from:a},f=[];this._typeError&&f.push(this._typeError),this._whitelistError&&f.push(this._whitelistError),this._blacklistError&&f.push(this._blacklistError),pO({args:l,value:c,path:i,sync:r,tests:f,endEarly:u},e=>{if(e)return void n(e,c);pO({tests:this.tests,args:l,path:i,sync:r,value:c,endEarly:u},n)})}validate(e,t,n){let r=this.resolve(pB({},t,{value:e}));return"function"==typeof n?r._validate(e,t,n):new Promise((n,i)=>r._validate(e,t,(e,t)=>{e?i(e):n(t)}))}validateSync(e,t){let n;return this.resolve(pB({},t,{value:e}))._validate(e,pB({},t,{sync:!0}),(e,t)=>{if(e)throw e;n=t}),n}isValid(e,t){return this.validate(e,t).then(()=>!0,e=>{if(pT.isError(e))return!1;throw e})}isValidSync(e,t){try{return this.validateSync(e,t),!0}catch(n){if(pT.isError(n))return!1;throw n}}_getDefault(){let e=this.spec.default;return null==e?e:"function"==typeof e?e.call(this):pn(e)}getDefault(e){return this.resolve(e||{})._getDefault()}default(e){return 0===arguments.length?this._getDefault():this.clone({default:e})}strict(e=!0){var t=this.clone();return t.spec.strict=e,t}_isPresent(e){return null!=e}defined(e=pf.defined){return this.test({message:e,name:"defined",exclusive:!0,test:e=>void 0!==e})}required(e=pf.required){return this.clone({presence:"required"}).withMutation(t=>t.test({message:e,name:"required",exclusive:!0,test(e){return this.schema._isPresent(e)}}))}notRequired(){var e=this.clone({presence:"optional"});return e.tests=e.tests.filter(e=>"required"!==e.OPTIONS.name),e}nullable(e=!0){return this.clone({nullable:!1!==e})}transform(e){var t=this.clone();return t.transforms.push(e),t}test(...e){let t;if(void 0===(t=1===e.length?"function"==typeof e[0]?{test:e[0]}:e[0]:2===e.length?{name:e[0],test:e[1]}:{name:e[0],message:e[1],test:e[2]}).message&&(t.message=pf.default),"function"!=typeof t.test)throw TypeError("`test` is a required parameters");let n=this.clone(),r=pR(t),i=t.exclusive||t.name&&!0===n.exclusiveTests[t.name];if(t.exclusive&&!t.name)throw TypeError("Exclusive tests must provide a unique `name` identifying the test");return t.name&&(n.exclusiveTests[t.name]=!!t.exclusive),n.tests=n.tests.filter(e=>e.OPTIONS.name!==t.name||!i&&e.OPTIONS.test!==r.OPTIONS.test),n.tests.push(r),n}when(e,t){Array.isArray(e)||"string"==typeof e||(t=e,e=".");let n=this.clone(),r=pS(e).map(e=>new pD(e));return r.forEach(e=>{e.isSibling&&n.deps.push(e.key)}),n.conditions.push(new pE(r,t)),n}typeError(e){var t=this.clone();return t._typeError=pR({message:e,name:"typeError",test(e){return!!(void 0===e||this.schema.isType(e))||this.createError({params:{type:this.schema._type}})}}),t}oneOf(e,t=pf.oneOf){var n=this.clone();return e.forEach(e=>{n._whitelist.add(e),n._blacklist.delete(e)}),n._whitelistError=pR({message:t,name:"oneOf",test(e){if(void 0===e)return!0;let t=this.schema._whitelist;return!!t.has(e,this.resolve)||this.createError({params:{values:t.toArray().join(", ")}})}}),n}notOneOf(e,t=pf.notOneOf){var n=this.clone();return e.forEach(e=>{n._blacklist.add(e),n._whitelist.delete(e)}),n._blacklistError=pR({message:t,name:"notOneOf",test(e){let t=this.schema._blacklist;return!t.has(e,this.resolve)||this.createError({params:{values:t.toArray().join(", ")}})}}),n}strip(e=!0){let t=this.clone();return t.spec.strip=e,t}describe(){let e=this.clone(),{label:t,meta:n}=e.spec,r={meta:n,label:t,type:e.type,oneOf:e._whitelist.describe(),notOneOf:e._blacklist.describe(),tests:e.tests.map(e=>({name:e.OPTIONS.name,params:e.OPTIONS.params})).filter((e,t,n)=>n.findIndex(t=>t.name===e.name)===t)};return r}}for(let pH of(pU.prototype.__isYupSchema__=!0,["validate","validateSync"]))pU.prototype[`${pH}At`]=function(e,t,n={}){let{parent:r,parentPath:i,schema:a}=pF(this,e,t,n.context);return a[pH](r&&r[i],pB({},n,{parent:r,path:e}))};for(let p$ of["equals","is"])pU.prototype[p$]=pU.prototype.oneOf;for(let pz of["not","nope"])pU.prototype[pz]=pU.prototype.notOneOf;pU.prototype.optional=pU.prototype.notRequired;let pG=pU;function pW(){return new pG}pW.prototype=pG.prototype;let pK=e=>null==e;function pV(){return new pq}class pq extends pU{constructor(){super({type:"boolean"}),this.withMutation(()=>{this.transform(function(e){if(!this.isType(e)){if(/^(true|1)$/i.test(String(e)))return!0;if(/^(false|0)$/i.test(String(e)))return!1}return e})})}_typeCheck(e){return e instanceof Boolean&&(e=e.valueOf()),"boolean"==typeof e}isTrue(e=pb.isValue){return this.test({message:e,name:"is-value",exclusive:!0,params:{value:"true"},test:e=>pK(e)||!0===e})}isFalse(e=pb.isValue){return this.test({message:e,name:"is-value",exclusive:!0,params:{value:"false"},test:e=>pK(e)||!1===e})}}pV.prototype=pq.prototype;let pZ=/^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i,pX=/^((https?|ftp):)?\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i,pJ=/^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i,pQ=e=>pK(e)||e===e.trim(),p1=({}).toString();function p0(){return new p2}class p2 extends pU{constructor(){super({type:"string"}),this.withMutation(()=>{this.transform(function(e){if(this.isType(e)||Array.isArray(e))return e;let t=null!=e&&e.toString?e.toString():e;return t===p1?e:t})})}_typeCheck(e){return e instanceof String&&(e=e.valueOf()),"string"==typeof e}_isPresent(e){return super._isPresent(e)&&!!e.length}length(e,t=pd.length){return this.test({message:t,name:"length",exclusive:!0,params:{length:e},test(t){return pK(t)||t.length===this.resolve(e)}})}min(e,t=pd.min){return this.test({message:t,name:"min",exclusive:!0,params:{min:e},test(t){return pK(t)||t.length>=this.resolve(e)}})}max(e,t=pd.max){return this.test({name:"max",exclusive:!0,message:t,params:{max:e},test(t){return pK(t)||t.length<=this.resolve(e)}})}matches(e,t){let n=!1,r,i;return t&&("object"==typeof t?{excludeEmptyString:n=!1,message:r,name:i}=t:r=t),this.test({name:i||"matches",message:r||pd.matches,params:{regex:e},test:t=>pK(t)||""===t&&n||-1!==t.search(e)})}email(e=pd.email){return this.matches(pZ,{name:"email",message:e,excludeEmptyString:!0})}url(e=pd.url){return this.matches(pX,{name:"url",message:e,excludeEmptyString:!0})}uuid(e=pd.uuid){return this.matches(pJ,{name:"uuid",message:e,excludeEmptyString:!1})}ensure(){return this.default("").transform(e=>null===e?"":e)}trim(e=pd.trim){return this.transform(e=>null!=e?e.trim():e).test({message:e,name:"trim",test:pQ})}lowercase(e=pd.lowercase){return this.transform(e=>pK(e)?e:e.toLowerCase()).test({message:e,name:"string_case",exclusive:!0,test:e=>pK(e)||e===e.toLowerCase()})}uppercase(e=pd.uppercase){return this.transform(e=>pK(e)?e:e.toUpperCase()).test({message:e,name:"string_case",exclusive:!0,test:e=>pK(e)||e===e.toUpperCase()})}}p0.prototype=p2.prototype;let p3=e=>e!=+e;function p4(){return new p6}class p6 extends pU{constructor(){super({type:"number"}),this.withMutation(()=>{this.transform(function(e){let t=e;if("string"==typeof t){if(""===(t=t.replace(/\s/g,"")))return NaN;t=+t}return this.isType(t)?t:parseFloat(t)})})}_typeCheck(e){return e instanceof Number&&(e=e.valueOf()),"number"==typeof e&&!p3(e)}min(e,t=ph.min){return this.test({message:t,name:"min",exclusive:!0,params:{min:e},test(t){return pK(t)||t>=this.resolve(e)}})}max(e,t=ph.max){return this.test({message:t,name:"max",exclusive:!0,params:{max:e},test(t){return pK(t)||t<=this.resolve(e)}})}lessThan(e,t=ph.lessThan){return this.test({message:t,name:"max",exclusive:!0,params:{less:e},test(t){return pK(t)||tthis.resolve(e)}})}positive(e=ph.positive){return this.moreThan(0,e)}negative(e=ph.negative){return this.lessThan(0,e)}integer(e=ph.integer){return this.test({name:"integer",message:e,test:e=>pK(e)||Number.isInteger(e)})}truncate(){return this.transform(e=>pK(e)?e:0|e)}round(e){var t,n=["ceil","floor","round","trunc"];if("trunc"===(e=(null==(t=e)?void 0:t.toLowerCase())||"round"))return this.truncate();if(-1===n.indexOf(e.toLowerCase()))throw TypeError("Only valid options for round() are: "+n.join(", "));return this.transform(t=>pK(t)?t:Math[e](t))}}p4.prototype=p6.prototype;var p5=/^(\d{4}|[+\-]\d{6})(?:-?(\d{2})(?:-?(\d{2}))?)?(?:[ T]?(\d{2}):?(\d{2})(?::?(\d{2})(?:[,\.](\d{1,}))?)?(?:(Z)|([+\-])(\d{2})(?::?(\d{2}))?)?)?$/;function p8(e){var t,n,r=[1,4,5,6,7,10,11],i=0;if(n=p5.exec(e)){for(var a,o=0;a=r[o];++o)n[a]=+n[a]||0;n[2]=(+n[2]||1)-1,n[3]=+n[3]||1,n[7]=n[7]?String(n[7]).substr(0,3):0,(void 0===n[8]||""===n[8])&&(void 0===n[9]||""===n[9])?t=+new Date(n[1],n[2],n[3],n[4],n[5],n[6],n[7]):("Z"!==n[8]&&void 0!==n[9]&&(i=60*n[10]+n[11],"+"===n[9]&&(i=0-i)),t=Date.UTC(n[1],n[2],n[3],n[4],n[5]+i,n[6],n[7]))}else t=Date.parse?Date.parse(e):NaN;return t}let p9=new Date(""),p7=e=>"[object Date]"===Object.prototype.toString.call(e);function be(){return new bt}class bt extends pU{constructor(){super({type:"date"}),this.withMutation(()=>{this.transform(function(e){return this.isType(e)?e:(e=p8(e),isNaN(e)?p9:new Date(e))})})}_typeCheck(e){return p7(e)&&!isNaN(e.getTime())}prepareParam(e,t){let n;if(pD.isRef(e))n=e;else{let r=this.cast(e);if(!this._typeCheck(r))throw TypeError(`\`${t}\` must be a Date or a value that can be \`cast()\` to a Date`);n=r}return n}min(e,t=pp.min){let n=this.prepareParam(e,"min");return this.test({message:t,name:"min",exclusive:!0,params:{min:e},test(e){return pK(e)||e>=this.resolve(n)}})}max(e,t=pp.max){var n=this.prepareParam(e,"max");return this.test({message:t,name:"max",exclusive:!0,params:{max:e},test(e){return pK(e)||e<=this.resolve(n)}})}}bt.INVALID_DATE=p9,be.prototype=bt.prototype,be.INVALID_DATE=p9;var bn=n(11865),br=n.n(bn),bi=n(68929),ba=n.n(bi),bo=n(67523),bs=n.n(bo),bu=n(94633),bc=n.n(bu);function bl(e,t=[]){let n=[],r=[];function i(e,i){var a=(0,pC.split)(e)[0];~r.indexOf(a)||r.push(a),~t.indexOf(`${i}-${a}`)||n.push([i,a])}for(let a in e)if(py()(e,a)){let o=e[a];~r.indexOf(a)||r.push(a),pD.isRef(o)&&o.isSibling?i(o.path,a):pw(o)&&"deps"in o&&o.deps.forEach(e=>i(e,a))}return bc().array(r,n).reverse()}function bf(e,t){let n=1/0;return e.some((e,r)=>{var i;if((null==(i=t.path)?void 0:i.indexOf(e))!==-1)return n=r,!0}),n}function bd(e){return(t,n)=>bf(e,t)-bf(e,n)}function bh(){return(bh=Object.assign||function(e){for(var t=1;t"[object Object]"===Object.prototype.toString.call(e);function bb(e,t){let n=Object.keys(e.fields);return Object.keys(t).filter(e=>-1===n.indexOf(e))}let bm=bd([]);class bg extends pU{constructor(e){super({type:"object"}),this.fields=Object.create(null),this._sortErrors=bm,this._nodes=[],this._excludedEdges=[],this.withMutation(()=>{this.transform(function(e){if("string"==typeof e)try{e=JSON.parse(e)}catch(t){e=null}return this.isType(e)?e:null}),e&&this.shape(e)})}_typeCheck(e){return bp(e)||"function"==typeof e}_cast(e,t={}){var n;let r=super._cast(e,t);if(void 0===r)return this.getDefault();if(!this._typeCheck(r))return r;let i=this.fields,a=null!=(n=t.stripUnknown)?n:this.spec.noUnknown,o=this._nodes.concat(Object.keys(r).filter(e=>-1===this._nodes.indexOf(e))),s={},u=bh({},t,{parent:s,__validating:t.__validating||!1}),c=!1;for(let l of o){let f=i[l],d=py()(r,l);if(f){let h,p=r[l];u.path=(t.path?`${t.path}.`:"")+l;let b="spec"in(f=f.resolve({value:p,context:t.context,parent:s}))?f.spec:void 0,m=null==b?void 0:b.strict;if(null==b?void 0:b.strip){c=c||l in r;continue}void 0!==(h=t.__validating&&m?r[l]:f.cast(r[l],u))&&(s[l]=h)}else d&&!a&&(s[l]=r[l]);s[l]!==r[l]&&(c=!0)}return c?s:r}_validate(e,t={},n){let r=[],{sync:i,from:a=[],originalValue:o=e,abortEarly:s=this.spec.abortEarly,recursive:u=this.spec.recursive}=t;a=[{schema:this,value:o},...a],t.__validating=!0,t.originalValue=o,t.from=a,super._validate(e,t,(e,c)=>{if(e){if(!pT.isError(e)||s)return void n(e,c);r.push(e)}if(!u||!bp(c)){n(r[0]||null,c);return}o=o||c;let l=this._nodes.map(e=>(n,r)=>{let i=-1===e.indexOf(".")?(t.path?`${t.path}.`:"")+e:`${t.path||""}["${e}"]`,s=this.fields[e];if(s&&"validate"in s){s.validate(c[e],bh({},t,{path:i,from:a,strict:!0,parent:c,originalValue:o[e]}),r);return}r(null)});pO({sync:i,tests:l,value:c,errors:r,endEarly:s,sort:this._sortErrors,path:t.path},n)})}clone(e){let t=super.clone(e);return t.fields=bh({},this.fields),t._nodes=this._nodes,t._excludedEdges=this._excludedEdges,t._sortErrors=this._sortErrors,t}concat(e){let t=super.concat(e),n=t.fields;for(let[r,i]of Object.entries(this.fields)){let a=n[r];void 0===a?n[r]=i:a instanceof pU&&i instanceof pU&&(n[r]=i.concat(a))}return t.withMutation(()=>t.shape(n))}getDefaultFromShape(){let e={};return this._nodes.forEach(t=>{let n=this.fields[t];e[t]="default"in n?n.getDefault():void 0}),e}_getDefault(){return"default"in this.spec?super._getDefault():this._nodes.length?this.getDefaultFromShape():void 0}shape(e,t=[]){let n=this.clone(),r=Object.assign(n.fields,e);if(n.fields=r,n._sortErrors=bd(Object.keys(r)),t.length){Array.isArray(t[0])||(t=[t]);let i=t.map(([e,t])=>`${e}-${t}`);n._excludedEdges=n._excludedEdges.concat(i)}return n._nodes=bl(r,n._excludedEdges),n}pick(e){let t={};for(let n of e)this.fields[n]&&(t[n]=this.fields[n]);return this.clone().withMutation(e=>(e.fields={},e.shape(t)))}omit(e){let t=this.clone(),n=t.fields;for(let r of(t.fields={},e))delete n[r];return t.withMutation(()=>t.shape(n))}from(e,t,n){let r=(0,pC.getter)(e,!0);return this.transform(i=>{if(null==i)return i;let a=i;return py()(i,e)&&(a=bh({},i),n||delete a[e],a[t]=r(i)),a})}noUnknown(e=!0,t=pm.noUnknown){"string"==typeof e&&(t=e,e=!0);let n=this.test({name:"noUnknown",exclusive:!0,message:t,test(t){if(null==t)return!0;let n=bb(this.schema,t);return!e||0===n.length||this.createError({params:{unknown:n.join(", ")}})}});return n.spec.noUnknown=e,n}unknown(e=!0,t=pm.noUnknown){return this.noUnknown(!e,t)}transformKeys(e){return this.transform(t=>t&&bs()(t,(t,n)=>e(n)))}camelCase(){return this.transformKeys(ba())}snakeCase(){return this.transformKeys(br())}constantCase(){return this.transformKeys(e=>br()(e).toUpperCase())}describe(){let e=super.describe();return e.fields=pL()(this.fields,e=>e.describe()),e}}function bv(e){return new bg(e)}function by(){return(by=Object.assign||function(e){for(var t=1;t{this.transform(function(e){if("string"==typeof e)try{e=JSON.parse(e)}catch(t){e=null}return this.isType(e)?e:null})})}_typeCheck(e){return Array.isArray(e)}get _subType(){return this.innerType}_cast(e,t){let n=super._cast(e,t);if(!this._typeCheck(n)||!this.innerType)return n;let r=!1,i=n.map((e,n)=>{let i=this.innerType.cast(e,by({},t,{path:`${t.path||""}[${n}]`}));return i!==e&&(r=!0),i});return r?i:n}_validate(e,t={},n){var r,i;let a=[],o=t.sync,s=t.path,u=this.innerType,c=null!=(r=t.abortEarly)?r:this.spec.abortEarly,l=null!=(i=t.recursive)?i:this.spec.recursive,f=null!=t.originalValue?t.originalValue:e;super._validate(e,t,(e,r)=>{if(e){if(!pT.isError(e)||c)return void n(e,r);a.push(e)}if(!l||!u||!this._typeCheck(r)){n(a[0]||null,r);return}f=f||r;let i=Array(r.length);for(let d=0;du.validate(h,b,t)}pO({sync:o,path:s,value:r,errors:a,endEarly:c,tests:i},n)})}clone(e){let t=super.clone(e);return t.innerType=this.innerType,t}concat(e){let t=super.concat(e);return t.innerType=this.innerType,e.innerType&&(t.innerType=t.innerType?t.innerType.concat(e.innerType):e.innerType),t}of(e){let t=this.clone();if(!pw(e))throw TypeError("`array.of()` sub-schema must be a valid yup schema not: "+pl(e));return t.innerType=e,t}length(e,t=pg.length){return this.test({message:t,name:"length",exclusive:!0,params:{length:e},test(t){return pK(t)||t.length===this.resolve(e)}})}min(e,t){return t=t||pg.min,this.test({message:t,name:"min",exclusive:!0,params:{min:e},test(t){return pK(t)||t.length>=this.resolve(e)}})}max(e,t){return t=t||pg.max,this.test({message:t,name:"max",exclusive:!0,params:{max:e},test(t){return pK(t)||t.length<=this.resolve(e)}})}ensure(){return this.default(()=>[]).transform((e,t)=>this._typeCheck(e)?e:null==t?[]:[].concat(t))}compact(e){let t=e?(t,n,r)=>!e(t,n,r):e=>!!e;return this.transform(e=>null!=e?e.filter(t):e)}describe(){let e=super.describe();return this.innerType&&(e.innerType=this.innerType.describe()),e}nullable(e=!0){return super.nullable(e)}defined(){return super.defined()}required(e){return super.required(e)}}bw.prototype=b_.prototype;var bE=bv().shape({name:p0().required("Required"),url:p0().required("Required")}),bS=function(e){var t=e.initialValues,n=e.onSubmit,r=e.submitButtonText,i=e.nameDisabled,a=void 0!==i&&i;return l.createElement(hT,{initialValues:t,validationSchema:bE,onSubmit:n},function(e){var t=e.isSubmitting;return l.createElement(l.Fragment,null,l.createElement(hR,{"data-testid":"bridge-form",noValidate:!0},l.createElement(d.Z,{container:!0,spacing:16},l.createElement(d.Z,{item:!0,xs:12,md:7},l.createElement(hP,{component:hX,id:"name",name:"name",label:"Name",disabled:a,required:!0,fullWidth:!0,FormHelperTextProps:{"data-testid":"name-helper-text"}})),l.createElement(d.Z,{item:!0,xs:12,md:7},l.createElement(hP,{component:hX,id:"url",name:"url",label:"Bridge URL",placeholder:"https://",required:!0,fullWidth:!0,FormHelperTextProps:{"data-testid":"url-helper-text"}})),l.createElement(d.Z,{item:!0,xs:12,md:7},l.createElement(d.Z,{container:!0,spacing:16},l.createElement(d.Z,{item:!0,xs:7},l.createElement(hP,{component:hX,id:"minimumContractPayment",name:"minimumContractPayment",label:"Minimum Contract Payment",placeholder:"0",fullWidth:!0,inputProps:{min:0},FormHelperTextProps:{"data-testid":"minimumContractPayment-helper-text"}})),l.createElement(d.Z,{item:!0,xs:7},l.createElement(hP,{component:hX,id:"confirmations",name:"confirmations",label:"Confirmations",placeholder:"0",type:"number",fullWidth:!0,inputProps:{min:0},FormHelperTextProps:{"data-testid":"confirmations-helper-text"}})))),l.createElement(d.Z,{item:!0,xs:12,md:7},l.createElement(ok.default,{variant:"contained",color:"primary",type:"submit",disabled:t,size:"large"},r)))))})},bk=function(e){var t=e.bridge,n=e.onSubmit,r={name:t.name,url:t.url,minimumContractPayment:t.minimumContractPayment,confirmations:t.confirmations};return l.createElement(ig,null,l.createElement(d.Z,{container:!0,spacing:40},l.createElement(d.Z,{item:!0,xs:12,md:11,lg:9},l.createElement(r5.Z,null,l.createElement(sl.Z,{title:"Edit Bridge",action:l.createElement(aA.Z,{component:tz,href:"/bridges/".concat(t.id)},"Cancel")}),l.createElement(aW.Z,null,l.createElement(bS,{nameDisabled:!0,initialValues:r,onSubmit:n,submitButtonText:"Save Bridge"}))))))};function bx(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&void 0!==arguments[0]&&arguments[0],t=e?function(){return l.createElement(x.default,{variant:"body1"},"Loading...")}:function(){return null};return{isLoading:e,LoadingPlaceholder:t}},mc=n(76023);function ml(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]=0)&&Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}function mB(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n=4?[e[0],e[1],e[2],e[3],"".concat(e[0],".").concat(e[1]),"".concat(e[0],".").concat(e[2]),"".concat(e[0],".").concat(e[3]),"".concat(e[1],".").concat(e[0]),"".concat(e[1],".").concat(e[2]),"".concat(e[1],".").concat(e[3]),"".concat(e[2],".").concat(e[0]),"".concat(e[2],".").concat(e[1]),"".concat(e[2],".").concat(e[3]),"".concat(e[3],".").concat(e[0]),"".concat(e[3],".").concat(e[1]),"".concat(e[3],".").concat(e[2]),"".concat(e[0],".").concat(e[1],".").concat(e[2]),"".concat(e[0],".").concat(e[1],".").concat(e[3]),"".concat(e[0],".").concat(e[2],".").concat(e[1]),"".concat(e[0],".").concat(e[2],".").concat(e[3]),"".concat(e[0],".").concat(e[3],".").concat(e[1]),"".concat(e[0],".").concat(e[3],".").concat(e[2]),"".concat(e[1],".").concat(e[0],".").concat(e[2]),"".concat(e[1],".").concat(e[0],".").concat(e[3]),"".concat(e[1],".").concat(e[2],".").concat(e[0]),"".concat(e[1],".").concat(e[2],".").concat(e[3]),"".concat(e[1],".").concat(e[3],".").concat(e[0]),"".concat(e[1],".").concat(e[3],".").concat(e[2]),"".concat(e[2],".").concat(e[0],".").concat(e[1]),"".concat(e[2],".").concat(e[0],".").concat(e[3]),"".concat(e[2],".").concat(e[1],".").concat(e[0]),"".concat(e[2],".").concat(e[1],".").concat(e[3]),"".concat(e[2],".").concat(e[3],".").concat(e[0]),"".concat(e[2],".").concat(e[3],".").concat(e[1]),"".concat(e[3],".").concat(e[0],".").concat(e[1]),"".concat(e[3],".").concat(e[0],".").concat(e[2]),"".concat(e[3],".").concat(e[1],".").concat(e[0]),"".concat(e[3],".").concat(e[1],".").concat(e[2]),"".concat(e[3],".").concat(e[2],".").concat(e[0]),"".concat(e[3],".").concat(e[2],".").concat(e[1]),"".concat(e[0],".").concat(e[1],".").concat(e[2],".").concat(e[3]),"".concat(e[0],".").concat(e[1],".").concat(e[3],".").concat(e[2]),"".concat(e[0],".").concat(e[2],".").concat(e[1],".").concat(e[3]),"".concat(e[0],".").concat(e[2],".").concat(e[3],".").concat(e[1]),"".concat(e[0],".").concat(e[3],".").concat(e[1],".").concat(e[2]),"".concat(e[0],".").concat(e[3],".").concat(e[2],".").concat(e[1]),"".concat(e[1],".").concat(e[0],".").concat(e[2],".").concat(e[3]),"".concat(e[1],".").concat(e[0],".").concat(e[3],".").concat(e[2]),"".concat(e[1],".").concat(e[2],".").concat(e[0],".").concat(e[3]),"".concat(e[1],".").concat(e[2],".").concat(e[3],".").concat(e[0]),"".concat(e[1],".").concat(e[3],".").concat(e[0],".").concat(e[2]),"".concat(e[1],".").concat(e[3],".").concat(e[2],".").concat(e[0]),"".concat(e[2],".").concat(e[0],".").concat(e[1],".").concat(e[3]),"".concat(e[2],".").concat(e[0],".").concat(e[3],".").concat(e[1]),"".concat(e[2],".").concat(e[1],".").concat(e[0],".").concat(e[3]),"".concat(e[2],".").concat(e[1],".").concat(e[3],".").concat(e[0]),"".concat(e[2],".").concat(e[3],".").concat(e[0],".").concat(e[1]),"".concat(e[2],".").concat(e[3],".").concat(e[1],".").concat(e[0]),"".concat(e[3],".").concat(e[0],".").concat(e[1],".").concat(e[2]),"".concat(e[3],".").concat(e[0],".").concat(e[2],".").concat(e[1]),"".concat(e[3],".").concat(e[1],".").concat(e[0],".").concat(e[2]),"".concat(e[3],".").concat(e[1],".").concat(e[2],".").concat(e[0]),"".concat(e[3],".").concat(e[2],".").concat(e[0],".").concat(e[1]),"".concat(e[3],".").concat(e[2],".").concat(e[1],".").concat(e[0])]:void 0}var mZ={};function mX(e){if(0===e.length||1===e.length)return e;var t=e.join(".");return mZ[t]||(mZ[t]=mq(e)),mZ[t]}function mJ(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=arguments.length>2?arguments[2]:void 0;return mX(e.filter(function(e){return"token"!==e})).reduce(function(e,t){return mK({},e,n[t])},t)}function mQ(e){return e.join(" ")}function m1(e,t){var n=0;return function(r){return n+=1,r.map(function(r,i){return m0({node:r,stylesheet:e,useInlineStyles:t,key:"code-segment-".concat(n,"-").concat(i)})})}}function m0(e){var t=e.node,n=e.stylesheet,r=e.style,i=void 0===r?{}:r,a=e.useInlineStyles,o=e.key,s=t.properties,u=t.type,c=t.tagName,f=t.value;if("text"===u)return f;if(c){var d,h=m1(n,a);if(a){var p=Object.keys(n).reduce(function(e,t){return t.split(".").forEach(function(t){e.includes(t)||e.push(t)}),e},[]),b=s.className&&s.className.includes("token")?["token"]:[],m=s.className&&b.concat(s.className.filter(function(e){return!p.includes(e)}));d=mK({},s,{className:mQ(m)||void 0,style:mJ(s.className,Object.assign({},s.style,i),n)})}else d=mK({},s,{className:mQ(s.className)});var g=h(t.children);return l.createElement(c,(0,mV.Z)({key:o},d),g)}}let m2=function(e,t){return -1!==e.listLanguages().indexOf(t)};var m3=/\n/g;function m4(e){return e.match(m3)}function m6(e){var t=e.lines,n=e.startingLineNumber,r=e.style;return t.map(function(e,t){var i=t+n;return l.createElement("span",{key:"line-".concat(t),className:"react-syntax-highlighter-line-number",style:"function"==typeof r?r(i):r},"".concat(i,"\n"))})}function m5(e){var t=e.codeString,n=e.codeStyle,r=e.containerStyle,i=void 0===r?{float:"left",paddingRight:"10px"}:r,a=e.numberStyle,o=void 0===a?{}:a,s=e.startingLineNumber;return l.createElement("code",{style:Object.assign({},n,i)},m6({lines:t.replace(/\n$/,"").split("\n"),style:o,startingLineNumber:s}))}function m8(e){return"".concat(e.toString().length,".25em")}function m9(e,t){return{type:"element",tagName:"span",properties:{key:"line-number--".concat(e),className:["comment","linenumber","react-syntax-highlighter-line-number"],style:t},children:[{type:"text",value:e}]}}function m7(e,t,n){var r,i={display:"inline-block",minWidth:m8(n),paddingRight:"1em",textAlign:"right",userSelect:"none"};return mK({},i,"function"==typeof e?e(t):e)}function ge(e){var t=e.children,n=e.lineNumber,r=e.lineNumberStyle,i=e.largestLineNumber,a=e.showInlineLineNumbers,o=e.lineProps,s=void 0===o?{}:o,u=e.className,c=void 0===u?[]:u,l=e.showLineNumbers,f=e.wrapLongLines,d="function"==typeof s?s(n):s;if(d.className=c,n&&a){var h=m7(r,n,i);t.unshift(m9(n,h))}return f&l&&(d.style=mK({},d.style,{display:"flex"})),{type:"element",tagName:"span",properties:d,children:t}}function gt(e){for(var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[],n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:[],r=0;r2&&void 0!==arguments[2]?arguments[2]:[];return ge({children:e,lineNumber:t,lineNumberStyle:s,largestLineNumber:o,showInlineLineNumbers:i,lineProps:n,className:a,showLineNumbers:r,wrapLongLines:u})}function b(e,t){if(r&&t&&i){var n=m7(s,t,o);e.unshift(m9(t,n))}return e}function m(e,n){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:[];return t||r.length>0?p(e,n,r):b(e,n)}for(var g=function(){var e=l[h],t=e.children[0].value;if(m4(t)){var n=t.split("\n");n.forEach(function(t,i){var o=r&&f.length+a,s={type:"text",value:"".concat(t,"\n")};if(0===i){var u=l.slice(d+1,h).concat(ge({children:[s],className:e.properties.className})),c=m(u,o);f.push(c)}else if(i===n.length-1){if(l[h+1]&&l[h+1].children&&l[h+1].children[0]){var p={type:"text",value:"".concat(t)},b=ge({children:[p],className:e.properties.className});l.splice(h+1,0,b)}else{var g=[s],v=m(g,o,e.properties.className);f.push(v)}}else{var y=[s],w=m(y,o,e.properties.className);f.push(w)}}),d=h}h++};h code[class*="language-"]':{background:"#f5f2f0",padding:".1em",borderRadius:".3em",whiteSpace:"normal"},comment:{color:"slategray"},prolog:{color:"slategray"},doctype:{color:"slategray"},cdata:{color:"slategray"},punctuation:{color:"#999"},namespace:{Opacity:".7"},property:{color:"#905"},tag:{color:"#905"},boolean:{color:"#905"},number:{color:"#905"},constant:{color:"#905"},symbol:{color:"#905"},deleted:{color:"#905"},selector:{color:"#690"},"attr-name":{color:"#690"},string:{color:"#690"},char:{color:"#690"},builtin:{color:"#690"},inserted:{color:"#690"},operator:{color:"#9a6e3a",background:"hsla(0, 0%, 100%, .5)"},entity:{color:"#9a6e3a",background:"hsla(0, 0%, 100%, .5)",cursor:"help"},url:{color:"#9a6e3a",background:"hsla(0, 0%, 100%, .5)"},".language-css .token.string":{color:"#9a6e3a",background:"hsla(0, 0%, 100%, .5)"},".style .token.string":{color:"#9a6e3a",background:"hsla(0, 0%, 100%, .5)"},atrule:{color:"#07a"},"attr-value":{color:"#07a"},keyword:{color:"#07a"},function:{color:"#DD4A68"},"class-name":{color:"#DD4A68"},regex:{color:"#e90"},important:{color:"#e90",fontWeight:"bold"},variable:{color:"#e90"},bold:{fontWeight:"bold"},italic:{fontStyle:"italic"}};var gu=n(98695),gc=n.n(gu);let gl=["abap","abnf","actionscript","ada","agda","al","antlr4","apacheconf","apl","applescript","aql","arduino","arff","asciidoc","asm6502","aspnet","autohotkey","autoit","bash","basic","batch","bbcode","birb","bison","bnf","brainfuck","brightscript","bro","bsl","c","cil","clike","clojure","cmake","coffeescript","concurnas","cpp","crystal","csharp","csp","css-extras","css","cypher","d","dart","dax","dhall","diff","django","dns-zone-file","docker","ebnf","editorconfig","eiffel","ejs","elixir","elm","erb","erlang","etlua","excel-formula","factor","firestore-security-rules","flow","fortran","fsharp","ftl","gcode","gdscript","gedcom","gherkin","git","glsl","gml","go","graphql","groovy","haml","handlebars","haskell","haxe","hcl","hlsl","hpkp","hsts","http","ichigojam","icon","iecst","ignore","inform7","ini","io","j","java","javadoc","javadoclike","javascript","javastacktrace","jolie","jq","js-extras","js-templates","jsdoc","json","json5","jsonp","jsstacktrace","jsx","julia","keyman","kotlin","latex","latte","less","lilypond","liquid","lisp","livescript","llvm","lolcode","lua","makefile","markdown","markup-templating","markup","matlab","mel","mizar","mongodb","monkey","moonscript","n1ql","n4js","nand2tetris-hdl","naniscript","nasm","neon","nginx","nim","nix","nsis","objectivec","ocaml","opencl","oz","parigp","parser","pascal","pascaligo","pcaxis","peoplecode","perl","php-extras","php","phpdoc","plsql","powerquery","powershell","processing","prolog","properties","protobuf","pug","puppet","pure","purebasic","purescript","python","q","qml","qore","r","racket","reason","regex","renpy","rest","rip","roboconf","robotframework","ruby","rust","sas","sass","scala","scheme","scss","shell-session","smali","smalltalk","smarty","sml","solidity","solution-file","soy","sparql","splunk-spl","sqf","sql","stan","stylus","swift","t4-cs","t4-templating","t4-vb","tap","tcl","textile","toml","tsx","tt2","turtle","twig","typescript","typoscript","unrealscript","vala","vbnet","velocity","verilog","vhdl","vim","visual-basic","warpscript","wasm","wiki","xeora","xml-doc","xojo","xquery","yaml","yang","zig"];var gf=go(gc(),gs);gf.supportedLanguages=gl;let gd=gf;var gh=n(64566);function gp(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function gb(){var e=gp(["\n query FetchConfigV2 {\n configv2 {\n user\n effective\n }\n }\n"]);return gb=function(){return e},e}var gm=n0(gb()),gg=function(e){var t=e.children;return l.createElement(ir.Z,null,l.createElement(r7.default,{component:"th",scope:"row",colSpan:3},t))},gv=function(){return l.createElement(gg,null,"...")},gy=function(e){var t=e.children;return l.createElement(gg,null,t)},gw=function(e){var t=e.loading,n=e.toml,r=e.error,i=void 0===r?"":r,a=e.title,o=e.expanded;if(i)return l.createElement(gy,null,i);if(t)return l.createElement(gv,null);a||(a="TOML");var s={display:"block"};return l.createElement(x.default,null,l.createElement(mP.Z,{defaultExpanded:o},l.createElement(mR.Z,{expandIcon:l.createElement(gh.Z,null)},a),l.createElement(mj.Z,{style:s},l.createElement(gd,{language:"toml",style:gs},n))))},g_=function(){var e=rv(gm,{fetchPolicy:"cache-and-network"}),t=e.data,n=e.loading,r=e.error;return(null==t?void 0:t.configv2.effective)=="N/A"?l.createElement(l.Fragment,null,l.createElement(d.Z,{item:!0,xs:12},l.createElement(r5.Z,null,l.createElement(sl.Z,{title:"TOML Configuration"}),l.createElement(gw,{title:"V2 config dump:",error:null==r?void 0:r.message,loading:n,toml:null==t?void 0:t.configv2.user,showHead:!0})))):l.createElement(l.Fragment,null,l.createElement(d.Z,{container:!0},l.createElement(d.Z,{item:!0,xs:12},l.createElement(r5.Z,null,l.createElement(sl.Z,{title:"TOML Configuration"}),l.createElement(gw,{title:"User specified:",error:null==r?void 0:r.message,loading:n,toml:null==t?void 0:t.configv2.user,showHead:!0,expanded:!0}),l.createElement(gw,{title:"Effective (with defaults):",error:null==r?void 0:r.message,loading:n,toml:null==t?void 0:t.configv2.effective,showHead:!0})))))},gE=n(34823),gS=function(e){return(0,b.createStyles)({cell:{paddingTop:1.5*e.spacing.unit,paddingBottom:1.5*e.spacing.unit}})},gk=(0,b.withStyles)(gS)(function(e){var t=e.classes,n=(0,A.I0)();(0,l.useEffect)(function(){n((0,ty.DQ)())});var r=(0,A.v9)(gE.N,A.wU);return l.createElement(r5.Z,null,l.createElement(sl.Z,{title:"Node"}),l.createElement(r8.Z,null,l.createElement(r9.Z,null,l.createElement(ir.Z,null,l.createElement(r7.default,{className:t.cell},l.createElement(x.default,null,"Version"),l.createElement(x.default,{variant:"subtitle1",color:"textSecondary"},r.version))),l.createElement(ir.Z,null,l.createElement(r7.default,{className:t.cell},l.createElement(x.default,null,"SHA"),l.createElement(x.default,{variant:"subtitle1",color:"textSecondary"},r.commitSHA))))))}),gx=function(){return l.createElement(ig,null,l.createElement(d.Z,{container:!0},l.createElement(d.Z,{item:!0,sm:12,md:8},l.createElement(d.Z,{container:!0},l.createElement(g_,null))),l.createElement(d.Z,{item:!0,sm:12,md:4},l.createElement(d.Z,{container:!0},l.createElement(d.Z,{item:!0,xs:12},l.createElement(gk,null)),l.createElement(d.Z,{item:!0,xs:12},l.createElement(mN,null)),l.createElement(d.Z,{item:!0,xs:12},l.createElement(mE,null))))))},gT=function(){return l.createElement(gx,null)},gM=function(){return l.createElement(gT,null)},gO=n(44431),gA=1e18,gL=function(e){return new gO.BigNumber(e).dividedBy(gA).toFixed(8)},gC=function(e){var t=e.keys,n=e.chainID,r=e.hideHeaderTitle;return l.createElement(l.Fragment,null,l.createElement(sl.Z,{title:!r&&"Account Balances",subheader:"Chain ID "+n}),l.createElement(aW.Z,null,l.createElement(w.default,{dense:!1,disablePadding:!0},t&&t.map(function(e,r){return l.createElement(l.Fragment,null,l.createElement(_.default,{disableGutters:!0,key:["acc-balance",n.toString(),r.toString()].join("-")},l.createElement(E.Z,{primary:l.createElement(l.Fragment,null,l.createElement(d.Z,{container:!0,spacing:16},l.createElement(d.Z,{item:!0,xs:12},l.createElement(op,{title:"Address"}),l.createElement(ob,{value:e.address})),l.createElement(d.Z,{item:!0,xs:6},l.createElement(op,{title:"Native Token Balance"}),l.createElement(ob,{value:e.ethBalance||"--"})),l.createElement(d.Z,{item:!0,xs:6},l.createElement(op,{title:"LINK Balance"}),l.createElement(ob,{value:e.linkBalance?gL(e.linkBalance):"--"}))))})),r+1s&&l.createElement(gB.Z,null,l.createElement(ir.Z,null,l.createElement(r7.default,{className:r.footer},l.createElement(aA.Z,{href:"/runs",component:tz},"View More"))))))});function vt(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function vn(){var e=vt(["\n ","\n query FetchRecentJobRuns($offset: Int, $limit: Int) {\n jobRuns(offset: $offset, limit: $limit) {\n results {\n ...RecentJobRunsPayload_ResultsFields\n }\n metadata {\n total\n }\n }\n }\n"]);return vn=function(){return e},e}var vr=5,vi=n0(vn(),g9),va=function(){var e=rv(vi,{variables:{offset:0,limit:vr},fetchPolicy:"cache-and-network"}),t=e.data,n=e.loading,r=e.error;return l.createElement(ve,{data:t,errorMsg:null==r?void 0:r.message,loading:n,maxRunsSize:vr})},vo=function(e){return(0,b.createStyles)({style:{textAlign:"center",padding:2.5*e.spacing.unit,position:"fixed",left:"0",bottom:"0",width:"100%",borderRadius:0},bareAnchor:{color:e.palette.common.black,textDecoration:"none"}})},vs=(0,b.withStyles)(vo)(function(e){var t=e.classes,n=(0,A.v9)(gE.N,A.wU),r=(0,A.I0)();return(0,l.useEffect)(function(){r((0,ty.DQ)())}),l.createElement(ii.default,{className:t.style},l.createElement(x.default,null,"Chainlink Node ",n.version," at commit"," ",l.createElement("a",{target:"_blank",rel:"noopener noreferrer",href:"https://github.com/smartcontractkit/chainlink/commit/".concat(n.commitSHA),className:t.bareAnchor},n.commitSHA)))}),vu=function(e){return(0,b.createStyles)({cell:{borderColor:e.palette.divider,borderTop:"1px solid",borderBottom:"none",paddingTop:2*e.spacing.unit,paddingBottom:2*e.spacing.unit,paddingLeft:2*e.spacing.unit},block:{display:"block"},overflowEllipsis:{textOverflow:"ellipsis",overflow:"hidden"}})},vc=(0,b.withStyles)(vu)(function(e){var t=e.classes,n=e.job;return l.createElement(ir.Z,null,l.createElement(r7.default,{scope:"row",className:t.cell},l.createElement(d.Z,{container:!0,spacing:0},l.createElement(d.Z,{item:!0,xs:12},l.createElement(ih,{href:"/jobs/".concat(n.id),classes:{linkContent:t.block}},l.createElement(x.default,{className:t.overflowEllipsis,variant:"body1",component:"span",color:"primary"},n.name||n.id))),l.createElement(d.Z,{item:!0,xs:12},l.createElement(x.default,{variant:"body1",color:"textSecondary"},"Created ",l.createElement(aO,{tooltip:!0},n.createdAt))))))});function vl(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function vf(){var e=vl(["\n fragment RecentJobsPayload_ResultsFields on Job {\n id\n name\n createdAt\n }\n"]);return vf=function(){return e},e}var vd=n0(vf()),vh=function(){return(0,b.createStyles)({cardHeader:{borderBottom:0},table:{tableLayout:"fixed"}})},vp=(0,b.withStyles)(vh)(function(e){var t,n,r=e.classes,i=e.data,a=e.errorMsg,o=e.loading;return l.createElement(r5.Z,null,l.createElement(sl.Z,{title:"Recent Jobs",className:r.cardHeader}),l.createElement(r8.Z,{className:r.table},l.createElement(r9.Z,null,l.createElement(g$,{visible:o}),l.createElement(gz,{visible:(null===(t=null==i?void 0:i.jobs.results)||void 0===t?void 0:t.length)===0},"No recently created jobs"),l.createElement(gU,{msg:a}),null===(n=null==i?void 0:i.jobs.results)||void 0===n?void 0:n.map(function(e,t){return l.createElement(vc,{job:e,key:t})}))))});function vb(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function vm(){var e=vb(["\n ","\n query FetchRecentJobs($offset: Int, $limit: Int) {\n jobs(offset: $offset, limit: $limit) {\n results {\n ...RecentJobsPayload_ResultsFields\n }\n }\n }\n"]);return vm=function(){return e},e}var vg=5,vv=n0(vm(),vd),vy=function(){var e=rv(vv,{variables:{offset:0,limit:vg},fetchPolicy:"cache-and-network"}),t=e.data,n=e.loading,r=e.error;return l.createElement(vp,{data:t,errorMsg:null==r?void 0:r.message,loading:n})},vw=function(){return l.createElement(ig,null,l.createElement(d.Z,{container:!0},l.createElement(d.Z,{item:!0,xs:8},l.createElement(va,null)),l.createElement(d.Z,{item:!0,xs:4},l.createElement(d.Z,{container:!0},l.createElement(d.Z,{item:!0,xs:12},l.createElement(gY,null)),l.createElement(d.Z,{item:!0,xs:12},l.createElement(vy,null))))),l.createElement(vs,null))},v_=function(){return l.createElement(vw,null)},vE=function(){return l.createElement(v_,null)},vS=n(87239),vk=function(e){switch(e){case"DirectRequestSpec":return"Direct Request";case"FluxMonitorSpec":return"Flux Monitor";default:return e.replace(/Spec$/,"")}},vx=n(5022),vT=n(78718),vM=n.n(vT);function vO(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n1?t-1:0),r=1;r1?t-1:0),r=1;re.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&n.map(function(e){return l.createElement(ir.Z,{key:e.id,style:{cursor:"pointer"},onClick:function(){return r.push("/runs/".concat(e.id))}},l.createElement(r7.default,{className:t.idCell,scope:"row"},l.createElement("div",{className:t.runDetails},l.createElement(x.default,{variant:"h5",color:"primary",component:"span"},e.id))),l.createElement(r7.default,{className:t.stampCell},l.createElement(x.default,{variant:"body1",color:"textSecondary",className:t.stamp},"Created ",l.createElement(aO,{tooltip:!0},e.createdAt))),l.createElement(r7.default,{className:t.statusCell,scope:"row"},l.createElement(x.default,{variant:"body1",className:O()(t.status,yh(t,e.status))},e.status.toLowerCase())))})))}),yb=n(16839),ym=n.n(yb);function yg(e){var t=e.replace(/\w+\s*=\s*<([^>]|[\r\n])*>/g,""),n=ym().read(t),r=n.edges();return n.nodes().map(function(e){var t={id:e,parentIds:r.filter(function(t){return t.w===e}).map(function(e){return e.v})};return Object.keys(n.node(e)).length>0&&(t.attributes=n.node(e)),t})}var yv=n(94164),yy=function(e){var t=e.data,n=[];return(null==t?void 0:t.attributes)&&Object.keys(t.attributes).forEach(function(e){var r;n.push(l.createElement("div",{key:e},l.createElement(x.default,{variant:"body1",color:"textSecondary",component:"div"},l.createElement("b",null,e,":")," ",null===(r=t.attributes)||void 0===r?void 0:r[e])))}),l.createElement("div",null,t&&l.createElement(x.default,{variant:"body1",color:"textPrimary"},l.createElement("b",null,t.id)),n)},yw=n(73343),y_=n(3379),yE=n.n(y_);function yS(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);nwindow.innerWidth?u-r.getBoundingClientRect().width-a:u+a,n=c+r.getBoundingClientRect().height+i>window.innerHeight?c-r.getBoundingClientRect().height-a:c+a,r.style.opacity=String(1),r.style.top="".concat(n,"px"),r.style.left="".concat(t,"px"),r.style.zIndex=String(1)}},h=function(e){var t=document.getElementById("tooltip-d3-chart-".concat(e));t&&(t.style.opacity=String(0),t.style.zIndex=String(-1))};return l.createElement("div",{style:{fontFamily:"sans-serif",fontWeight:"normal"}},l.createElement(yv.kJ,{id:"task-list-graph-d3",data:i,config:s,onMouseOverNode:d,onMouseOutNode:h},"D3 chart"),n.map(function(e){return l.createElement("div",{key:"d3-tooltip-key-".concat(e.id),id:"tooltip-d3-chart-".concat(e.id),style:{position:"absolute",opacity:"0",border:"1px solid rgba(0, 0, 0, 0.1)",padding:yw.r.spacing.unit,background:"white",borderRadius:5,zIndex:-1,inlineSize:"min-content"}},l.createElement(yy,{data:e}))}))};function yL(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);nyY&&l.createElement("div",{className:t.runDetails},l.createElement(aA.Z,{href:"/jobs/".concat(n.id,"/runs"),component:tz},"View more")))),l.createElement(d.Z,{item:!0,xs:12,sm:6},l.createElement(yF,{observationSource:n.observationSource})))});function yH(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&void 0!==arguments[0]?arguments[0]:"";try{return vx.parse(e),!0}catch(t){return!1}})}),wW=function(e){var t=e.initialValues,n=e.onSubmit,r=e.onTOMLChange;return l.createElement(hT,{initialValues:t,validationSchema:wG,onSubmit:n},function(e){var t=e.isSubmitting,n=e.values;return r&&r(n.toml),l.createElement(hR,{"data-testid":"job-form",noValidate:!0},l.createElement(d.Z,{container:!0,spacing:16},l.createElement(d.Z,{item:!0,xs:12},l.createElement(hP,{component:hX,id:"toml",name:"toml",label:"Job Spec (TOML)",required:!0,fullWidth:!0,multiline:!0,rows:10,rowsMax:25,variant:"outlined",autoComplete:"off",FormHelperTextProps:{"data-testid":"toml-helper-text"}})),l.createElement(d.Z,{item:!0,xs:12,md:7},l.createElement(ok.default,{variant:"contained",color:"primary",type:"submit",disabled:t,size:"large"},"Create Job"))))})},wK=n(50109),wV="persistSpec";function wq(e){var t=e.query,n=new URLSearchParams(t).get("definition");return n?(wK.t8(wV,n),{toml:n}):{toml:wK.U2(wV)||""}}var wZ=function(e){var t=e.onSubmit,n=e.onTOMLChange,r=wq({query:(0,h.TH)().search}),i=function(e){var t=e.replace(/[\u200B-\u200D\uFEFF]/g,"");wK.t8("".concat(wV),t),n&&n(t)};return l.createElement(r5.Z,null,l.createElement(sl.Z,{title:"New Job"}),l.createElement(aW.Z,null,l.createElement(wW,{initialValues:r,onSubmit:t,onTOMLChange:i})))};function wX(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n1&&void 0!==arguments[1]?arguments[1]:{},n=t.start,r=void 0===n?6:n,i=t.end,a=void 0===i?4:i;return e.substring(0,r)+"..."+e.substring(e.length-a)}function _M(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0&&void 0!==arguments[0]?arguments[0]:{};return rv(_W,e)},_V=function(){var e=_K({fetchPolicy:"cache-and-network"}),t=e.data,n=e.loading,r=e.error,i=e.refetch;return l.createElement(_U,{loading:n,data:t,errorMsg:null==r?void 0:r.message,refetch:i})},_q=function(e){var t=e.csaKey;return l.createElement(ir.Z,{hover:!0},l.createElement(r7.default,null,l.createElement(x.default,{variant:"body1"},t.publicKey," ",l.createElement(_x,{data:t.publicKey}))))};function _Z(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function _X(){var e=_Z(["\n fragment CSAKeysPayload_ResultsFields on CSAKey {\n id\n publicKey\n }\n"]);return _X=function(){return e},e}var _J=n0(_X()),_Q=function(e){var t,n,r,i=e.data,a=e.errorMsg,o=e.loading,s=e.onCreate;return l.createElement(r5.Z,null,l.createElement(sl.Z,{action:(null===(t=null==i?void 0:i.csaKeys.results)||void 0===t?void 0:t.length)===0&&l.createElement(ok.default,{variant:"outlined",color:"primary",onClick:s},"New CSA Key"),title:"CSA Key",subheader:"Manage your CSA Key"}),l.createElement(r8.Z,null,l.createElement(ie.Z,null,l.createElement(ir.Z,null,l.createElement(r7.default,null,"Public Key"))),l.createElement(r9.Z,null,l.createElement(g$,{visible:o}),l.createElement(gz,{visible:(null===(n=null==i?void 0:i.csaKeys.results)||void 0===n?void 0:n.length)===0}),l.createElement(gU,{msg:a}),null===(r=null==i?void 0:i.csaKeys.results)||void 0===r?void 0:r.map(function(e,t){return l.createElement(_q,{csaKey:e,key:t})}))))};function _1(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0&&void 0!==arguments[0]?arguments[0]:{};return rv(EM,e)};function EA(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0&&void 0!==arguments[0]?arguments[0]:{};return rv(EJ,e)},E3=function(){return oo(EQ)},E4=function(){return oo(E1)},E6=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return rv(E0,e)};function E5(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0&&void 0!==arguments[0]?arguments[0]:{};return rv(SK,e)};function Sq(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n=0)&&Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}function kV(e,t){if(null==e)return{};var n,r,i={},a=Object.keys(e);for(r=0;r=0||(i[n]=e[n]);return i}var kq=function(e){var t=e.run,n=l.useMemo(function(){var e=t.inputs,n=t.outputs,r=t.taskRuns,i=kK(t,["inputs","outputs","taskRuns"]),a={};try{a=JSON.parse(e)}catch(o){a={}}return kW(kz({},i),{inputs:a,outputs:n,taskRuns:r})},[t]);return l.createElement(r5.Z,null,l.createElement(aW.Z,null,l.createElement(kH,{object:n})))};function kZ(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function kX(e){for(var t=1;t0&&l.createElement(kr,{errors:t.allErrors})),l.createElement(d.Z,{item:!0,xs:12},l.createElement(h.rs,null,l.createElement(h.AW,{path:"".concat(n,"/json")},l.createElement(kq,{run:t})),l.createElement(h.AW,{path:n},t.taskRuns.length>0&&l.createElement(kN,{taskRuns:t.taskRuns,observationSource:t.job.observationSource}))))))))};function k5(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function k8(){var e=k5(["\n ","\n query FetchJobRun($id: ID!) {\n jobRun(id: $id) {\n __typename\n ... on JobRun {\n ...JobRunPayload_Fields\n }\n ... on NotFoundError {\n message\n }\n }\n }\n"]);return k8=function(){return e},e}var k9=n0(k8(),k4),k7=function(){var e=rv(k9,{variables:{id:(0,h.UO)().id}}),t=e.data,n=e.loading,r=e.error;if(n)return l.createElement(iR,null);if(r)return l.createElement(iD,{error:r});var i=null==t?void 0:t.jobRun;switch(null==i?void 0:i.__typename){case"JobRun":return l.createElement(k6,{run:i});case"NotFoundError":return l.createElement(oa,null);default:return null}};function xe(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function xt(){var e=xe(["\n fragment JobRunsPayload_ResultsFields on JobRun {\n id\n allErrors\n createdAt\n finishedAt\n status\n job {\n id\n }\n }\n"]);return xt=function(){return e},e}var xn=n0(xt()),xr=function(e){var t=e.loading,n=e.data,r=e.page,i=e.pageSize,a=(0,h.k6)(),o=l.useMemo(function(){return null==n?void 0:n.jobRuns.results.map(function(e){var t,n=e.allErrors,r=e.id,i=e.createdAt;return{id:r,createdAt:i,errors:n,finishedAt:e.finishedAt,status:e.status}})},[n]);return l.createElement(ig,null,l.createElement(d.Z,{container:!0,spacing:32},l.createElement(d.Z,{item:!0,xs:12},l.createElement(iy,null,"Job Runs")),t&&l.createElement(iR,null),n&&o&&l.createElement(d.Z,{item:!0,xs:12},l.createElement(r5.Z,null,l.createElement(yp,{runs:o}),l.createElement(it.Z,{component:"div",count:n.jobRuns.metadata.total,rowsPerPage:i,rowsPerPageOptions:[i],page:r-1,onChangePage:function(e,t){a.push("/runs?page=".concat(t+1,"&per=").concat(i))},onChangeRowsPerPage:function(){},backIconButtonProps:{"aria-label":"prev-page"},nextIconButtonProps:{"aria-label":"next-page"}})))))};function xi(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function xa(){var e=xi(["\n ","\n query FetchJobRuns($offset: Int, $limit: Int) {\n jobRuns(offset: $offset, limit: $limit) {\n results {\n ...JobRunsPayload_ResultsFields\n }\n metadata {\n total\n }\n }\n }\n"]);return xa=function(){return e},e}var xo=n0(xa(),xn),xs=function(){var e=ij(),t=parseInt(e.get("page")||"1",10),n=parseInt(e.get("per")||"25",10),r=rv(xo,{variables:{offset:(t-1)*n,limit:n},fetchPolicy:"cache-and-network"}),i=r.data,a=r.loading,o=r.error;return o?l.createElement(iD,{error:o}):l.createElement(xr,{loading:a,data:i,page:t,pageSize:n})},xu=function(){var e=(0,h.$B)().path;return l.createElement(h.rs,null,l.createElement(h.AW,{exact:!0,path:e},l.createElement(xs,null)),l.createElement(h.AW,{path:"".concat(e,"/:id")},l.createElement(k7,null)))};function xc(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function xl(){var e=xc(["\n fragment FetchFeedsManagersPayload_ResultsFields on FeedsManager {\n __typename\n id\n name\n uri\n publicKey\n isConnectionActive\n createdAt\n disabledAt\n }\n query FetchFeedsManagers {\n feedsManagers {\n results {\n ...FetchFeedsManagersPayload_ResultsFields\n }\n }\n }\n"]);return xl=function(){return e},e}var xf=n0(xl()),xd=function(e){return rv(xf,e)},xh=n(47559),xp=n(83165),xb=n(47298),xm=n(81395),xg=function(){return(0,b.createStyles)({root:{display:"flex"},activeIcon:{color:xh.default[500]},inactiveIcon:{color:xp.default[500]},text:{marginLeft:4}})},xv=(0,b.withStyles)(xg)(function(e){var t=e.isActive,n=e.activeText,r=e.inactiveText,i=e.classes;return l.createElement("div",{className:i.root},t?l.createElement(xm.Z,{fontSize:"small",className:i.activeIcon}):l.createElement(xb.Z,{fontSize:"small",className:i.inactiveIcon}),l.createElement(x.default,{variant:"body1",inline:!0,className:i.text},t?n:r))}),xy=(0,b.withStyles)(iu)(function(e){var t=e.jobDistributor,n=e.classes;return l.createElement(ir.Z,{className:n.row,hover:!0},l.createElement(r7.default,{className:n.cell,component:"th",scope:"row"},l.createElement(ih,{className:n.link,href:"/job_distributors/".concat(t.id)},t.name)),l.createElement(r7.default,null,l.createElement(xv,{isActive:t.isConnectionActive,activeText:"Connected",inactiveText:"Disconnected"})),l.createElement(r7.default,null,l.createElement(xv,{isActive:!t.disabledAt,activeText:"Enabled",inactiveText:"Disabled"})),l.createElement(r7.default,null,_T(t.publicKey,{start:6,end:6}),l.createElement(_x,{data:t.publicKey})),l.createElement(r7.default,null,t.uri))}),xw=function(e){var t=e.jobDistributors;return l.createElement(ig,null,l.createElement(d.Z,{container:!0},l.createElement(d.Z,{item:!0,xs:9},l.createElement(iy,null,"Job Distributors")),l.createElement(d.Z,{item:!0,xs:3},l.createElement(d.Z,{container:!0,justify:"flex-end"},l.createElement(d.Z,{item:!0},l.createElement(aA.Z,{variant:"secondary",component:tz,href:"/job_distributors/new"},"New Job Distributor")))),l.createElement(d.Z,{item:!0,xs:12},l.createElement(r5.Z,null,l.createElement(r8.Z,null,l.createElement(ie.Z,null,l.createElement(ir.Z,null,l.createElement(r7.default,null,"Name"),l.createElement(r7.default,null,"Connection Status"),l.createElement(r7.default,null,"Status"),l.createElement(r7.default,null,"CSA Public Key"),l.createElement(r7.default,null,"RPC URL"))),l.createElement(r9.Z,null,0===t.length&&l.createElement(ir.Z,null,l.createElement(r7.default,{component:"th",scope:"row",colSpan:3},"Job Distributors have not been registered")),t.map(function(e){return l.createElement(xy,{key:e.id,jobDistributor:e})})))))))},x_=function(){var e,t=xd({fetchPolicy:"cache-and-network"}),n=t.data,r=t.loading,i=t.error;return r?l.createElement(iR,null):i?l.createElement(iD,{error:i}):l.createElement(xw,{jobDistributors:null!==(e=null==n?void 0:n.feedsManagers.results)&&void 0!==e?e:[]})},xE=bv().shape({name:p0().required("Required"),uri:p0().required("Required"),publicKey:p0().required("Required")}),xS=function(e){var t=e.initialValues,n=e.onSubmit;return l.createElement(hT,{initialValues:t,validationSchema:xE,onSubmit:n},function(e){var t=e.isSubmitting,n=e.submitForm;return l.createElement(hR,{"data-testid":"feeds-manager-form"},l.createElement(d.Z,{container:!0,spacing:16},l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(hP,{component:hX,id:"name",name:"name",label:"Name",required:!0,fullWidth:!0,FormHelperTextProps:{"data-testid":"name-helper-text"}})),l.createElement(d.Z,{item:!0,xs:!1,md:6}),l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(hP,{component:hX,id:"uri",name:"uri",label:"URI",required:!0,fullWidth:!0,helperText:"Provided by the Job Distributor operator",FormHelperTextProps:{"data-testid":"uri-helper-text"}})),l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(hP,{component:hX,id:"publicKey",name:"publicKey",label:"Public Key",required:!0,fullWidth:!0,helperText:"Provided by the Job Distributor operator",FormHelperTextProps:{"data-testid":"publicKey-helper-text"}})),l.createElement(d.Z,{item:!0,xs:12},l.createElement(ok.default,{variant:"contained",color:"primary",disabled:t,onClick:n},"Submit"))))})},xk=function(e){var t=e.data,n=e.onSubmit,r={name:t.name,uri:t.uri,publicKey:t.publicKey};return l.createElement(d.Z,{container:!0},l.createElement(d.Z,{item:!0,xs:12,md:11,lg:9},l.createElement(r5.Z,null,l.createElement(sl.Z,{title:"Edit Job Distributor"}),l.createElement(aW.Z,null,l.createElement(xS,{initialValues:r,onSubmit:n})))))};function xx(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0&&void 0!==arguments[0]?arguments[0]:{};return rv(xZ,e)},xJ=function(){return(0,b.createStyles)({root:{fontSize:24}})},xQ=(0,b.withStyles)(xJ)(function(e){var t=e.children,n=e.classes;return l.createElement(x.default,{variant:"h2",className:n.root},t)}),x1=n(9290),x0=n(74923);function x2(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n=0)&&Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}function TT(e,t){if(null==e)return{};var n,r,i={},a=Object.keys(e);for(r=0;r=0||(i[n]=e[n]);return i}function TM(e,t){return Tv(e)||TE(e,t)||TA(e,t)||TS()}function TO(e){return Ty(e)||T_(e)||TA(e)||Tk()}function TA(e,t){if(e){if("string"==typeof e)return Tg(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);if("Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n)return Array.from(n);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return Tg(e,t)}}var TL=function(e){return"SN_MAIN"===e||"SN_SEPOLIA"===e},TC=bv().shape({chainID:p0().required("Required"),chainType:p0().required("Required"),accountAddr:p0().required("Required"),accountAddrPubKey:p0().nullable(),adminAddr:p0(),ocr1Multiaddr:p0().when(["ocr1Enabled","ocr1IsBootstrap"],{is:function(e,t){return e&&t},then:p0().required("Required").nullable()}).nullable(),ocr1P2PPeerID:p0().when(["ocr1Enabled","ocr1IsBootstrap"],{is:function(e,t){return e&&!t},then:p0().required("Required").nullable()}).nullable(),ocr1KeyBundleID:p0().when(["ocr1Enabled","ocr1IsBootstrap"],{is:function(e,t){return e&&!t},then:p0().required("Required").nullable()}).nullable(),ocr2Multiaddr:p0().when(["ocr2Enabled","ocr2IsBootstrap"],{is:function(e,t){return e&&t},then:p0().required("Required").nullable()}).nullable(),ocr2P2PPeerID:p0().when(["ocr2Enabled","ocr2IsBootstrap"],{is:function(e,t){return e&&!t},then:p0().required("Required").nullable()}).nullable(),ocr2KeyBundleID:p0().when(["ocr2Enabled","ocr2IsBootstrap"],{is:function(e,t){return e&&!t},then:p0().required("Required").nullable()}).nullable(),ocr2CommitPluginEnabled:pV().required("Required"),ocr2ExecutePluginEnabled:pV().required("Required"),ocr2MedianPluginEnabled:pV().required("Required"),ocr2MercuryPluginEnabled:pV().required("Required"),ocr2ForwarderAddress:p0().nullable()}),TI=function(e){return(0,b.createStyles)({supportedJobOptionsPaper:{padding:2*e.spacing.unit}})},TD=function(e){var t=e.addresses,n=Tx(e,["addresses"]),r=h_(),i=r.values,a=i.chainID,o=i.accountAddr,s=r.setFieldValue,u=TM(l.useState(!1),2),c=u[0],f=u[1],d=l.useRef();l.useEffect(function(){d.current=a},[a]),l.useEffect(function(){a!==d.current&&(s(n.name,""),f(!1))},[a,s,n.name]);var h=function(e){var t=e.target.value;"custom"===t?(f(!0),s(n.name,"")):(f(!1),s(n.name,t))};return l.createElement(l.Fragment,null,!TL(a)&&l.createElement(hP,Tw({},n,{select:!0,value:c?"custom":o,onChange:h}),t.map(function(e){return l.createElement(tE.default,{key:e,value:e},e)})),TL(a)&&l.createElement(hP,{component:hX,id:"accountAddr",name:"accountAddr",label:"Enter your account address",inputProps:{"data-testid":"customAccountAddr-input"},helperText:"The account address used for this chain",required:!0,fullWidth:!0}),TL(a)&&l.createElement("div",null,l.createElement(hP,{component:hX,id:"accountAddrPubKey",name:"accountAddrPubKey",label:"Account Address Public Key",required:!0,fullWidth:!0,helperText:"The public key for your account address",FormHelperTextProps:{"data-testid":"accountAddrPubKey-helper-text"}})))},TN=(0,b.withStyles)(TI)(function(e){var t=e.classes,n=e.editing,r=void 0!==n&&n,i=e.innerRef,a=e.initialValues,o=e.onSubmit,s=e.chains,u=void 0===s?[]:s,c=e.accountsEVM,f=void 0===c?[]:c,h=e.accountsNonEvm,p=e.p2pKeys,b=void 0===p?[]:p,m=e.ocrKeys,g=void 0===m?[]:m,v=e.ocr2Keys,y=void 0===v?[]:v,w=e.showSubmit,_=void 0!==w&&w,E=TO(y).sort(function(e,t){var n,r,i;return e.chainType===t.chainType?e.id.localeCompare(t.id):null!==(i=null===(n=e.chainType)||void 0===n?void 0:n.localeCompare(null!==(r=t.chainType)&&void 0!==r?r:""))&&void 0!==i?i:0});return l.createElement(hT,{innerRef:i,initialValues:a,validationSchema:TC,onSubmit:o},function(e){var n,i,a=e.values,o=[];switch(a.chainType){case Tm.EVM:o=f.filter(function(e){return e.chain.id==a.chainID&&!e.isDisabled}).map(function(e){return e.address});break;case Tm.APTOS:o=null!==(n=null==h?void 0:h.aptosKeys.results.map(function(e){return e.account}))&&void 0!==n?n:[];break;case Tm.SOLANA:o=null!==(i=null==h?void 0:h.solanaKeys.results.map(function(e){return e.id}))&&void 0!==i?i:[];break;default:o=[]}var s=u.filter(function(e){return e.network.toUpperCase()===a.chainType});return l.createElement(hR,{"data-testid":"feeds-manager-form",id:"chain-configuration-form",noValidate:!0},l.createElement(d.Z,{container:!0,spacing:16},l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(hP,{component:hX,id:"chainType",name:"chainType",label:"Chain Type",select:!0,required:!0,fullWidth:!0,disabled:r},l.createElement(tE.default,{key:Tm.EVM,value:Tm.EVM},"EVM"),l.createElement(tE.default,{key:Tm.APTOS,value:Tm.APTOS},"APTOS"),l.createElement(tE.default,{key:Tm.SOLANA,value:Tm.SOLANA},"SOLANA"))),l.createElement(d.Z,{item:!0,xs:12,md:6},s.length>0&&!r?l.createElement(hP,{component:hX,id:"chainID",name:"chainID",label:"Chain ID",required:!0,fullWidth:!0,select:!0,disabled:r,inputProps:{"data-testid":"chainID-input"},FormHelperTextProps:{"data-testid":"chainID-helper-text"}},s.map(function(e){return l.createElement(tE.default,{key:e.id,value:e.id},e.id)})):l.createElement(hP,{component:hX,id:"chainID",name:"chainID",label:"Chain ID",required:!0,fullWidth:!0,disabled:r,inputProps:{"data-testid":"chainID-manual-input"},FormHelperTextProps:{"data-testid":"chainID-helper-manual-text"}})),l.createElement(d.Z,{item:!0,xs:12,md:6},o.length>0?l.createElement(TD,{component:hX,id:"accountAddr",name:"accountAddr",label:"Account Address",inputProps:{"data-testid":"accountAddr-input"},required:!0,fullWidth:!0,select:!0,helperText:"The account address used for this chain",addresses:o,FormHelperTextProps:{"data-testid":"accountAddr-helper-text"}}):l.createElement(hP,{component:hX,id:"accountAddr",name:"accountAddr",label:"Account Address",inputProps:{"data-testid":"accountAddr-manual-input"},required:!0,fullWidth:!0,helperText:"The account address used for this chain",FormHelperTextProps:{"data-testid":"accountAddr-helper-manual-text"}})),l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(hP,{component:hX,id:"adminAddr",name:"adminAddr",label:"Admin Address",fullWidth:!0,helperText:"The address used for LINK payments",FormHelperTextProps:{"data-testid":"adminAddr-helper-text"}})),l.createElement(d.Z,{item:!0,xs:12},l.createElement(x.default,null,"Supported Job Types")),l.createElement(d.Z,{item:!0,xs:12},l.createElement(hP,{component:h2,name:"fluxMonitorEnabled",type:"checkbox",Label:{label:"Flux Monitor"}})),l.createElement(d.Z,{item:!0,xs:12},l.createElement(hP,{component:h2,name:"ocr1Enabled",type:"checkbox",Label:{label:"OCR"}}),a.ocr1Enabled&&l.createElement(ii.default,{className:t.supportedJobOptionsPaper},l.createElement(d.Z,{container:!0,spacing:8},l.createElement(l.Fragment,null,l.createElement(d.Z,{item:!0,xs:12},l.createElement(hP,{component:h2,name:"ocr1IsBootstrap",type:"checkbox",Label:{label:"Is this node running as a bootstrap peer?"}})),a.ocr1IsBootstrap?l.createElement(d.Z,{item:!0,xs:12},l.createElement(hP,{component:hX,id:"ocr1Multiaddr",name:"ocr1Multiaddr",label:"Multiaddr",required:!0,fullWidth:!0,helperText:"The OCR Multiaddr which oracles use to query for network information",FormHelperTextProps:{"data-testid":"ocr1Multiaddr-helper-text"}})):l.createElement(l.Fragment,null,l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(hP,{component:hX,id:"ocr1P2PPeerID",name:"ocr1P2PPeerID",label:"Peer ID",select:!0,required:!0,fullWidth:!0,helperText:"The Peer ID used for this chain",FormHelperTextProps:{"data-testid":"ocr1P2PPeerID-helper-text"}},b.map(function(e){return l.createElement(tE.default,{key:e.peerID,value:e.peerID},e.peerID)}))),l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(hP,{component:hX,id:"ocr1KeyBundleID",name:"ocr1KeyBundleID",label:"Key Bundle ID",select:!0,required:!0,fullWidth:!0,helperText:"The OCR Key Bundle ID used for this chain",FormHelperTextProps:{"data-testid":"ocr1KeyBundleID-helper-text"}},g.map(function(e){return l.createElement(tE.default,{key:e.id,value:e.id},e.id)})))))))),l.createElement(d.Z,{item:!0,xs:12},l.createElement(hP,{component:h2,name:"ocr2Enabled",type:"checkbox",Label:{label:"OCR2"}}),a.ocr2Enabled&&l.createElement(ii.default,{className:t.supportedJobOptionsPaper},l.createElement(d.Z,{container:!0,spacing:8},l.createElement(l.Fragment,null,l.createElement(d.Z,{item:!0,xs:12},l.createElement(hP,{component:h2,name:"ocr2IsBootstrap",type:"checkbox",Label:{label:"Is this node running as a bootstrap peer?"}})),l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(hP,{component:hX,id:"ocr2P2PPeerID",name:"ocr2P2PPeerID",label:"Peer ID",select:!0,required:!a.ocr2IsBootstrap,fullWidth:!0,helperText:"The Peer ID used for this chain",FormHelperTextProps:{"data-testid":"ocr2P2PPeerID-helper-text"}},b.map(function(e){return l.createElement(tE.default,{key:e.peerID,value:e.peerID},e.peerID)}))),a.ocr2IsBootstrap?l.createElement(d.Z,{item:!0,xs:12},l.createElement(hP,{component:hX,id:"ocr2Multiaddr",name:"ocr2Multiaddr",label:"Multiaddr",required:!0,fullWidth:!0,helperText:"The OCR2 Multiaddr which oracles use to query for network information",FormHelperTextProps:{"data-testid":"ocr2Multiaddr-helper-text"}})):l.createElement(l.Fragment,null,l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(hP,{component:hX,id:"ocr2KeyBundleID",name:"ocr2KeyBundleID",label:"Key Bundle ID",select:!0,required:!0,fullWidth:!0,helperText:"The OCR2 Key Bundle ID used for this chain",FormHelperTextProps:{"data-testid":"ocr2KeyBundleID-helper-text"}},E.map(function(e){return l.createElement(tE.default,{key:e.id,value:e.id},e.id," (",e.chainType,")")}))),l.createElement(d.Z,{item:!0,xs:12},l.createElement(x.default,null,"Supported Plugins")),l.createElement(d.Z,{item:!0,xs:6},l.createElement(hP,{component:h2,name:"ocr2CommitPluginEnabled",type:"checkbox",Label:{label:"Commit"}})),l.createElement(d.Z,{item:!0,xs:6},l.createElement(hP,{component:h2,name:"ocr2ExecutePluginEnabled",type:"checkbox",Label:{label:"Execute"}})),l.createElement(d.Z,{item:!0,xs:6},l.createElement(hP,{component:h2,name:"ocr2RebalancerPluginEnabled",type:"checkbox",Label:{label:"Rebalancer"}})),l.createElement(d.Z,{item:!0,xs:6},l.createElement(hP,{component:h2,name:"ocr2MedianPluginEnabled",type:"checkbox",Label:{label:"Median"}})),l.createElement(d.Z,{item:!0,xs:6},l.createElement(hP,{component:h2,name:"ocr2MercuryPluginEnabled",type:"checkbox",Label:{label:"Mercury"}})),l.createElement(d.Z,{item:!0,xs:12,md:12},l.createElement(hP,{component:hX,id:"ocr2ForwarderAddress",name:"ocr2ForwarderAddress",label:"Forwarder Address (optional)",fullWidth:!0,helperText:"The forwarder address from the Operator Forwarder Contract",FormHelperTextProps:{"data-testid":"ocr2ForwarderAddress-helper-text"}}))))))),_&&l.createElement(d.Z,{item:!0,xs:12,md:7},l.createElement(ok.default,{variant:"contained",color:"primary",type:"submit",size:"large"},"Submit"))))})});function TP(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function TR(){var e=TP(["\n fragment AptosKeysPayload_ResultsFields on AptosKey {\n account\n id\n }\n"]);return TR=function(){return e},e}function Tj(){var e=TP(["\n fragment SolanaKeysPayload_ResultsFields on SolanaKey {\n id\n }\n"]);return Tj=function(){return e},e}function TF(){var e=TP(["\n ","\n ","\n query FetchNonEvmKeys {\n aptosKeys {\n results {\n ...AptosKeysPayload_ResultsFields\n }\n }\n solanaKeys {\n results {\n ...SolanaKeysPayload_ResultsFields\n }\n }\n }\n"]);return TF=function(){return e},e}var TY=n0(TR()),TB=n0(Tj()),TU=n0(TF(),TY,TB),TH=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return rv(TU,e)},T$=function(e){var t=e.onClose,n=e.open,r=e.onSubmit,i=l.useRef(),a=i$({fetchPolicy:"network-only"}).data,o=_K({fetchPolicy:"cache-and-network"}).data,s=TH({fetchPolicy:"cache-and-network"}).data,u=SV({fetchPolicy:"cache-and-network"}).data,c=EO({fetchPolicy:"cache-and-network"}).data,f=E2({fetchPolicy:"cache-and-network"}).data,d={chainID:"",chainType:Tm.EVM,accountAddr:"",adminAddr:"",accountAddrPubKey:"",fluxMonitorEnabled:!1,ocr1Enabled:!1,ocr1IsBootstrap:!1,ocr1Multiaddr:"",ocr1P2PPeerID:"",ocr1KeyBundleID:"",ocr2Enabled:!1,ocr2IsBootstrap:!1,ocr2Multiaddr:"",ocr2P2PPeerID:"",ocr2KeyBundleID:"",ocr2CommitPluginEnabled:!1,ocr2ExecutePluginEnabled:!1,ocr2MedianPluginEnabled:!1,ocr2MercuryPluginEnabled:!1,ocr2RebalancerPluginEnabled:!1,ocr2ForwarderAddress:""},h=a?a.chains.results:[],p=o?o.ethKeys.results:[],b=u?u.p2pKeys.results:[],m=c?c.ocrKeyBundles.results:[],g=f?f.ocr2KeyBundles.results:[];return l.createElement(aD.Z,{onClose:t,open:n,disableBackdropClick:!0},l.createElement(oO.Z,{disableTypography:!0},l.createElement(x.default,{variant:"body2"},"New Supported Chain")),l.createElement(oT.Z,null,l.createElement(TN,{innerRef:i,initialValues:d,onSubmit:r,chains:h,accountsEVM:p,accountsNonEvm:s,p2pKeys:b,ocrKeys:m,ocr2Keys:g})),l.createElement(ox.Z,null,l.createElement(ok.default,{onClick:t},"Cancel"),l.createElement(ok.default,{color:"primary",type:"submit",form:"chain-configuration-form",variant:"contained"},"Submit")))},Tz=function(e){var t=e.cfg,n=e.onClose,r=e.open,i=e.onSubmit,a=l.useRef(),o=i$({fetchPolicy:"network-only"}).data,s=_K({fetchPolicy:"cache-and-network"}).data,u=TH({fetchPolicy:"cache-and-network"}).data,c=SV({fetchPolicy:"cache-and-network"}).data,f=EO({fetchPolicy:"cache-and-network"}).data,d=E2({fetchPolicy:"cache-and-network"}).data;if(!t)return null;var h={chainID:t.chainID,chainType:t.chainType,accountAddr:t.accountAddr,adminAddr:t.adminAddr,accountAddrPubKey:t.accountAddrPubKey,fluxMonitorEnabled:t.fluxMonitorJobConfig.enabled,ocr1Enabled:t.ocr1JobConfig.enabled,ocr1IsBootstrap:t.ocr1JobConfig.isBootstrap,ocr1Multiaddr:t.ocr1JobConfig.multiaddr,ocr1P2PPeerID:t.ocr1JobConfig.p2pPeerID,ocr1KeyBundleID:t.ocr1JobConfig.keyBundleID,ocr2Enabled:t.ocr2JobConfig.enabled,ocr2IsBootstrap:t.ocr2JobConfig.isBootstrap,ocr2Multiaddr:t.ocr2JobConfig.multiaddr,ocr2P2PPeerID:t.ocr2JobConfig.p2pPeerID,ocr2KeyBundleID:t.ocr2JobConfig.keyBundleID,ocr2CommitPluginEnabled:t.ocr2JobConfig.plugins.commit,ocr2ExecutePluginEnabled:t.ocr2JobConfig.plugins.execute,ocr2MedianPluginEnabled:t.ocr2JobConfig.plugins.median,ocr2MercuryPluginEnabled:t.ocr2JobConfig.plugins.mercury,ocr2RebalancerPluginEnabled:t.ocr2JobConfig.plugins.rebalancer,ocr2ForwarderAddress:t.ocr2JobConfig.forwarderAddress},p=o?o.chains.results:[],b=s?s.ethKeys.results:[],m=c?c.p2pKeys.results:[],g=f?f.ocrKeyBundles.results:[],v=d?d.ocr2KeyBundles.results:[];return l.createElement(aD.Z,{onClose:n,open:r,disableBackdropClick:!0},l.createElement(oO.Z,{disableTypography:!0},l.createElement(x.default,{variant:"body2"},"Edit Supported Chain")),l.createElement(oT.Z,null,l.createElement(TN,{innerRef:a,initialValues:h,onSubmit:i,chains:p,accountsEVM:b,accountsNonEvm:u,p2pKeys:m,ocrKeys:g,ocr2Keys:v,editing:!0})),l.createElement(ox.Z,null,l.createElement(ok.default,{onClick:n},"Cancel"),l.createElement(ok.default,{color:"primary",type:"submit",form:"chain-configuration-form",variant:"contained"},"Submit")))};function TG(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);nt.version?e:t})},[o]),g=l.useMemo(function(){return M1(o).sort(function(e,t){return t.version-e.version})},[o]),v=function(e,t,n){switch(e){case"PENDING":return l.createElement(l.Fragment,null,l.createElement(ok.default,{variant:"text",color:"secondary",onClick:function(){return b("reject",t)}},"Reject"),m.id===t&&"DELETED"!==n.status&&"REVOKED"!==n.status&&l.createElement(ok.default,{variant:"contained",color:"primary",onClick:function(){return b("approve",t)}},"Approve"),m.id===t&&"DELETED"===n.status&&n.pendingUpdate&&l.createElement(l.Fragment,null,l.createElement(ok.default,{variant:"contained",color:"primary",onClick:function(){return b("cancel",t)}},"Cancel"),l.createElement(x.default,{color:"error"},"This proposal was deleted. Cancel the spec to delete any running jobs")));case"APPROVED":return l.createElement(l.Fragment,null,l.createElement(ok.default,{variant:"contained",onClick:function(){return b("cancel",t)}},"Cancel"),"DELETED"===n.status&&n.pendingUpdate&&l.createElement(x.default,{color:"error"},"This proposal was deleted. Cancel the spec to delete any running jobs"));case"CANCELLED":if(m.id===t&&"DELETED"!==n.status&&"REVOKED"!==n.status)return l.createElement(ok.default,{variant:"contained",color:"primary",onClick:function(){return b("approve",t)}},"Approve");return null;default:return null}};return l.createElement("div",null,g.map(function(e,n){return l.createElement(mP.Z,{defaultExpanded:0===n,key:n},l.createElement(mR.Z,{expandIcon:l.createElement(gh.Z,null)},l.createElement(x.default,{className:t.versionText},"Version ",e.version),l.createElement(Es.Z,{label:e.status,color:"APPROVED"===e.status?"primary":"default",variant:"REJECTED"===e.status||"CANCELLED"===e.status?"outlined":"default"}),l.createElement("div",{className:t.proposedAtContainer},l.createElement(x.default,null,"Proposed ",l.createElement(aO,{tooltip:!0},e.createdAt)))),l.createElement(mj.Z,{className:t.expansionPanelDetails},l.createElement("div",{className:t.actions},l.createElement("div",{className:t.editContainer},0===n&&("PENDING"===e.status||"CANCELLED"===e.status)&&"DELETED"!==s.status&&"REVOKED"!==s.status&&l.createElement(ok.default,{variant:"contained",onClick:function(){return p(!0)}},"Edit")),l.createElement("div",{className:t.actionsContainer},v(e.status,e.id,s))),l.createElement(gd,{language:"toml",style:gs,"data-testid":"codeblock"},e.definition)))}),l.createElement(oC,{open:null!=c,title:c?M6[c.action].title:"",body:c?M6[c.action].body:"",onConfirm:function(){if(c){switch(c.action){case"approve":n(c.id);break;case"cancel":r(c.id);break;case"reject":i(c.id)}f(null)}},cancelButtonText:"Cancel",onCancel:function(){return f(null)}}),l.createElement(Mz,{open:h,onClose:function(){return p(!1)},initialValues:{definition:m.definition,id:m.id},onSubmit:a}))});function M8(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function M9(){var e=M8(["\n ","\n fragment JobProposalPayloadFields on JobProposal {\n id\n externalJobID\n remoteUUID\n jobID\n specs {\n ...JobProposal_SpecsFields\n }\n status\n pendingUpdate\n }\n"]);return M9=function(){return e},e}var M7=n0(M9(),M3),Oe=function(e){var t=e.onApprove,n=e.onCancel,r=e.onReject,i=e.onUpdateSpec,a=e.proposal;return l.createElement(ig,null,l.createElement(d.Z,{container:!0,spacing:32},l.createElement(d.Z,{item:!0,xs:9},l.createElement(iy,null,"Job Proposal #",a.id))),l.createElement(MF,{proposal:a}),l.createElement(d.Z,{container:!0,spacing:32},l.createElement(d.Z,{item:!0,xs:9},l.createElement(xQ,null,"Specs"))),l.createElement(d.Z,{container:!0,spacing:32},l.createElement(d.Z,{item:!0,xs:12},l.createElement(M5,{proposal:a,specs:a.specs,onReject:r,onApprove:t,onCancel:n,onUpdateSpec:i}))))};function Ot(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);nU,tA:()=>$,KL:()=>H,Iw:()=>V,DQ:()=>W,cB:()=>T,LO:()=>M,t5:()=>k,qt:()=>x,Jc:()=>C,L7:()=>Y,EO:()=>B});var r,i,a=n(66289),o=n(41800),s=n.n(o),u=n(67932);(i=r||(r={})).IN_PROGRESS="in_progress",i.PENDING_INCOMING_CONFIRMATIONS="pending_incoming_confirmations",i.PENDING_CONNECTION="pending_connection",i.PENDING_BRIDGE="pending_bridge",i.PENDING_SLEEP="pending_sleep",i.ERRORED="errored",i.COMPLETED="completed";var c=n(87013),l=n(19084),f=n(34823);function d(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]j,v2:()=>F});var r=n(66289);function i(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var a="/sessions",o="/sessions",s=function e(t){var n=this;i(this,e),this.api=t,this.createSession=function(e){return n.create(e)},this.destroySession=function(){return n.destroy()},this.create=this.api.createResource(a),this.destroy=this.api.deleteResource(o)};function u(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var c="/v2/bulk_delete_runs",l=function e(t){var n=this;u(this,e),this.api=t,this.bulkDeleteJobRuns=function(e){return n.destroy(e)},this.destroy=this.api.deleteResource(c)};function f(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var d="/v2/chains/evm",h="".concat(d,"/:id"),p=function e(t){var n=this;f(this,e),this.api=t,this.getChains=function(){return n.index()},this.createChain=function(e){return n.create(e)},this.destroyChain=function(e){return n.destroy(void 0,{id:e})},this.updateChain=function(e,t){return n.update(t,{id:e})},this.index=this.api.fetchResource(d),this.create=this.api.createResource(d),this.destroy=this.api.deleteResource(h),this.update=this.api.updateResource(h)};function b(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var m="/v2/keys/evm/chain",g=function e(t){var n=this;b(this,e),this.api=t,this.chain=function(e){var t=new URLSearchParams;t.append("address",e.address),t.append("evmChainID",e.evmChainID),null!==e.abandon&&t.append("abandon",String(e.abandon)),null!==e.enabled&&t.append("enabled",String(e.enabled));var r=m+"?"+t.toString();return n.api.createResource(r)()}};function v(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var y="/v2/jobs",w="".concat(y,"/:specId/runs"),_=function e(t){var n=this;v(this,e),this.api=t,this.createJobRunV2=function(e,t){return n.post(t,{specId:e})},this.post=this.api.createResource(w,!0)};function E(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var S="/v2/log",k=function e(t){var n=this;E(this,e),this.api=t,this.getLogConfig=function(){return n.show()},this.updateLogConfig=function(e){return n.update(e)},this.show=this.api.fetchResource(S),this.update=this.api.updateResource(S)};function x(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var T="/v2/nodes",M=function e(t){var n=this;x(this,e),this.api=t,this.getNodes=function(){return n.index()},this.createNode=function(e){return n.create(e)},this.index=this.api.fetchResource(T),this.create=this.api.createResource(T)};function O(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var A="/v2/enroll_webauthn",L=function e(t){var n=this;O(this,e),this.api=t,this.beginKeyRegistration=function(e){return n.create(e)},this.finishKeyRegistration=function(e){return n.put(e)},this.create=this.api.fetchResource(A),this.put=this.api.createResource(A)};function C(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var I="/v2/build_info",D=function e(t){var n=this;C(this,e),this.api=t,this.show=function(){return n.api.GET(I)()}};function N(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var P=function e(t){N(this,e),this.api=t,this.buildInfo=new D(this.api),this.bulkDeleteRuns=new l(this.api),this.chains=new p(this.api),this.logConfig=new k(this.api),this.nodes=new M(this.api),this.jobs=new _(this.api),this.webauthn=new L(this.api),this.evmKeys=new g(this.api)},R=new r.V0({base:void 0}),j=new s(R),F=new P(R)},1398(e,t,n){"use strict";n.d(t,{Z:()=>d});var r=n(67294),i=n(32316),a=n(83638),o=n(94184),s=n.n(o);function u(){return(u=Object.assign||function(e){for(var t=1;tc});var r=n(67294),i=n(32316);function a(){return(a=Object.assign||function(e){for(var t=1;tx,jK:()=>v});var r=n(67294),i=n(37703),a=n(45697),o=n.n(a),s=n(82204),u=n(71426),c=n(94184),l=n.n(c),f=n(32316),d=function(e){var t=e.palette.success||{},n=e.palette.warning||{};return{base:{paddingLeft:5*e.spacing.unit,paddingRight:5*e.spacing.unit},success:{backgroundColor:t.main,color:t.contrastText},error:{backgroundColor:e.palette.error.dark,color:e.palette.error.contrastText},warning:{backgroundColor:n.contrastText,color:n.main}}},h=function(e){var t,n=e.success,r=e.error,i=e.warning,a=e.classes,o=e.className;return n?t=a.success:r?t=a.error:i&&(t=a.warning),l()(a.base,o,t)},p=function(e){return r.createElement(s.Z,{className:h(e),square:!0},r.createElement(u.default,{variant:"body2",color:"inherit",component:"div"},e.children))};p.defaultProps={success:!1,error:!1,warning:!1},p.propTypes={success:o().bool,error:o().bool,warning:o().bool};let b=(0,f.withStyles)(d)(p);var m=function(){return r.createElement(r.Fragment,null,"Unhandled error. Please help us by opening a"," ",r.createElement("a",{href:"https://github.com/smartcontractkit/chainlink/issues/new"},"bug report"))};let g=m;function v(e){return"string"==typeof e?e:e.component?e.component(e.props):r.createElement(g,null)}function y(e,t){var n;return n="string"==typeof e?e:e.component?e.component(e.props):r.createElement(g,null),r.createElement("p",{key:t},n)}var w=function(e){var t=e.notifications;return r.createElement(b,{error:!0},t.map(y))},_=function(e){var t=e.notifications;return r.createElement(b,{success:!0},t.map(y))},E=function(e){var t=e.errors,n=e.successes;return r.createElement("div",null,(null==t?void 0:t.length)>0&&r.createElement(w,{notifications:t}),n.length>0&&r.createElement(_,{notifications:n}))},S=function(e){return{errors:e.notifications.errors,successes:e.notifications.successes}},k=(0,i.$j)(S)(E);let x=k},9409(e,t,n){"use strict";n.d(t,{ZP:()=>j});var r=n(67294),i=n(37703),a=n(5977),o=n(32316),s=n(1398),u=n(82204),c=n(30060),l=n(71426),f=n(60520),d=n(39814),h=n(57209),p=n(26842),b=n(3950),m=n(5536),g=n(45697),v=n.n(g);let y=n.p+"9f6d832ef97e8493764e.svg";function w(){return(w=Object.assign||function(e){for(var t=1;te.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&_.map(function(e,t){return r.createElement(d.Z,{item:!0,xs:12,key:t},r.createElement(u.Z,{raised:!1,className:v.error},r.createElement(c.Z,null,r.createElement(l.default,{variant:"body1",className:v.errorText},(0,b.jK)(e)))))}),r.createElement(d.Z,{item:!0,xs:12},r.createElement(f.Z,{id:"email",label:"Email",margin:"normal",value:n,onChange:m("email"),error:_.length>0,variant:"outlined",fullWidth:!0})),r.createElement(d.Z,{item:!0,xs:12},r.createElement(f.Z,{id:"password",label:"Password",type:"password",autoComplete:"password",margin:"normal",value:h,onChange:m("password"),error:_.length>0,variant:"outlined",fullWidth:!0})),r.createElement(d.Z,{item:!0,xs:12},r.createElement(d.Z,{container:!0,spacing:0,justify:"center"},r.createElement(d.Z,{item:!0},r.createElement(s.Z,{type:"submit",variant:"primary"},"Access Account")))),y&&r.createElement(l.default,{variant:"body1",color:"textSecondary"},"Signing in...")))))))},P=function(e){return{fetching:e.authentication.fetching,authenticated:e.authentication.allowed,errors:e.notifications.errors}},R=(0,i.$j)(P,x({submitSignIn:p.L7}))(N);let j=(0,h.wU)(e)((0,o.withStyles)(D)(R))},16353(e,t,n){"use strict";n.d(t,{ZP:()=>H,rH:()=>U});var r,i=n(37703),a=n(97779),o=n(9541),s=n(19084);function u(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function c(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:h,t=arguments.length>1?arguments[1]:void 0;switch(t.type){case s.Mk.RECEIVE_SIGNOUT_SUCCESS:case s.Mk.RECEIVE_SIGNIN_SUCCESS:var n={allowed:t.authenticated};return o.Ks(n),f(c({},e,n),{errors:[]});case s.Mk.RECEIVE_SIGNIN_FAIL:var r={allowed:!1};return o.Ks(r),f(c({},e,r),{errors:[]});case s.Mk.RECEIVE_SIGNIN_ERROR:case s.Mk.RECEIVE_SIGNOUT_ERROR:var i={allowed:!1};return o.Ks(i),f(c({},e,i),{errors:t.errors||[]});default:return e}};let b=p;function m(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function g(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:_,t=arguments.length>1?arguments[1]:void 0;return t.type?t.type.startsWith(r.REQUEST)?y(g({},e),{count:e.count+1}):t.type.startsWith(r.RECEIVE)?y(g({},e),{count:Math.max(e.count-1,0)}):t.type.startsWith(r.RESPONSE)?y(g({},e),{count:Math.max(e.count-1,0)}):t.type===s.di.REDIRECT?y(g({},e),{count:0}):e:e};let S=E;function k(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function x(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:O,t=arguments.length>1?arguments[1]:void 0;switch(t.type){case s.di.MATCH_ROUTE:return M(x({},O),{currentUrl:t.pathname});case s.Ih.NOTIFY_SUCCESS:var n={component:t.component,props:t.props};return M(x({},e),{successes:[n],errors:[]});case s.Ih.NOTIFY_SUCCESS_MSG:return M(x({},e),{successes:[t.msg],errors:[]});case s.Ih.NOTIFY_ERROR:var r=t.error.errors,i=null==r?void 0:r.map(function(e){return L(t,e)});return M(x({},e),{successes:[],errors:i});case s.Ih.NOTIFY_ERROR_MSG:return M(x({},e),{successes:[],errors:[t.msg]});case s.Mk.RECEIVE_SIGNIN_FAIL:return M(x({},e),{successes:[],errors:["Your email or password is incorrect. Please try again"]});default:return e}};function L(e,t){return{component:e.component,props:{msg:t.detail}}}let C=A;function I(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function D(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:R,t=arguments.length>1?arguments[1]:void 0;switch(t.type){case s.di.REDIRECT:return P(D({},e),{to:t.to});case s.di.MATCH_ROUTE:return P(D({},e),{to:void 0});default:return e}};let F=j;var Y=n(87013),B=(0,a.UY)({authentication:b,fetching:S,notifications:C,redirect:F,buildInfo:Y.Z});B(void 0,{type:"INITIAL_STATE"});var U=i.v9;let H=B},19084(e,t,n){"use strict";var r,i,a,o,s,u,c,l,f,d;n.d(t,{Ih:()=>i,Mk:()=>a,Y0:()=>s,di:()=>r,jp:()=>o}),n(67294),(u=r||(r={})).REDIRECT="REDIRECT",u.MATCH_ROUTE="MATCH_ROUTE",(c=i||(i={})).NOTIFY_SUCCESS="NOTIFY_SUCCESS",c.NOTIFY_SUCCESS_MSG="NOTIFY_SUCCESS_MSG",c.NOTIFY_ERROR="NOTIFY_ERROR",c.NOTIFY_ERROR_MSG="NOTIFY_ERROR_MSG",(l=a||(a={})).REQUEST_SIGNIN="REQUEST_SIGNIN",l.RECEIVE_SIGNIN_SUCCESS="RECEIVE_SIGNIN_SUCCESS",l.RECEIVE_SIGNIN_FAIL="RECEIVE_SIGNIN_FAIL",l.RECEIVE_SIGNIN_ERROR="RECEIVE_SIGNIN_ERROR",l.RECEIVE_SIGNOUT_SUCCESS="RECEIVE_SIGNOUT_SUCCESS",l.RECEIVE_SIGNOUT_ERROR="RECEIVE_SIGNOUT_ERROR",(f=o||(o={})).RECEIVE_CREATE_ERROR="RECEIVE_CREATE_ERROR",f.RECEIVE_CREATE_SUCCESS="RECEIVE_CREATE_SUCCESS",f.RECEIVE_DELETE_ERROR="RECEIVE_DELETE_ERROR",f.RECEIVE_DELETE_SUCCESS="RECEIVE_DELETE_SUCCESS",f.RECEIVE_UPDATE_ERROR="RECEIVE_UPDATE_ERROR",f.RECEIVE_UPDATE_SUCCESS="RECEIVE_UPDATE_SUCCESS",f.REQUEST_CREATE="REQUEST_CREATE",f.REQUEST_DELETE="REQUEST_DELETE",f.REQUEST_UPDATE="REQUEST_UPDATE",f.UPSERT_CONFIGURATION="UPSERT_CONFIGURATION",f.UPSERT_JOB_RUN="UPSERT_JOB_RUN",f.UPSERT_JOB_RUNS="UPSERT_JOB_RUNS",f.UPSERT_TRANSACTION="UPSERT_TRANSACTION",f.UPSERT_TRANSACTIONS="UPSERT_TRANSACTIONS",f.UPSERT_BUILD_INFO="UPSERT_BUILD_INFO",(d=s||(s={})).FETCH_BUILD_INFO_REQUESTED="FETCH_BUILD_INFO_REQUESTED",d.FETCH_BUILD_INFO_SUCCEEDED="FETCH_BUILD_INFO_SUCCEEDED",d.FETCH_BUILD_INFO_FAILED="FETCH_BUILD_INFO_FAILED"},87013(e,t,n){"use strict";n.d(t,{Y:()=>o,Z:()=>u});var r=n(19084);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:o,t=arguments.length>1?arguments[1]:void 0;return t.type===r.Y0.FETCH_BUILD_INFO_SUCCEEDED?a({},t.buildInfo):e};let u=s},34823(e,t,n){"use strict";n.d(t,{N:()=>r});var r=function(e){return e.buildInfo}},73343(e,t,n){"use strict";n.d(t,{r:()=>u});var r=n(19350),i=n(32316),a=n(59114),o=n(5324),s={props:{MuiGrid:{spacing:3*o.default.unit},MuiCardHeader:{titleTypographyProps:{color:"secondary"}}},palette:{action:{hoverOpacity:.3},primary:{light:"#E5F1FF",main:"#3c40c6",contrastText:"#fff"},secondary:{main:"#3d5170"},success:{light:"#e8faf1",main:r.ek.A700,dark:r.ek[700],contrastText:r.y0.white},warning:{light:"#FFFBF1",main:"#fff6b6",contrastText:"#fad27a"},error:{light:"#ffdada",main:"#f44336",dark:"#d32f2f",contrastText:"#fff"},background:{default:"#f5f6f8",appBar:"#3c40c6"},text:{primary:(0,a.darken)(r.BA.A700,.7),secondary:"#818ea3"},listPendingStatus:{background:"#fef7e5",color:"#fecb4c"},listCompletedStatus:{background:"#e9faf2",color:"#4ed495"}},shape:{borderRadius:o.default.unit},overrides:{MuiButton:{root:{borderRadius:o.default.unit/2,textTransform:"none"},sizeLarge:{padding:void 0,fontSize:void 0,paddingTop:o.default.unit,paddingBottom:o.default.unit,paddingLeft:5*o.default.unit,paddingRight:5*o.default.unit}},MuiTableCell:{body:{fontSize:"1rem"},head:{fontSize:"1rem",fontWeight:400}},MuiCardHeader:{root:{borderBottom:"1px solid rgba(0, 0, 0, 0.12)"},action:{marginTop:-2,marginRight:0,"& >*":{marginLeft:2*o.default.unit}},subheader:{marginTop:.5*o.default.unit}}},typography:{useNextVariants:!0,fontFamily:"-apple-system,BlinkMacSystemFont,Roboto,Helvetica,Arial,sans-serif",button:{textTransform:"none",fontSize:"1.2em"},body1:{fontSize:"1.0rem",fontWeight:400,lineHeight:"1.46429em",color:"rgba(0, 0, 0, 0.87)",letterSpacing:-.4},body2:{fontSize:"1.0rem",fontWeight:500,lineHeight:"1.71429em",color:"rgba(0, 0, 0, 0.87)",letterSpacing:-.4},body1Next:{color:"rgb(29, 29, 29)",fontWeight:400,fontSize:"1rem",lineHeight:1.5,letterSpacing:-.4},body2Next:{color:"rgb(29, 29, 29)",fontWeight:400,fontSize:"0.875rem",lineHeight:1.5,letterSpacing:-.4},display1:{color:"#818ea3",fontSize:"2.125rem",fontWeight:400,lineHeight:"1.20588em",letterSpacing:-.4},display2:{color:"#818ea3",fontSize:"2.8125rem",fontWeight:400,lineHeight:"1.13333em",marginLeft:"-.02em",letterSpacing:-.4},display3:{color:"#818ea3",fontSize:"3.5rem",fontWeight:400,lineHeight:"1.30357em",marginLeft:"-.02em",letterSpacing:-.4},display4:{fontSize:14,fontWeightLight:300,fontWeightMedium:500,fontWeightRegular:400,letterSpacing:-.4},h1:{color:"rgb(29, 29, 29)",fontSize:"6rem",fontWeight:300,lineHeight:1},h2:{color:"rgb(29, 29, 29)",fontSize:"3.75rem",fontWeight:300,lineHeight:1},h3:{color:"rgb(29, 29, 29)",fontSize:"3rem",fontWeight:400,lineHeight:1.04},h4:{color:"rgb(29, 29, 29)",fontSize:"2.125rem",fontWeight:400,lineHeight:1.17},h5:{color:"rgb(29, 29, 29)",fontSize:"1.5rem",fontWeight:400,lineHeight:1.33,letterSpacing:-.4},h6:{fontSize:"0.8rem",fontWeight:450,lineHeight:"1.71429em",color:"rgba(0, 0, 0, 0.87)",letterSpacing:-.4},subheading:{color:"rgb(29, 29, 29)",fontSize:"1rem",fontWeight:400,lineHeight:"1.5em",letterSpacing:-.4},subtitle1:{color:"rgb(29, 29, 29)",fontSize:"1rem",fontWeight:400,lineHeight:1.75,letterSpacing:-.4},subtitle2:{color:"rgb(29, 29, 29)",fontSize:"0.875rem",fontWeight:500,lineHeight:1.57,letterSpacing:-.4}},shadows:["none","0px 1px 3px 0px rgba(0, 0, 0, 0.1),0px 1px 1px 0px rgba(0, 0, 0, 0.04),0px 2px 1px -1px rgba(0, 0, 0, 0.02)","0px 1px 5px 0px rgba(0, 0, 0, 0.1),0px 2px 2px 0px rgba(0, 0, 0, 0.04),0px 3px 1px -2px rgba(0, 0, 0, 0.02)","0px 1px 8px 0px rgba(0, 0, 0, 0.1),0px 3px 4px 0px rgba(0, 0, 0, 0.04),0px 3px 3px -2px rgba(0, 0, 0, 0.02)","0px 2px 4px -1px rgba(0, 0, 0, 0.1),0px 4px 5px 0px rgba(0, 0, 0, 0.04),0px 1px 10px 0px rgba(0, 0, 0, 0.02)","0px 3px 5px -1px rgba(0, 0, 0, 0.1),0px 5px 8px 0px rgba(0, 0, 0, 0.04),0px 1px 14px 0px rgba(0, 0, 0, 0.02)","0px 3px 5px -1px rgba(0, 0, 0, 0.1),0px 6px 10px 0px rgba(0, 0, 0, 0.04),0px 1px 18px 0px rgba(0, 0, 0, 0.02)","0px 4px 5px -2px rgba(0, 0, 0, 0.1),0px 7px 10px 1px rgba(0, 0, 0, 0.04),0px 2px 16px 1px rgba(0, 0, 0, 0.02)","0px 5px 5px -3px rgba(0, 0, 0, 0.1),0px 8px 10px 1px rgba(0, 0, 0, 0.04),0px 3px 14px 2px rgba(0, 0, 0, 0.02)","0px 5px 6px -3px rgba(0, 0, 0, 0.1),0px 9px 12px 1px rgba(0, 0, 0, 0.04),0px 3px 16px 2px rgba(0, 0, 0, 0.02)","0px 6px 6px -3px rgba(0, 0, 0, 0.1),0px 10px 14px 1px rgba(0, 0, 0, 0.04),0px 4px 18px 3px rgba(0, 0, 0, 0.02)","0px 6px 7px -4px rgba(0, 0, 0, 0.1),0px 11px 15px 1px rgba(0, 0, 0, 0.04),0px 4px 20px 3px rgba(0, 0, 0, 0.02)","0px 7px 8px -4px rgba(0, 0, 0, 0.1),0px 12px 17px 2px rgba(0, 0, 0, 0.04),0px 5px 22px 4px rgba(0, 0, 0, 0.02)","0px 7px 8px -4px rgba(0, 0, 0, 0.1),0px 13px 19px 2px rgba(0, 0, 0, 0.04),0px 5px 24px 4px rgba(0, 0, 0, 0.02)","0px 7px 9px -4px rgba(0, 0, 0, 0.1),0px 14px 21px 2px rgba(0, 0, 0, 0.04),0px 5px 26px 4px rgba(0, 0, 0, 0.02)","0px 8px 9px -5px rgba(0, 0, 0, 0.1),0px 15px 22px 2px rgba(0, 0, 0, 0.04),0px 6px 28px 5px rgba(0, 0, 0, 0.02)","0px 8px 10px -5px rgba(0, 0, 0, 0.1),0px 16px 24px 2px rgba(0, 0, 0, 0.04),0px 6px 30px 5px rgba(0, 0, 0, 0.02)","0px 8px 11px -5px rgba(0, 0, 0, 0.1),0px 17px 26px 2px rgba(0, 0, 0, 0.04),0px 6px 32px 5px rgba(0, 0, 0, 0.02)","0px 9px 11px -5px rgba(0, 0, 0, 0.1),0px 18px 28px 2px rgba(0, 0, 0, 0.04),0px 7px 34px 6px rgba(0, 0, 0, 0.02)","0px 9px 12px -6px rgba(0, 0, 0, 0.1),0px 19px 29px 2px rgba(0, 0, 0, 0.04),0px 7px 36px 6px rgba(0, 0, 0, 0.02)","0px 10px 13px -6px rgba(0, 0, 0, 0.1),0px 20px 31px 3px rgba(0, 0, 0, 0.04),0px 8px 38px 7px rgba(0, 0, 0, 0.02)","0px 10px 13px -6px rgba(0, 0, 0, 0.1),0px 21px 33px 3px rgba(0, 0, 0, 0.04),0px 8px 40px 7px rgba(0, 0, 0, 0.02)","0px 10px 14px -6px rgba(0, 0, 0, 0.1),0px 22px 35px 3px rgba(0, 0, 0, 0.04),0px 8px 42px 7px rgba(0, 0, 0, 0.02)","0px 11px 14px -7px rgba(0, 0, 0, 0.1),0px 23px 36px 3px rgba(0, 0, 0, 0.04),0px 9px 44px 8px rgba(0, 0, 0, 0.02)","0px 11px 15px -7px rgba(0, 0, 0, 0.1),0px 24px 38px 3px rgba(0, 0, 0, 0.04),0px 9px 46px 8px rgba(0, 0, 0, 0.02)",]},u=(0,i.createMuiTheme)(s)},66289(e,t,n){"use strict";function r(e){if(void 0===e)throw ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function i(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}function a(){if("undefined"==typeof Reflect||!Reflect.construct||Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],function(){})),!0}catch(e){return!1}}function o(e,t,n){return(o=a()?Reflect.construct:function(e,t,n){var r=[null];r.push.apply(r,t);var i=new(Function.bind.apply(e,r));return n&&f(i,n.prototype),i}).apply(null,arguments)}function s(e){return(s=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function u(e,t){if("function"!=typeof t&&null!==t)throw TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&f(e,t)}function c(e){return -1!==Function.toString.call(e).indexOf("[native code]")}function l(e,t){return t&&("object"===p(t)||"function"==typeof t)?t:r(e)}function f(e,t){return(f=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}n.d(t,{V0:()=>B,_7:()=>v});var d,h,p=function(e){return e&&"undefined"!=typeof Symbol&&e.constructor===Symbol?"symbol":typeof e};function b(e){var t="function"==typeof Map?new Map:void 0;return(b=function(e){if(null===e||!c(e))return e;if("function"!=typeof e)throw TypeError("Super expression must either be null or a function");if(void 0!==t){if(t.has(e))return t.get(e);t.set(e,n)}function n(){return o(e,arguments,s(this).constructor)}return n.prototype=Object.create(e.prototype,{constructor:{value:n,enumerable:!1,writable:!0,configurable:!0}}),f(n,e)})(e)}function m(){if("undefined"==typeof Reflect||!Reflect.construct||Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){})),!0}catch(e){return!1}}function g(e){var t=m();return function(){var n,r=s(e);if(t){var i=s(this).constructor;n=Reflect.construct(r,arguments,i)}else n=r.apply(this,arguments);return l(this,n)}}var v=function(e){u(n,e);var t=g(n);function n(e){var r;return i(this,n),(r=t.call(this,"AuthenticationError(".concat(e.statusText,")"))).errors=[{status:e.status,detail:e},],r}return n}(b(Error)),y=function(e){u(n,e);var t=g(n);function n(e){var r,a=e.errors;return i(this,n),(r=t.call(this,"BadRequestError")).errors=a,r}return n}(b(Error)),w=function(e){u(n,e);var t=g(n);function n(e){var r;return i(this,n),(r=t.call(this,"UnprocessableEntityError")).errors=e,r}return n}(b(Error)),_=function(e){u(n,e);var t=g(n);function n(e){var r;return i(this,n),(r=t.call(this,"ServerError")).errors=e,r}return n}(b(Error)),E=function(e){u(n,e);var t=g(n);function n(e){var r,a=e.errors;return i(this,n),(r=t.call(this,"ConflictError")).errors=a,r}return n}(b(Error)),S=function(e){u(n,e);var t=g(n);function n(e){var r;return i(this,n),(r=t.call(this,"UnknownResponseError(".concat(e.statusText,")"))).errors=[{status:e.status,detail:e.statusText},],r}return n}(b(Error));function k(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:2e4;return Promise.race([fetch(e,t),new Promise(function(e,t){return setTimeout(function(){return t(Error("timeout"))},n)}),])}function x(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]=200&&e.status<300))return[3,2];return[2,e.json()];case 2:if(400!==e.status)return[3,3];return[2,e.json().then(function(e){throw new y(e)})];case 3:if(401!==e.status)return[3,4];throw new v(e);case 4:if(422!==e.status)return[3,6];return[4,$(e)];case 5:throw n=i.sent(),new w(n);case 6:if(409!==e.status)return[3,7];return[2,e.json().then(function(e){throw new E(e)})];case 7:if(!(e.status>=500))return[3,9];return[4,$(e)];case 8:throw r=i.sent(),new _(r);case 9:throw new S(e);case 10:return[2]}})})).apply(this,arguments)}function $(e){return z.apply(this,arguments)}function z(){return(z=j(function(e){return Y(this,function(t){return[2,e.json().then(function(t){return t.errors?t.errors.map(function(t){return{status:e.status,detail:t.detail}}):G(e)}).catch(function(){return G(e)})]})})).apply(this,arguments)}function G(e){return[{status:e.status,detail:e.statusText},]}},50109(e,t,n){"use strict";n.d(t,{LK:()=>o,U2:()=>i,eT:()=>s,t8:()=>a});var r=n(12795);function i(e){return r.ZP.getItem("chainlink.".concat(e))}function a(e,t){r.ZP.setItem("chainlink.".concat(e),t)}function o(e){var t=i(e),n={};if(t)try{return JSON.parse(t)}catch(r){}return n}function s(e,t){a(e,JSON.stringify(t))}},9541(e,t,n){"use strict";n.d(t,{Ks:()=>u,Tp:()=>a,iR:()=>o,pm:()=>s});var r=n(50109),i="persistURL";function a(){return r.U2(i)||""}function o(e){r.t8(i,e)}function s(){return r.LK("authentication")}function u(e){r.eT("authentication",e)}},67121(e,t,n){"use strict";function r(e){var t,n=e.Symbol;return"function"==typeof n?n.observable?t=n.observable:(t=n("observable"),n.observable=t):t="@@observable",t}n.r(t),n.d(t,{default:()=>o}),e=n.hmd(e),i="undefined"!=typeof self?self:"undefined"!=typeof window?window:void 0!==n.g?n.g:e;var i,a=r(i);let o=a},2177(e,t,n){"use strict";n.d(t,{Z:()=>o});var r=!0,i="Invariant failed";function a(e,t){if(!e){if(r)throw Error(i);throw Error(i+": "+(t||""))}}let o=a},11742(e){e.exports=function(){var e=document.getSelection();if(!e.rangeCount)return function(){};for(var t=document.activeElement,n=[],r=0;ru,ZT:()=>i,_T:()=>o,ev:()=>c,mG:()=>s,pi:()=>a});var r=function(e,t){return(r=Object.setPrototypeOf||({__proto__:[]})instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])})(e,t)};function i(e,t){if("function"!=typeof t&&null!==t)throw TypeError("Class extends value "+String(t)+" is not a constructor or null");function n(){this.constructor=e}r(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}var a=function(){return(a=Object.assign||function(e){for(var t,n=1,r=arguments.length;nt.indexOf(r)&&(n[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var i=0,r=Object.getOwnPropertySymbols(e);it.indexOf(r[i])&&Object.prototype.propertyIsEnumerable.call(e,r[i])&&(n[r[i]]=e[r[i]]);return n}function s(e,t,n,r){function i(e){return e instanceof n?e:new n(function(t){t(e)})}return new(n||(n=Promise))(function(n,a){function o(e){try{u(r.next(e))}catch(t){a(t)}}function s(e){try{u(r.throw(e))}catch(t){a(t)}}function u(e){e.done?n(e.value):i(e.value).then(o,s)}u((r=r.apply(e,t||[])).next())})}function u(e,t){var n,r,i,a,o={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return a={next:s(0),throw:s(1),return:s(2)},"function"==typeof Symbol&&(a[Symbol.iterator]=function(){return this}),a;function s(e){return function(t){return u([e,t])}}function u(a){if(n)throw TypeError("Generator is already executing.");for(;o;)try{if(n=1,r&&(i=2&a[0]?r.return:a[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,a[1])).done)return i;switch(r=0,i&&(a=[2&a[0],i.value]),a[0]){case 0:case 1:i=a;break;case 4:return o.label++,{value:a[1],done:!1};case 5:o.label++,r=a[1],a=[0];continue;case 7:a=o.ops.pop(),o.trys.pop();continue;default:if(!(i=(i=o.trys).length>0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]r})},94927(e,t,n){function r(e,t){if(i("noDeprecation"))return e;var n=!1;function r(){if(!n){if(i("throwDeprecation"))throw Error(t);i("traceDeprecation")?console.trace(t):console.warn(t),n=!0}return e.apply(this,arguments)}return r}function i(e){try{if(!n.g.localStorage)return!1}catch(t){return!1}var r=n.g.localStorage[e];return null!=r&&"true"===String(r).toLowerCase()}e.exports=r},42473(e){"use strict";var t=function(){};e.exports=t},84763(e){e.exports=Worker},47529(e){e.exports=n;var t=Object.prototype.hasOwnProperty;function n(){for(var e={},n=0;ne.length)&&(t=e.length);for(var n=0,r=Array(t);n=0)&&Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}e.exports=i,e.exports.__esModule=!0,e.exports.default=e.exports},7071(e){function t(e,t){if(null==e)return{};var n,r,i={},a=Object.keys(e);for(r=0;r=0||(i[n]=e[n]);return i}e.exports=t,e.exports.__esModule=!0,e.exports.default=e.exports},94993(e,t,n){var r=n(18698).default,i=n(66115);function a(e,t){if(t&&("object"===r(t)||"function"==typeof t))return t;if(void 0!==t)throw TypeError("Derived constructors may only return object or undefined");return i(e)}e.exports=a,e.exports.__esModule=!0,e.exports.default=e.exports},6015(e){function t(n,r){return e.exports=t=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(e,t){return e.__proto__=t,e},e.exports.__esModule=!0,e.exports.default=e.exports,t(n,r)}e.exports=t,e.exports.__esModule=!0,e.exports.default=e.exports},861(e,t,n){var r=n(63405),i=n(79498),a=n(86116),o=n(42281);function s(e){return r(e)||i(e)||a(e)||o()}e.exports=s,e.exports.__esModule=!0,e.exports.default=e.exports},18698(e){function t(n){return e.exports=t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},e.exports.__esModule=!0,e.exports.default=e.exports,t(n)}e.exports=t,e.exports.__esModule=!0,e.exports.default=e.exports},86116(e,t,n){var r=n(73897);function i(e,t){if(e){if("string"==typeof e)return r(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);if("Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n)return Array.from(e);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return r(e,t)}}e.exports=i,e.exports.__esModule=!0,e.exports.default=e.exports},1644(e,t,n){"use strict";var r,i;function a(e){return!!e&&e<7}n.d(t,{I:()=>r,O:()=>a}),(i=r||(r={}))[i.loading=1]="loading",i[i.setVariables=2]="setVariables",i[i.fetchMore=3]="fetchMore",i[i.refetch=4]="refetch",i[i.poll=6]="poll",i[i.ready=7]="ready",i[i.error=8]="error"},30990(e,t,n){"use strict";n.d(t,{MS:()=>s,YG:()=>a,cA:()=>c,ls:()=>o});var r=n(70655);n(83952);var i=n(13154),a=Symbol();function o(e){return!!e.extensions&&Array.isArray(e.extensions[a])}function s(e){return e.hasOwnProperty("graphQLErrors")}var u=function(e){var t=(0,r.ev)((0,r.ev)((0,r.ev)([],e.graphQLErrors,!0),e.clientErrors,!0),e.protocolErrors,!0);return e.networkError&&t.push(e.networkError),t.map(function(e){return(0,i.s)(e)&&e.message||"Error message not found."}).join("\n")},c=function(e){function t(n){var r=n.graphQLErrors,i=n.protocolErrors,a=n.clientErrors,o=n.networkError,s=n.errorMessage,c=n.extraInfo,l=e.call(this,s)||this;return l.name="ApolloError",l.graphQLErrors=r||[],l.protocolErrors=i||[],l.clientErrors=a||[],l.networkError=o||null,l.message=s||u(l),l.extraInfo=c,l.__proto__=t.prototype,l}return(0,r.ZT)(t,e),t}(Error)},85317(e,t,n){"use strict";n.d(t,{K:()=>a});var r=n(67294),i=n(30320).aS?Symbol.for("__APOLLO_CONTEXT__"):"__APOLLO_CONTEXT__";function a(){var e=r.createContext[i];return e||(Object.defineProperty(r.createContext,i,{value:e=r.createContext({}),enumerable:!1,writable:!1,configurable:!0}),e.displayName="ApolloContext"),e}},21436(e,t,n){"use strict";n.d(t,{O:()=>i,k:()=>r});var r=Array.isArray;function i(e){return Array.isArray(e)&&e.length>0}},30320(e,t,n){"use strict";n.d(t,{DN:()=>s,JC:()=>l,aS:()=>o,mr:()=>i,sy:()=>a});var r=n(83952),i="function"==typeof WeakMap&&"ReactNative"!==(0,r.wY)(function(){return navigator.product}),a="function"==typeof WeakSet,o="function"==typeof Symbol&&"function"==typeof Symbol.for,s=o&&Symbol.asyncIterator,u="function"==typeof(0,r.wY)(function(){return window.document.createElement}),c=(0,r.wY)(function(){return navigator.userAgent.indexOf("jsdom")>=0})||!1,l=u&&!c},53712(e,t,n){"use strict";function r(){for(var e=[],t=0;tr})},10542(e,t,n){"use strict";n.d(t,{J:()=>o}),n(83952);var r=n(13154);function i(e){var t=new Set([e]);return t.forEach(function(e){(0,r.s)(e)&&a(e)===e&&Object.getOwnPropertyNames(e).forEach(function(n){(0,r.s)(e[n])&&t.add(e[n])})}),e}function a(e){if(__DEV__&&!Object.isFrozen(e))try{Object.freeze(e)}catch(t){if(t instanceof TypeError)return null;throw t}return e}function o(e){return __DEV__&&i(e),e}},14012(e,t,n){"use strict";n.d(t,{J:()=>a});var r=n(70655),i=n(53712);function a(e,t){return(0,i.o)(e,t,t.variables&&{variables:(0,r.pi)((0,r.pi)({},e&&e.variables),t.variables)})}},13154(e,t,n){"use strict";function r(e){return null!==e&&"object"==typeof e}n.d(t,{s:()=>r})},83952(e,t,n){"use strict";n.d(t,{ej:()=>u,kG:()=>c,wY:()=>h});var r,i=n(70655),a="Invariant Violation",o=Object.setPrototypeOf,s=void 0===o?function(e,t){return e.__proto__=t,e}:o,u=function(e){function t(n){void 0===n&&(n=a);var r=e.call(this,"number"==typeof n?a+": "+n+" (see https://github.com/apollographql/invariant-packages)":n)||this;return r.framesToPop=1,r.name=a,s(r,t.prototype),r}return(0,i.ZT)(t,e),t}(Error);function c(e,t){if(!e)throw new u(t)}var l=["debug","log","warn","error","silent"],f=l.indexOf("log");function d(e){return function(){if(l.indexOf(e)>=f)return(console[e]||console.log).apply(console,arguments)}}function h(e){try{return e()}catch(t){}}(r=c||(c={})).debug=d("debug"),r.log=d("log"),r.warn=d("warn"),r.error=d("error");let p=h(function(){return globalThis})||h(function(){return window})||h(function(){return self})||h(function(){return global})||h(function(){return h.constructor("return this")()});var b="__",m=[b,b].join("DEV");function g(){try{return Boolean(__DEV__)}catch(e){return Object.defineProperty(p,m,{value:"production"!==h(function(){return"production"}),enumerable:!1,configurable:!0,writable:!0}),p[m]}}let v=g();function y(e){try{return e()}catch(t){}}var w=y(function(){return globalThis})||y(function(){return window})||y(function(){return self})||y(function(){return global})||y(function(){return y.constructor("return this")()}),_=!1;function E(){!w||y(function(){return"production"})||y(function(){return process})||(Object.defineProperty(w,"process",{value:{env:{NODE_ENV:"production"}},configurable:!0,enumerable:!1,writable:!0}),_=!0)}function S(){_&&(delete w.process,_=!1)}E();var k=n(10143);function x(){return k.H,S()}function T(){__DEV__?c("boolean"==typeof v,v):c("boolean"==typeof v,39)}x(),T()},4942(e,t,n){"use strict";function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}n.d(t,{Z:()=>r})},87462(e,t,n){"use strict";function r(){return(r=Object.assign?Object.assign.bind():function(e){for(var t=1;tr})},51721(e,t,n){"use strict";function r(e,t){return(r=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(e,t){return e.__proto__=t,e})(e,t)}function i(e,t){e.prototype=Object.create(t.prototype),e.prototype.constructor=e,r(e,t)}n.d(t,{Z:()=>i})},63366(e,t,n){"use strict";function r(e,t){if(null==e)return{};var n,r,i={},a=Object.keys(e);for(r=0;r=0||(i[n]=e[n]);return i}n.d(t,{Z:()=>r})},25821(e,t,n){"use strict";n.d(t,{Z:()=>s});var r=n(45695);function i(e){return(i="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}var a=10,o=2;function s(e){return u(e,[])}function u(e,t){switch(i(e)){case"string":return JSON.stringify(e);case"function":return e.name?"[function ".concat(e.name,"]"):"[function]";case"object":if(null===e)return"null";return c(e,t);default:return String(e)}}function c(e,t){if(-1!==t.indexOf(e))return"[Circular]";var n=[].concat(t,[e]),r=d(e);if(void 0!==r){var i=r.call(e);if(i!==e)return"string"==typeof i?i:u(i,n)}else if(Array.isArray(e))return f(e,n);return l(e,n)}function l(e,t){var n=Object.keys(e);return 0===n.length?"{}":t.length>o?"["+h(e)+"]":"{ "+n.map(function(n){var r=u(e[n],t);return n+": "+r}).join(", ")+" }"}function f(e,t){if(0===e.length)return"[]";if(t.length>o)return"[Array]";for(var n=Math.min(a,e.length),r=e.length-n,i=[],s=0;s1&&i.push("... ".concat(r," more items")),"["+i.join(", ")+"]"}function d(e){var t=e[String(r.Z)];return"function"==typeof t?t:"function"==typeof e.inspect?e.inspect:void 0}function h(e){var t=Object.prototype.toString.call(e).replace(/^\[object /,"").replace(/]$/,"");if("Object"===t&&"function"==typeof e.constructor){var n=e.constructor.name;if("string"==typeof n&&""!==n)return n}return t}},45695(e,t,n){"use strict";n.d(t,{Z:()=>i});var r="function"==typeof Symbol&&"function"==typeof Symbol.for?Symbol.for("nodejs.util.inspect.custom"):void 0;let i=r},25217(e,t,n){"use strict";function r(e,t){if(!Boolean(e))throw Error(null!=t?t:"Unexpected invariant triggered.")}n.d(t,{Ye:()=>o,WU:()=>s,UG:()=>u});var i=n(45695);function a(e){var t=e.prototype.toJSON;"function"==typeof t||r(0),e.prototype.inspect=t,i.Z&&(e.prototype[i.Z]=t)}var o=function(){function e(e,t,n){this.start=e.start,this.end=t.end,this.startToken=e,this.endToken=t,this.source=n}return e.prototype.toJSON=function(){return{start:this.start,end:this.end}},e}();a(o);var s=function(){function e(e,t,n,r,i,a,o){this.kind=e,this.start=t,this.end=n,this.line=r,this.column=i,this.value=o,this.prev=a,this.next=null}return e.prototype.toJSON=function(){return{kind:this.kind,value:this.value,line:this.line,column:this.column}},e}();function u(e){return null!=e&&"string"==typeof e.kind}a(s)},87392(e,t,n){"use strict";function r(e){var t=e.split(/\r\n|[\n\r]/g),n=a(e);if(0!==n)for(var r=1;ro&&i(t[s-1]);)--s;return t.slice(o,s).join("\n")}function i(e){for(var t=0;t1&&void 0!==arguments[1]?arguments[1]:"",n=arguments.length>2&&void 0!==arguments[2]&&arguments[2],r=-1===e.indexOf("\n"),i=" "===e[0]||" "===e[0],a='"'===e[e.length-1],o="\\"===e[e.length-1],s=!r||a||o||n,u="";return s&&!(r&&i)&&(u+="\n"+t),u+=t?e.replace(/\n/g,"\n"+t):e,s&&(u+="\n"),'"""'+u.replace(/"""/g,'\\"""')+'"""'}n.d(t,{LZ:()=>o,W7:()=>r})},97359(e,t,n){"use strict";n.d(t,{h:()=>r});var r=Object.freeze({NAME:"Name",DOCUMENT:"Document",OPERATION_DEFINITION:"OperationDefinition",VARIABLE_DEFINITION:"VariableDefinition",SELECTION_SET:"SelectionSet",FIELD:"Field",ARGUMENT:"Argument",FRAGMENT_SPREAD:"FragmentSpread",INLINE_FRAGMENT:"InlineFragment",FRAGMENT_DEFINITION:"FragmentDefinition",VARIABLE:"Variable",INT:"IntValue",FLOAT:"FloatValue",STRING:"StringValue",BOOLEAN:"BooleanValue",NULL:"NullValue",ENUM:"EnumValue",LIST:"ListValue",OBJECT:"ObjectValue",OBJECT_FIELD:"ObjectField",DIRECTIVE:"Directive",NAMED_TYPE:"NamedType",LIST_TYPE:"ListType",NON_NULL_TYPE:"NonNullType",SCHEMA_DEFINITION:"SchemaDefinition",OPERATION_TYPE_DEFINITION:"OperationTypeDefinition",SCALAR_TYPE_DEFINITION:"ScalarTypeDefinition",OBJECT_TYPE_DEFINITION:"ObjectTypeDefinition",FIELD_DEFINITION:"FieldDefinition",INPUT_VALUE_DEFINITION:"InputValueDefinition",INTERFACE_TYPE_DEFINITION:"InterfaceTypeDefinition",UNION_TYPE_DEFINITION:"UnionTypeDefinition",ENUM_TYPE_DEFINITION:"EnumTypeDefinition",ENUM_VALUE_DEFINITION:"EnumValueDefinition",INPUT_OBJECT_TYPE_DEFINITION:"InputObjectTypeDefinition",DIRECTIVE_DEFINITION:"DirectiveDefinition",SCHEMA_EXTENSION:"SchemaExtension",SCALAR_TYPE_EXTENSION:"ScalarTypeExtension",OBJECT_TYPE_EXTENSION:"ObjectTypeExtension",INTERFACE_TYPE_EXTENSION:"InterfaceTypeExtension",UNION_TYPE_EXTENSION:"UnionTypeExtension",ENUM_TYPE_EXTENSION:"EnumTypeExtension",INPUT_OBJECT_TYPE_EXTENSION:"InputObjectTypeExtension"})},10143(e,t,n){"use strict";n.d(t,{H:()=>c,T:()=>l});var r=n(99763),i=n(25821);function a(e,t){if(!Boolean(e))throw Error(t)}let o=function(e,t){return e instanceof t};function s(e,t){for(var n=0;n1&&void 0!==arguments[1]?arguments[1]:"GraphQL request",n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{line:1,column:1};"string"==typeof e||a(0,"Body must be a string. Received: ".concat((0,i.Z)(e),".")),this.body=e,this.name=t,this.locationOffset=n,this.locationOffset.line>0||a(0,"line in locationOffset is 1-indexed and must be positive."),this.locationOffset.column>0||a(0,"column in locationOffset is 1-indexed and must be positive.")}return u(e,[{key:r.YF,get:function(){return"Source"}}]),e}();function l(e){return o(e,c)}},99763(e,t,n){"use strict";n.d(t,{YF:()=>r});var r="function"==typeof Symbol&&null!=Symbol.toStringTag?Symbol.toStringTag:"@@toStringTag"},37452(e){"use strict";e.exports=JSON.parse('{"AElig":"\xc6","AMP":"&","Aacute":"\xc1","Acirc":"\xc2","Agrave":"\xc0","Aring":"\xc5","Atilde":"\xc3","Auml":"\xc4","COPY":"\xa9","Ccedil":"\xc7","ETH":"\xd0","Eacute":"\xc9","Ecirc":"\xca","Egrave":"\xc8","Euml":"\xcb","GT":">","Iacute":"\xcd","Icirc":"\xce","Igrave":"\xcc","Iuml":"\xcf","LT":"<","Ntilde":"\xd1","Oacute":"\xd3","Ocirc":"\xd4","Ograve":"\xd2","Oslash":"\xd8","Otilde":"\xd5","Ouml":"\xd6","QUOT":"\\"","REG":"\xae","THORN":"\xde","Uacute":"\xda","Ucirc":"\xdb","Ugrave":"\xd9","Uuml":"\xdc","Yacute":"\xdd","aacute":"\xe1","acirc":"\xe2","acute":"\xb4","aelig":"\xe6","agrave":"\xe0","amp":"&","aring":"\xe5","atilde":"\xe3","auml":"\xe4","brvbar":"\xa6","ccedil":"\xe7","cedil":"\xb8","cent":"\xa2","copy":"\xa9","curren":"\xa4","deg":"\xb0","divide":"\xf7","eacute":"\xe9","ecirc":"\xea","egrave":"\xe8","eth":"\xf0","euml":"\xeb","frac12":"\xbd","frac14":"\xbc","frac34":"\xbe","gt":">","iacute":"\xed","icirc":"\xee","iexcl":"\xa1","igrave":"\xec","iquest":"\xbf","iuml":"\xef","laquo":"\xab","lt":"<","macr":"\xaf","micro":"\xb5","middot":"\xb7","nbsp":"\xa0","not":"\xac","ntilde":"\xf1","oacute":"\xf3","ocirc":"\xf4","ograve":"\xf2","ordf":"\xaa","ordm":"\xba","oslash":"\xf8","otilde":"\xf5","ouml":"\xf6","para":"\xb6","plusmn":"\xb1","pound":"\xa3","quot":"\\"","raquo":"\xbb","reg":"\xae","sect":"\xa7","shy":"\xad","sup1":"\xb9","sup2":"\xb2","sup3":"\xb3","szlig":"\xdf","thorn":"\xfe","times":"\xd7","uacute":"\xfa","ucirc":"\xfb","ugrave":"\xf9","uml":"\xa8","uuml":"\xfc","yacute":"\xfd","yen":"\xa5","yuml":"\xff"}')},93580(e){"use strict";e.exports=JSON.parse('{"0":"�","128":"€","130":"‚","131":"ƒ","132":"„","133":"…","134":"†","135":"‡","136":"ˆ","137":"‰","138":"Š","139":"‹","140":"Œ","142":"Ž","145":"‘","146":"’","147":"“","148":"”","149":"•","150":"–","151":"—","152":"˜","153":"™","154":"š","155":"›","156":"œ","158":"ž","159":"Ÿ"}')},67946(e){"use strict";e.exports=JSON.parse('{"locale":"en","long":{"year":{"previous":"last year","current":"this year","next":"next year","past":{"one":"{0} year ago","other":"{0} years ago"},"future":{"one":"in {0} year","other":"in {0} years"}},"quarter":{"previous":"last quarter","current":"this quarter","next":"next quarter","past":{"one":"{0} quarter ago","other":"{0} quarters ago"},"future":{"one":"in {0} quarter","other":"in {0} quarters"}},"month":{"previous":"last month","current":"this month","next":"next month","past":{"one":"{0} month ago","other":"{0} months ago"},"future":{"one":"in {0} month","other":"in {0} months"}},"week":{"previous":"last week","current":"this week","next":"next week","past":{"one":"{0} week ago","other":"{0} weeks ago"},"future":{"one":"in {0} week","other":"in {0} weeks"}},"day":{"previous":"yesterday","current":"today","next":"tomorrow","past":{"one":"{0} day ago","other":"{0} days ago"},"future":{"one":"in {0} day","other":"in {0} days"}},"hour":{"current":"this hour","past":{"one":"{0} hour ago","other":"{0} hours ago"},"future":{"one":"in {0} hour","other":"in {0} hours"}},"minute":{"current":"this minute","past":{"one":"{0} minute ago","other":"{0} minutes ago"},"future":{"one":"in {0} minute","other":"in {0} minutes"}},"second":{"current":"now","past":{"one":"{0} second ago","other":"{0} seconds ago"},"future":{"one":"in {0} second","other":"in {0} seconds"}}},"short":{"year":{"previous":"last yr.","current":"this yr.","next":"next yr.","past":"{0} yr. ago","future":"in {0} yr."},"quarter":{"previous":"last qtr.","current":"this qtr.","next":"next qtr.","past":{"one":"{0} qtr. ago","other":"{0} qtrs. ago"},"future":{"one":"in {0} qtr.","other":"in {0} qtrs."}},"month":{"previous":"last mo.","current":"this mo.","next":"next mo.","past":"{0} mo. ago","future":"in {0} mo."},"week":{"previous":"last wk.","current":"this wk.","next":"next wk.","past":"{0} wk. ago","future":"in {0} wk."},"day":{"previous":"yesterday","current":"today","next":"tomorrow","past":{"one":"{0} day ago","other":"{0} days ago"},"future":{"one":"in {0} day","other":"in {0} days"}},"hour":{"current":"this hour","past":"{0} hr. ago","future":"in {0} hr."},"minute":{"current":"this minute","past":"{0} min. ago","future":"in {0} min."},"second":{"current":"now","past":"{0} sec. ago","future":"in {0} sec."}},"narrow":{"year":{"previous":"last yr.","current":"this yr.","next":"next yr.","past":"{0} yr. ago","future":"in {0} yr."},"quarter":{"previous":"last qtr.","current":"this qtr.","next":"next qtr.","past":{"one":"{0} qtr. ago","other":"{0} qtrs. ago"},"future":{"one":"in {0} qtr.","other":"in {0} qtrs."}},"month":{"previous":"last mo.","current":"this mo.","next":"next mo.","past":"{0} mo. ago","future":"in {0} mo."},"week":{"previous":"last wk.","current":"this wk.","next":"next wk.","past":"{0} wk. ago","future":"in {0} wk."},"day":{"previous":"yesterday","current":"today","next":"tomorrow","past":{"one":"{0} day ago","other":"{0} days ago"},"future":{"one":"in {0} day","other":"in {0} days"}},"hour":{"current":"this hour","past":"{0} hr. ago","future":"in {0} hr."},"minute":{"current":"this minute","past":"{0} min. ago","future":"in {0} min."},"second":{"current":"now","past":"{0} sec. ago","future":"in {0} sec."}},"now":{"now":{"current":"now","future":"in a moment","past":"just now"}},"mini":{"year":"{0}yr","month":"{0}mo","week":"{0}wk","day":"{0}d","hour":"{0}h","minute":"{0}m","second":"{0}s","now":"now"},"short-time":{"year":"{0} yr.","month":"{0} mo.","week":"{0} wk.","day":{"one":"{0} day","other":"{0} days"},"hour":"{0} hr.","minute":"{0} min.","second":"{0} sec."},"long-time":{"year":{"one":"{0} year","other":"{0} years"},"month":{"one":"{0} month","other":"{0} months"},"week":{"one":"{0} week","other":"{0} weeks"},"day":{"one":"{0} day","other":"{0} days"},"hour":{"one":"{0} hour","other":"{0} hours"},"minute":{"one":"{0} minute","other":"{0} minutes"},"second":{"one":"{0} second","other":"{0} seconds"}}}')}},__webpack_module_cache__={};function __webpack_require__(e){var t=__webpack_module_cache__[e];if(void 0!==t)return t.exports;var n=__webpack_module_cache__[e]={id:e,loaded:!1,exports:{}};return __webpack_modules__[e].call(n.exports,n,n.exports,__webpack_require__),n.loaded=!0,n.exports}__webpack_require__.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return __webpack_require__.d(t,{a:t}),t},(()=>{var e,t=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__;__webpack_require__.t=function(n,r){if(1&r&&(n=this(n)),8&r||"object"==typeof n&&n&&(4&r&&n.__esModule||16&r&&"function"==typeof n.then))return n;var i=Object.create(null);__webpack_require__.r(i);var a={};e=e||[null,t({}),t([]),t(t)];for(var o=2&r&&n;"object"==typeof o&&!~e.indexOf(o);o=t(o))Object.getOwnPropertyNames(o).forEach(e=>a[e]=()=>n[e]);return a.default=()=>n,__webpack_require__.d(i,a),i}})(),__webpack_require__.d=(e,t)=>{for(var n in t)__webpack_require__.o(t,n)&&!__webpack_require__.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},__webpack_require__.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||Function("return this")()}catch(e){if("object"==typeof window)return window}}(),__webpack_require__.hmd=e=>((e=Object.create(e)).children||(e.children=[]),Object.defineProperty(e,"exports",{enumerable:!0,set(){throw Error("ES Modules may not assign module.exports or exports.*, Use ESM export syntax, instead: "+e.id)}}),e),__webpack_require__.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),__webpack_require__.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},__webpack_require__.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),__webpack_require__.p="/assets/",__webpack_require__.nc=void 0;var __webpack_exports__={};(()=>{"use strict";var e,t,n,r,i=__webpack_require__(32316),a=__webpack_require__(8126),o=__webpack_require__(5690),s=__webpack_require__(30381),u=__webpack_require__.n(s),c=__webpack_require__(67294),l=__webpack_require__(73935),f=__webpack_require__.n(l),d=__webpack_require__(57209),h=__webpack_require__(37703),p=__webpack_require__(97779),b=__webpack_require__(28500);function m(e){return function(t){var n=t.dispatch,r=t.getState;return function(t){return function(i){return"function"==typeof i?i(n,r,e):t(i)}}}}var g=m();g.withExtraArgument=m;let v=g;var y=__webpack_require__(76489);function w(e){return function(t){return function(n){return function(r){n(r);var i=e||document&&document.cookie||"",a=t.getState();if("MATCH_ROUTE"===r.type&&"/signin"!==a.notifications.currentUrl){var o=(0,y.Q)(i);if(o.explorer)try{var s=JSON.parse(o.explorer);if("error"===s.status){var u=_(s.url);n({type:"NOTIFY_ERROR_MSG",msg:u})}}catch(c){n({type:"NOTIFY_ERROR_MSG",msg:"Invalid explorer status"})}}}}}}function _(e){var t="Can't connect to explorer: ".concat(e);return e.match(/^wss?:.+/)?t:"".concat(t,". You must use a websocket.")}var E=__webpack_require__(16353);function S(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n=e.length?{done:!0}:{done:!1,value:e[r++]}}}throw TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function ei(e,t){if(e){if("string"==typeof e)return ea(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);if("Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n)return Array.from(e);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return ea(e,t)}}function ea(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n1,i=!1,a=arguments[1],o=a;return new n(function(n){return t.subscribe({next:function(t){var a=!i;if(i=!0,!a||r)try{o=e(o,t)}catch(s){return n.error(s)}else o=t},error:function(e){n.error(e)},complete:function(){if(!i&&!r)return n.error(TypeError("Cannot reduce an empty sequence"));n.next(o),n.complete()}})})},t.concat=function(){for(var e=this,t=arguments.length,n=Array(t),r=0;r=0&&i.splice(e,1),o()}});i.push(s)},error:function(e){r.error(e)},complete:function(){o()}});function o(){a.closed&&0===i.length&&r.complete()}return function(){i.forEach(function(e){return e.unsubscribe()}),a.unsubscribe()}})},t[ed]=function(){return this},e.from=function(t){var n="function"==typeof this?this:e;if(null==t)throw TypeError(t+" is not an object");var r=ep(t,ed);if(r){var i=r.call(t);if(Object(i)!==i)throw TypeError(i+" is not an object");return em(i)&&i.constructor===n?i:new n(function(e){return i.subscribe(e)})}if(ec("iterator")&&(r=ep(t,ef)))return new n(function(e){ev(function(){if(!e.closed){for(var n,i=er(r.call(t));!(n=i()).done;){var a=n.value;if(e.next(a),e.closed)return}e.complete()}})});if(Array.isArray(t))return new n(function(e){ev(function(){if(!e.closed){for(var n=0;n0))return n.connection.key;var r=n.connection.filter?n.connection.filter:[];r.sort();var i={};return r.forEach(function(e){i[e]=t[e]}),"".concat(n.connection.key,"(").concat(eV(i),")")}var a=e;if(t){var o=eV(t);a+="(".concat(o,")")}return n&&Object.keys(n).forEach(function(e){-1===eW.indexOf(e)&&(n[e]&&Object.keys(n[e]).length?a+="@".concat(e,"(").concat(eV(n[e]),")"):a+="@".concat(e))}),a},{setStringify:function(e){var t=eV;return eV=e,t}}),eV=function(e){return JSON.stringify(e,eq)};function eq(e,t){return(0,eO.s)(t)&&!Array.isArray(t)&&(t=Object.keys(t).sort().reduce(function(e,n){return e[n]=t[n],e},{})),t}function eZ(e,t){if(e.arguments&&e.arguments.length){var n={};return e.arguments.forEach(function(e){var r;return ez(n,e.name,e.value,t)}),n}return null}function eX(e){return e.alias?e.alias.value:e.name.value}function eJ(e,t,n){for(var r,i=0,a=t.selections;it.indexOf(i))throw __DEV__?new Q.ej("illegal argument: ".concat(i)):new Q.ej(27)}return e}function tt(e,t){return t?t(e):eT.of()}function tn(e){return"function"==typeof e?new ta(e):e}function tr(e){return e.request.length<=1}var ti=function(e){function t(t,n){var r=e.call(this,t)||this;return r.link=n,r}return(0,en.ZT)(t,e),t}(Error),ta=function(){function e(e){e&&(this.request=e)}return e.empty=function(){return new e(function(){return eT.of()})},e.from=function(t){return 0===t.length?e.empty():t.map(tn).reduce(function(e,t){return e.concat(t)})},e.split=function(t,n,r){var i=tn(n),a=tn(r||new e(tt));return new e(tr(i)&&tr(a)?function(e){return t(e)?i.request(e)||eT.of():a.request(e)||eT.of()}:function(e,n){return t(e)?i.request(e,n)||eT.of():a.request(e,n)||eT.of()})},e.execute=function(e,t){return e.request(eM(t.context,e7(te(t))))||eT.of()},e.concat=function(t,n){var r=tn(t);if(tr(r))return __DEV__&&Q.kG.warn(new ti("You are calling concat on a terminating link, which will have no effect",r)),r;var i=tn(n);return new e(tr(i)?function(e){return r.request(e,function(e){return i.request(e)||eT.of()})||eT.of()}:function(e,t){return r.request(e,function(e){return i.request(e,t)||eT.of()})||eT.of()})},e.prototype.split=function(t,n,r){return this.concat(e.split(t,n,r||new e(tt)))},e.prototype.concat=function(t){return e.concat(this,t)},e.prototype.request=function(e,t){throw __DEV__?new Q.ej("request is not implemented"):new Q.ej(22)},e.prototype.onError=function(e,t){if(t&&t.error)return t.error(e),!1;throw e},e.prototype.setOnError=function(e){return this.onError=e,this},e}(),to=__webpack_require__(25821),ts=__webpack_require__(25217),tu={Name:[],Document:["definitions"],OperationDefinition:["name","variableDefinitions","directives","selectionSet"],VariableDefinition:["variable","type","defaultValue","directives"],Variable:["name"],SelectionSet:["selections"],Field:["alias","name","arguments","directives","selectionSet"],Argument:["name","value"],FragmentSpread:["name","directives"],InlineFragment:["typeCondition","directives","selectionSet"],FragmentDefinition:["name","variableDefinitions","typeCondition","directives","selectionSet"],IntValue:[],FloatValue:[],StringValue:[],BooleanValue:[],NullValue:[],EnumValue:[],ListValue:["values"],ObjectValue:["fields"],ObjectField:["name","value"],Directive:["name","arguments"],NamedType:["name"],ListType:["type"],NonNullType:["type"],SchemaDefinition:["description","directives","operationTypes"],OperationTypeDefinition:["type"],ScalarTypeDefinition:["description","name","directives"],ObjectTypeDefinition:["description","name","interfaces","directives","fields"],FieldDefinition:["description","name","arguments","type","directives"],InputValueDefinition:["description","name","type","defaultValue","directives"],InterfaceTypeDefinition:["description","name","interfaces","directives","fields"],UnionTypeDefinition:["description","name","directives","types"],EnumTypeDefinition:["description","name","directives","values"],EnumValueDefinition:["description","name","directives"],InputObjectTypeDefinition:["description","name","directives","fields"],DirectiveDefinition:["description","name","arguments","locations"],SchemaExtension:["directives","operationTypes"],ScalarTypeExtension:["name","directives"],ObjectTypeExtension:["name","interfaces","directives","fields"],InterfaceTypeExtension:["name","interfaces","directives","fields"],UnionTypeExtension:["name","directives","types"],EnumTypeExtension:["name","directives","values"],InputObjectTypeExtension:["name","directives","fields"]},tc=Object.freeze({});function tl(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:tu,r=void 0,i=Array.isArray(e),a=[e],o=-1,s=[],u=void 0,c=void 0,l=void 0,f=[],d=[],h=e;do{var p,b=++o===a.length,m=b&&0!==s.length;if(b){if(c=0===d.length?void 0:f[f.length-1],u=l,l=d.pop(),m){if(i)u=u.slice();else{for(var g={},v=0,y=Object.keys(u);v1)for(var r=new tB,i=1;i=0;--a){var o=i[a],s=isNaN(+o)?{}:[];s[o]=t,t=s}n=r.merge(n,t)}),n}var tW=Object.prototype.hasOwnProperty;function tK(e,t){var n,r,i,a,o;return(0,en.mG)(this,void 0,void 0,function(){var s,u,c,l,f,d,h,p,b,m,g,v,y,w,_,E,S,k,x,T,M,O,A;return(0,en.Jh)(this,function(L){switch(L.label){case 0:if(void 0===TextDecoder)throw Error("TextDecoder must be defined in the environment: please import a polyfill.");s=new TextDecoder("utf-8"),u=null===(n=e.headers)||void 0===n?void 0:n.get("content-type"),c="boundary=",l=(null==u?void 0:u.includes(c))?null==u?void 0:u.substring((null==u?void 0:u.indexOf(c))+c.length).replace(/['"]/g,"").replace(/\;(.*)/gm,"").trim():"-",f="\r\n--".concat(l),d="",h=tI(e),p=!0,L.label=1;case 1:if(!p)return[3,3];return[4,h.next()];case 2:for(m=(b=L.sent()).value,g=b.done,v="string"==typeof m?m:s.decode(m),y=d.length-f.length+1,p=!g,d+=v,w=d.indexOf(f,y);w>-1;){if(_=void 0,_=(O=[d.slice(0,w),d.slice(w+f.length),])[0],d=O[1],E=_.indexOf("\r\n\r\n"),(k=(S=tV(_.slice(0,E)))["content-type"])&&-1===k.toLowerCase().indexOf("application/json"))throw Error("Unsupported patch content type: application/json is required.");if(x=_.slice(E))try{T=tq(e,x),Object.keys(T).length>1||"data"in T||"incremental"in T||"errors"in T||"payload"in T?tz(T)?(M={},"payload"in T&&(M=(0,en.pi)({},T.payload)),"errors"in T&&(M=(0,en.pi)((0,en.pi)({},M),{extensions:(0,en.pi)((0,en.pi)({},"extensions"in M?M.extensions:null),((A={})[tN.YG]=T.errors,A))})),null===(r=t.next)||void 0===r||r.call(t,M)):null===(i=t.next)||void 0===i||i.call(t,T):1===Object.keys(T).length&&"hasNext"in T&&!T.hasNext&&(null===(a=t.complete)||void 0===a||a.call(t))}catch(C){tZ(C,t)}w=d.indexOf(f)}return[3,1];case 3:return null===(o=t.complete)||void 0===o||o.call(t),[2]}})})}function tV(e){var t={};return e.split("\n").forEach(function(e){var n=e.indexOf(":");if(n>-1){var r=e.slice(0,n).trim().toLowerCase(),i=e.slice(n+1).trim();t[r]=i}}),t}function tq(e,t){e.status>=300&&tD(e,function(){try{return JSON.parse(t)}catch(e){return t}}(),"Response not successful: Received status code ".concat(e.status));try{return JSON.parse(t)}catch(n){var r=n;throw r.name="ServerParseError",r.response=e,r.statusCode=e.status,r.bodyText=t,r}}function tZ(e,t){var n,r;"AbortError"!==e.name&&(e.result&&e.result.errors&&e.result.data&&(null===(n=t.next)||void 0===n||n.call(t,e.result)),null===(r=t.error)||void 0===r||r.call(t,e))}function tX(e,t,n){tJ(t)(e).then(function(e){var t,r;null===(t=n.next)||void 0===t||t.call(n,e),null===(r=n.complete)||void 0===r||r.call(n)}).catch(function(e){return tZ(e,n)})}function tJ(e){return function(t){return t.text().then(function(e){return tq(t,e)}).then(function(n){return t.status>=300&&tD(t,n,"Response not successful: Received status code ".concat(t.status)),Array.isArray(n)||tW.call(n,"data")||tW.call(n,"errors")||tD(t,n,"Server response was missing for query '".concat(Array.isArray(e)?e.map(function(e){return e.operationName}):e.operationName,"'.")),n})}}var tQ=function(e){if(!e&&"undefined"==typeof fetch)throw __DEV__?new Q.ej("\n\"fetch\" has not been found globally and no fetcher has been configured. To fix this, install a fetch package (like https://www.npmjs.com/package/cross-fetch), instantiate the fetcher, and pass it into your HttpLink constructor. For example:\n\nimport fetch from 'cross-fetch';\nimport { ApolloClient, HttpLink } from '@apollo/client';\nconst client = new ApolloClient({\n link: new HttpLink({ uri: '/graphql', fetch })\n});\n "):new Q.ej(23)},t1=__webpack_require__(87392);function t0(e){return tl(e,{leave:t3})}var t2=80,t3={Name:function(e){return e.value},Variable:function(e){return"$"+e.name},Document:function(e){return t6(e.definitions,"\n\n")+"\n"},OperationDefinition:function(e){var t=e.operation,n=e.name,r=t8("(",t6(e.variableDefinitions,", "),")"),i=t6(e.directives," "),a=e.selectionSet;return n||i||r||"query"!==t?t6([t,t6([n,r]),i,a]," "):a},VariableDefinition:function(e){var t=e.variable,n=e.type,r=e.defaultValue,i=e.directives;return t+": "+n+t8(" = ",r)+t8(" ",t6(i," "))},SelectionSet:function(e){return t5(e.selections)},Field:function(e){var t=e.alias,n=e.name,r=e.arguments,i=e.directives,a=e.selectionSet,o=t8("",t,": ")+n,s=o+t8("(",t6(r,", "),")");return s.length>t2&&(s=o+t8("(\n",t9(t6(r,"\n")),"\n)")),t6([s,t6(i," "),a]," ")},Argument:function(e){var t;return e.name+": "+e.value},FragmentSpread:function(e){var t;return"..."+e.name+t8(" ",t6(e.directives," "))},InlineFragment:function(e){var t=e.typeCondition,n=e.directives,r=e.selectionSet;return t6(["...",t8("on ",t),t6(n," "),r]," ")},FragmentDefinition:function(e){var t=e.name,n=e.typeCondition,r=e.variableDefinitions,i=e.directives,a=e.selectionSet;return"fragment ".concat(t).concat(t8("(",t6(r,", "),")")," ")+"on ".concat(n," ").concat(t8("",t6(i," ")," "))+a},IntValue:function(e){return e.value},FloatValue:function(e){return e.value},StringValue:function(e,t){var n=e.value;return e.block?(0,t1.LZ)(n,"description"===t?"":" "):JSON.stringify(n)},BooleanValue:function(e){return e.value?"true":"false"},NullValue:function(){return"null"},EnumValue:function(e){return e.value},ListValue:function(e){return"["+t6(e.values,", ")+"]"},ObjectValue:function(e){return"{"+t6(e.fields,", ")+"}"},ObjectField:function(e){var t;return e.name+": "+e.value},Directive:function(e){var t;return"@"+e.name+t8("(",t6(e.arguments,", "),")")},NamedType:function(e){return e.name},ListType:function(e){return"["+e.type+"]"},NonNullType:function(e){return e.type+"!"},SchemaDefinition:t4(function(e){var t=e.directives,n=e.operationTypes;return t6(["schema",t6(t," "),t5(n)]," ")}),OperationTypeDefinition:function(e){var t;return e.operation+": "+e.type},ScalarTypeDefinition:t4(function(e){var t;return t6(["scalar",e.name,t6(e.directives," ")]," ")}),ObjectTypeDefinition:t4(function(e){var t=e.name,n=e.interfaces,r=e.directives,i=e.fields;return t6(["type",t,t8("implements ",t6(n," & ")),t6(r," "),t5(i)]," ")}),FieldDefinition:t4(function(e){var t=e.name,n=e.arguments,r=e.type,i=e.directives;return t+(ne(n)?t8("(\n",t9(t6(n,"\n")),"\n)"):t8("(",t6(n,", "),")"))+": "+r+t8(" ",t6(i," "))}),InputValueDefinition:t4(function(e){var t=e.name,n=e.type,r=e.defaultValue,i=e.directives;return t6([t+": "+n,t8("= ",r),t6(i," ")]," ")}),InterfaceTypeDefinition:t4(function(e){var t=e.name,n=e.interfaces,r=e.directives,i=e.fields;return t6(["interface",t,t8("implements ",t6(n," & ")),t6(r," "),t5(i)]," ")}),UnionTypeDefinition:t4(function(e){var t=e.name,n=e.directives,r=e.types;return t6(["union",t,t6(n," "),r&&0!==r.length?"= "+t6(r," | "):""]," ")}),EnumTypeDefinition:t4(function(e){var t=e.name,n=e.directives,r=e.values;return t6(["enum",t,t6(n," "),t5(r)]," ")}),EnumValueDefinition:t4(function(e){var t;return t6([e.name,t6(e.directives," ")]," ")}),InputObjectTypeDefinition:t4(function(e){var t=e.name,n=e.directives,r=e.fields;return t6(["input",t,t6(n," "),t5(r)]," ")}),DirectiveDefinition:t4(function(e){var t=e.name,n=e.arguments,r=e.repeatable,i=e.locations;return"directive @"+t+(ne(n)?t8("(\n",t9(t6(n,"\n")),"\n)"):t8("(",t6(n,", "),")"))+(r?" repeatable":"")+" on "+t6(i," | ")}),SchemaExtension:function(e){var t=e.directives,n=e.operationTypes;return t6(["extend schema",t6(t," "),t5(n)]," ")},ScalarTypeExtension:function(e){var t;return t6(["extend scalar",e.name,t6(e.directives," ")]," ")},ObjectTypeExtension:function(e){var t=e.name,n=e.interfaces,r=e.directives,i=e.fields;return t6(["extend type",t,t8("implements ",t6(n," & ")),t6(r," "),t5(i)]," ")},InterfaceTypeExtension:function(e){var t=e.name,n=e.interfaces,r=e.directives,i=e.fields;return t6(["extend interface",t,t8("implements ",t6(n," & ")),t6(r," "),t5(i)]," ")},UnionTypeExtension:function(e){var t=e.name,n=e.directives,r=e.types;return t6(["extend union",t,t6(n," "),r&&0!==r.length?"= "+t6(r," | "):""]," ")},EnumTypeExtension:function(e){var t=e.name,n=e.directives,r=e.values;return t6(["extend enum",t,t6(n," "),t5(r)]," ")},InputObjectTypeExtension:function(e){var t=e.name,n=e.directives,r=e.fields;return t6(["extend input",t,t6(n," "),t5(r)]," ")}};function t4(e){return function(t){return t6([t.description,e(t)],"\n")}}function t6(e){var t,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";return null!==(t=null==e?void 0:e.filter(function(e){return e}).join(n))&&void 0!==t?t:""}function t5(e){return t8("{\n",t9(t6(e,"\n")),"\n}")}function t8(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"";return null!=t&&""!==t?e+t+n:""}function t9(e){return t8(" ",e.replace(/\n/g,"\n "))}function t7(e){return -1!==e.indexOf("\n")}function ne(e){return null!=e&&e.some(t7)}var nt,nn,nr,ni={http:{includeQuery:!0,includeExtensions:!1,preserveHeaderCase:!1},headers:{accept:"*/*","content-type":"application/json"},options:{method:"POST"}},na=function(e,t){return t(e)};function no(e,t){for(var n=[],r=2;rObject.create(null),{forEach:nv,slice:ny}=Array.prototype,{hasOwnProperty:nw}=Object.prototype;class n_{constructor(e=!0,t=ng){this.weakness=e,this.makeData=t}lookup(...e){return this.lookupArray(e)}lookupArray(e){let t=this;return nv.call(e,e=>t=t.getChildTrie(e)),nw.call(t,"data")?t.data:t.data=this.makeData(ny.call(e))}peek(...e){return this.peekArray(e)}peekArray(e){let t=this;for(let n=0,r=e.length;t&&n=0;--o)t.definitions[o].kind===nL.h.OPERATION_DEFINITION&&++a;var s=nN(e),u=e.some(function(e){return e.remove}),c=function(e){return u&&e&&e.some(s)},l=new Map,f=!1,d={enter:function(e){if(c(e.directives))return f=!0,null}},h=tl(t,{Field:d,InlineFragment:d,VariableDefinition:{enter:function(){return!1}},Variable:{enter:function(e,t,n,r,a){var o=i(a);o&&o.variables.add(e.name.value)}},FragmentSpread:{enter:function(e,t,n,r,a){if(c(e.directives))return f=!0,null;var o=i(a);o&&o.fragmentSpreads.add(e.name.value)}},FragmentDefinition:{enter:function(e,t,n,r){l.set(JSON.stringify(r),e)},leave:function(e,t,n,i){return e===l.get(JSON.stringify(i))?e:a>0&&e.selectionSet.selections.every(function(e){return e.kind===nL.h.FIELD&&"__typename"===e.name.value})?(r(e.name.value).removed=!0,f=!0,null):void 0}},Directive:{leave:function(e){if(s(e))return f=!0,null}}});if(!f)return t;var p=function(e){return e.transitiveVars||(e.transitiveVars=new Set(e.variables),e.removed||e.fragmentSpreads.forEach(function(t){p(r(t)).transitiveVars.forEach(function(t){e.transitiveVars.add(t)})})),e},b=new Set;h.definitions.forEach(function(e){e.kind===nL.h.OPERATION_DEFINITION?p(n(e.name&&e.name.value)).fragmentSpreads.forEach(function(e){b.add(e)}):e.kind!==nL.h.FRAGMENT_DEFINITION||0!==a||r(e.name.value).removed||b.add(e.name.value)}),b.forEach(function(e){p(r(e)).fragmentSpreads.forEach(function(e){b.add(e)})});var m=function(e){return!!(!b.has(e)||r(e).removed)},g={enter:function(e){if(m(e.name.value))return null}};return nD(tl(h,{FragmentSpread:g,FragmentDefinition:g,OperationDefinition:{leave:function(e){if(e.variableDefinitions){var t=p(n(e.name&&e.name.value)).transitiveVars;if(t.size0},t.prototype.tearDownQuery=function(){this.isTornDown||(this.concast&&this.observer&&(this.concast.removeObserver(this.observer),delete this.concast,delete this.observer),this.stopPolling(),this.subscriptions.forEach(function(e){return e.unsubscribe()}),this.subscriptions.clear(),this.queryManager.stopQuery(this.queryId),this.observers.clear(),this.isTornDown=!0)},t}(eT);function n4(e){var t=e.options,n=t.fetchPolicy,r=t.nextFetchPolicy;return"cache-and-network"===n||"network-only"===n?e.reobserve({fetchPolicy:"cache-first",nextFetchPolicy:function(){return(this.nextFetchPolicy=r,"function"==typeof r)?r.apply(this,arguments):n}}):e.reobserve()}function n6(e){__DEV__&&Q.kG.error("Unhandled error",e.message,e.stack)}function n5(e){__DEV__&&e&&__DEV__&&Q.kG.debug("Missing cache result fields: ".concat(JSON.stringify(e)),e)}function n8(e){return"network-only"===e||"no-cache"===e||"standby"===e}nK(n3);function n9(e){return e.kind===nL.h.FIELD||e.kind===nL.h.FRAGMENT_SPREAD||e.kind===nL.h.INLINE_FRAGMENT}function n7(e){return e.kind===Kind.SCALAR_TYPE_DEFINITION||e.kind===Kind.OBJECT_TYPE_DEFINITION||e.kind===Kind.INTERFACE_TYPE_DEFINITION||e.kind===Kind.UNION_TYPE_DEFINITION||e.kind===Kind.ENUM_TYPE_DEFINITION||e.kind===Kind.INPUT_OBJECT_TYPE_DEFINITION}function re(e){return e.kind===Kind.SCALAR_TYPE_EXTENSION||e.kind===Kind.OBJECT_TYPE_EXTENSION||e.kind===Kind.INTERFACE_TYPE_EXTENSION||e.kind===Kind.UNION_TYPE_EXTENSION||e.kind===Kind.ENUM_TYPE_EXTENSION||e.kind===Kind.INPUT_OBJECT_TYPE_EXTENSION}var rt=function(){return Object.create(null)},rn=Array.prototype,rr=rn.forEach,ri=rn.slice,ra=function(){function e(e,t){void 0===e&&(e=!0),void 0===t&&(t=rt),this.weakness=e,this.makeData=t}return e.prototype.lookup=function(){for(var e=[],t=0;tclass{constructor(){this.id=["slot",rc++,Date.now(),Math.random().toString(36).slice(2),].join(":")}hasValue(){for(let e=rs;e;e=e.parent)if(this.id in e.slots){let t=e.slots[this.id];if(t===ru)break;return e!==rs&&(rs.slots[this.id]=t),!0}return rs&&(rs.slots[this.id]=ru),!1}getValue(){if(this.hasValue())return rs.slots[this.id]}withValue(e,t,n,r){let i={__proto__:null,[this.id]:e},a=rs;rs={parent:a,slots:i};try{return t.apply(r,n)}finally{rs=a}}static bind(e){let t=rs;return function(){let n=rs;try{return rs=t,e.apply(this,arguments)}finally{rs=n}}}static noContext(e,t,n){if(!rs)return e.apply(n,t);{let r=rs;try{return rs=null,e.apply(n,t)}finally{rs=r}}}};function rf(e){try{return e()}catch(t){}}let rd="@wry/context:Slot",rh=rf(()=>globalThis)||rf(()=>global)||Object.create(null),rp=rh,rb=rp[rd]||Array[rd]||function(e){try{Object.defineProperty(rp,rd,{value:e,enumerable:!1,writable:!1,configurable:!0})}finally{return e}}(rl()),{bind:rm,noContext:rg}=rb;function rv(){}var ry=function(){function e(e,t){void 0===e&&(e=1/0),void 0===t&&(t=rv),this.max=e,this.dispose=t,this.map=new Map,this.newest=null,this.oldest=null}return e.prototype.has=function(e){return this.map.has(e)},e.prototype.get=function(e){var t=this.getNode(e);return t&&t.value},e.prototype.getNode=function(e){var t=this.map.get(e);if(t&&t!==this.newest){var n=t.older,r=t.newer;r&&(r.older=n),n&&(n.newer=r),t.older=this.newest,t.older.newer=t,t.newer=null,this.newest=t,t===this.oldest&&(this.oldest=r)}return t},e.prototype.set=function(e,t){var n=this.getNode(e);return n?n.value=t:(n={key:e,value:t,newer:null,older:this.newest},this.newest&&(this.newest.newer=n),this.newest=n,this.oldest=this.oldest||n,this.map.set(e,n),n.value)},e.prototype.clean=function(){for(;this.oldest&&this.map.size>this.max;)this.delete(this.oldest.key)},e.prototype.delete=function(e){var t=this.map.get(e);return!!t&&(t===this.newest&&(this.newest=t.older),t===this.oldest&&(this.oldest=t.newer),t.newer&&(t.newer.older=t.older),t.older&&(t.older.newer=t.newer),this.map.delete(e),this.dispose(t.value,e),!0)},e}(),rw=new rb,r_=Object.prototype.hasOwnProperty,rE=void 0===(n=Array.from)?function(e){var t=[];return e.forEach(function(e){return t.push(e)}),t}:n;function rS(e){var t=e.unsubscribe;"function"==typeof t&&(e.unsubscribe=void 0,t())}var rk=[],rx=100;function rT(e,t){if(!e)throw Error(t||"assertion failure")}function rM(e,t){var n=e.length;return n>0&&n===t.length&&e[n-1]===t[n-1]}function rO(e){switch(e.length){case 0:throw Error("unknown value");case 1:return e[0];case 2:throw e[1]}}function rA(e){return e.slice(0)}var rL=function(){function e(t){this.fn=t,this.parents=new Set,this.childValues=new Map,this.dirtyChildren=null,this.dirty=!0,this.recomputing=!1,this.value=[],this.deps=null,++e.count}return e.prototype.peek=function(){if(1===this.value.length&&!rN(this))return rC(this),this.value[0]},e.prototype.recompute=function(e){return rT(!this.recomputing,"already recomputing"),rC(this),rN(this)?rI(this,e):rO(this.value)},e.prototype.setDirty=function(){this.dirty||(this.dirty=!0,this.value.length=0,rR(this),rS(this))},e.prototype.dispose=function(){var e=this;this.setDirty(),rH(this),rF(this,function(t,n){t.setDirty(),r$(t,e)})},e.prototype.forget=function(){this.dispose()},e.prototype.dependOn=function(e){e.add(this),this.deps||(this.deps=rk.pop()||new Set),this.deps.add(e)},e.prototype.forgetDeps=function(){var e=this;this.deps&&(rE(this.deps).forEach(function(t){return t.delete(e)}),this.deps.clear(),rk.push(this.deps),this.deps=null)},e.count=0,e}();function rC(e){var t=rw.getValue();if(t)return e.parents.add(t),t.childValues.has(e)||t.childValues.set(e,[]),rN(e)?rY(t,e):rB(t,e),t}function rI(e,t){return rH(e),rw.withValue(e,rD,[e,t]),rz(e,t)&&rP(e),rO(e.value)}function rD(e,t){e.recomputing=!0,e.value.length=0;try{e.value[0]=e.fn.apply(null,t)}catch(n){e.value[1]=n}e.recomputing=!1}function rN(e){return e.dirty||!!(e.dirtyChildren&&e.dirtyChildren.size)}function rP(e){e.dirty=!1,!rN(e)&&rj(e)}function rR(e){rF(e,rY)}function rj(e){rF(e,rB)}function rF(e,t){var n=e.parents.size;if(n)for(var r=rE(e.parents),i=0;i0&&e.childValues.forEach(function(t,n){r$(e,n)}),e.forgetDeps(),rT(null===e.dirtyChildren)}function r$(e,t){t.parents.delete(e),e.childValues.delete(t),rU(e,t)}function rz(e,t){if("function"==typeof e.subscribe)try{rS(e),e.unsubscribe=e.subscribe.apply(null,t)}catch(n){return e.setDirty(),!1}return!0}var rG={setDirty:!0,dispose:!0,forget:!0};function rW(e){var t=new Map,n=e&&e.subscribe;function r(e){var r=rw.getValue();if(r){var i=t.get(e);i||t.set(e,i=new Set),r.dependOn(i),"function"==typeof n&&(rS(i),i.unsubscribe=n(e))}}return r.dirty=function(e,n){var r=t.get(e);if(r){var i=n&&r_.call(rG,n)?n:"setDirty";rE(r).forEach(function(e){return e[i]()}),t.delete(e),rS(r)}},r}function rK(){var e=new ra("function"==typeof WeakMap);return function(){return e.lookupArray(arguments)}}var rV=rK(),rq=new Set;function rZ(e,t){void 0===t&&(t=Object.create(null));var n=new ry(t.max||65536,function(e){return e.dispose()}),r=t.keyArgs,i=t.makeCacheKey||rK(),a=function(){var a=i.apply(null,r?r.apply(null,arguments):arguments);if(void 0===a)return e.apply(null,arguments);var o=n.get(a);o||(n.set(a,o=new rL(e)),o.subscribe=t.subscribe,o.forget=function(){return n.delete(a)});var s=o.recompute(Array.prototype.slice.call(arguments));return n.set(a,o),rq.add(n),rw.hasValue()||(rq.forEach(function(e){return e.clean()}),rq.clear()),s};function o(e){var t=n.get(e);t&&t.setDirty()}function s(e){var t=n.get(e);if(t)return t.peek()}function u(e){return n.delete(e)}return Object.defineProperty(a,"size",{get:function(){return n.map.size},configurable:!1,enumerable:!1}),a.dirtyKey=o,a.dirty=function(){o(i.apply(null,arguments))},a.peekKey=s,a.peek=function(){return s(i.apply(null,arguments))},a.forgetKey=u,a.forget=function(){return u(i.apply(null,arguments))},a.makeCacheKey=i,a.getKey=r?function(){return i.apply(null,r.apply(null,arguments))}:i,Object.freeze(a)}var rX=new rb,rJ=new WeakMap;function rQ(e){var t=rJ.get(e);return t||rJ.set(e,t={vars:new Set,dep:rW()}),t}function r1(e){rQ(e).vars.forEach(function(t){return t.forgetCache(e)})}function r0(e){rQ(e).vars.forEach(function(t){return t.attachCache(e)})}function r2(e){var t=new Set,n=new Set,r=function(a){if(arguments.length>0){if(e!==a){e=a,t.forEach(function(e){rQ(e).dep.dirty(r),r3(e)});var o=Array.from(n);n.clear(),o.forEach(function(t){return t(e)})}}else{var s=rX.getValue();s&&(i(s),rQ(s).dep(r))}return e};r.onNextChange=function(e){return n.add(e),function(){n.delete(e)}};var i=r.attachCache=function(e){return t.add(e),rQ(e).vars.add(r),r};return r.forgetCache=function(e){return t.delete(e)},r}function r3(e){e.broadcastWatches&&e.broadcastWatches()}var r4=function(){function e(e){var t=e.cache,n=e.client,r=e.resolvers,i=e.fragmentMatcher;this.selectionsToResolveCache=new WeakMap,this.cache=t,n&&(this.client=n),r&&this.addResolvers(r),i&&this.setFragmentMatcher(i)}return e.prototype.addResolvers=function(e){var t=this;this.resolvers=this.resolvers||{},Array.isArray(e)?e.forEach(function(e){t.resolvers=tj(t.resolvers,e)}):this.resolvers=tj(this.resolvers,e)},e.prototype.setResolvers=function(e){this.resolvers={},this.addResolvers(e)},e.prototype.getResolvers=function(){return this.resolvers||{}},e.prototype.runResolvers=function(e){var t=e.document,n=e.remoteResult,r=e.context,i=e.variables,a=e.onlyRunForcedResolvers,o=void 0!==a&&a;return(0,en.mG)(this,void 0,void 0,function(){return(0,en.Jh)(this,function(e){return t?[2,this.resolveDocument(t,n.data,r,i,this.fragmentMatcher,o).then(function(e){return(0,en.pi)((0,en.pi)({},n),{data:e.result})})]:[2,n]})})},e.prototype.setFragmentMatcher=function(e){this.fragmentMatcher=e},e.prototype.getFragmentMatcher=function(){return this.fragmentMatcher},e.prototype.clientQuery=function(e){return tb(["client"],e)&&this.resolvers?e:null},e.prototype.serverQuery=function(e){return n$(e)},e.prototype.prepareContext=function(e){var t=this.cache;return(0,en.pi)((0,en.pi)({},e),{cache:t,getCacheKey:function(e){return t.identify(e)}})},e.prototype.addExportedVariables=function(e,t,n){return void 0===t&&(t={}),void 0===n&&(n={}),(0,en.mG)(this,void 0,void 0,function(){return(0,en.Jh)(this,function(r){return e?[2,this.resolveDocument(e,this.buildRootValueFromCache(e,t)||{},this.prepareContext(n),t).then(function(e){return(0,en.pi)((0,en.pi)({},t),e.exportedVariables)})]:[2,(0,en.pi)({},t)]})})},e.prototype.shouldForceResolvers=function(e){var t=!1;return tl(e,{Directive:{enter:function(e){if("client"===e.name.value&&e.arguments&&(t=e.arguments.some(function(e){return"always"===e.name.value&&"BooleanValue"===e.value.kind&&!0===e.value.value})))return tc}}}),t},e.prototype.buildRootValueFromCache=function(e,t){return this.cache.diff({query:nH(e),variables:t,returnPartialData:!0,optimistic:!1}).result},e.prototype.resolveDocument=function(e,t,n,r,i,a){return void 0===n&&(n={}),void 0===r&&(r={}),void 0===i&&(i=function(){return!0}),void 0===a&&(a=!1),(0,en.mG)(this,void 0,void 0,function(){var o,s,u,c,l,f,d,h,p,b,m;return(0,en.Jh)(this,function(g){return o=e8(e),s=e4(e),u=eL(s),c=this.collectSelectionsToResolve(o,u),f=(l=o.operation)?l.charAt(0).toUpperCase()+l.slice(1):"Query",d=this,h=d.cache,p=d.client,b={fragmentMap:u,context:(0,en.pi)((0,en.pi)({},n),{cache:h,client:p}),variables:r,fragmentMatcher:i,defaultOperationType:f,exportedVariables:{},selectionsToResolve:c,onlyRunForcedResolvers:a},m=!1,[2,this.resolveSelectionSet(o.selectionSet,m,t,b).then(function(e){return{result:e,exportedVariables:b.exportedVariables}})]})})},e.prototype.resolveSelectionSet=function(e,t,n,r){return(0,en.mG)(this,void 0,void 0,function(){var i,a,o,s,u,c=this;return(0,en.Jh)(this,function(l){return i=r.fragmentMap,a=r.context,o=r.variables,s=[n],u=function(e){return(0,en.mG)(c,void 0,void 0,function(){var u,c;return(0,en.Jh)(this,function(l){return(t||r.selectionsToResolve.has(e))&&td(e,o)?eQ(e)?[2,this.resolveField(e,t,n,r).then(function(t){var n;void 0!==t&&s.push(((n={})[eX(e)]=t,n))})]:(e1(e)?u=e:(u=i[e.name.value],__DEV__?(0,Q.kG)(u,"No fragment named ".concat(e.name.value)):(0,Q.kG)(u,11)),u&&u.typeCondition&&(c=u.typeCondition.name.value,r.fragmentMatcher(n,c,a)))?[2,this.resolveSelectionSet(u.selectionSet,t,n,r).then(function(e){s.push(e)})]:[2]:[2]})})},[2,Promise.all(e.selections.map(u)).then(function(){return tF(s)})]})})},e.prototype.resolveField=function(e,t,n,r){return(0,en.mG)(this,void 0,void 0,function(){var i,a,o,s,u,c,l,f,d,h=this;return(0,en.Jh)(this,function(p){return n?(i=r.variables,a=e.name.value,o=eX(e),s=a!==o,c=Promise.resolve(u=n[o]||n[a]),(!r.onlyRunForcedResolvers||this.shouldForceResolvers(e))&&(l=n.__typename||r.defaultOperationType,(f=this.resolvers&&this.resolvers[l])&&(d=f[s?a:o])&&(c=Promise.resolve(rX.withValue(this.cache,d,[n,eZ(e,i),r.context,{field:e,fragmentMap:r.fragmentMap},])))),[2,c.then(function(n){if(void 0===n&&(n=u),e.directives&&e.directives.forEach(function(e){"export"===e.name.value&&e.arguments&&e.arguments.forEach(function(e){"as"===e.name.value&&"StringValue"===e.value.kind&&(r.exportedVariables[e.value.value]=n)})}),!e.selectionSet||null==n)return n;var i,a,o=null!==(a=null===(i=e.directives)||void 0===i?void 0:i.some(function(e){return"client"===e.name.value}))&&void 0!==a&&a;return Array.isArray(n)?h.resolveSubSelectedArray(e,t||o,n,r):e.selectionSet?h.resolveSelectionSet(e.selectionSet,t||o,n,r):void 0})]):[2,null]})})},e.prototype.resolveSubSelectedArray=function(e,t,n,r){var i=this;return Promise.all(n.map(function(n){return null===n?null:Array.isArray(n)?i.resolveSubSelectedArray(e,t,n,r):e.selectionSet?i.resolveSelectionSet(e.selectionSet,t,n,r):void 0}))},e.prototype.collectSelectionsToResolve=function(e,t){var n=function(e){return!Array.isArray(e)},r=this.selectionsToResolveCache;function i(e){if(!r.has(e)){var a=new Set;r.set(e,a),tl(e,{Directive:function(e,t,r,i,o){"client"===e.name.value&&o.forEach(function(e){n(e)&&n9(e)&&a.add(e)})},FragmentSpread:function(e,r,o,s,u){var c=t[e.name.value];__DEV__?(0,Q.kG)(c,"No fragment named ".concat(e.name.value)):(0,Q.kG)(c,12);var l=i(c);l.size>0&&(u.forEach(function(e){n(e)&&n9(e)&&a.add(e)}),a.add(e),l.forEach(function(e){a.add(e)}))}})}return r.get(e)}return i(e)},e}(),r6=new(t_.mr?WeakMap:Map);function r5(e,t){var n=e[t];"function"==typeof n&&(e[t]=function(){return r6.set(e,(r6.get(e)+1)%1e15),n.apply(this,arguments)})}function r8(e){e.notifyTimeout&&(clearTimeout(e.notifyTimeout),e.notifyTimeout=void 0)}var r9=function(){function e(e,t){void 0===t&&(t=e.generateQueryId()),this.queryId=t,this.listeners=new Set,this.document=null,this.lastRequestId=1,this.subscriptions=new Set,this.stopped=!1,this.dirty=!1,this.observableQuery=null;var n=this.cache=e.cache;r6.has(n)||(r6.set(n,0),r5(n,"evict"),r5(n,"modify"),r5(n,"reset"))}return e.prototype.init=function(e){var t=e.networkStatus||nZ.I.loading;return this.variables&&this.networkStatus!==nZ.I.loading&&!(0,nm.D)(this.variables,e.variables)&&(t=nZ.I.setVariables),(0,nm.D)(e.variables,this.variables)||(this.lastDiff=void 0),Object.assign(this,{document:e.document,variables:e.variables,networkError:null,graphQLErrors:this.graphQLErrors||[],networkStatus:t}),e.observableQuery&&this.setObservableQuery(e.observableQuery),e.lastRequestId&&(this.lastRequestId=e.lastRequestId),this},e.prototype.reset=function(){r8(this),this.dirty=!1},e.prototype.getDiff=function(e){void 0===e&&(e=this.variables);var t=this.getDiffOptions(e);if(this.lastDiff&&(0,nm.D)(t,this.lastDiff.options))return this.lastDiff.diff;this.updateWatch(this.variables=e);var n=this.observableQuery;if(n&&"no-cache"===n.options.fetchPolicy)return{complete:!1};var r=this.cache.diff(t);return this.updateLastDiff(r,t),r},e.prototype.updateLastDiff=function(e,t){this.lastDiff=e?{diff:e,options:t||this.getDiffOptions()}:void 0},e.prototype.getDiffOptions=function(e){var t;return void 0===e&&(e=this.variables),{query:this.document,variables:e,returnPartialData:!0,optimistic:!0,canonizeResults:null===(t=this.observableQuery)||void 0===t?void 0:t.options.canonizeResults}},e.prototype.setDiff=function(e){var t=this,n=this.lastDiff&&this.lastDiff.diff;this.updateLastDiff(e),this.dirty||(0,nm.D)(n&&n.result,e&&e.result)||(this.dirty=!0,this.notifyTimeout||(this.notifyTimeout=setTimeout(function(){return t.notify()},0)))},e.prototype.setObservableQuery=function(e){var t=this;e!==this.observableQuery&&(this.oqListener&&this.listeners.delete(this.oqListener),this.observableQuery=e,e?(e.queryInfo=this,this.listeners.add(this.oqListener=function(){t.getDiff().fromOptimisticTransaction?e.observe():n4(e)})):delete this.oqListener)},e.prototype.notify=function(){var e=this;r8(this),this.shouldNotify()&&this.listeners.forEach(function(t){return t(e)}),this.dirty=!1},e.prototype.shouldNotify=function(){if(!this.dirty||!this.listeners.size)return!1;if((0,nZ.O)(this.networkStatus)&&this.observableQuery){var e=this.observableQuery.options.fetchPolicy;if("cache-only"!==e&&"cache-and-network"!==e)return!1}return!0},e.prototype.stop=function(){if(!this.stopped){this.stopped=!0,this.reset(),this.cancel(),this.cancel=e.prototype.cancel,this.subscriptions.forEach(function(e){return e.unsubscribe()});var t=this.observableQuery;t&&t.stopPolling()}},e.prototype.cancel=function(){},e.prototype.updateWatch=function(e){var t=this;void 0===e&&(e=this.variables);var n=this.observableQuery;if(!n||"no-cache"!==n.options.fetchPolicy){var r=(0,en.pi)((0,en.pi)({},this.getDiffOptions(e)),{watcher:this,callback:function(e){return t.setDiff(e)}});this.lastWatch&&(0,nm.D)(r,this.lastWatch)||(this.cancel(),this.cancel=this.cache.watch(this.lastWatch=r))}},e.prototype.resetLastWrite=function(){this.lastWrite=void 0},e.prototype.shouldWrite=function(e,t){var n=this.lastWrite;return!(n&&n.dmCount===r6.get(this.cache)&&(0,nm.D)(t,n.variables)&&(0,nm.D)(e.data,n.result.data))},e.prototype.markResult=function(e,t,n,r){var i=this,a=new tB,o=(0,tP.O)(e.errors)?e.errors.slice(0):[];if(this.reset(),"incremental"in e&&(0,tP.O)(e.incremental)){var s=tG(this.getDiff().result,e);e.data=s}else if("hasNext"in e&&e.hasNext){var u=this.getDiff();e.data=a.merge(u.result,e.data)}this.graphQLErrors=o,"no-cache"===n.fetchPolicy?this.updateLastDiff({result:e.data,complete:!0},this.getDiffOptions(n.variables)):0!==r&&(r7(e,n.errorPolicy)?this.cache.performTransaction(function(a){if(i.shouldWrite(e,n.variables))a.writeQuery({query:t,data:e.data,variables:n.variables,overwrite:1===r}),i.lastWrite={result:e,variables:n.variables,dmCount:r6.get(i.cache)};else if(i.lastDiff&&i.lastDiff.diff.complete){e.data=i.lastDiff.diff.result;return}var o=i.getDiffOptions(n.variables),s=a.diff(o);i.stopped||i.updateWatch(n.variables),i.updateLastDiff(s,o),s.complete&&(e.data=s.result)}):this.lastWrite=void 0)},e.prototype.markReady=function(){return this.networkError=null,this.networkStatus=nZ.I.ready},e.prototype.markError=function(e){return this.networkStatus=nZ.I.error,this.lastWrite=void 0,this.reset(),e.graphQLErrors&&(this.graphQLErrors=e.graphQLErrors),e.networkError&&(this.networkError=e.networkError),e},e}();function r7(e,t){void 0===t&&(t="none");var n="ignore"===t||"all"===t,r=!nO(e);return!r&&n&&e.data&&(r=!0),r}var ie=Object.prototype.hasOwnProperty,it=function(){function e(e){var t=e.cache,n=e.link,r=e.defaultOptions,i=e.queryDeduplication,a=void 0!==i&&i,o=e.onBroadcast,s=e.ssrMode,u=void 0!==s&&s,c=e.clientAwareness,l=void 0===c?{}:c,f=e.localState,d=e.assumeImmutableResults;this.clientAwareness={},this.queries=new Map,this.fetchCancelFns=new Map,this.transformCache=new(t_.mr?WeakMap:Map),this.queryIdCounter=1,this.requestIdCounter=1,this.mutationIdCounter=1,this.inFlightLinkObservables=new Map,this.cache=t,this.link=n,this.defaultOptions=r||Object.create(null),this.queryDeduplication=a,this.clientAwareness=l,this.localState=f||new r4({cache:t}),this.ssrMode=u,this.assumeImmutableResults=!!d,(this.onBroadcast=o)&&(this.mutationStore=Object.create(null))}return e.prototype.stop=function(){var e=this;this.queries.forEach(function(t,n){e.stopQueryNoBroadcast(n)}),this.cancelPendingFetches(__DEV__?new Q.ej("QueryManager stopped while query was in flight"):new Q.ej(14))},e.prototype.cancelPendingFetches=function(e){this.fetchCancelFns.forEach(function(t){return t(e)}),this.fetchCancelFns.clear()},e.prototype.mutate=function(e){var t,n,r=e.mutation,i=e.variables,a=e.optimisticResponse,o=e.updateQueries,s=e.refetchQueries,u=void 0===s?[]:s,c=e.awaitRefetchQueries,l=void 0!==c&&c,f=e.update,d=e.onQueryUpdated,h=e.fetchPolicy,p=void 0===h?(null===(t=this.defaultOptions.mutate)||void 0===t?void 0:t.fetchPolicy)||"network-only":h,b=e.errorPolicy,m=void 0===b?(null===(n=this.defaultOptions.mutate)||void 0===n?void 0:n.errorPolicy)||"none":b,g=e.keepRootFields,v=e.context;return(0,en.mG)(this,void 0,void 0,function(){var e,t,n,s,c,h;return(0,en.Jh)(this,function(b){switch(b.label){case 0:if(__DEV__?(0,Q.kG)(r,"mutation option is required. You must specify your GraphQL document in the mutation option."):(0,Q.kG)(r,15),__DEV__?(0,Q.kG)("network-only"===p||"no-cache"===p,"Mutations support only 'network-only' or 'no-cache' fetchPolicy strings. The default `network-only` behavior automatically writes mutation results to the cache. Passing `no-cache` skips the cache write."):(0,Q.kG)("network-only"===p||"no-cache"===p,16),e=this.generateMutationId(),n=(t=this.transform(r)).document,s=t.hasClientExports,r=this.cache.transformForLink(n),i=this.getVariables(r,i),!s)return[3,2];return[4,this.localState.addExportedVariables(r,i,v)];case 1:i=b.sent(),b.label=2;case 2:return c=this.mutationStore&&(this.mutationStore[e]={mutation:r,variables:i,loading:!0,error:null}),a&&this.markMutationOptimistic(a,{mutationId:e,document:r,variables:i,fetchPolicy:p,errorPolicy:m,context:v,updateQueries:o,update:f,keepRootFields:g}),this.broadcastQueries(),h=this,[2,new Promise(function(t,n){return nM(h.getObservableFromLink(r,(0,en.pi)((0,en.pi)({},v),{optimisticResponse:a}),i,!1),function(t){if(nO(t)&&"none"===m)throw new tN.cA({graphQLErrors:nA(t)});c&&(c.loading=!1,c.error=null);var n=(0,en.pi)({},t);return"function"==typeof u&&(u=u(n)),"ignore"===m&&nO(n)&&delete n.errors,h.markMutationResult({mutationId:e,result:n,document:r,variables:i,fetchPolicy:p,errorPolicy:m,context:v,update:f,updateQueries:o,awaitRefetchQueries:l,refetchQueries:u,removeOptimistic:a?e:void 0,onQueryUpdated:d,keepRootFields:g})}).subscribe({next:function(e){h.broadcastQueries(),"hasNext"in e&&!1!==e.hasNext||t(e)},error:function(t){c&&(c.loading=!1,c.error=t),a&&h.cache.removeOptimistic(e),h.broadcastQueries(),n(t instanceof tN.cA?t:new tN.cA({networkError:t}))}})})]}})})},e.prototype.markMutationResult=function(e,t){var n=this;void 0===t&&(t=this.cache);var r=e.result,i=[],a="no-cache"===e.fetchPolicy;if(!a&&r7(r,e.errorPolicy)){if(tU(r)||i.push({result:r.data,dataId:"ROOT_MUTATION",query:e.document,variables:e.variables}),tU(r)&&(0,tP.O)(r.incremental)){var o=t.diff({id:"ROOT_MUTATION",query:this.transform(e.document).asQuery,variables:e.variables,optimistic:!1,returnPartialData:!0}),s=void 0;o.result&&(s=tG(o.result,r)),void 0!==s&&(r.data=s,i.push({result:s,dataId:"ROOT_MUTATION",query:e.document,variables:e.variables}))}var u=e.updateQueries;u&&this.queries.forEach(function(e,a){var o=e.observableQuery,s=o&&o.queryName;if(s&&ie.call(u,s)){var c,l=u[s],f=n.queries.get(a),d=f.document,h=f.variables,p=t.diff({query:d,variables:h,returnPartialData:!0,optimistic:!1}),b=p.result;if(p.complete&&b){var m=l(b,{mutationResult:r,queryName:d&&e3(d)||void 0,queryVariables:h});m&&i.push({result:m,dataId:"ROOT_QUERY",query:d,variables:h})}}})}if(i.length>0||e.refetchQueries||e.update||e.onQueryUpdated||e.removeOptimistic){var c=[];if(this.refetchQueries({updateCache:function(t){a||i.forEach(function(e){return t.write(e)});var o=e.update,s=!t$(r)||tU(r)&&!r.hasNext;if(o){if(!a){var u=t.diff({id:"ROOT_MUTATION",query:n.transform(e.document).asQuery,variables:e.variables,optimistic:!1,returnPartialData:!0});u.complete&&("incremental"in(r=(0,en.pi)((0,en.pi)({},r),{data:u.result}))&&delete r.incremental,"hasNext"in r&&delete r.hasNext)}s&&o(t,r,{context:e.context,variables:e.variables})}a||e.keepRootFields||!s||t.modify({id:"ROOT_MUTATION",fields:function(e,t){var n=t.fieldName,r=t.DELETE;return"__typename"===n?e:r}})},include:e.refetchQueries,optimistic:!1,removeOptimistic:e.removeOptimistic,onQueryUpdated:e.onQueryUpdated||null}).forEach(function(e){return c.push(e)}),e.awaitRefetchQueries||e.onQueryUpdated)return Promise.all(c).then(function(){return r})}return Promise.resolve(r)},e.prototype.markMutationOptimistic=function(e,t){var n=this,r="function"==typeof e?e(t.variables):e;return this.cache.recordOptimisticTransaction(function(e){try{n.markMutationResult((0,en.pi)((0,en.pi)({},t),{result:{data:r}}),e)}catch(i){__DEV__&&Q.kG.error(i)}},t.mutationId)},e.prototype.fetchQuery=function(e,t,n){return this.fetchQueryObservable(e,t,n).promise},e.prototype.getQueryStore=function(){var e=Object.create(null);return this.queries.forEach(function(t,n){e[n]={variables:t.variables,networkStatus:t.networkStatus,networkError:t.networkError,graphQLErrors:t.graphQLErrors}}),e},e.prototype.resetErrors=function(e){var t=this.queries.get(e);t&&(t.networkError=void 0,t.graphQLErrors=[])},e.prototype.transform=function(e){var t=this.transformCache;if(!t.has(e)){var n=this.cache.transformDocument(e),r=nY(n),i=this.localState.clientQuery(n),a=r&&this.localState.serverQuery(r),o={document:n,hasClientExports:tm(n),hasForcedResolvers:this.localState.shouldForceResolvers(n),clientQuery:i,serverQuery:a,defaultVars:e9(e2(n)),asQuery:(0,en.pi)((0,en.pi)({},n),{definitions:n.definitions.map(function(e){return"OperationDefinition"===e.kind&&"query"!==e.operation?(0,en.pi)((0,en.pi)({},e),{operation:"query"}):e})})},s=function(e){e&&!t.has(e)&&t.set(e,o)};s(e),s(n),s(i),s(a)}return t.get(e)},e.prototype.getVariables=function(e,t){return(0,en.pi)((0,en.pi)({},this.transform(e).defaultVars),t)},e.prototype.watchQuery=function(e){void 0===(e=(0,en.pi)((0,en.pi)({},e),{variables:this.getVariables(e.query,e.variables)})).notifyOnNetworkStatusChange&&(e.notifyOnNetworkStatusChange=!1);var t=new r9(this),n=new n3({queryManager:this,queryInfo:t,options:e});return this.queries.set(n.queryId,t),t.init({document:n.query,observableQuery:n,variables:n.variables}),n},e.prototype.query=function(e,t){var n=this;return void 0===t&&(t=this.generateQueryId()),__DEV__?(0,Q.kG)(e.query,"query option is required. You must specify your GraphQL document in the query option."):(0,Q.kG)(e.query,17),__DEV__?(0,Q.kG)("Document"===e.query.kind,'You must wrap the query string in a "gql" tag.'):(0,Q.kG)("Document"===e.query.kind,18),__DEV__?(0,Q.kG)(!e.returnPartialData,"returnPartialData option only supported on watchQuery."):(0,Q.kG)(!e.returnPartialData,19),__DEV__?(0,Q.kG)(!e.pollInterval,"pollInterval option only supported on watchQuery."):(0,Q.kG)(!e.pollInterval,20),this.fetchQuery(t,e).finally(function(){return n.stopQuery(t)})},e.prototype.generateQueryId=function(){return String(this.queryIdCounter++)},e.prototype.generateRequestId=function(){return this.requestIdCounter++},e.prototype.generateMutationId=function(){return String(this.mutationIdCounter++)},e.prototype.stopQueryInStore=function(e){this.stopQueryInStoreNoBroadcast(e),this.broadcastQueries()},e.prototype.stopQueryInStoreNoBroadcast=function(e){var t=this.queries.get(e);t&&t.stop()},e.prototype.clearStore=function(e){return void 0===e&&(e={discardWatches:!0}),this.cancelPendingFetches(__DEV__?new Q.ej("Store reset while query was in flight (not completed in link chain)"):new Q.ej(21)),this.queries.forEach(function(e){e.observableQuery?e.networkStatus=nZ.I.loading:e.stop()}),this.mutationStore&&(this.mutationStore=Object.create(null)),this.cache.reset(e)},e.prototype.getObservableQueries=function(e){var t=this;void 0===e&&(e="active");var n=new Map,r=new Map,i=new Set;return Array.isArray(e)&&e.forEach(function(e){"string"==typeof e?r.set(e,!1):eN(e)?r.set(t.transform(e).document,!1):(0,eO.s)(e)&&e.query&&i.add(e)}),this.queries.forEach(function(t,i){var a=t.observableQuery,o=t.document;if(a){if("all"===e){n.set(i,a);return}var s=a.queryName;if("standby"===a.options.fetchPolicy||"active"===e&&!a.hasObservers())return;("active"===e||s&&r.has(s)||o&&r.has(o))&&(n.set(i,a),s&&r.set(s,!0),o&&r.set(o,!0))}}),i.size&&i.forEach(function(e){var r=nG("legacyOneTimeQuery"),i=t.getQuery(r).init({document:e.query,variables:e.variables}),a=new n3({queryManager:t,queryInfo:i,options:(0,en.pi)((0,en.pi)({},e),{fetchPolicy:"network-only"})});(0,Q.kG)(a.queryId===r),i.setObservableQuery(a),n.set(r,a)}),__DEV__&&r.size&&r.forEach(function(e,t){!e&&__DEV__&&Q.kG.warn("Unknown query ".concat("string"==typeof t?"named ":"").concat(JSON.stringify(t,null,2)," requested in refetchQueries options.include array"))}),n},e.prototype.reFetchObservableQueries=function(e){var t=this;void 0===e&&(e=!1);var n=[];return this.getObservableQueries(e?"all":"active").forEach(function(r,i){var a=r.options.fetchPolicy;r.resetLastResults(),(e||"standby"!==a&&"cache-only"!==a)&&n.push(r.refetch()),t.getQuery(i).setDiff(null)}),this.broadcastQueries(),Promise.all(n)},e.prototype.setObservableQuery=function(e){this.getQuery(e.queryId).setObservableQuery(e)},e.prototype.startGraphQLSubscription=function(e){var t=this,n=e.query,r=e.fetchPolicy,i=e.errorPolicy,a=e.variables,o=e.context,s=void 0===o?{}:o;n=this.transform(n).document,a=this.getVariables(n,a);var u=function(e){return t.getObservableFromLink(n,s,e).map(function(a){"no-cache"!==r&&(r7(a,i)&&t.cache.write({query:n,result:a.data,dataId:"ROOT_SUBSCRIPTION",variables:e}),t.broadcastQueries());var o=nO(a),s=(0,tN.ls)(a);if(o||s){var u={};throw o&&(u.graphQLErrors=a.errors),s&&(u.protocolErrors=a.extensions[tN.YG]),new tN.cA(u)}return a})};if(this.transform(n).hasClientExports){var c=this.localState.addExportedVariables(n,a,s).then(u);return new eT(function(e){var t=null;return c.then(function(n){return t=n.subscribe(e)},e.error),function(){return t&&t.unsubscribe()}})}return u(a)},e.prototype.stopQuery=function(e){this.stopQueryNoBroadcast(e),this.broadcastQueries()},e.prototype.stopQueryNoBroadcast=function(e){this.stopQueryInStoreNoBroadcast(e),this.removeQuery(e)},e.prototype.removeQuery=function(e){this.fetchCancelFns.delete(e),this.queries.has(e)&&(this.getQuery(e).stop(),this.queries.delete(e))},e.prototype.broadcastQueries=function(){this.onBroadcast&&this.onBroadcast(),this.queries.forEach(function(e){return e.notify()})},e.prototype.getLocalState=function(){return this.localState},e.prototype.getObservableFromLink=function(e,t,n,r){var i,a,o=this;void 0===r&&(r=null!==(i=null==t?void 0:t.queryDeduplication)&&void 0!==i?i:this.queryDeduplication);var s=this.transform(e).serverQuery;if(s){var u=this,c=u.inFlightLinkObservables,l=u.link,f={query:s,variables:n,operationName:e3(s)||void 0,context:this.prepareContext((0,en.pi)((0,en.pi)({},t),{forceFetch:!r}))};if(t=f.context,r){var d=c.get(s)||new Map;c.set(s,d);var h=nx(n);if(!(a=d.get(h))){var p=new nq([np(l,f)]);d.set(h,a=p),p.beforeNext(function(){d.delete(h)&&d.size<1&&c.delete(s)})}}else a=new nq([np(l,f)])}else a=new nq([eT.of({data:{}})]),t=this.prepareContext(t);var b=this.transform(e).clientQuery;return b&&(a=nM(a,function(e){return o.localState.runResolvers({document:b,remoteResult:e,context:t,variables:n})})),a},e.prototype.getResultsFromLink=function(e,t,n){var r=e.lastRequestId=this.generateRequestId(),i=this.cache.transformForLink(this.transform(e.document).document);return nM(this.getObservableFromLink(i,n.context,n.variables),function(a){var o=nA(a),s=o.length>0;if(r>=e.lastRequestId){if(s&&"none"===n.errorPolicy)throw e.markError(new tN.cA({graphQLErrors:o}));e.markResult(a,i,n,t),e.markReady()}var u={data:a.data,loading:!1,networkStatus:nZ.I.ready};return s&&"ignore"!==n.errorPolicy&&(u.errors=o,u.networkStatus=nZ.I.error),u},function(t){var n=(0,tN.MS)(t)?t:new tN.cA({networkError:t});throw r>=e.lastRequestId&&e.markError(n),n})},e.prototype.fetchQueryObservable=function(e,t,n){return this.fetchConcastWithInfo(e,t,n).concast},e.prototype.fetchConcastWithInfo=function(e,t,n){var r,i,a=this;void 0===n&&(n=nZ.I.loading);var o=this.transform(t.query).document,s=this.getVariables(o,t.variables),u=this.getQuery(e),c=this.defaultOptions.watchQuery,l=t.fetchPolicy,f=void 0===l?c&&c.fetchPolicy||"cache-first":l,d=t.errorPolicy,h=void 0===d?c&&c.errorPolicy||"none":d,p=t.returnPartialData,b=void 0!==p&&p,m=t.notifyOnNetworkStatusChange,g=void 0!==m&&m,v=t.context,y=void 0===v?{}:v,w=Object.assign({},t,{query:o,variables:s,fetchPolicy:f,errorPolicy:h,returnPartialData:b,notifyOnNetworkStatusChange:g,context:y}),_=function(e){w.variables=e;var r=a.fetchQueryByPolicy(u,w,n);return"standby"!==w.fetchPolicy&&r.sources.length>0&&u.observableQuery&&u.observableQuery.applyNextFetchPolicy("after-fetch",t),r},E=function(){return a.fetchCancelFns.delete(e)};if(this.fetchCancelFns.set(e,function(e){E(),setTimeout(function(){return r.cancel(e)})}),this.transform(w.query).hasClientExports)r=new nq(this.localState.addExportedVariables(w.query,w.variables,w.context).then(_).then(function(e){return e.sources})),i=!0;else{var S=_(w.variables);i=S.fromLink,r=new nq(S.sources)}return r.promise.then(E,E),{concast:r,fromLink:i}},e.prototype.refetchQueries=function(e){var t=this,n=e.updateCache,r=e.include,i=e.optimistic,a=void 0!==i&&i,o=e.removeOptimistic,s=void 0===o?a?nG("refetchQueries"):void 0:o,u=e.onQueryUpdated,c=new Map;r&&this.getObservableQueries(r).forEach(function(e,n){c.set(n,{oq:e,lastDiff:t.getQuery(n).getDiff()})});var l=new Map;return n&&this.cache.batch({update:n,optimistic:a&&s||!1,removeOptimistic:s,onWatchUpdated:function(e,t,n){var r=e.watcher instanceof r9&&e.watcher.observableQuery;if(r){if(u){c.delete(r.queryId);var i=u(r,t,n);return!0===i&&(i=r.refetch()),!1!==i&&l.set(r,i),i}null!==u&&c.set(r.queryId,{oq:r,lastDiff:n,diff:t})}}}),c.size&&c.forEach(function(e,n){var r,i=e.oq,a=e.lastDiff,o=e.diff;if(u){if(!o){var s=i.queryInfo;s.reset(),o=s.getDiff()}r=u(i,o,a)}u&&!0!==r||(r=i.refetch()),!1!==r&&l.set(i,r),n.indexOf("legacyOneTimeQuery")>=0&&t.stopQueryNoBroadcast(n)}),s&&this.cache.removeOptimistic(s),l},e.prototype.fetchQueryByPolicy=function(e,t,n){var r=this,i=t.query,a=t.variables,o=t.fetchPolicy,s=t.refetchWritePolicy,u=t.errorPolicy,c=t.returnPartialData,l=t.context,f=t.notifyOnNetworkStatusChange,d=e.networkStatus;e.init({document:this.transform(i).document,variables:a,networkStatus:n});var h=function(){return e.getDiff(a)},p=function(t,n){void 0===n&&(n=e.networkStatus||nZ.I.loading);var o=t.result;!__DEV__||c||(0,nm.D)(o,{})||n5(t.missing);var s=function(e){return eT.of((0,en.pi)({data:e,loading:(0,nZ.O)(n),networkStatus:n},t.complete?null:{partial:!0}))};return o&&r.transform(i).hasForcedResolvers?r.localState.runResolvers({document:i,remoteResult:{data:o},context:l,variables:a,onlyRunForcedResolvers:!0}).then(function(e){return s(e.data||void 0)}):"none"===u&&n===nZ.I.refetch&&Array.isArray(t.missing)?s(void 0):s(o)},b="no-cache"===o?0:n===nZ.I.refetch&&"merge"!==s?1:2,m=function(){return r.getResultsFromLink(e,b,{variables:a,context:l,fetchPolicy:o,errorPolicy:u})},g=f&&"number"==typeof d&&d!==n&&(0,nZ.O)(n);switch(o){default:case"cache-first":var v=h();if(v.complete)return{fromLink:!1,sources:[p(v,e.markReady())]};if(c||g)return{fromLink:!0,sources:[p(v),m()]};return{fromLink:!0,sources:[m()]};case"cache-and-network":var v=h();if(v.complete||c||g)return{fromLink:!0,sources:[p(v),m()]};return{fromLink:!0,sources:[m()]};case"cache-only":return{fromLink:!1,sources:[p(h(),e.markReady())]};case"network-only":if(g)return{fromLink:!0,sources:[p(h()),m()]};return{fromLink:!0,sources:[m()]};case"no-cache":if(g)return{fromLink:!0,sources:[p(e.getDiff()),m(),]};return{fromLink:!0,sources:[m()]};case"standby":return{fromLink:!1,sources:[]}}},e.prototype.getQuery=function(e){return e&&!this.queries.has(e)&&this.queries.set(e,new r9(this,e)),this.queries.get(e)},e.prototype.prepareContext=function(e){void 0===e&&(e={});var t=this.localState.prepareContext(e);return(0,en.pi)((0,en.pi)({},t),{clientAwareness:this.clientAwareness})},e}(),ir=__webpack_require__(14012),ii=!1,ia=function(){function e(e){var t=this;this.resetStoreCallbacks=[],this.clearStoreCallbacks=[];var n=e.uri,r=e.credentials,i=e.headers,a=e.cache,o=e.ssrMode,s=void 0!==o&&o,u=e.ssrForceFetchDelay,c=void 0===u?0:u,l=e.connectToDevTools,f=void 0===l?"object"==typeof window&&!window.__APOLLO_CLIENT__&&__DEV__:l,d=e.queryDeduplication,h=void 0===d||d,p=e.defaultOptions,b=e.assumeImmutableResults,m=void 0!==b&&b,g=e.resolvers,v=e.typeDefs,y=e.fragmentMatcher,w=e.name,_=e.version,E=e.link;if(E||(E=n?new nh({uri:n,credentials:r,headers:i}):ta.empty()),!a)throw __DEV__?new Q.ej("To initialize Apollo Client, you must specify a 'cache' property in the options object. \nFor more information, please visit: https://go.apollo.dev/c/docs"):new Q.ej(9);if(this.link=E,this.cache=a,this.disableNetworkFetches=s||c>0,this.queryDeduplication=h,this.defaultOptions=p||Object.create(null),this.typeDefs=v,c&&setTimeout(function(){return t.disableNetworkFetches=!1},c),this.watchQuery=this.watchQuery.bind(this),this.query=this.query.bind(this),this.mutate=this.mutate.bind(this),this.resetStore=this.resetStore.bind(this),this.reFetchObservableQueries=this.reFetchObservableQueries.bind(this),f&&"object"==typeof window&&(window.__APOLLO_CLIENT__=this),!ii&&f&&__DEV__&&(ii=!0,"undefined"!=typeof window&&window.document&&window.top===window.self&&!window.__APOLLO_DEVTOOLS_GLOBAL_HOOK__)){var S=window.navigator,k=S&&S.userAgent,x=void 0;"string"==typeof k&&(k.indexOf("Chrome/")>-1?x="https://chrome.google.com/webstore/detail/apollo-client-developer-t/jdkknkkbebbapilgoeccciglkfbmbnfm":k.indexOf("Firefox/")>-1&&(x="https://addons.mozilla.org/en-US/firefox/addon/apollo-developer-tools/")),x&&__DEV__&&Q.kG.log("Download the Apollo DevTools for a better development experience: "+x)}this.version=nb,this.localState=new r4({cache:a,client:this,resolvers:g,fragmentMatcher:y}),this.queryManager=new it({cache:this.cache,link:this.link,defaultOptions:this.defaultOptions,queryDeduplication:h,ssrMode:s,clientAwareness:{name:w,version:_},localState:this.localState,assumeImmutableResults:m,onBroadcast:f?function(){t.devToolsHookCb&&t.devToolsHookCb({action:{},state:{queries:t.queryManager.getQueryStore(),mutations:t.queryManager.mutationStore||{}},dataWithOptimisticResults:t.cache.extract(!0)})}:void 0})}return e.prototype.stop=function(){this.queryManager.stop()},e.prototype.watchQuery=function(e){return this.defaultOptions.watchQuery&&(e=(0,ir.J)(this.defaultOptions.watchQuery,e)),this.disableNetworkFetches&&("network-only"===e.fetchPolicy||"cache-and-network"===e.fetchPolicy)&&(e=(0,en.pi)((0,en.pi)({},e),{fetchPolicy:"cache-first"})),this.queryManager.watchQuery(e)},e.prototype.query=function(e){return this.defaultOptions.query&&(e=(0,ir.J)(this.defaultOptions.query,e)),__DEV__?(0,Q.kG)("cache-and-network"!==e.fetchPolicy,"The cache-and-network fetchPolicy does not work with client.query, because client.query can only return a single result. Please use client.watchQuery to receive multiple results from the cache and the network, or consider using a different fetchPolicy, such as cache-first or network-only."):(0,Q.kG)("cache-and-network"!==e.fetchPolicy,10),this.disableNetworkFetches&&"network-only"===e.fetchPolicy&&(e=(0,en.pi)((0,en.pi)({},e),{fetchPolicy:"cache-first"})),this.queryManager.query(e)},e.prototype.mutate=function(e){return this.defaultOptions.mutate&&(e=(0,ir.J)(this.defaultOptions.mutate,e)),this.queryManager.mutate(e)},e.prototype.subscribe=function(e){return this.queryManager.startGraphQLSubscription(e)},e.prototype.readQuery=function(e,t){return void 0===t&&(t=!1),this.cache.readQuery(e,t)},e.prototype.readFragment=function(e,t){return void 0===t&&(t=!1),this.cache.readFragment(e,t)},e.prototype.writeQuery=function(e){var t=this.cache.writeQuery(e);return!1!==e.broadcast&&this.queryManager.broadcastQueries(),t},e.prototype.writeFragment=function(e){var t=this.cache.writeFragment(e);return!1!==e.broadcast&&this.queryManager.broadcastQueries(),t},e.prototype.__actionHookForDevTools=function(e){this.devToolsHookCb=e},e.prototype.__requestRaw=function(e){return np(this.link,e)},e.prototype.resetStore=function(){var e=this;return Promise.resolve().then(function(){return e.queryManager.clearStore({discardWatches:!1})}).then(function(){return Promise.all(e.resetStoreCallbacks.map(function(e){return e()}))}).then(function(){return e.reFetchObservableQueries()})},e.prototype.clearStore=function(){var e=this;return Promise.resolve().then(function(){return e.queryManager.clearStore({discardWatches:!0})}).then(function(){return Promise.all(e.clearStoreCallbacks.map(function(e){return e()}))})},e.prototype.onResetStore=function(e){var t=this;return this.resetStoreCallbacks.push(e),function(){t.resetStoreCallbacks=t.resetStoreCallbacks.filter(function(t){return t!==e})}},e.prototype.onClearStore=function(e){var t=this;return this.clearStoreCallbacks.push(e),function(){t.clearStoreCallbacks=t.clearStoreCallbacks.filter(function(t){return t!==e})}},e.prototype.reFetchObservableQueries=function(e){return this.queryManager.reFetchObservableQueries(e)},e.prototype.refetchQueries=function(e){var t=this.queryManager.refetchQueries(e),n=[],r=[];t.forEach(function(e,t){n.push(t),r.push(e)});var i=Promise.all(r);return i.queries=n,i.results=r,i.catch(function(e){__DEV__&&Q.kG.debug("In client.refetchQueries, Promise.all promise rejected with error ".concat(e))}),i},e.prototype.getObservableQueries=function(e){return void 0===e&&(e="active"),this.queryManager.getObservableQueries(e)},e.prototype.extract=function(e){return this.cache.extract(e)},e.prototype.restore=function(e){return this.cache.restore(e)},e.prototype.addResolvers=function(e){this.localState.addResolvers(e)},e.prototype.setResolvers=function(e){this.localState.setResolvers(e)},e.prototype.getResolvers=function(){return this.localState.getResolvers()},e.prototype.setLocalStateFragmentMatcher=function(e){this.localState.setFragmentMatcher(e)},e.prototype.setLink=function(e){this.link=this.queryManager.link=e},e}(),io=function(){function e(){this.getFragmentDoc=rZ(eA)}return e.prototype.batch=function(e){var t,n=this,r="string"==typeof e.optimistic?e.optimistic:!1===e.optimistic?null:void 0;return this.performTransaction(function(){return t=e.update(n)},r),t},e.prototype.recordOptimisticTransaction=function(e,t){this.performTransaction(e,t)},e.prototype.transformDocument=function(e){return e},e.prototype.transformForLink=function(e){return e},e.prototype.identify=function(e){},e.prototype.gc=function(){return[]},e.prototype.modify=function(e){return!1},e.prototype.readQuery=function(e,t){return void 0===t&&(t=!!e.optimistic),this.read((0,en.pi)((0,en.pi)({},e),{rootId:e.id||"ROOT_QUERY",optimistic:t}))},e.prototype.readFragment=function(e,t){return void 0===t&&(t=!!e.optimistic),this.read((0,en.pi)((0,en.pi)({},e),{query:this.getFragmentDoc(e.fragment,e.fragmentName),rootId:e.id,optimistic:t}))},e.prototype.writeQuery=function(e){var t=e.id,n=e.data,r=(0,en._T)(e,["id","data"]);return this.write(Object.assign(r,{dataId:t||"ROOT_QUERY",result:n}))},e.prototype.writeFragment=function(e){var t=e.id,n=e.data,r=e.fragment,i=e.fragmentName,a=(0,en._T)(e,["id","data","fragment","fragmentName"]);return this.write(Object.assign(a,{query:this.getFragmentDoc(r,i),dataId:t,result:n}))},e.prototype.updateQuery=function(e,t){return this.batch({update:function(n){var r=n.readQuery(e),i=t(r);return null==i?r:(n.writeQuery((0,en.pi)((0,en.pi)({},e),{data:i})),i)}})},e.prototype.updateFragment=function(e,t){return this.batch({update:function(n){var r=n.readFragment(e),i=t(r);return null==i?r:(n.writeFragment((0,en.pi)((0,en.pi)({},e),{data:i})),i)}})},e}(),is=function(e){function t(n,r,i,a){var o,s=e.call(this,n)||this;if(s.message=n,s.path=r,s.query=i,s.variables=a,Array.isArray(s.path)){s.missing=s.message;for(var u=s.path.length-1;u>=0;--u)s.missing=((o={})[s.path[u]]=s.missing,o)}else s.missing=s.path;return s.__proto__=t.prototype,s}return(0,en.ZT)(t,e),t}(Error),iu=__webpack_require__(10542),ic=Object.prototype.hasOwnProperty;function il(e){return null==e}function id(e,t){var n=e.__typename,r=e.id,i=e._id;if("string"==typeof n&&(t&&(t.keyObject=il(r)?il(i)?void 0:{_id:i}:{id:r}),il(r)&&!il(i)&&(r=i),!il(r)))return"".concat(n,":").concat("number"==typeof r||"string"==typeof r?r:JSON.stringify(r))}var ih={dataIdFromObject:id,addTypename:!0,resultCaching:!0,canonizeResults:!1};function ip(e){return(0,n1.o)(ih,e)}function ib(e){var t=e.canonizeResults;return void 0===t?ih.canonizeResults:t}function im(e,t){return eD(t)?e.get(t.__ref,"__typename"):t&&t.__typename}var ig=/^[_a-z][_0-9a-z]*/i;function iv(e){var t=e.match(ig);return t?t[0]:e}function iy(e,t,n){return!!(0,eO.s)(t)&&((0,tP.k)(t)?t.every(function(t){return iy(e,t,n)}):e.selections.every(function(e){if(eQ(e)&&td(e,n)){var r=eX(e);return ic.call(t,r)&&(!e.selectionSet||iy(e.selectionSet,t[r],n))}return!0}))}function iw(e){return(0,eO.s)(e)&&!eD(e)&&!(0,tP.k)(e)}function i_(){return new tB}function iE(e,t){var n=eL(e4(e));return{fragmentMap:n,lookupFragment:function(e){var r=n[e];return!r&&t&&(r=t.lookup(e)),r||null}}}var iS=Object.create(null),ik=function(){return iS},ix=Object.create(null),iT=function(){function e(e,t){var n=this;this.policies=e,this.group=t,this.data=Object.create(null),this.rootIds=Object.create(null),this.refs=Object.create(null),this.getFieldValue=function(e,t){return(0,iu.J)(eD(e)?n.get(e.__ref,t):e&&e[t])},this.canRead=function(e){return eD(e)?n.has(e.__ref):"object"==typeof e},this.toReference=function(e,t){if("string"==typeof e)return eI(e);if(eD(e))return e;var r=n.policies.identify(e)[0];if(r){var i=eI(r);return t&&n.merge(r,e),i}}}return e.prototype.toObject=function(){return(0,en.pi)({},this.data)},e.prototype.has=function(e){return void 0!==this.lookup(e,!0)},e.prototype.get=function(e,t){if(this.group.depend(e,t),ic.call(this.data,e)){var n=this.data[e];if(n&&ic.call(n,t))return n[t]}return"__typename"===t&&ic.call(this.policies.rootTypenamesById,e)?this.policies.rootTypenamesById[e]:this instanceof iL?this.parent.get(e,t):void 0},e.prototype.lookup=function(e,t){return(t&&this.group.depend(e,"__exists"),ic.call(this.data,e))?this.data[e]:this instanceof iL?this.parent.lookup(e,t):this.policies.rootTypenamesById[e]?Object.create(null):void 0},e.prototype.merge=function(e,t){var n,r=this;eD(e)&&(e=e.__ref),eD(t)&&(t=t.__ref);var i="string"==typeof e?this.lookup(n=e):e,a="string"==typeof t?this.lookup(n=t):t;if(a){__DEV__?(0,Q.kG)("string"==typeof n,"store.merge expects a string ID"):(0,Q.kG)("string"==typeof n,1);var o=new tB(iI).merge(i,a);if(this.data[n]=o,o!==i&&(delete this.refs[n],this.group.caching)){var s=Object.create(null);i||(s.__exists=1),Object.keys(a).forEach(function(e){if(!i||i[e]!==o[e]){s[e]=1;var t=iv(e);t===e||r.policies.hasKeyArgs(o.__typename,t)||(s[t]=1),void 0!==o[e]||r instanceof iL||delete o[e]}}),s.__typename&&!(i&&i.__typename)&&this.policies.rootTypenamesById[n]===o.__typename&&delete s.__typename,Object.keys(s).forEach(function(e){return r.group.dirty(n,e)})}}},e.prototype.modify=function(e,t){var n=this,r=this.lookup(e);if(r){var i=Object.create(null),a=!1,o=!0,s={DELETE:iS,INVALIDATE:ix,isReference:eD,toReference:this.toReference,canRead:this.canRead,readField:function(t,r){return n.policies.readField("string"==typeof t?{fieldName:t,from:r||eI(e)}:t,{store:n})}};if(Object.keys(r).forEach(function(u){var c=iv(u),l=r[u];if(void 0!==l){var f="function"==typeof t?t:t[u]||t[c];if(f){var d=f===ik?iS:f((0,iu.J)(l),(0,en.pi)((0,en.pi)({},s),{fieldName:c,storeFieldName:u,storage:n.getStorage(e,u)}));d===ix?n.group.dirty(e,u):(d===iS&&(d=void 0),d!==l&&(i[u]=d,a=!0,l=d))}void 0!==l&&(o=!1)}}),a)return this.merge(e,i),o&&(this instanceof iL?this.data[e]=void 0:delete this.data[e],this.group.dirty(e,"__exists")),!0}return!1},e.prototype.delete=function(e,t,n){var r,i=this.lookup(e);if(i){var a=this.getFieldValue(i,"__typename"),o=t&&n?this.policies.getStoreFieldName({typename:a,fieldName:t,args:n}):t;return this.modify(e,o?((r={})[o]=ik,r):ik)}return!1},e.prototype.evict=function(e,t){var n=!1;return e.id&&(ic.call(this.data,e.id)&&(n=this.delete(e.id,e.fieldName,e.args)),this instanceof iL&&this!==t&&(n=this.parent.evict(e,t)||n),(e.fieldName||n)&&this.group.dirty(e.id,e.fieldName||"__exists")),n},e.prototype.clear=function(){this.replace(null)},e.prototype.extract=function(){var e=this,t=this.toObject(),n=[];return this.getRootIdSet().forEach(function(t){ic.call(e.policies.rootTypenamesById,t)||n.push(t)}),n.length&&(t.__META={extraRootIds:n.sort()}),t},e.prototype.replace=function(e){var t=this;if(Object.keys(this.data).forEach(function(n){e&&ic.call(e,n)||t.delete(n)}),e){var n=e.__META,r=(0,en._T)(e,["__META"]);Object.keys(r).forEach(function(e){t.merge(e,r[e])}),n&&n.extraRootIds.forEach(this.retain,this)}},e.prototype.retain=function(e){return this.rootIds[e]=(this.rootIds[e]||0)+1},e.prototype.release=function(e){if(this.rootIds[e]>0){var t=--this.rootIds[e];return t||delete this.rootIds[e],t}return 0},e.prototype.getRootIdSet=function(e){return void 0===e&&(e=new Set),Object.keys(this.rootIds).forEach(e.add,e),this instanceof iL?this.parent.getRootIdSet(e):Object.keys(this.policies.rootTypenamesById).forEach(e.add,e),e},e.prototype.gc=function(){var e=this,t=this.getRootIdSet(),n=this.toObject();t.forEach(function(r){ic.call(n,r)&&(Object.keys(e.findChildRefIds(r)).forEach(t.add,t),delete n[r])});var r=Object.keys(n);if(r.length){for(var i=this;i instanceof iL;)i=i.parent;r.forEach(function(e){return i.delete(e)})}return r},e.prototype.findChildRefIds=function(e){if(!ic.call(this.refs,e)){var t=this.refs[e]=Object.create(null),n=this.data[e];if(!n)return t;var r=new Set([n]);r.forEach(function(e){eD(e)&&(t[e.__ref]=!0),(0,eO.s)(e)&&Object.keys(e).forEach(function(t){var n=e[t];(0,eO.s)(n)&&r.add(n)})})}return this.refs[e]},e.prototype.makeCacheKey=function(){return this.group.keyMaker.lookupArray(arguments)},e}(),iM=function(){function e(e,t){void 0===t&&(t=null),this.caching=e,this.parent=t,this.d=null,this.resetCaching()}return e.prototype.resetCaching=function(){this.d=this.caching?rW():null,this.keyMaker=new n_(t_.mr)},e.prototype.depend=function(e,t){if(this.d){this.d(iO(e,t));var n=iv(t);n!==t&&this.d(iO(e,n)),this.parent&&this.parent.depend(e,t)}},e.prototype.dirty=function(e,t){this.d&&this.d.dirty(iO(e,t),"__exists"===t?"forget":"setDirty")},e}();function iO(e,t){return t+"#"+e}function iA(e,t){iD(e)&&e.group.depend(t,"__exists")}!function(e){var t=function(e){function t(t){var n=t.policies,r=t.resultCaching,i=void 0===r||r,a=t.seed,o=e.call(this,n,new iM(i))||this;return o.stump=new iC(o),o.storageTrie=new n_(t_.mr),a&&o.replace(a),o}return(0,en.ZT)(t,e),t.prototype.addLayer=function(e,t){return this.stump.addLayer(e,t)},t.prototype.removeLayer=function(){return this},t.prototype.getStorage=function(){return this.storageTrie.lookupArray(arguments)},t}(e);e.Root=t}(iT||(iT={}));var iL=function(e){function t(t,n,r,i){var a=e.call(this,n.policies,i)||this;return a.id=t,a.parent=n,a.replay=r,a.group=i,r(a),a}return(0,en.ZT)(t,e),t.prototype.addLayer=function(e,n){return new t(e,this,n,this.group)},t.prototype.removeLayer=function(e){var t=this,n=this.parent.removeLayer(e);return e===this.id?(this.group.caching&&Object.keys(this.data).forEach(function(e){var r=t.data[e],i=n.lookup(e);i?r?r!==i&&Object.keys(r).forEach(function(n){(0,nm.D)(r[n],i[n])||t.group.dirty(e,n)}):(t.group.dirty(e,"__exists"),Object.keys(i).forEach(function(n){t.group.dirty(e,n)})):t.delete(e)}),n):n===this.parent?this:n.addLayer(this.id,this.replay)},t.prototype.toObject=function(){return(0,en.pi)((0,en.pi)({},this.parent.toObject()),this.data)},t.prototype.findChildRefIds=function(t){var n=this.parent.findChildRefIds(t);return ic.call(this.data,t)?(0,en.pi)((0,en.pi)({},n),e.prototype.findChildRefIds.call(this,t)):n},t.prototype.getStorage=function(){for(var e=this.parent;e.parent;)e=e.parent;return e.getStorage.apply(e,arguments)},t}(iT),iC=function(e){function t(t){return e.call(this,"EntityStore.Stump",t,function(){},new iM(t.group.caching,t.group))||this}return(0,en.ZT)(t,e),t.prototype.removeLayer=function(){return this},t.prototype.merge=function(){return this.parent.merge.apply(this.parent,arguments)},t}(iL);function iI(e,t,n){var r=e[n],i=t[n];return(0,nm.D)(r,i)?r:i}function iD(e){return!!(e instanceof iT&&e.group.caching)}function iN(e){return[e.selectionSet,e.objectOrReference,e.context,e.context.canonizeResults,]}var iP=function(){function e(e){var t=this;this.knownResults=new(t_.mr?WeakMap:Map),this.config=(0,n1.o)(e,{addTypename:!1!==e.addTypename,canonizeResults:ib(e)}),this.canon=e.canon||new nk,this.executeSelectionSet=rZ(function(e){var n,r=e.context.canonizeResults,i=iN(e);i[3]=!r;var a=(n=t.executeSelectionSet).peek.apply(n,i);return a?r?(0,en.pi)((0,en.pi)({},a),{result:t.canon.admit(a.result)}):a:(iA(e.context.store,e.enclosingRef.__ref),t.execSelectionSetImpl(e))},{max:this.config.resultCacheMaxSize,keyArgs:iN,makeCacheKey:function(e,t,n,r){if(iD(n.store))return n.store.makeCacheKey(e,eD(t)?t.__ref:t,n.varString,r)}}),this.executeSubSelectedArray=rZ(function(e){return iA(e.context.store,e.enclosingRef.__ref),t.execSubSelectedArrayImpl(e)},{max:this.config.resultCacheMaxSize,makeCacheKey:function(e){var t=e.field,n=e.array,r=e.context;if(iD(r.store))return r.store.makeCacheKey(t,n,r.varString)}})}return e.prototype.resetCanon=function(){this.canon=new nk},e.prototype.diffQueryAgainstStore=function(e){var t,n=e.store,r=e.query,i=e.rootId,a=void 0===i?"ROOT_QUERY":i,o=e.variables,s=e.returnPartialData,u=void 0===s||s,c=e.canonizeResults,l=void 0===c?this.config.canonizeResults:c,f=this.config.cache.policies;o=(0,en.pi)((0,en.pi)({},e9(e6(r))),o);var d=eI(a),h=this.executeSelectionSet({selectionSet:e8(r).selectionSet,objectOrReference:d,enclosingRef:d,context:(0,en.pi)({store:n,query:r,policies:f,variables:o,varString:nx(o),canonizeResults:l},iE(r,this.config.fragments))});if(h.missing&&(t=[new is(iR(h.missing),h.missing,r,o)],!u))throw t[0];return{result:h.result,complete:!t,missing:t}},e.prototype.isFresh=function(e,t,n,r){if(iD(r.store)&&this.knownResults.get(e)===n){var i=this.executeSelectionSet.peek(n,t,r,this.canon.isKnown(e));if(i&&e===i.result)return!0}return!1},e.prototype.execSelectionSetImpl=function(e){var t,n=this,r=e.selectionSet,i=e.objectOrReference,a=e.enclosingRef,o=e.context;if(eD(i)&&!o.policies.rootTypenamesById[i.__ref]&&!o.store.has(i.__ref))return{result:this.canon.empty,missing:"Dangling reference to missing ".concat(i.__ref," object")};var s=o.variables,u=o.policies,c=o.store.getFieldValue(i,"__typename"),l=[],f=new tB;function d(e,n){var r;return e.missing&&(t=f.merge(t,((r={})[n]=e.missing,r))),e.result}this.config.addTypename&&"string"==typeof c&&!u.rootIdsByTypename[c]&&l.push({__typename:c});var h=new Set(r.selections);h.forEach(function(e){var r,p;if(td(e,s)){if(eQ(e)){var b=u.readField({fieldName:e.name.value,field:e,variables:o.variables,from:i},o),m=eX(e);void 0===b?nj.added(e)||(t=f.merge(t,((r={})[m]="Can't find field '".concat(e.name.value,"' on ").concat(eD(i)?i.__ref+" object":"object "+JSON.stringify(i,null,2)),r))):(0,tP.k)(b)?b=d(n.executeSubSelectedArray({field:e,array:b,enclosingRef:a,context:o}),m):e.selectionSet?null!=b&&(b=d(n.executeSelectionSet({selectionSet:e.selectionSet,objectOrReference:b,enclosingRef:eD(b)?b:a,context:o}),m)):o.canonizeResults&&(b=n.canon.pass(b)),void 0!==b&&l.push(((p={})[m]=b,p))}else{var g=eC(e,o.lookupFragment);if(!g&&e.kind===nL.h.FRAGMENT_SPREAD)throw __DEV__?new Q.ej("No fragment named ".concat(e.name.value)):new Q.ej(5);g&&u.fragmentMatches(g,c)&&g.selectionSet.selections.forEach(h.add,h)}}});var p={result:tF(l),missing:t},b=o.canonizeResults?this.canon.admit(p):(0,iu.J)(p);return b.result&&this.knownResults.set(b.result,r),b},e.prototype.execSubSelectedArrayImpl=function(e){var t,n=this,r=e.field,i=e.array,a=e.enclosingRef,o=e.context,s=new tB;function u(e,n){var r;return e.missing&&(t=s.merge(t,((r={})[n]=e.missing,r))),e.result}return r.selectionSet&&(i=i.filter(o.store.canRead)),i=i.map(function(e,t){return null===e?null:(0,tP.k)(e)?u(n.executeSubSelectedArray({field:r,array:e,enclosingRef:a,context:o}),t):r.selectionSet?u(n.executeSelectionSet({selectionSet:r.selectionSet,objectOrReference:e,enclosingRef:eD(e)?e:a,context:o}),t):(__DEV__&&ij(o.store,r,e),e)}),{result:o.canonizeResults?this.canon.admit(i):i,missing:t}},e}();function iR(e){try{JSON.stringify(e,function(e,t){if("string"==typeof t)throw t;return t})}catch(t){return t}}function ij(e,t,n){if(!t.selectionSet){var r=new Set([n]);r.forEach(function(n){(0,eO.s)(n)&&(__DEV__?(0,Q.kG)(!eD(n),"Missing selection set for object of type ".concat(im(e,n)," returned for query field ").concat(t.name.value)):(0,Q.kG)(!eD(n),6),Object.values(n).forEach(r.add,r))})}}function iF(e){var t=nG("stringifyForDisplay");return JSON.stringify(e,function(e,n){return void 0===n?t:n}).split(JSON.stringify(t)).join("")}var iY=Object.create(null);function iB(e){var t=JSON.stringify(e);return iY[t]||(iY[t]=Object.create(null))}function iU(e){var t=iB(e);return t.keyFieldsFn||(t.keyFieldsFn=function(t,n){var r=function(e,t){return n.readField(t,e)},i=n.keyObject=i$(e,function(e){var i=iW(n.storeObject,e,r);return void 0===i&&t!==n.storeObject&&ic.call(t,e[0])&&(i=iW(t,e,iG)),__DEV__?(0,Q.kG)(void 0!==i,"Missing field '".concat(e.join("."),"' while extracting keyFields from ").concat(JSON.stringify(t))):(0,Q.kG)(void 0!==i,2),i});return"".concat(n.typename,":").concat(JSON.stringify(i))})}function iH(e){var t=iB(e);return t.keyArgsFn||(t.keyArgsFn=function(t,n){var r=n.field,i=n.variables,a=n.fieldName,o=JSON.stringify(i$(e,function(e){var n=e[0],a=n.charAt(0);if("@"===a){if(r&&(0,tP.O)(r.directives)){var o=n.slice(1),s=r.directives.find(function(e){return e.name.value===o}),u=s&&eZ(s,i);return u&&iW(u,e.slice(1))}return}if("$"===a){var c=n.slice(1);if(i&&ic.call(i,c)){var l=e.slice(0);return l[0]=c,iW(i,l)}return}if(t)return iW(t,e)}));return(t||"{}"!==o)&&(a+=":"+o),a})}function i$(e,t){var n=new tB;return iz(e).reduce(function(e,r){var i,a=t(r);if(void 0!==a){for(var o=r.length-1;o>=0;--o)a=((i={})[r[o]]=a,i);e=n.merge(e,a)}return e},Object.create(null))}function iz(e){var t=iB(e);if(!t.paths){var n=t.paths=[],r=[];e.forEach(function(t,i){(0,tP.k)(t)?(iz(t).forEach(function(e){return n.push(r.concat(e))}),r.length=0):(r.push(t),(0,tP.k)(e[i+1])||(n.push(r.slice(0)),r.length=0))})}return t.paths}function iG(e,t){return e[t]}function iW(e,t,n){return n=n||iG,iK(t.reduce(function e(t,r){return(0,tP.k)(t)?t.map(function(t){return e(t,r)}):t&&n(t,r)},e))}function iK(e){return(0,eO.s)(e)?(0,tP.k)(e)?e.map(iK):i$(Object.keys(e).sort(),function(t){return iW(e,t)}):e}function iV(e){return void 0!==e.args?e.args:e.field?eZ(e.field,e.variables):null}eK.setStringify(nx);var iq=function(){},iZ=function(e,t){return t.fieldName},iX=function(e,t,n){return(0,n.mergeObjects)(e,t)},iJ=function(e,t){return t},iQ=function(){function e(e){this.config=e,this.typePolicies=Object.create(null),this.toBeAdded=Object.create(null),this.supertypeMap=new Map,this.fuzzySubtypes=new Map,this.rootIdsByTypename=Object.create(null),this.rootTypenamesById=Object.create(null),this.usingPossibleTypes=!1,this.config=(0,en.pi)({dataIdFromObject:id},e),this.cache=this.config.cache,this.setRootTypename("Query"),this.setRootTypename("Mutation"),this.setRootTypename("Subscription"),e.possibleTypes&&this.addPossibleTypes(e.possibleTypes),e.typePolicies&&this.addTypePolicies(e.typePolicies)}return e.prototype.identify=function(e,t){var n,r,i=this,a=t&&(t.typename||(null===(n=t.storeObject)||void 0===n?void 0:n.__typename))||e.__typename;if(a===this.rootTypenamesById.ROOT_QUERY)return["ROOT_QUERY"];for(var o=t&&t.storeObject||e,s=(0,en.pi)((0,en.pi)({},t),{typename:a,storeObject:o,readField:t&&t.readField||function(){var e=i0(arguments,o);return i.readField(e,{store:i.cache.data,variables:e.variables})}}),u=a&&this.getTypePolicy(a),c=u&&u.keyFn||this.config.dataIdFromObject;c;){var l=c((0,en.pi)((0,en.pi)({},e),o),s);if((0,tP.k)(l))c=iU(l);else{r=l;break}}return r=r?String(r):void 0,s.keyObject?[r,s.keyObject]:[r]},e.prototype.addTypePolicies=function(e){var t=this;Object.keys(e).forEach(function(n){var r=e[n],i=r.queryType,a=r.mutationType,o=r.subscriptionType,s=(0,en._T)(r,["queryType","mutationType","subscriptionType"]);i&&t.setRootTypename("Query",n),a&&t.setRootTypename("Mutation",n),o&&t.setRootTypename("Subscription",n),ic.call(t.toBeAdded,n)?t.toBeAdded[n].push(s):t.toBeAdded[n]=[s]})},e.prototype.updateTypePolicy=function(e,t){var n=this,r=this.getTypePolicy(e),i=t.keyFields,a=t.fields;function o(e,t){e.merge="function"==typeof t?t:!0===t?iX:!1===t?iJ:e.merge}o(r,t.merge),r.keyFn=!1===i?iq:(0,tP.k)(i)?iU(i):"function"==typeof i?i:r.keyFn,a&&Object.keys(a).forEach(function(t){var r=n.getFieldPolicy(e,t,!0),i=a[t];if("function"==typeof i)r.read=i;else{var s=i.keyArgs,u=i.read,c=i.merge;r.keyFn=!1===s?iZ:(0,tP.k)(s)?iH(s):"function"==typeof s?s:r.keyFn,"function"==typeof u&&(r.read=u),o(r,c)}r.read&&r.merge&&(r.keyFn=r.keyFn||iZ)})},e.prototype.setRootTypename=function(e,t){void 0===t&&(t=e);var n="ROOT_"+e.toUpperCase(),r=this.rootTypenamesById[n];t!==r&&(__DEV__?(0,Q.kG)(!r||r===e,"Cannot change root ".concat(e," __typename more than once")):(0,Q.kG)(!r||r===e,3),r&&delete this.rootIdsByTypename[r],this.rootIdsByTypename[t]=n,this.rootTypenamesById[n]=t)},e.prototype.addPossibleTypes=function(e){var t=this;this.usingPossibleTypes=!0,Object.keys(e).forEach(function(n){t.getSupertypeSet(n,!0),e[n].forEach(function(e){t.getSupertypeSet(e,!0).add(n);var r=e.match(ig);r&&r[0]===e||t.fuzzySubtypes.set(e,RegExp(e))})})},e.prototype.getTypePolicy=function(e){var t=this;if(!ic.call(this.typePolicies,e)){var n=this.typePolicies[e]=Object.create(null);n.fields=Object.create(null);var r=this.supertypeMap.get(e);r&&r.size&&r.forEach(function(e){var r=t.getTypePolicy(e),i=r.fields;Object.assign(n,(0,en._T)(r,["fields"])),Object.assign(n.fields,i)})}var i=this.toBeAdded[e];return i&&i.length&&i.splice(0).forEach(function(n){t.updateTypePolicy(e,n)}),this.typePolicies[e]},e.prototype.getFieldPolicy=function(e,t,n){if(e){var r=this.getTypePolicy(e).fields;return r[t]||n&&(r[t]=Object.create(null))}},e.prototype.getSupertypeSet=function(e,t){var n=this.supertypeMap.get(e);return!n&&t&&this.supertypeMap.set(e,n=new Set),n},e.prototype.fragmentMatches=function(e,t,n,r){var i=this;if(!e.typeCondition)return!0;if(!t)return!1;var a=e.typeCondition.name.value;if(t===a)return!0;if(this.usingPossibleTypes&&this.supertypeMap.has(a))for(var o=this.getSupertypeSet(t,!0),s=[o],u=function(e){var t=i.getSupertypeSet(e,!1);t&&t.size&&0>s.indexOf(t)&&s.push(t)},c=!!(n&&this.fuzzySubtypes.size),l=!1,f=0;f1?a:t}:(r=(0,en.pi)({},i),ic.call(r,"from")||(r.from=t)),__DEV__&&void 0===r.from&&__DEV__&&Q.kG.warn("Undefined 'from' passed to readField with arguments ".concat(iF(Array.from(e)))),void 0===r.variables&&(r.variables=n),r}function i2(e){return function(t,n){if((0,tP.k)(t)||(0,tP.k)(n))throw __DEV__?new Q.ej("Cannot automatically merge arrays"):new Q.ej(4);if((0,eO.s)(t)&&(0,eO.s)(n)){var r=e.getFieldValue(t,"__typename"),i=e.getFieldValue(n,"__typename");if(r&&i&&r!==i)return n;if(eD(t)&&iw(n))return e.merge(t.__ref,n),t;if(iw(t)&&eD(n))return e.merge(t,n.__ref),n;if(iw(t)&&iw(n))return(0,en.pi)((0,en.pi)({},t),n)}return n}}function i3(e,t,n){var r="".concat(t).concat(n),i=e.flavors.get(r);return i||e.flavors.set(r,i=e.clientOnly===t&&e.deferred===n?e:(0,en.pi)((0,en.pi)({},e),{clientOnly:t,deferred:n})),i}var i4=function(){function e(e,t,n){this.cache=e,this.reader=t,this.fragments=n}return e.prototype.writeToStore=function(e,t){var n=this,r=t.query,i=t.result,a=t.dataId,o=t.variables,s=t.overwrite,u=e2(r),c=i_();o=(0,en.pi)((0,en.pi)({},e9(u)),o);var l=(0,en.pi)((0,en.pi)({store:e,written:Object.create(null),merge:function(e,t){return c.merge(e,t)},variables:o,varString:nx(o)},iE(r,this.fragments)),{overwrite:!!s,incomingById:new Map,clientOnly:!1,deferred:!1,flavors:new Map}),f=this.processSelectionSet({result:i||Object.create(null),dataId:a,selectionSet:u.selectionSet,mergeTree:{map:new Map},context:l});if(!eD(f))throw __DEV__?new Q.ej("Could not identify object ".concat(JSON.stringify(i))):new Q.ej(7);return l.incomingById.forEach(function(t,r){var i=t.storeObject,a=t.mergeTree,o=t.fieldNodeSet,s=eI(r);if(a&&a.map.size){var u=n.applyMerges(a,s,i,l);if(eD(u))return;i=u}if(__DEV__&&!l.overwrite){var c=Object.create(null);o.forEach(function(e){e.selectionSet&&(c[e.name.value]=!0)});var f=function(e){return!0===c[iv(e)]},d=function(e){var t=a&&a.map.get(e);return Boolean(t&&t.info&&t.info.merge)};Object.keys(i).forEach(function(e){f(e)&&!d(e)&&at(s,i,e,l.store)})}e.merge(r,i)}),e.retain(f.__ref),f},e.prototype.processSelectionSet=function(e){var t=this,n=e.dataId,r=e.result,i=e.selectionSet,a=e.context,o=e.mergeTree,s=this.cache.policies,u=Object.create(null),c=n&&s.rootTypenamesById[n]||eJ(r,i,a.fragmentMap)||n&&a.store.get(n,"__typename");"string"==typeof c&&(u.__typename=c);var l=function(){var e=i0(arguments,u,a.variables);if(eD(e.from)){var t=a.incomingById.get(e.from.__ref);if(t){var n=s.readField((0,en.pi)((0,en.pi)({},e),{from:t.storeObject}),a);if(void 0!==n)return n}}return s.readField(e,a)},f=new Set;this.flattenFields(i,r,a,c).forEach(function(e,n){var i,a=r[eX(n)];if(f.add(n),void 0!==a){var d=s.getStoreFieldName({typename:c,fieldName:n.name.value,field:n,variables:e.variables}),h=i5(o,d),p=t.processFieldValue(a,n,n.selectionSet?i3(e,!1,!1):e,h),b=void 0;n.selectionSet&&(eD(p)||iw(p))&&(b=l("__typename",p));var m=s.getMergeFunction(c,n.name.value,b);m?h.info={field:n,typename:c,merge:m}:i7(o,d),u=e.merge(u,((i={})[d]=p,i))}else __DEV__&&!e.clientOnly&&!e.deferred&&!nj.added(n)&&!s.getReadFunction(c,n.name.value)&&__DEV__&&Q.kG.error("Missing field '".concat(eX(n),"' while writing result ").concat(JSON.stringify(r,null,2)).substring(0,1e3))});try{var d=s.identify(r,{typename:c,selectionSet:i,fragmentMap:a.fragmentMap,storeObject:u,readField:l}),h=d[0],p=d[1];n=n||h,p&&(u=a.merge(u,p))}catch(b){if(!n)throw b}if("string"==typeof n){var m=eI(n),g=a.written[n]||(a.written[n]=[]);if(g.indexOf(i)>=0||(g.push(i),this.reader&&this.reader.isFresh(r,m,i,a)))return m;var v=a.incomingById.get(n);return v?(v.storeObject=a.merge(v.storeObject,u),v.mergeTree=i8(v.mergeTree,o),f.forEach(function(e){return v.fieldNodeSet.add(e)})):a.incomingById.set(n,{storeObject:u,mergeTree:i9(o)?void 0:o,fieldNodeSet:f}),m}return u},e.prototype.processFieldValue=function(e,t,n,r){var i=this;return t.selectionSet&&null!==e?(0,tP.k)(e)?e.map(function(e,a){var o=i.processFieldValue(e,t,n,i5(r,a));return i7(r,a),o}):this.processSelectionSet({result:e,selectionSet:t.selectionSet,context:n,mergeTree:r}):__DEV__?nJ(e):e},e.prototype.flattenFields=function(e,t,n,r){void 0===r&&(r=eJ(t,e,n.fragmentMap));var i=new Map,a=this.cache.policies,o=new n_(!1);return function e(s,u){var c=o.lookup(s,u.clientOnly,u.deferred);c.visited||(c.visited=!0,s.selections.forEach(function(o){if(td(o,n.variables)){var s=u.clientOnly,c=u.deferred;if(!(s&&c)&&(0,tP.O)(o.directives)&&o.directives.forEach(function(e){var t=e.name.value;if("client"===t&&(s=!0),"defer"===t){var r=eZ(e,n.variables);r&&!1===r.if||(c=!0)}}),eQ(o)){var l=i.get(o);l&&(s=s&&l.clientOnly,c=c&&l.deferred),i.set(o,i3(n,s,c))}else{var f=eC(o,n.lookupFragment);if(!f&&o.kind===nL.h.FRAGMENT_SPREAD)throw __DEV__?new Q.ej("No fragment named ".concat(o.name.value)):new Q.ej(8);f&&a.fragmentMatches(f,r,t,n.variables)&&e(f.selectionSet,i3(n,s,c))}}}))}(e,n),i},e.prototype.applyMerges=function(e,t,n,r,i){var a=this;if(e.map.size&&!eD(n)){var o,s,u=!(0,tP.k)(n)&&(eD(t)||iw(t))?t:void 0,c=n;u&&!i&&(i=[eD(u)?u.__ref:u]);var l=function(e,t){return(0,tP.k)(e)?"number"==typeof t?e[t]:void 0:r.store.getFieldValue(e,String(t))};e.map.forEach(function(e,t){var n=l(u,t),o=l(c,t);if(void 0!==o){i&&i.push(t);var f=a.applyMerges(e,n,o,r,i);f!==o&&(s=s||new Map).set(t,f),i&&(0,Q.kG)(i.pop()===t)}}),s&&(n=(0,tP.k)(c)?c.slice(0):(0,en.pi)({},c),s.forEach(function(e,t){n[t]=e}))}return e.info?this.cache.policies.runMergeFunction(t,n,e.info,r,i&&(o=r.store).getStorage.apply(o,i)):n},e}(),i6=[];function i5(e,t){var n=e.map;return n.has(t)||n.set(t,i6.pop()||{map:new Map}),n.get(t)}function i8(e,t){if(e===t||!t||i9(t))return e;if(!e||i9(e))return t;var n=e.info&&t.info?(0,en.pi)((0,en.pi)({},e.info),t.info):e.info||t.info,r=e.map.size&&t.map.size,i=r?new Map:e.map.size?e.map:t.map,a={info:n,map:i};if(r){var o=new Set(t.map.keys());e.map.forEach(function(e,n){a.map.set(n,i8(e,t.map.get(n))),o.delete(n)}),o.forEach(function(n){a.map.set(n,i8(t.map.get(n),e.map.get(n)))})}return a}function i9(e){return!e||!(e.info||e.map.size)}function i7(e,t){var n=e.map,r=n.get(t);r&&i9(r)&&(i6.push(r),n.delete(t))}var ae=new Set;function at(e,t,n,r){var i=function(e){var t=r.getFieldValue(e,n);return"object"==typeof t&&t},a=i(e);if(a){var o=i(t);if(!(!o||eD(a)||(0,nm.D)(a,o)||Object.keys(a).every(function(e){return void 0!==r.getFieldValue(o,e)}))){var s=r.getFieldValue(e,"__typename")||r.getFieldValue(t,"__typename"),u=iv(n),c="".concat(s,".").concat(u);if(!ae.has(c)){ae.add(c);var l=[];(0,tP.k)(a)||(0,tP.k)(o)||[a,o].forEach(function(e){var t=r.getFieldValue(e,"__typename");"string"!=typeof t||l.includes(t)||l.push(t)}),__DEV__&&Q.kG.warn("Cache data may be lost when replacing the ".concat(u," field of a ").concat(s," object.\n\nThis could cause additional (usually avoidable) network requests to fetch data that were otherwise cached.\n\nTo address this problem (which is not a bug in Apollo Client), ").concat(l.length?"either ensure all objects of type "+l.join(" and ")+" have an ID or a custom merge function, or ":"","define a custom merge function for the ").concat(c," field, so InMemoryCache can safely merge these objects:\n\n existing: ").concat(JSON.stringify(a).slice(0,1e3),"\n incoming: ").concat(JSON.stringify(o).slice(0,1e3),"\n\nFor more information about these options, please refer to the documentation:\n\n * Ensuring entity objects have IDs: https://go.apollo.dev/c/generating-unique-identifiers\n * Defining custom merge functions: https://go.apollo.dev/c/merging-non-normalized-objects\n"))}}}}var an=function(e){function t(t){void 0===t&&(t={});var n=e.call(this)||this;return n.watches=new Set,n.typenameDocumentCache=new Map,n.makeVar=r2,n.txCount=0,n.config=ip(t),n.addTypename=!!n.config.addTypename,n.policies=new iQ({cache:n,dataIdFromObject:n.config.dataIdFromObject,possibleTypes:n.config.possibleTypes,typePolicies:n.config.typePolicies}),n.init(),n}return(0,en.ZT)(t,e),t.prototype.init=function(){var e=this.data=new iT.Root({policies:this.policies,resultCaching:this.config.resultCaching});this.optimisticData=e.stump,this.resetResultCache()},t.prototype.resetResultCache=function(e){var t=this,n=this.storeReader,r=this.config.fragments;this.storeWriter=new i4(this,this.storeReader=new iP({cache:this,addTypename:this.addTypename,resultCacheMaxSize:this.config.resultCacheMaxSize,canonizeResults:ib(this.config),canon:e?void 0:n&&n.canon,fragments:r}),r),this.maybeBroadcastWatch=rZ(function(e,n){return t.broadcastWatch(e,n)},{max:this.config.resultCacheMaxSize,makeCacheKey:function(e){var n=e.optimistic?t.optimisticData:t.data;if(iD(n)){var r=e.optimistic,i=e.id,a=e.variables;return n.makeCacheKey(e.query,e.callback,nx({optimistic:r,id:i,variables:a}))}}}),new Set([this.data.group,this.optimisticData.group,]).forEach(function(e){return e.resetCaching()})},t.prototype.restore=function(e){return this.init(),e&&this.data.replace(e),this},t.prototype.extract=function(e){return void 0===e&&(e=!1),(e?this.optimisticData:this.data).extract()},t.prototype.read=function(e){var t=e.returnPartialData,n=void 0!==t&&t;try{return this.storeReader.diffQueryAgainstStore((0,en.pi)((0,en.pi)({},e),{store:e.optimistic?this.optimisticData:this.data,config:this.config,returnPartialData:n})).result||null}catch(r){if(r instanceof is)return null;throw r}},t.prototype.write=function(e){try{return++this.txCount,this.storeWriter.writeToStore(this.data,e)}finally{--this.txCount||!1===e.broadcast||this.broadcastWatches()}},t.prototype.modify=function(e){if(ic.call(e,"id")&&!e.id)return!1;var t=e.optimistic?this.optimisticData:this.data;try{return++this.txCount,t.modify(e.id||"ROOT_QUERY",e.fields)}finally{--this.txCount||!1===e.broadcast||this.broadcastWatches()}},t.prototype.diff=function(e){return this.storeReader.diffQueryAgainstStore((0,en.pi)((0,en.pi)({},e),{store:e.optimistic?this.optimisticData:this.data,rootId:e.id||"ROOT_QUERY",config:this.config}))},t.prototype.watch=function(e){var t=this;return this.watches.size||r0(this),this.watches.add(e),e.immediate&&this.maybeBroadcastWatch(e),function(){t.watches.delete(e)&&!t.watches.size&&r1(t),t.maybeBroadcastWatch.forget(e)}},t.prototype.gc=function(e){nx.reset();var t=this.optimisticData.gc();return e&&!this.txCount&&(e.resetResultCache?this.resetResultCache(e.resetResultIdentities):e.resetResultIdentities&&this.storeReader.resetCanon()),t},t.prototype.retain=function(e,t){return(t?this.optimisticData:this.data).retain(e)},t.prototype.release=function(e,t){return(t?this.optimisticData:this.data).release(e)},t.prototype.identify=function(e){if(eD(e))return e.__ref;try{return this.policies.identify(e)[0]}catch(t){__DEV__&&Q.kG.warn(t)}},t.prototype.evict=function(e){if(!e.id){if(ic.call(e,"id"))return!1;e=(0,en.pi)((0,en.pi)({},e),{id:"ROOT_QUERY"})}try{return++this.txCount,this.optimisticData.evict(e,this.data)}finally{--this.txCount||!1===e.broadcast||this.broadcastWatches()}},t.prototype.reset=function(e){var t=this;return this.init(),nx.reset(),e&&e.discardWatches?(this.watches.forEach(function(e){return t.maybeBroadcastWatch.forget(e)}),this.watches.clear(),r1(this)):this.broadcastWatches(),Promise.resolve()},t.prototype.removeOptimistic=function(e){var t=this.optimisticData.removeLayer(e);t!==this.optimisticData&&(this.optimisticData=t,this.broadcastWatches())},t.prototype.batch=function(e){var t,n=this,r=e.update,i=e.optimistic,a=void 0===i||i,o=e.removeOptimistic,s=e.onWatchUpdated,u=function(e){var i=n,a=i.data,o=i.optimisticData;++n.txCount,e&&(n.data=n.optimisticData=e);try{return t=r(n)}finally{--n.txCount,n.data=a,n.optimisticData=o}},c=new Set;return s&&!this.txCount&&this.broadcastWatches((0,en.pi)((0,en.pi)({},e),{onWatchUpdated:function(e){return c.add(e),!1}})),"string"==typeof a?this.optimisticData=this.optimisticData.addLayer(a,u):!1===a?u(this.data):u(),"string"==typeof o&&(this.optimisticData=this.optimisticData.removeLayer(o)),s&&c.size?(this.broadcastWatches((0,en.pi)((0,en.pi)({},e),{onWatchUpdated:function(e,t){var n=s.call(this,e,t);return!1!==n&&c.delete(e),n}})),c.size&&c.forEach(function(e){return n.maybeBroadcastWatch.dirty(e)})):this.broadcastWatches(e),t},t.prototype.performTransaction=function(e,t){return this.batch({update:e,optimistic:t||null!==t})},t.prototype.transformDocument=function(e){if(this.addTypename){var t=this.typenameDocumentCache.get(e);return t||(t=nj(e),this.typenameDocumentCache.set(e,t),this.typenameDocumentCache.set(t,t)),t}return e},t.prototype.transformForLink=function(e){var t=this.config.fragments;return t?t.transform(e):e},t.prototype.broadcastWatches=function(e){var t=this;this.txCount||this.watches.forEach(function(n){return t.maybeBroadcastWatch(n,e)})},t.prototype.broadcastWatch=function(e,t){var n=e.lastDiff,r=this.diff(e);(!t||(e.optimistic&&"string"==typeof t.optimistic&&(r.fromOptimisticTransaction=!0),!t.onWatchUpdated||!1!==t.onWatchUpdated.call(this,e,r,n)))&&(n&&(0,nm.D)(n.result,r.result)||e.callback(e.lastDiff=r,n))},t}(io),ar={possibleTypes:{ApproveJobProposalSpecPayload:["ApproveJobProposalSpecSuccess","JobAlreadyExistsError","NotFoundError"],BridgePayload:["Bridge","NotFoundError"],CancelJobProposalSpecPayload:["CancelJobProposalSpecSuccess","NotFoundError"],ChainPayload:["Chain","NotFoundError"],CreateAPITokenPayload:["CreateAPITokenSuccess","InputErrors"],CreateBridgePayload:["CreateBridgeSuccess"],CreateCSAKeyPayload:["CSAKeyExistsError","CreateCSAKeySuccess"],CreateFeedsManagerChainConfigPayload:["CreateFeedsManagerChainConfigSuccess","InputErrors","NotFoundError"],CreateFeedsManagerPayload:["CreateFeedsManagerSuccess","DuplicateFeedsManagerError","InputErrors","NotFoundError","SingleFeedsManagerError"],CreateJobPayload:["CreateJobSuccess","InputErrors"],CreateOCR2KeyBundlePayload:["CreateOCR2KeyBundleSuccess"],CreateOCRKeyBundlePayload:["CreateOCRKeyBundleSuccess"],CreateP2PKeyPayload:["CreateP2PKeySuccess"],DeleteAPITokenPayload:["DeleteAPITokenSuccess","InputErrors"],DeleteBridgePayload:["DeleteBridgeConflictError","DeleteBridgeInvalidNameError","DeleteBridgeSuccess","NotFoundError"],DeleteCSAKeyPayload:["DeleteCSAKeySuccess","NotFoundError"],DeleteFeedsManagerChainConfigPayload:["DeleteFeedsManagerChainConfigSuccess","NotFoundError"],DeleteJobPayload:["DeleteJobSuccess","NotFoundError"],DeleteOCR2KeyBundlePayload:["DeleteOCR2KeyBundleSuccess","NotFoundError"],DeleteOCRKeyBundlePayload:["DeleteOCRKeyBundleSuccess","NotFoundError"],DeleteP2PKeyPayload:["DeleteP2PKeySuccess","NotFoundError"],DeleteVRFKeyPayload:["DeleteVRFKeySuccess","NotFoundError"],DisableFeedsManagerPayload:["DisableFeedsManagerSuccess","NotFoundError"],DismissJobErrorPayload:["DismissJobErrorSuccess","NotFoundError"],EnableFeedsManagerPayload:["EnableFeedsManagerSuccess","NotFoundError"],Error:["CSAKeyExistsError","DeleteBridgeConflictError","DeleteBridgeInvalidNameError","DuplicateFeedsManagerError","InputError","JobAlreadyExistsError","NotFoundError","RunJobCannotRunError","SingleFeedsManagerError"],EthTransactionPayload:["EthTransaction","NotFoundError"],FeaturesPayload:["Features"],FeedsManagerPayload:["FeedsManager","NotFoundError"],GetSQLLoggingPayload:["SQLLogging"],GlobalLogLevelPayload:["GlobalLogLevel"],JobPayload:["Job","NotFoundError"],JobProposalPayload:["JobProposal","NotFoundError"],JobRunPayload:["JobRun","NotFoundError"],JobSpec:["BlockHeaderFeederSpec","BlockhashStoreSpec","BootstrapSpec","CronSpec","DirectRequestSpec","FluxMonitorSpec","GatewaySpec","KeeperSpec","OCR2Spec","OCRSpec","StandardCapabilitiesSpec","StreamSpec","VRFSpec","WebhookSpec","WorkflowSpec"],NodePayload:["Node","NotFoundError"],PaginatedPayload:["BridgesPayload","ChainsPayload","EthTransactionAttemptsPayload","EthTransactionsPayload","JobRunsPayload","JobsPayload","NodesPayload"],RejectJobProposalSpecPayload:["NotFoundError","RejectJobProposalSpecSuccess"],RunJobPayload:["NotFoundError","RunJobCannotRunError","RunJobSuccess"],SetGlobalLogLevelPayload:["InputErrors","SetGlobalLogLevelSuccess"],SetSQLLoggingPayload:["SetSQLLoggingSuccess"],SetServicesLogLevelsPayload:["InputErrors","SetServicesLogLevelsSuccess"],UpdateBridgePayload:["NotFoundError","UpdateBridgeSuccess"],UpdateFeedsManagerChainConfigPayload:["InputErrors","NotFoundError","UpdateFeedsManagerChainConfigSuccess"],UpdateFeedsManagerPayload:["InputErrors","NotFoundError","UpdateFeedsManagerSuccess"],UpdateJobProposalSpecDefinitionPayload:["NotFoundError","UpdateJobProposalSpecDefinitionSuccess"],UpdatePasswordPayload:["InputErrors","UpdatePasswordSuccess"],VRFKeyPayload:["NotFoundError","VRFKeySuccess"]}};let ai=ar;var aa=(r=void 0,location.origin),ao=new nh({uri:"".concat(aa,"/query"),credentials:"include"}),as=new ia({cache:new an({possibleTypes:ai.possibleTypes,dataIdFromObject:function(e){if("Chain"===e.__typename){if(!e.network)throw Error("Due to Chain ID not being unique across chain, ensure network is fetched too");return"Chain:".concat(e.network,":").concat(e.id)}return id(e)}}),link:ao});if(a.Z.locale(o),u().defaultFormat="YYYY-MM-DD h:mm:ss A","undefined"!=typeof document){var au,ac,al=f().hydrate;ac=X,al(c.createElement(et,{client:as},c.createElement(d.zj,null,c.createElement(i.MuiThemeProvider,{theme:J.r},c.createElement(ac,null)))),document.getElementById("root"))}})()})();
\ No newline at end of file
+`+(a!==i?`result of cast: ${a}`:""))}return r}_cast(e,t){let n=void 0===e?e:this.transforms.reduce((t,n)=>n.call(this,t,e,this),e);return void 0===n&&(n=this.getDefault()),n}_validate(e,t={},n){let{sync:r,path:i,from:a=[],originalValue:o=e,strict:s=this.spec.strict,abortEarly:u=this.spec.abortEarly}=t,c=e;s||(c=this._cast(c,pA({assert:!1},t)));let l={value:c,path:i,options:t,originalValue:o,schema:this,label:this.spec.label,sync:r,from:a},f=[];this._typeError&&f.push(this._typeError),this._whitelistError&&f.push(this._whitelistError),this._blacklistError&&f.push(this._blacklistError),pg({args:l,value:c,path:i,sync:r,tests:f,endEarly:u},e=>{if(e)return void n(e,c);pg({tests:this.tests,args:l,path:i,sync:r,value:c,endEarly:u},n)})}validate(e,t,n){let r=this.resolve(pA({},t,{value:e}));return"function"==typeof n?r._validate(e,t,n):new Promise((n,i)=>r._validate(e,t,(e,t)=>{e?i(e):n(t)}))}validateSync(e,t){let n;return this.resolve(pA({},t,{value:e}))._validate(e,pA({},t,{sync:!0}),(e,t)=>{if(e)throw e;n=t}),n}isValid(e,t){return this.validate(e,t).then(()=>!0,e=>{if(pb.isError(e))return!1;throw e})}isValidSync(e,t){try{return this.validateSync(e,t),!0}catch(n){if(pb.isError(n))return!1;throw n}}_getDefault(){let e=this.spec.default;return null==e?e:"function"==typeof e?e.call(this):h0(e)}getDefault(e){return this.resolve(e||{})._getDefault()}default(e){return 0===arguments.length?this._getDefault():this.clone({default:e})}strict(e=!0){var t=this.clone();return t.spec.strict=e,t}_isPresent(e){return null!=e}defined(e=pe.defined){return this.test({message:e,name:"defined",exclusive:!0,test:e=>void 0!==e})}required(e=pe.required){return this.clone({presence:"required"}).withMutation(t=>t.test({message:e,name:"required",exclusive:!0,test(e){return this.schema._isPresent(e)}}))}notRequired(){var e=this.clone({presence:"optional"});return e.tests=e.tests.filter(e=>"required"!==e.OPTIONS.name),e}nullable(e=!0){return this.clone({nullable:!1!==e})}transform(e){var t=this.clone();return t.transforms.push(e),t}test(...e){let t;if(void 0===(t=1===e.length?"function"==typeof e[0]?{test:e[0]}:e[0]:2===e.length?{name:e[0],test:e[1]}:{name:e[0],message:e[1],test:e[2]}).message&&(t.message=pe.default),"function"!=typeof t.test)throw TypeError("`test` is a required parameters");let n=this.clone(),r=px(t),i=t.exclusive||t.name&&!0===n.exclusiveTests[t.name];if(t.exclusive&&!t.name)throw TypeError("Exclusive tests must provide a unique `name` identifying the test");return t.name&&(n.exclusiveTests[t.name]=!!t.exclusive),n.tests=n.tests.filter(e=>e.OPTIONS.name!==t.name||!i&&e.OPTIONS.test!==r.OPTIONS.test),n.tests.push(r),n}when(e,t){Array.isArray(e)||"string"==typeof e||(t=e,e=".");let n=this.clone(),r=pd(e).map(e=>new pE(e));return r.forEach(e=>{e.isSibling&&n.deps.push(e.key)}),n.conditions.push(new pf(r,t)),n}typeError(e){var t=this.clone();return t._typeError=px({message:e,name:"typeError",test(e){return!!(void 0===e||this.schema.isType(e))||this.createError({params:{type:this.schema._type}})}}),t}oneOf(e,t=pe.oneOf){var n=this.clone();return e.forEach(e=>{n._whitelist.add(e),n._blacklist.delete(e)}),n._whitelistError=px({message:t,name:"oneOf",test(e){if(void 0===e)return!0;let t=this.schema._whitelist;return!!t.has(e,this.resolve)||this.createError({params:{values:t.toArray().join(", ")}})}}),n}notOneOf(e,t=pe.notOneOf){var n=this.clone();return e.forEach(e=>{n._blacklist.add(e),n._whitelist.delete(e)}),n._blacklistError=px({message:t,name:"notOneOf",test(e){let t=this.schema._blacklist;return!t.has(e,this.resolve)||this.createError({params:{values:t.toArray().join(", ")}})}}),n}strip(e=!0){let t=this.clone();return t.spec.strip=e,t}describe(){let e=this.clone(),{label:t,meta:n}=e.spec,r={meta:n,label:t,type:e.type,oneOf:e._whitelist.describe(),notOneOf:e._blacklist.describe(),tests:e.tests.map(e=>({name:e.OPTIONS.name,params:e.OPTIONS.params})).filter((e,t,n)=>n.findIndex(t=>t.name===e.name)===t)};return r}}for(let pC of(pL.prototype.__isYupSchema__=!0,["validate","validateSync"]))pL.prototype[`${pC}At`]=function(e,t,n={}){let{parent:r,parentPath:i,schema:a}=pM(this,e,t,n.context);return a[pC](r&&r[i],pA({},n,{parent:r,path:e}))};for(let pI of["equals","is"])pL.prototype[pI]=pL.prototype.oneOf;for(let pD of["not","nope"])pL.prototype[pD]=pL.prototype.notOneOf;pL.prototype.optional=pL.prototype.notRequired;let pN=pL;function pP(){return new pN}pP.prototype=pN.prototype;let pR=e=>null==e;function pj(){return new pF}class pF extends pL{constructor(){super({type:"boolean"}),this.withMutation(()=>{this.transform(function(e){if(!this.isType(e)){if(/^(true|1)$/i.test(String(e)))return!0;if(/^(false|0)$/i.test(String(e)))return!1}return e})})}_typeCheck(e){return e instanceof Boolean&&(e=e.valueOf()),"boolean"==typeof e}isTrue(e=pi.isValue){return this.test({message:e,name:"is-value",exclusive:!0,params:{value:"true"},test:e=>pR(e)||!0===e})}isFalse(e=pi.isValue){return this.test({message:e,name:"is-value",exclusive:!0,params:{value:"false"},test:e=>pR(e)||!1===e})}}pj.prototype=pF.prototype;let pY=/^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i,pB=/^((https?|ftp):)?\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i,pU=/^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i,pH=e=>pR(e)||e===e.trim(),p$=({}).toString();function pz(){return new pG}class pG extends pL{constructor(){super({type:"string"}),this.withMutation(()=>{this.transform(function(e){if(this.isType(e)||Array.isArray(e))return e;let t=null!=e&&e.toString?e.toString():e;return t===p$?e:t})})}_typeCheck(e){return e instanceof String&&(e=e.valueOf()),"string"==typeof e}_isPresent(e){return super._isPresent(e)&&!!e.length}length(e,t=pt.length){return this.test({message:t,name:"length",exclusive:!0,params:{length:e},test(t){return pR(t)||t.length===this.resolve(e)}})}min(e,t=pt.min){return this.test({message:t,name:"min",exclusive:!0,params:{min:e},test(t){return pR(t)||t.length>=this.resolve(e)}})}max(e,t=pt.max){return this.test({name:"max",exclusive:!0,message:t,params:{max:e},test(t){return pR(t)||t.length<=this.resolve(e)}})}matches(e,t){let n=!1,r,i;return t&&("object"==typeof t?{excludeEmptyString:n=!1,message:r,name:i}=t:r=t),this.test({name:i||"matches",message:r||pt.matches,params:{regex:e},test:t=>pR(t)||""===t&&n||-1!==t.search(e)})}email(e=pt.email){return this.matches(pY,{name:"email",message:e,excludeEmptyString:!0})}url(e=pt.url){return this.matches(pB,{name:"url",message:e,excludeEmptyString:!0})}uuid(e=pt.uuid){return this.matches(pU,{name:"uuid",message:e,excludeEmptyString:!1})}ensure(){return this.default("").transform(e=>null===e?"":e)}trim(e=pt.trim){return this.transform(e=>null!=e?e.trim():e).test({message:e,name:"trim",test:pH})}lowercase(e=pt.lowercase){return this.transform(e=>pR(e)?e:e.toLowerCase()).test({message:e,name:"string_case",exclusive:!0,test:e=>pR(e)||e===e.toLowerCase()})}uppercase(e=pt.uppercase){return this.transform(e=>pR(e)?e:e.toUpperCase()).test({message:e,name:"string_case",exclusive:!0,test:e=>pR(e)||e===e.toUpperCase()})}}pz.prototype=pG.prototype;let pW=e=>e!=+e;function pK(){return new pV}class pV extends pL{constructor(){super({type:"number"}),this.withMutation(()=>{this.transform(function(e){let t=e;if("string"==typeof t){if(""===(t=t.replace(/\s/g,"")))return NaN;t=+t}return this.isType(t)?t:parseFloat(t)})})}_typeCheck(e){return e instanceof Number&&(e=e.valueOf()),"number"==typeof e&&!pW(e)}min(e,t=pn.min){return this.test({message:t,name:"min",exclusive:!0,params:{min:e},test(t){return pR(t)||t>=this.resolve(e)}})}max(e,t=pn.max){return this.test({message:t,name:"max",exclusive:!0,params:{max:e},test(t){return pR(t)||t<=this.resolve(e)}})}lessThan(e,t=pn.lessThan){return this.test({message:t,name:"max",exclusive:!0,params:{less:e},test(t){return pR(t)||tthis.resolve(e)}})}positive(e=pn.positive){return this.moreThan(0,e)}negative(e=pn.negative){return this.lessThan(0,e)}integer(e=pn.integer){return this.test({name:"integer",message:e,test:e=>pR(e)||Number.isInteger(e)})}truncate(){return this.transform(e=>pR(e)?e:0|e)}round(e){var t,n=["ceil","floor","round","trunc"];if("trunc"===(e=(null==(t=e)?void 0:t.toLowerCase())||"round"))return this.truncate();if(-1===n.indexOf(e.toLowerCase()))throw TypeError("Only valid options for round() are: "+n.join(", "));return this.transform(t=>pR(t)?t:Math[e](t))}}pK.prototype=pV.prototype;var pq=/^(\d{4}|[+\-]\d{6})(?:-?(\d{2})(?:-?(\d{2}))?)?(?:[ T]?(\d{2}):?(\d{2})(?::?(\d{2})(?:[,\.](\d{1,}))?)?(?:(Z)|([+\-])(\d{2})(?::?(\d{2}))?)?)?$/;function pZ(e){var t,n,r=[1,4,5,6,7,10,11],i=0;if(n=pq.exec(e)){for(var a,o=0;a=r[o];++o)n[a]=+n[a]||0;n[2]=(+n[2]||1)-1,n[3]=+n[3]||1,n[7]=n[7]?String(n[7]).substr(0,3):0,(void 0===n[8]||""===n[8])&&(void 0===n[9]||""===n[9])?t=+new Date(n[1],n[2],n[3],n[4],n[5],n[6],n[7]):("Z"!==n[8]&&void 0!==n[9]&&(i=60*n[10]+n[11],"+"===n[9]&&(i=0-i)),t=Date.UTC(n[1],n[2],n[3],n[4],n[5]+i,n[6],n[7]))}else t=Date.parse?Date.parse(e):NaN;return t}let pX=new Date(""),pJ=e=>"[object Date]"===Object.prototype.toString.call(e);function pQ(){return new p1}class p1 extends pL{constructor(){super({type:"date"}),this.withMutation(()=>{this.transform(function(e){return this.isType(e)?e:(e=pZ(e),isNaN(e)?pX:new Date(e))})})}_typeCheck(e){return pJ(e)&&!isNaN(e.getTime())}prepareParam(e,t){let n;if(pE.isRef(e))n=e;else{let r=this.cast(e);if(!this._typeCheck(r))throw TypeError(`\`${t}\` must be a Date or a value that can be \`cast()\` to a Date`);n=r}return n}min(e,t=pr.min){let n=this.prepareParam(e,"min");return this.test({message:t,name:"min",exclusive:!0,params:{min:e},test(e){return pR(e)||e>=this.resolve(n)}})}max(e,t=pr.max){var n=this.prepareParam(e,"max");return this.test({message:t,name:"max",exclusive:!0,params:{max:e},test(e){return pR(e)||e<=this.resolve(n)}})}}p1.INVALID_DATE=pX,pQ.prototype=p1.prototype,pQ.INVALID_DATE=pX;var p0=n(11865),p2=n.n(p0),p3=n(68929),p4=n.n(p3),p6=n(67523),p5=n.n(p6),p8=n(94633),p9=n.n(p8);function p7(e,t=[]){let n=[],r=[];function i(e,i){var a=(0,pw.split)(e)[0];~r.indexOf(a)||r.push(a),~t.indexOf(`${i}-${a}`)||n.push([i,a])}for(let a in e)if(pu()(e,a)){let o=e[a];~r.indexOf(a)||r.push(a),pE.isRef(o)&&o.isSibling?i(o.path,a):pc(o)&&"deps"in o&&o.deps.forEach(e=>i(e,a))}return p9().array(r,n).reverse()}function be(e,t){let n=1/0;return e.some((e,r)=>{var i;if((null==(i=t.path)?void 0:i.indexOf(e))!==-1)return n=r,!0}),n}function bt(e){return(t,n)=>be(e,t)-be(e,n)}function bn(){return(bn=Object.assign||function(e){for(var t=1;t"[object Object]"===Object.prototype.toString.call(e);function bi(e,t){let n=Object.keys(e.fields);return Object.keys(t).filter(e=>-1===n.indexOf(e))}let ba=bt([]);class bo extends pL{constructor(e){super({type:"object"}),this.fields=Object.create(null),this._sortErrors=ba,this._nodes=[],this._excludedEdges=[],this.withMutation(()=>{this.transform(function(e){if("string"==typeof e)try{e=JSON.parse(e)}catch(t){e=null}return this.isType(e)?e:null}),e&&this.shape(e)})}_typeCheck(e){return br(e)||"function"==typeof e}_cast(e,t={}){var n;let r=super._cast(e,t);if(void 0===r)return this.getDefault();if(!this._typeCheck(r))return r;let i=this.fields,a=null!=(n=t.stripUnknown)?n:this.spec.noUnknown,o=this._nodes.concat(Object.keys(r).filter(e=>-1===this._nodes.indexOf(e))),s={},u=bn({},t,{parent:s,__validating:t.__validating||!1}),c=!1;for(let l of o){let f=i[l],d=pu()(r,l);if(f){let h,p=r[l];u.path=(t.path?`${t.path}.`:"")+l;let b="spec"in(f=f.resolve({value:p,context:t.context,parent:s}))?f.spec:void 0,m=null==b?void 0:b.strict;if(null==b?void 0:b.strip){c=c||l in r;continue}void 0!==(h=t.__validating&&m?r[l]:f.cast(r[l],u))&&(s[l]=h)}else d&&!a&&(s[l]=r[l]);s[l]!==r[l]&&(c=!0)}return c?s:r}_validate(e,t={},n){let r=[],{sync:i,from:a=[],originalValue:o=e,abortEarly:s=this.spec.abortEarly,recursive:u=this.spec.recursive}=t;a=[{schema:this,value:o},...a],t.__validating=!0,t.originalValue=o,t.from=a,super._validate(e,t,(e,c)=>{if(e){if(!pb.isError(e)||s)return void n(e,c);r.push(e)}if(!u||!br(c)){n(r[0]||null,c);return}o=o||c;let l=this._nodes.map(e=>(n,r)=>{let i=-1===e.indexOf(".")?(t.path?`${t.path}.`:"")+e:`${t.path||""}["${e}"]`,s=this.fields[e];if(s&&"validate"in s){s.validate(c[e],bn({},t,{path:i,from:a,strict:!0,parent:c,originalValue:o[e]}),r);return}r(null)});pg({sync:i,tests:l,value:c,errors:r,endEarly:s,sort:this._sortErrors,path:t.path},n)})}clone(e){let t=super.clone(e);return t.fields=bn({},this.fields),t._nodes=this._nodes,t._excludedEdges=this._excludedEdges,t._sortErrors=this._sortErrors,t}concat(e){let t=super.concat(e),n=t.fields;for(let[r,i]of Object.entries(this.fields)){let a=n[r];void 0===a?n[r]=i:a instanceof pL&&i instanceof pL&&(n[r]=i.concat(a))}return t.withMutation(()=>t.shape(n))}getDefaultFromShape(){let e={};return this._nodes.forEach(t=>{let n=this.fields[t];e[t]="default"in n?n.getDefault():void 0}),e}_getDefault(){return"default"in this.spec?super._getDefault():this._nodes.length?this.getDefaultFromShape():void 0}shape(e,t=[]){let n=this.clone(),r=Object.assign(n.fields,e);if(n.fields=r,n._sortErrors=bt(Object.keys(r)),t.length){Array.isArray(t[0])||(t=[t]);let i=t.map(([e,t])=>`${e}-${t}`);n._excludedEdges=n._excludedEdges.concat(i)}return n._nodes=p7(r,n._excludedEdges),n}pick(e){let t={};for(let n of e)this.fields[n]&&(t[n]=this.fields[n]);return this.clone().withMutation(e=>(e.fields={},e.shape(t)))}omit(e){let t=this.clone(),n=t.fields;for(let r of(t.fields={},e))delete n[r];return t.withMutation(()=>t.shape(n))}from(e,t,n){let r=(0,pw.getter)(e,!0);return this.transform(i=>{if(null==i)return i;let a=i;return pu()(i,e)&&(a=bn({},i),n||delete a[e],a[t]=r(i)),a})}noUnknown(e=!0,t=pa.noUnknown){"string"==typeof e&&(t=e,e=!0);let n=this.test({name:"noUnknown",exclusive:!0,message:t,test(t){if(null==t)return!0;let n=bi(this.schema,t);return!e||0===n.length||this.createError({params:{unknown:n.join(", ")}})}});return n.spec.noUnknown=e,n}unknown(e=!0,t=pa.noUnknown){return this.noUnknown(!e,t)}transformKeys(e){return this.transform(t=>t&&p5()(t,(t,n)=>e(n)))}camelCase(){return this.transformKeys(p4())}snakeCase(){return this.transformKeys(p2())}constantCase(){return this.transformKeys(e=>p2()(e).toUpperCase())}describe(){let e=super.describe();return e.fields=py()(this.fields,e=>e.describe()),e}}function bs(e){return new bo(e)}function bu(){return(bu=Object.assign||function(e){for(var t=1;t{this.transform(function(e){if("string"==typeof e)try{e=JSON.parse(e)}catch(t){e=null}return this.isType(e)?e:null})})}_typeCheck(e){return Array.isArray(e)}get _subType(){return this.innerType}_cast(e,t){let n=super._cast(e,t);if(!this._typeCheck(n)||!this.innerType)return n;let r=!1,i=n.map((e,n)=>{let i=this.innerType.cast(e,bu({},t,{path:`${t.path||""}[${n}]`}));return i!==e&&(r=!0),i});return r?i:n}_validate(e,t={},n){var r,i;let a=[],o=t.sync,s=t.path,u=this.innerType,c=null!=(r=t.abortEarly)?r:this.spec.abortEarly,l=null!=(i=t.recursive)?i:this.spec.recursive,f=null!=t.originalValue?t.originalValue:e;super._validate(e,t,(e,r)=>{if(e){if(!pb.isError(e)||c)return void n(e,r);a.push(e)}if(!l||!u||!this._typeCheck(r)){n(a[0]||null,r);return}f=f||r;let i=Array(r.length);for(let d=0;du.validate(h,b,t)}pg({sync:o,path:s,value:r,errors:a,endEarly:c,tests:i},n)})}clone(e){let t=super.clone(e);return t.innerType=this.innerType,t}concat(e){let t=super.concat(e);return t.innerType=this.innerType,e.innerType&&(t.innerType=t.innerType?t.innerType.concat(e.innerType):e.innerType),t}of(e){let t=this.clone();if(!pc(e))throw TypeError("`array.of()` sub-schema must be a valid yup schema not: "+h7(e));return t.innerType=e,t}length(e,t=po.length){return this.test({message:t,name:"length",exclusive:!0,params:{length:e},test(t){return pR(t)||t.length===this.resolve(e)}})}min(e,t){return t=t||po.min,this.test({message:t,name:"min",exclusive:!0,params:{min:e},test(t){return pR(t)||t.length>=this.resolve(e)}})}max(e,t){return t=t||po.max,this.test({message:t,name:"max",exclusive:!0,params:{max:e},test(t){return pR(t)||t.length<=this.resolve(e)}})}ensure(){return this.default(()=>[]).transform((e,t)=>this._typeCheck(e)?e:null==t?[]:[].concat(t))}compact(e){let t=e?(t,n,r)=>!e(t,n,r):e=>!!e;return this.transform(e=>null!=e?e.filter(t):e)}describe(){let e=super.describe();return this.innerType&&(e.innerType=this.innerType.describe()),e}nullable(e=!0){return super.nullable(e)}defined(){return super.defined()}required(e){return super.required(e)}}bc.prototype=bl.prototype;var bf=bs().shape({name:pz().required("Required"),url:pz().required("Required")}),bd=function(e){var t=e.initialValues,n=e.onSubmit,r=e.submitButtonText,i=e.nameDisabled,a=void 0!==i&&i;return l.createElement(hb,{initialValues:t,validationSchema:bf,onSubmit:n},function(e){var t=e.isSubmitting;return l.createElement(l.Fragment,null,l.createElement(hx,{"data-testid":"bridge-form",noValidate:!0},l.createElement(d.Z,{container:!0,spacing:16},l.createElement(d.Z,{item:!0,xs:12,md:7},l.createElement(hk,{component:hB,id:"name",name:"name",label:"Name",disabled:a,required:!0,fullWidth:!0,FormHelperTextProps:{"data-testid":"name-helper-text"}})),l.createElement(d.Z,{item:!0,xs:12,md:7},l.createElement(hk,{component:hB,id:"url",name:"url",label:"Bridge URL",placeholder:"https://",required:!0,fullWidth:!0,FormHelperTextProps:{"data-testid":"url-helper-text"}})),l.createElement(d.Z,{item:!0,xs:12,md:7},l.createElement(d.Z,{container:!0,spacing:16},l.createElement(d.Z,{item:!0,xs:7},l.createElement(hk,{component:hB,id:"minimumContractPayment",name:"minimumContractPayment",label:"Minimum Contract Payment",placeholder:"0",fullWidth:!0,inputProps:{min:0},FormHelperTextProps:{"data-testid":"minimumContractPayment-helper-text"}})),l.createElement(d.Z,{item:!0,xs:7},l.createElement(hk,{component:hB,id:"confirmations",name:"confirmations",label:"Confirmations",placeholder:"0",type:"number",fullWidth:!0,inputProps:{min:0},FormHelperTextProps:{"data-testid":"confirmations-helper-text"}})))),l.createElement(d.Z,{item:!0,xs:12,md:7},l.createElement(of.default,{variant:"contained",color:"primary",type:"submit",disabled:t,size:"large"},r)))))})},bh=function(e){var t=e.bridge,n=e.onSubmit,r={name:t.name,url:t.url,minimumContractPayment:t.minimumContractPayment,confirmations:t.confirmations};return l.createElement(ig,null,l.createElement(d.Z,{container:!0,spacing:40},l.createElement(d.Z,{item:!0,xs:12,md:11,lg:9},l.createElement(r5.Z,null,l.createElement(o7.Z,{title:"Edit Bridge",action:l.createElement(o1.Z,{component:tz,href:"/bridges/".concat(t.id)},"Cancel")}),l.createElement(aD.Z,null,l.createElement(bd,{nameDisabled:!0,initialValues:r,onSubmit:n,submitButtonText:"Save Bridge"}))))))};function bp(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&void 0!==arguments[0]&&arguments[0],t=e?function(){return l.createElement(x.default,{variant:"body1"},"Loading...")}:function(){return null};return{isLoading:e,LoadingPlaceholder:t}},b7=n(76023);function me(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]=0)&&Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}function mL(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n=4?[e[0],e[1],e[2],e[3],"".concat(e[0],".").concat(e[1]),"".concat(e[0],".").concat(e[2]),"".concat(e[0],".").concat(e[3]),"".concat(e[1],".").concat(e[0]),"".concat(e[1],".").concat(e[2]),"".concat(e[1],".").concat(e[3]),"".concat(e[2],".").concat(e[0]),"".concat(e[2],".").concat(e[1]),"".concat(e[2],".").concat(e[3]),"".concat(e[3],".").concat(e[0]),"".concat(e[3],".").concat(e[1]),"".concat(e[3],".").concat(e[2]),"".concat(e[0],".").concat(e[1],".").concat(e[2]),"".concat(e[0],".").concat(e[1],".").concat(e[3]),"".concat(e[0],".").concat(e[2],".").concat(e[1]),"".concat(e[0],".").concat(e[2],".").concat(e[3]),"".concat(e[0],".").concat(e[3],".").concat(e[1]),"".concat(e[0],".").concat(e[3],".").concat(e[2]),"".concat(e[1],".").concat(e[0],".").concat(e[2]),"".concat(e[1],".").concat(e[0],".").concat(e[3]),"".concat(e[1],".").concat(e[2],".").concat(e[0]),"".concat(e[1],".").concat(e[2],".").concat(e[3]),"".concat(e[1],".").concat(e[3],".").concat(e[0]),"".concat(e[1],".").concat(e[3],".").concat(e[2]),"".concat(e[2],".").concat(e[0],".").concat(e[1]),"".concat(e[2],".").concat(e[0],".").concat(e[3]),"".concat(e[2],".").concat(e[1],".").concat(e[0]),"".concat(e[2],".").concat(e[1],".").concat(e[3]),"".concat(e[2],".").concat(e[3],".").concat(e[0]),"".concat(e[2],".").concat(e[3],".").concat(e[1]),"".concat(e[3],".").concat(e[0],".").concat(e[1]),"".concat(e[3],".").concat(e[0],".").concat(e[2]),"".concat(e[3],".").concat(e[1],".").concat(e[0]),"".concat(e[3],".").concat(e[1],".").concat(e[2]),"".concat(e[3],".").concat(e[2],".").concat(e[0]),"".concat(e[3],".").concat(e[2],".").concat(e[1]),"".concat(e[0],".").concat(e[1],".").concat(e[2],".").concat(e[3]),"".concat(e[0],".").concat(e[1],".").concat(e[3],".").concat(e[2]),"".concat(e[0],".").concat(e[2],".").concat(e[1],".").concat(e[3]),"".concat(e[0],".").concat(e[2],".").concat(e[3],".").concat(e[1]),"".concat(e[0],".").concat(e[3],".").concat(e[1],".").concat(e[2]),"".concat(e[0],".").concat(e[3],".").concat(e[2],".").concat(e[1]),"".concat(e[1],".").concat(e[0],".").concat(e[2],".").concat(e[3]),"".concat(e[1],".").concat(e[0],".").concat(e[3],".").concat(e[2]),"".concat(e[1],".").concat(e[2],".").concat(e[0],".").concat(e[3]),"".concat(e[1],".").concat(e[2],".").concat(e[3],".").concat(e[0]),"".concat(e[1],".").concat(e[3],".").concat(e[0],".").concat(e[2]),"".concat(e[1],".").concat(e[3],".").concat(e[2],".").concat(e[0]),"".concat(e[2],".").concat(e[0],".").concat(e[1],".").concat(e[3]),"".concat(e[2],".").concat(e[0],".").concat(e[3],".").concat(e[1]),"".concat(e[2],".").concat(e[1],".").concat(e[0],".").concat(e[3]),"".concat(e[2],".").concat(e[1],".").concat(e[3],".").concat(e[0]),"".concat(e[2],".").concat(e[3],".").concat(e[0],".").concat(e[1]),"".concat(e[2],".").concat(e[3],".").concat(e[1],".").concat(e[0]),"".concat(e[3],".").concat(e[0],".").concat(e[1],".").concat(e[2]),"".concat(e[3],".").concat(e[0],".").concat(e[2],".").concat(e[1]),"".concat(e[3],".").concat(e[1],".").concat(e[0],".").concat(e[2]),"".concat(e[3],".").concat(e[1],".").concat(e[2],".").concat(e[0]),"".concat(e[3],".").concat(e[2],".").concat(e[0],".").concat(e[1]),"".concat(e[3],".").concat(e[2],".").concat(e[1],".").concat(e[0])]:void 0}var mB={};function mU(e){if(0===e.length||1===e.length)return e;var t=e.join(".");return mB[t]||(mB[t]=mY(e)),mB[t]}function mH(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=arguments.length>2?arguments[2]:void 0;return mU(e.filter(function(e){return"token"!==e})).reduce(function(e,t){return mj({},e,n[t])},t)}function m$(e){return e.join(" ")}function mz(e,t){var n=0;return function(r){return n+=1,r.map(function(r,i){return mG({node:r,stylesheet:e,useInlineStyles:t,key:"code-segment-".concat(n,"-").concat(i)})})}}function mG(e){var t=e.node,n=e.stylesheet,r=e.style,i=void 0===r?{}:r,a=e.useInlineStyles,o=e.key,s=t.properties,u=t.type,c=t.tagName,f=t.value;if("text"===u)return f;if(c){var d,h=mz(n,a);if(a){var p=Object.keys(n).reduce(function(e,t){return t.split(".").forEach(function(t){e.includes(t)||e.push(t)}),e},[]),b=s.className&&s.className.includes("token")?["token"]:[],m=s.className&&b.concat(s.className.filter(function(e){return!p.includes(e)}));d=mj({},s,{className:m$(m)||void 0,style:mH(s.className,Object.assign({},s.style,i),n)})}else d=mj({},s,{className:m$(s.className)});var g=h(t.children);return l.createElement(c,(0,mF.Z)({key:o},d),g)}}let mW=function(e,t){return -1!==e.listLanguages().indexOf(t)};var mK=/\n/g;function mV(e){return e.match(mK)}function mq(e){var t=e.lines,n=e.startingLineNumber,r=e.style;return t.map(function(e,t){var i=t+n;return l.createElement("span",{key:"line-".concat(t),className:"react-syntax-highlighter-line-number",style:"function"==typeof r?r(i):r},"".concat(i,"\n"))})}function mZ(e){var t=e.codeString,n=e.codeStyle,r=e.containerStyle,i=void 0===r?{float:"left",paddingRight:"10px"}:r,a=e.numberStyle,o=void 0===a?{}:a,s=e.startingLineNumber;return l.createElement("code",{style:Object.assign({},n,i)},mq({lines:t.replace(/\n$/,"").split("\n"),style:o,startingLineNumber:s}))}function mX(e){return"".concat(e.toString().length,".25em")}function mJ(e,t){return{type:"element",tagName:"span",properties:{key:"line-number--".concat(e),className:["comment","linenumber","react-syntax-highlighter-line-number"],style:t},children:[{type:"text",value:e}]}}function mQ(e,t,n){var r,i={display:"inline-block",minWidth:mX(n),paddingRight:"1em",textAlign:"right",userSelect:"none"};return mj({},i,"function"==typeof e?e(t):e)}function m1(e){var t=e.children,n=e.lineNumber,r=e.lineNumberStyle,i=e.largestLineNumber,a=e.showInlineLineNumbers,o=e.lineProps,s=void 0===o?{}:o,u=e.className,c=void 0===u?[]:u,l=e.showLineNumbers,f=e.wrapLongLines,d="function"==typeof s?s(n):s;if(d.className=c,n&&a){var h=mQ(r,n,i);t.unshift(mJ(n,h))}return f&l&&(d.style=mj({},d.style,{display:"flex"})),{type:"element",tagName:"span",properties:d,children:t}}function m0(e){for(var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[],n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:[],r=0;r