diff --git a/.changeset/afraid-cups-scream.md b/.changeset/afraid-cups-scream.md deleted file mode 100644 index 37226b8655..0000000000 --- a/.changeset/afraid-cups-scream.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"chainlink": patch ---- - -#added zkSync L1 GasPrice calculation diff --git a/.changeset/blue-phones-rest.md b/.changeset/blue-phones-rest.md deleted file mode 100644 index 4a427c7835..0000000000 --- a/.changeset/blue-phones-rest.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"chainlink": patch ---- - -#internal Use audited version of OCR2Base.sol in OCR3Capability.sol diff --git a/.changeset/chilled-panthers-joke.md b/.changeset/chilled-panthers-joke.md deleted file mode 100644 index 77dc481771..0000000000 --- a/.changeset/chilled-panthers-joke.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"chainlink": patch ---- - -#internal bump `chainlink-common` version to commit 6d926be950a6f6ca289a84edad938d4eef2ee337. diff --git a/.changeset/eleven-buckets-search.md b/.changeset/eleven-buckets-search.md deleted file mode 100644 index 6c68fbcfdc..0000000000 --- a/.changeset/eleven-buckets-search.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"chainlink": minor ---- - -#internal Add Log Poller support to Chain Reader through setting them in config. All filters should be part of the contract wide filter unless an event needs specific polling configuration, which can be set on a per event basis.. diff --git a/.changeset/fair-buttons-judge.md b/.changeset/fair-buttons-judge.md deleted file mode 100644 index 7aacd89787..0000000000 --- a/.changeset/fair-buttons-judge.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"chainlink": patch ---- - -#internal Bumped `chainlink-common` package version to commit `1fb0b48758af25d689b6957ebfb76598c9fb27ea`. diff --git a/.changeset/funny-snails-shake.md b/.changeset/funny-snails-shake.md deleted file mode 100644 index dad6c90872..0000000000 --- a/.changeset/funny-snails-shake.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"chainlink": minor ---- - -Added API for products to query a transaction's status in the TXM #internal diff --git a/.changeset/fuzzy-frogs-live.md b/.changeset/fuzzy-frogs-live.md deleted file mode 100644 index 58a9ffd228..0000000000 --- a/.changeset/fuzzy-frogs-live.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"chainlink": minor ---- - -#added EVM implementation of GetFeeComponents function for ChainWriter diff --git a/.changeset/giant-pumpkins-return.md b/.changeset/giant-pumpkins-return.md deleted file mode 100644 index c908db198f..0000000000 --- a/.changeset/giant-pumpkins-return.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"chainlink": patch ---- - -#internal bumped chainlink-common version to commit 1eff5dedc9857ed8811186dd2996603942dc1107 diff --git a/.changeset/happy-fans-enjoy.md b/.changeset/happy-fans-enjoy.md deleted file mode 100644 index 7e769cc413..0000000000 --- a/.changeset/happy-fans-enjoy.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"ccip": patch ---- - -#db_update ccip capability specs migration diff --git a/.changeset/healthy-plants-guess.md b/.changeset/healthy-plants-guess.md deleted file mode 100644 index 6ec74b5a04..0000000000 --- a/.changeset/healthy-plants-guess.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"chainlink": patch ---- - -#internal removed HistoryDepth to FinalityDepth validation diff --git a/.changeset/lemon-buttons-raise.md b/.changeset/lemon-buttons-raise.md deleted file mode 100644 index 0408383bd0..0000000000 --- a/.changeset/lemon-buttons-raise.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"chainlink": patch ---- - -#internal diff --git a/.changeset/metal-glasses-count.md b/.changeset/metal-glasses-count.md deleted file mode 100644 index 0408383bd0..0000000000 --- a/.changeset/metal-glasses-count.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"chainlink": patch ---- - -#internal diff --git a/.changeset/ninety-zebras-hammer.md b/.changeset/ninety-zebras-hammer.md deleted file mode 100644 index 0408383bd0..0000000000 --- a/.changeset/ninety-zebras-hammer.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"chainlink": patch ---- - -#internal diff --git a/.changeset/pink-ants-reply.md b/.changeset/pink-ants-reply.md deleted file mode 100644 index fab4ef31f7..0000000000 --- a/.changeset/pink-ants-reply.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"chainlink": minor ---- - -#internal remove shared secret from transmission schedule diff --git a/.changeset/pretty-dancers-pull.md b/.changeset/pretty-dancers-pull.md deleted file mode 100644 index af8ec9db0c..0000000000 --- a/.changeset/pretty-dancers-pull.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"chainlink": patch ---- - -#changed Expand EVM implementation compatibility pipeline diff --git a/.changeset/rare-carpets-cry.md b/.changeset/rare-carpets-cry.md deleted file mode 100644 index f096d31885..0000000000 --- a/.changeset/rare-carpets-cry.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"chainlink": minor ---- - -#internal capability dispatcher threading and context usage diff --git a/.changeset/red-balloons-repeat.md b/.changeset/red-balloons-repeat.md new file mode 100644 index 0000000000..674cae9602 --- /dev/null +++ b/.changeset/red-balloons-repeat.md @@ -0,0 +1,5 @@ +--- +"ccip": patch +--- + +Commit NewReportingPlugin retries on error diff --git a/.changeset/metal-cars-report.md b/.changeset/red-meals-mix.md similarity index 100% rename from .changeset/metal-cars-report.md rename to .changeset/red-meals-mix.md diff --git a/.changeset/rotten-emus-work.md b/.changeset/rotten-emus-work.md deleted file mode 100644 index ecf5ee0b9f..0000000000 --- a/.changeset/rotten-emus-work.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"chainlink": patch ---- - -#internal fixed a bug where we weren't sending the value param to the txm diff --git a/.changeset/shaggy-ears-share.md b/.changeset/shaggy-ears-share.md deleted file mode 100644 index 5946faf03c..0000000000 --- a/.changeset/shaggy-ears-share.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"chainlink": minor ---- - -#internal Add RegistrySyncer diff --git a/.changeset/shiny-poems-juggle.md b/.changeset/shiny-poems-juggle.md deleted file mode 100644 index 32fc2069ee..0000000000 --- a/.changeset/shiny-poems-juggle.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"chainlink": minor ---- - -#changed Refactored the BlockHistoryEstimator check to prevent excessively bumping transactions. Check no longer waits for CheckInclusionBlocks to pass before assessing an attempt. -#bugfix Fixed a bug that would use the oldest blocks in the cached history instead of the latest to perform gas estimations. diff --git a/.changeset/shy-deers-hope.md b/.changeset/shy-deers-hope.md deleted file mode 100644 index 0408383bd0..0000000000 --- a/.changeset/shy-deers-hope.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"chainlink": patch ---- - -#internal diff --git a/.changeset/sour-pigs-develop.md b/.changeset/sour-pigs-develop.md deleted file mode 100644 index 5737b20601..0000000000 --- a/.changeset/sour-pigs-develop.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"chainlink": minor ---- - -#internal remote target wait until initiated threads exit on close diff --git a/.changeset/stupid-poems-glow.md b/.changeset/stupid-poems-glow.md deleted file mode 100644 index bdd8acc66d..0000000000 --- a/.changeset/stupid-poems-glow.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"chainlink": minor ---- - -#internal rework operator_ui installer diff --git a/.changeset/tall-walls-agree.md b/.changeset/tall-walls-agree.md deleted file mode 100644 index 6f1f5505b6..0000000000 --- a/.changeset/tall-walls-agree.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"chainlink": patch ---- - -#internal upgrade keystone contracts to 0.8.24 diff --git a/.changeset/ten-falcons-suffer.md b/.changeset/ten-falcons-suffer.md deleted file mode 100644 index e4476b1045..0000000000 --- a/.changeset/ten-falcons-suffer.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"chainlink": patch ---- - -move v23 contracts #bugfix diff --git a/.changeset/thick-lemons-beam.md b/.changeset/thick-lemons-beam.md deleted file mode 100644 index 0ce6e04780..0000000000 --- a/.changeset/thick-lemons-beam.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"chainlink": minor ---- - -#internal Add script to provision capability registry diff --git a/.changeset/thick-moles-travel.md b/.changeset/thick-moles-travel.md deleted file mode 100644 index 45559e4715..0000000000 --- a/.changeset/thick-moles-travel.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"chainlink": minor ---- - -#internal moves workflow name and owner to the yaml spec diff --git a/.changeset/tiny-camels-flow.md b/.changeset/tiny-camels-flow.md deleted file mode 100644 index 3271bbb4a3..0000000000 --- a/.changeset/tiny-camels-flow.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"chainlink": patch ---- - -#bugfix fixed ureachable code bug which could result in stuck txns diff --git a/.changeset/unlucky-pillows-fly.md b/.changeset/unlucky-pillows-fly.md deleted file mode 100644 index c8f690eda1..0000000000 --- a/.changeset/unlucky-pillows-fly.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"chainlink": patch ---- - -add events, add getter and add comments #bugfix diff --git a/.changeset/warm-cobras-poke.md b/.changeset/warm-cobras-poke.md deleted file mode 100644 index 0bfb1dd44b..0000000000 --- a/.changeset/warm-cobras-poke.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"chainlink": patch ---- - -#updated Fix verb formatting in the log outputs. diff --git a/.changeset/wise-wasps-drum.md b/.changeset/wise-wasps-drum.md deleted file mode 100644 index 0a4a6eaf66..0000000000 --- a/.changeset/wise-wasps-drum.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"chainlink": patch ---- - -#internal Add loggercheck linter to verify that \*w logging methods have even number of args. diff --git a/.changeset/young-parrots-peel.md b/.changeset/young-parrots-peel.md deleted file mode 100644 index a7b696d14f..0000000000 --- a/.changeset/young-parrots-peel.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"chainlink": minor ---- - -Add registry syncer package #internal diff --git a/CODEOWNERS b/.github/CODEOWNERS similarity index 95% rename from CODEOWNERS rename to .github/CODEOWNERS index 0a5a8d8966..96f5c5b70e 100644 --- a/CODEOWNERS +++ b/.github/CODEOWNERS @@ -2,8 +2,8 @@ # 1. Per Github docs: "Order is important; the last matching pattern takes the most precedence." # Please define less specific codeowner paths before more specific codeowner paths in order for the more specific rule to have priority -# Core -/core @smartcontractkit/foundations +# Root +* @smartcontractkit/foundations # Chains /common @smartcontractkit/bix-framework @@ -12,6 +12,7 @@ # Services /core/services/directrequest @smartcontractkit/keepers /core/services/feeds @smartcontractkit/FMS +/core/services/synchronization/telem @smartcontractkit/realtime # To be deprecated in Chainlink V3 /core/services/fluxmonitorv2 @smartcontractkit/foundations @@ -117,8 +118,8 @@ contracts/scripts/requirements.txt @smartcontractkit/prodsec-public .nvmrc @smartcontractkit/prodsec-public contracts/package.json @smartcontractkit/prodsec-public contracts/pnpm.lock @smartcontractkit/prodsec-public -go.mod @smartcontractkit/prodsec-public -go.sum @smartcontractkit/prodsec-public +go.mod @smartcontractkit/prodsec-public @smartcontractkit/releng @smartcontractkit/foundations +go.sum @smartcontractkit/prodsec-public @smartcontractkit/releng @smartcontractkit/foundations integration-tests/go.mod @smartcontractkit/prodsec-public integration-tests/go.sum @smartcontractkit/prodsec-public flake.nix @smartcontractkit/prodsec-public diff --git a/.github/actions/split-tests/.npmrc b/.github/actions/split-tests/.npmrc deleted file mode 100644 index 4c2f52b3be..0000000000 --- a/.github/actions/split-tests/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -auto-install-peers=true -strict-peer-dependencies=false diff --git a/.github/actions/split-tests/action.yaml b/.github/actions/split-tests/action.yaml deleted file mode 100644 index d11f0bcbc3..0000000000 --- a/.github/actions/split-tests/action.yaml +++ /dev/null @@ -1,35 +0,0 @@ -name: Test Spliting -description: Split tests -inputs: - config: - required: true - description: The path to the splitting config -outputs: - splits: - description: The generated test splits - value: ${{ steps.split.outputs.splits }} -runs: - using: composite - steps: - - uses: pnpm/action-setup@a3252b78c470c02df07e9d59298aecedc3ccdd6d # v3.0.0 - with: - version: ^8.0.0 - - - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 - with: - node-version: "16" - cache: "pnpm" - cache-dependency-path: "./.github/actions/split-tests/pnpm-lock.yaml" - - - name: Install dependencies - shell: bash - run: pnpm i --prod - working-directory: "./.github/actions/split-tests" - - - name: Run test spliter - id: split - shell: bash - run: pnpm start - env: - CONFIG: ${{ inputs.config }} - working-directory: "./.github/actions/split-tests" diff --git a/.github/actions/split-tests/jest.config.js b/.github/actions/split-tests/jest.config.js deleted file mode 100644 index 7b3dcf296f..0000000000 --- a/.github/actions/split-tests/jest.config.js +++ /dev/null @@ -1,15 +0,0 @@ -/** @type {import('ts-jest').JestConfigWithTsJest} */ -const jestConfig = { - preset: "ts-jest/presets/default-esm", - resolver: "/mjs-resolver.ts", - transform: { - "^.+\\.mts?$": [ - "ts-jest", - { - useESM: true, - }, - ], - }, - testEnvironment: "node", -}; -export default jestConfig; diff --git a/.github/actions/split-tests/mjs-resolver.ts b/.github/actions/split-tests/mjs-resolver.ts deleted file mode 100644 index 92c66f7b6c..0000000000 --- a/.github/actions/split-tests/mjs-resolver.ts +++ /dev/null @@ -1,15 +0,0 @@ -const mjsResolver = (path, options) => { - const mjsExtRegex = /\.mjs$/i; - const resolver = options.defaultResolver; - if (mjsExtRegex.test(path)) { - try { - return resolver(path.replace(mjsExtRegex, ".mts"), options); - } catch { - // use default resolver - } - } - - return resolver(path, options); -}; - -module.exports = mjsResolver; diff --git a/.github/actions/split-tests/package.json b/.github/actions/split-tests/package.json deleted file mode 100644 index 10a71c0b35..0000000000 --- a/.github/actions/split-tests/package.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "shard-packages", - "version": "1.0.0", - "description": "", - "type": "module", - "main": "index.js", - "scripts": { - "start": "ts-node -T --esm ./src/index.mts", - "test": "node --experimental-vm-modules --no-warnings node_modules/jest/bin/jest.js" - }, - "keywords": [], - "author": "", - "license": "MIT", - "engines": { - "node": "^16.0.0", - "pnpm": "^8.0.0" - }, - "dependencies": { - "@actions/core": "^1.10.1", - "ts-node": "^10.9.2", - "zx": "^7.2.3" - }, - "devDependencies": { - "@types/jest": "^29.5.12", - "@types/node": "^18.19.32", - "jest": "^29.7.0", - "ts-jest": "^29.1.2", - "typescript": "^5.4.5" - } -} diff --git a/.github/actions/split-tests/pnpm-lock.yaml b/.github/actions/split-tests/pnpm-lock.yaml deleted file mode 100644 index 730971e0c6..0000000000 --- a/.github/actions/split-tests/pnpm-lock.yaml +++ /dev/null @@ -1,3071 +0,0 @@ -lockfileVersion: '6.0' - -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - -dependencies: - '@actions/core': - specifier: ^1.10.1 - version: 1.10.1 - ts-node: - specifier: ^10.9.2 - version: 10.9.2(@types/node@18.19.32)(typescript@5.4.5) - zx: - specifier: ^7.2.3 - version: 7.2.3 - -devDependencies: - '@types/jest': - specifier: ^29.5.12 - version: 29.5.12 - '@types/node': - specifier: ^18.19.32 - version: 18.19.32 - jest: - specifier: ^29.7.0 - version: 29.7.0(@types/node@18.19.32)(ts-node@10.9.2) - ts-jest: - specifier: ^29.1.2 - version: 29.1.2(@babel/core@7.19.3)(jest@29.7.0)(typescript@5.4.5) - typescript: - specifier: ^5.4.5 - version: 5.4.5 - -packages: - - /@actions/core@1.10.1: - resolution: {integrity: sha512-3lBR9EDAY+iYIpTnTIXmWcNbX3T2kCkAEQGIQx4NVQ0575nk2k3GRZDTPQG+vVtS2izSLmINlxXf0uLtnrTP+g==} - dependencies: - '@actions/http-client': 2.0.1 - uuid: 8.3.2 - dev: false - - /@actions/http-client@2.0.1: - resolution: {integrity: sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==} - dependencies: - tunnel: 0.0.6 - dev: false - - /@ampproject/remapping@2.2.0: - resolution: {integrity: sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==} - engines: {node: '>=6.0.0'} - dependencies: - '@jridgewell/gen-mapping': 0.1.1 - '@jridgewell/trace-mapping': 0.3.25 - dev: true - - /@babel/code-frame@7.18.6: - resolution: {integrity: sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/highlight': 7.18.6 - dev: true - - /@babel/code-frame@7.24.2: - resolution: {integrity: sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/highlight': 7.24.2 - picocolors: 1.0.0 - dev: true - - /@babel/compat-data@7.19.3: - resolution: {integrity: sha512-prBHMK4JYYK+wDjJF1q99KK4JLL+egWS4nmNqdlMUgCExMZ+iZW0hGhyC3VEbsPjvaN0TBhW//VIFwBrk8sEiw==} - engines: {node: '>=6.9.0'} - dev: true - - /@babel/compat-data@7.24.4: - resolution: {integrity: sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ==} - engines: {node: '>=6.9.0'} - dev: true - - /@babel/core@7.19.3: - resolution: {integrity: sha512-WneDJxdsjEvyKtXKsaBGbDeiyOjR5vYq4HcShxnIbG0qixpoHjI3MqeZM9NDvsojNCEBItQE4juOo/bU6e72gQ==} - engines: {node: '>=6.9.0'} - dependencies: - '@ampproject/remapping': 2.2.0 - '@babel/code-frame': 7.18.6 - '@babel/generator': 7.19.3 - '@babel/helper-compilation-targets': 7.19.3(@babel/core@7.19.3) - '@babel/helper-module-transforms': 7.19.0 - '@babel/helpers': 7.19.0 - '@babel/parser': 7.19.3 - '@babel/template': 7.18.10 - '@babel/traverse': 7.24.1 - '@babel/types': 7.19.3 - convert-source-map: 1.8.0 - debug: 4.3.4 - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.0 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/core@7.24.4: - resolution: {integrity: sha512-MBVlMXP+kkl5394RBLSxxk/iLTeVGuXTV3cIDXavPpMMqnSnt6apKgan/U8O3USWZCWZT/TbgfEpKa4uMgN4Dg==} - engines: {node: '>=6.9.0'} - dependencies: - '@ampproject/remapping': 2.2.0 - '@babel/code-frame': 7.24.2 - '@babel/generator': 7.24.4 - '@babel/helper-compilation-targets': 7.23.6 - '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.4) - '@babel/helpers': 7.24.4 - '@babel/parser': 7.24.4 - '@babel/template': 7.24.0 - '@babel/traverse': 7.24.1 - '@babel/types': 7.24.0 - convert-source-map: 2.0.0 - debug: 4.3.4 - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/generator@7.19.3: - resolution: {integrity: sha512-fqVZnmp1ncvZU757UzDheKZpfPgatqY59XtW2/j/18H7u76akb8xqvjw82f+i2UKd/ksYsSick/BCLQUUtJ/qQ==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.19.3 - '@jridgewell/gen-mapping': 0.3.2 - jsesc: 2.5.2 - dev: true - - /@babel/generator@7.24.4: - resolution: {integrity: sha512-Xd6+v6SnjWVx/nus+y0l1sxMOTOMBkyL4+BIdbALyatQnAe/SRVjANeDPSCYaX+i1iJmuGSKf3Z+E+V/va1Hvw==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.24.0 - '@jridgewell/gen-mapping': 0.3.5 - '@jridgewell/trace-mapping': 0.3.25 - jsesc: 2.5.2 - dev: true - - /@babel/helper-compilation-targets@7.19.3(@babel/core@7.19.3): - resolution: {integrity: sha512-65ESqLGyGmLvgR0mst5AdW1FkNlj9rQsCKduzEoEPhBCDFGXvz2jW6bXFG6i0/MrV2s7hhXjjb2yAzcPuQlLwg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/compat-data': 7.19.3 - '@babel/core': 7.19.3 - '@babel/helper-validator-option': 7.18.6 - browserslist: 4.21.4 - semver: 6.3.0 - dev: true - - /@babel/helper-compilation-targets@7.23.6: - resolution: {integrity: sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/compat-data': 7.24.4 - '@babel/helper-validator-option': 7.23.5 - browserslist: 4.23.0 - lru-cache: 5.1.1 - semver: 6.3.1 - dev: true - - /@babel/helper-environment-visitor@7.18.9: - resolution: {integrity: sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==} - engines: {node: '>=6.9.0'} - dev: true - - /@babel/helper-environment-visitor@7.22.20: - resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==} - engines: {node: '>=6.9.0'} - dev: true - - /@babel/helper-function-name@7.23.0: - resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/template': 7.24.0 - '@babel/types': 7.24.0 - dev: true - - /@babel/helper-hoist-variables@7.22.5: - resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.24.0 - dev: true - - /@babel/helper-module-imports@7.18.6: - resolution: {integrity: sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.19.3 - dev: true - - /@babel/helper-module-imports@7.24.3: - resolution: {integrity: sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.24.0 - dev: true - - /@babel/helper-module-transforms@7.19.0: - resolution: {integrity: sha512-3HBZ377Fe14RbLIA+ac3sY4PTgpxHVkFrESaWhoI5PuyXPBBX8+C34qblV9G89ZtycGJCmCI/Ut+VUDK4bltNQ==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-module-imports': 7.18.6 - '@babel/helper-simple-access': 7.18.6 - '@babel/helper-split-export-declaration': 7.18.6 - '@babel/helper-validator-identifier': 7.19.1 - '@babel/template': 7.18.10 - '@babel/traverse': 7.24.1 - '@babel/types': 7.19.3 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/helper-module-transforms@7.23.3(@babel/core@7.24.4): - resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.24.4 - '@babel/helper-environment-visitor': 7.22.20 - '@babel/helper-module-imports': 7.24.3 - '@babel/helper-simple-access': 7.22.5 - '@babel/helper-split-export-declaration': 7.22.6 - '@babel/helper-validator-identifier': 7.22.20 - dev: true - - /@babel/helper-plugin-utils@7.19.0: - resolution: {integrity: sha512-40Ryx7I8mT+0gaNxm8JGTZFUITNqdLAgdg0hXzeVZxVD6nFsdhQvip6v8dqkRHzsz1VFpFAaOCHNn0vKBL7Czw==} - engines: {node: '>=6.9.0'} - dev: true - - /@babel/helper-simple-access@7.18.6: - resolution: {integrity: sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.19.3 - dev: true - - /@babel/helper-simple-access@7.22.5: - resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.24.0 - dev: true - - /@babel/helper-split-export-declaration@7.18.6: - resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.19.3 - dev: true - - /@babel/helper-split-export-declaration@7.22.6: - resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.24.0 - dev: true - - /@babel/helper-string-parser@7.18.10: - resolution: {integrity: sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==} - engines: {node: '>=6.9.0'} - dev: true - - /@babel/helper-string-parser@7.24.1: - resolution: {integrity: sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==} - engines: {node: '>=6.9.0'} - dev: true - - /@babel/helper-validator-identifier@7.19.1: - resolution: {integrity: sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==} - engines: {node: '>=6.9.0'} - dev: true - - /@babel/helper-validator-identifier@7.22.20: - resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} - engines: {node: '>=6.9.0'} - dev: true - - /@babel/helper-validator-option@7.18.6: - resolution: {integrity: sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==} - engines: {node: '>=6.9.0'} - dev: true - - /@babel/helper-validator-option@7.23.5: - resolution: {integrity: sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==} - engines: {node: '>=6.9.0'} - dev: true - - /@babel/helpers@7.19.0: - resolution: {integrity: sha512-DRBCKGwIEdqY3+rPJgG/dKfQy9+08rHIAJx8q2p+HSWP87s2HCrQmaAMMyMll2kIXKCW0cO1RdQskx15Xakftg==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/template': 7.18.10 - '@babel/traverse': 7.24.1 - '@babel/types': 7.19.3 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/helpers@7.24.4: - resolution: {integrity: sha512-FewdlZbSiwaVGlgT1DPANDuCHaDMiOo+D/IDYRFYjHOuv66xMSJ7fQwwODwRNAPkADIO/z1EoF/l2BCWlWABDw==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/template': 7.24.0 - '@babel/traverse': 7.24.1 - '@babel/types': 7.24.0 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/highlight@7.18.6: - resolution: {integrity: sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-validator-identifier': 7.19.1 - chalk: 2.4.2 - js-tokens: 4.0.0 - dev: true - - /@babel/highlight@7.24.2: - resolution: {integrity: sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-validator-identifier': 7.22.20 - chalk: 2.4.2 - js-tokens: 4.0.0 - picocolors: 1.0.0 - dev: true - - /@babel/parser@7.19.3: - resolution: {integrity: sha512-pJ9xOlNWHiy9+FuFP09DEAFbAn4JskgRsVcc169w2xRBC3FRGuQEwjeIMMND9L2zc0iEhO/tGv4Zq+km+hxNpQ==} - engines: {node: '>=6.0.0'} - hasBin: true - dependencies: - '@babel/types': 7.19.3 - dev: true - - /@babel/parser@7.24.4: - resolution: {integrity: sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg==} - engines: {node: '>=6.0.0'} - hasBin: true - dependencies: - '@babel/types': 7.19.3 - dev: true - - /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.19.3): - resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.19.3 - '@babel/helper-plugin-utils': 7.19.0 - dev: true - - /@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.19.3): - resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.19.3 - '@babel/helper-plugin-utils': 7.19.0 - dev: true - - /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.19.3): - resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.19.3 - '@babel/helper-plugin-utils': 7.19.0 - dev: true - - /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.19.3): - resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.19.3 - '@babel/helper-plugin-utils': 7.19.0 - dev: true - - /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.19.3): - resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.19.3 - '@babel/helper-plugin-utils': 7.19.0 - dev: true - - /@babel/plugin-syntax-jsx@7.18.6(@babel/core@7.19.3): - resolution: {integrity: sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.19.3 - '@babel/helper-plugin-utils': 7.19.0 - dev: true - - /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.19.3): - resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.19.3 - '@babel/helper-plugin-utils': 7.19.0 - dev: true - - /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.19.3): - resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.19.3 - '@babel/helper-plugin-utils': 7.19.0 - dev: true - - /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.19.3): - resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.19.3 - '@babel/helper-plugin-utils': 7.19.0 - dev: true - - /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.19.3): - resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.19.3 - '@babel/helper-plugin-utils': 7.19.0 - dev: true - - /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.19.3): - resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.19.3 - '@babel/helper-plugin-utils': 7.19.0 - dev: true - - /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.19.3): - resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.19.3 - '@babel/helper-plugin-utils': 7.19.0 - dev: true - - /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.19.3): - resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.19.3 - '@babel/helper-plugin-utils': 7.19.0 - dev: true - - /@babel/plugin-syntax-typescript@7.18.6(@babel/core@7.19.3): - resolution: {integrity: sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.19.3 - '@babel/helper-plugin-utils': 7.19.0 - dev: true - - /@babel/template@7.18.10: - resolution: {integrity: sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/code-frame': 7.18.6 - '@babel/parser': 7.19.3 - '@babel/types': 7.19.3 - dev: true - - /@babel/template@7.24.0: - resolution: {integrity: sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/code-frame': 7.24.2 - '@babel/parser': 7.24.4 - '@babel/types': 7.24.0 - dev: true - - /@babel/traverse@7.24.1: - resolution: {integrity: sha512-xuU6o9m68KeqZbQuDt2TcKSxUw/mrsvavlEqQ1leZ/B+C9tk6E4sRWy97WaXgvq5E+nU3cXMxv3WKOCanVMCmQ==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/code-frame': 7.24.2 - '@babel/generator': 7.24.4 - '@babel/helper-environment-visitor': 7.22.20 - '@babel/helper-function-name': 7.23.0 - '@babel/helper-hoist-variables': 7.22.5 - '@babel/helper-split-export-declaration': 7.22.6 - '@babel/parser': 7.24.4 - '@babel/types': 7.24.0 - debug: 4.3.4 - globals: 11.12.0 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/types@7.19.3: - resolution: {integrity: sha512-hGCaQzIY22DJlDh9CH7NOxgKkFjBk0Cw9xDO1Xmh2151ti7wiGfQ3LauXzL4HP1fmFlTX6XjpRETTpUcv7wQLw==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-string-parser': 7.18.10 - '@babel/helper-validator-identifier': 7.19.1 - to-fast-properties: 2.0.0 - dev: true - - /@babel/types@7.24.0: - resolution: {integrity: sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-string-parser': 7.24.1 - '@babel/helper-validator-identifier': 7.22.20 - to-fast-properties: 2.0.0 - dev: true - - /@bcoe/v8-coverage@0.2.3: - resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} - dev: true - - /@cspotcode/source-map-support@0.8.1: - resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} - engines: {node: '>=12'} - dependencies: - '@jridgewell/trace-mapping': 0.3.9 - - /@istanbuljs/load-nyc-config@1.1.0: - resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} - engines: {node: '>=8'} - dependencies: - camelcase: 5.3.1 - find-up: 4.1.0 - get-package-type: 0.1.0 - js-yaml: 3.14.1 - resolve-from: 5.0.0 - dev: true - - /@istanbuljs/schema@0.1.3: - resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} - engines: {node: '>=8'} - dev: true - - /@jest/console@29.7.0: - resolution: {integrity: sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.6.3 - '@types/node': 18.19.32 - chalk: 4.1.2 - jest-message-util: 29.7.0 - jest-util: 29.7.0 - slash: 3.0.0 - dev: true - - /@jest/core@29.7.0(ts-node@10.9.2): - resolution: {integrity: sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - dependencies: - '@jest/console': 29.7.0 - '@jest/reporters': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 18.19.32 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - ci-info: 3.4.0 - exit: 0.1.2 - graceful-fs: 4.2.10 - jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@18.19.32)(ts-node@10.9.2) - jest-haste-map: 29.7.0 - jest-message-util: 29.7.0 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-resolve-dependencies: 29.7.0 - jest-runner: 29.7.0 - jest-runtime: 29.7.0 - jest-snapshot: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - jest-watcher: 29.7.0 - micromatch: 4.0.5 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-ansi: 6.0.1 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - ts-node - dev: true - - /@jest/environment@29.7.0: - resolution: {integrity: sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/fake-timers': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 18.19.32 - jest-mock: 29.7.0 - dev: true - - /@jest/expect-utils@29.1.2: - resolution: {integrity: sha512-4a48bhKfGj/KAH39u0ppzNTABXQ8QPccWAFUFobWBaEMSMp+sB31Z2fK/l47c4a/Mu1po2ffmfAIPxXbVTXdtg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - jest-get-type: 29.0.0 - dev: true - - /@jest/expect-utils@29.7.0: - resolution: {integrity: sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - jest-get-type: 29.6.3 - dev: true - - /@jest/expect@29.7.0: - resolution: {integrity: sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - expect: 29.7.0 - jest-snapshot: 29.7.0 - transitivePeerDependencies: - - supports-color - dev: true - - /@jest/fake-timers@29.7.0: - resolution: {integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.6.3 - '@sinonjs/fake-timers': 10.3.0 - '@types/node': 18.19.32 - jest-message-util: 29.7.0 - jest-mock: 29.7.0 - jest-util: 29.7.0 - dev: true - - /@jest/globals@29.7.0: - resolution: {integrity: sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/environment': 29.7.0 - '@jest/expect': 29.7.0 - '@jest/types': 29.6.3 - jest-mock: 29.7.0 - transitivePeerDependencies: - - supports-color - dev: true - - /@jest/reporters@29.7.0: - resolution: {integrity: sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - dependencies: - '@bcoe/v8-coverage': 0.2.3 - '@jest/console': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - '@jridgewell/trace-mapping': 0.3.25 - '@types/node': 18.19.32 - chalk: 4.1.2 - collect-v8-coverage: 1.0.1 - exit: 0.1.2 - glob: 7.2.3 - graceful-fs: 4.2.10 - istanbul-lib-coverage: 3.2.0 - istanbul-lib-instrument: 6.0.2 - istanbul-lib-report: 3.0.0 - istanbul-lib-source-maps: 4.0.1 - istanbul-reports: 3.1.5 - jest-message-util: 29.7.0 - jest-util: 29.7.0 - jest-worker: 29.7.0 - slash: 3.0.0 - string-length: 4.0.2 - strip-ansi: 6.0.1 - v8-to-istanbul: 9.0.1 - transitivePeerDependencies: - - supports-color - dev: true - - /@jest/schemas@29.0.0: - resolution: {integrity: sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@sinclair/typebox': 0.24.44 - dev: true - - /@jest/schemas@29.6.3: - resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@sinclair/typebox': 0.27.8 - dev: true - - /@jest/source-map@29.6.3: - resolution: {integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jridgewell/trace-mapping': 0.3.25 - callsites: 3.1.0 - graceful-fs: 4.2.10 - dev: true - - /@jest/test-result@29.7.0: - resolution: {integrity: sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/console': 29.7.0 - '@jest/types': 29.6.3 - '@types/istanbul-lib-coverage': 2.0.4 - collect-v8-coverage: 1.0.1 - dev: true - - /@jest/test-sequencer@29.7.0: - resolution: {integrity: sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/test-result': 29.7.0 - graceful-fs: 4.2.10 - jest-haste-map: 29.7.0 - slash: 3.0.0 - dev: true - - /@jest/transform@29.7.0: - resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@babel/core': 7.19.3 - '@jest/types': 29.6.3 - '@jridgewell/trace-mapping': 0.3.25 - babel-plugin-istanbul: 6.1.1 - chalk: 4.1.2 - convert-source-map: 2.0.0 - fast-json-stable-stringify: 2.1.0 - graceful-fs: 4.2.10 - jest-haste-map: 29.7.0 - jest-regex-util: 29.6.3 - jest-util: 29.7.0 - micromatch: 4.0.5 - pirates: 4.0.5 - slash: 3.0.0 - write-file-atomic: 4.0.2 - transitivePeerDependencies: - - supports-color - dev: true - - /@jest/types@29.1.2: - resolution: {integrity: sha512-DcXGtoTykQB5jiwCmVr8H4vdg2OJhQex3qPkG+ISyDO7xQXbt/4R6dowcRyPemRnkH7JoHvZuxPBdlq+9JxFCg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/schemas': 29.0.0 - '@types/istanbul-lib-coverage': 2.0.4 - '@types/istanbul-reports': 3.0.1 - '@types/node': 18.19.32 - '@types/yargs': 17.0.13 - chalk: 4.1.2 - dev: true - - /@jest/types@29.6.3: - resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/schemas': 29.6.3 - '@types/istanbul-lib-coverage': 2.0.4 - '@types/istanbul-reports': 3.0.1 - '@types/node': 18.19.32 - '@types/yargs': 17.0.13 - chalk: 4.1.2 - dev: true - - /@jridgewell/gen-mapping@0.1.1: - resolution: {integrity: sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==} - engines: {node: '>=6.0.0'} - dependencies: - '@jridgewell/set-array': 1.1.2 - '@jridgewell/sourcemap-codec': 1.4.14 - dev: true - - /@jridgewell/gen-mapping@0.3.2: - resolution: {integrity: sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==} - engines: {node: '>=6.0.0'} - dependencies: - '@jridgewell/set-array': 1.1.2 - '@jridgewell/sourcemap-codec': 1.4.14 - '@jridgewell/trace-mapping': 0.3.15 - dev: true - - /@jridgewell/gen-mapping@0.3.5: - resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} - engines: {node: '>=6.0.0'} - dependencies: - '@jridgewell/set-array': 1.2.1 - '@jridgewell/sourcemap-codec': 1.4.14 - '@jridgewell/trace-mapping': 0.3.25 - dev: true - - /@jridgewell/resolve-uri@3.1.0: - resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} - engines: {node: '>=6.0.0'} - - /@jridgewell/set-array@1.1.2: - resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} - engines: {node: '>=6.0.0'} - dev: true - - /@jridgewell/set-array@1.2.1: - resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} - engines: {node: '>=6.0.0'} - dev: true - - /@jridgewell/sourcemap-codec@1.4.14: - resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} - - /@jridgewell/trace-mapping@0.3.15: - resolution: {integrity: sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==} - dependencies: - '@jridgewell/resolve-uri': 3.1.0 - '@jridgewell/sourcemap-codec': 1.4.14 - dev: true - - /@jridgewell/trace-mapping@0.3.25: - resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} - dependencies: - '@jridgewell/resolve-uri': 3.1.0 - '@jridgewell/sourcemap-codec': 1.4.14 - dev: true - - /@jridgewell/trace-mapping@0.3.9: - resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} - dependencies: - '@jridgewell/resolve-uri': 3.1.0 - '@jridgewell/sourcemap-codec': 1.4.14 - - /@nodelib/fs.scandir@2.1.5: - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 - dev: false - - /@nodelib/fs.stat@2.0.5: - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} - dev: false - - /@nodelib/fs.walk@1.2.8: - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.13.0 - dev: false - - /@sinclair/typebox@0.24.44: - resolution: {integrity: sha512-ka0W0KN5i6LfrSocduwliMMpqVgohtPFidKdMEOUjoOFCHcOOYkKsPRxfs5f15oPNHTm6ERAm0GV/+/LTKeiWg==} - dev: true - - /@sinclair/typebox@0.27.8: - resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} - dev: true - - /@sinonjs/commons@3.0.1: - resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} - dependencies: - type-detect: 4.0.8 - dev: true - - /@sinonjs/fake-timers@10.3.0: - resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} - dependencies: - '@sinonjs/commons': 3.0.1 - dev: true - - /@tsconfig/node10@1.0.9: - resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} - - /@tsconfig/node12@1.0.11: - resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} - - /@tsconfig/node14@1.0.3: - resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} - - /@tsconfig/node16@1.0.3: - resolution: {integrity: sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==} - - /@types/babel__core@7.1.19: - resolution: {integrity: sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw==} - dependencies: - '@babel/parser': 7.19.3 - '@babel/types': 7.19.3 - '@types/babel__generator': 7.6.4 - '@types/babel__template': 7.4.1 - '@types/babel__traverse': 7.18.2 - dev: true - - /@types/babel__generator@7.6.4: - resolution: {integrity: sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==} - dependencies: - '@babel/types': 7.19.3 - dev: true - - /@types/babel__template@7.4.1: - resolution: {integrity: sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==} - dependencies: - '@babel/parser': 7.19.3 - '@babel/types': 7.19.3 - dev: true - - /@types/babel__traverse@7.18.2: - resolution: {integrity: sha512-FcFaxOr2V5KZCviw1TnutEMVUVsGt4D2hP1TAfXZAMKuHYW3xQhe3jTxNPWutgCJ3/X1c5yX8ZoGVEItxKbwBg==} - dependencies: - '@babel/types': 7.19.3 - dev: true - - /@types/fs-extra@11.0.4: - resolution: {integrity: sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==} - dependencies: - '@types/jsonfile': 6.1.4 - '@types/node': 18.19.32 - dev: false - - /@types/graceful-fs@4.1.5: - resolution: {integrity: sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==} - dependencies: - '@types/node': 18.19.32 - dev: true - - /@types/istanbul-lib-coverage@2.0.4: - resolution: {integrity: sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==} - dev: true - - /@types/istanbul-lib-report@3.0.0: - resolution: {integrity: sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==} - dependencies: - '@types/istanbul-lib-coverage': 2.0.4 - dev: true - - /@types/istanbul-reports@3.0.1: - resolution: {integrity: sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==} - dependencies: - '@types/istanbul-lib-report': 3.0.0 - dev: true - - /@types/jest@29.5.12: - resolution: {integrity: sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==} - dependencies: - expect: 29.1.2 - pretty-format: 29.1.2 - dev: true - - /@types/jsonfile@6.1.4: - resolution: {integrity: sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==} - dependencies: - '@types/node': 18.19.32 - dev: false - - /@types/minimist@1.2.2: - resolution: {integrity: sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==} - dev: false - - /@types/node@18.19.32: - resolution: {integrity: sha512-2bkg93YBSDKk8DLmmHnmj/Rwr18TLx7/n+I23BigFwgexUJoMHZOd8X1OFxuF/W3NN0S2W2E5sVabI5CPinNvA==} - dependencies: - undici-types: 5.26.5 - - /@types/ps-tree@1.1.2: - resolution: {integrity: sha512-ZREFYlpUmPQJ0esjxoG1fMvB2HNaD3z+mjqdSosZvd3RalncI9NEur73P8ZJz4YQdL64CmV1w0RuqoRUlhQRBw==} - dev: false - - /@types/stack-utils@2.0.1: - resolution: {integrity: sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==} - dev: true - - /@types/which@3.0.3: - resolution: {integrity: sha512-2C1+XoY0huExTbs8MQv1DuS5FS86+SEjdM9F/+GS61gg5Hqbtj8ZiDSx8MfWcyei907fIPbfPGCOrNUTnVHY1g==} - dev: false - - /@types/yargs-parser@21.0.0: - resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==} - dev: true - - /@types/yargs@17.0.13: - resolution: {integrity: sha512-9sWaruZk2JGxIQU+IhI1fhPYRcQ0UuTNuKuCW9bR5fp7qi2Llf7WDzNa17Cy7TKnh3cdxDOiyTu6gaLS0eDatg==} - dependencies: - '@types/yargs-parser': 21.0.0 - dev: true - - /acorn-walk@8.2.0: - resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} - engines: {node: '>=0.4.0'} - - /acorn@8.8.0: - resolution: {integrity: sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==} - engines: {node: '>=0.4.0'} - hasBin: true - - /ansi-escapes@4.3.2: - resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} - engines: {node: '>=8'} - dependencies: - type-fest: 0.21.3 - dev: true - - /ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} - dev: true - - /ansi-styles@3.2.1: - resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} - engines: {node: '>=4'} - dependencies: - color-convert: 1.9.3 - dev: true - - /ansi-styles@4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} - engines: {node: '>=8'} - dependencies: - color-convert: 2.0.1 - dev: true - - /ansi-styles@5.2.0: - resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} - engines: {node: '>=10'} - dev: true - - /anymatch@3.1.2: - resolution: {integrity: sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==} - engines: {node: '>= 8'} - dependencies: - normalize-path: 3.0.0 - picomatch: 2.3.1 - dev: true - - /arg@4.1.3: - resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} - - /argparse@1.0.10: - resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} - dependencies: - sprintf-js: 1.0.3 - dev: true - - /babel-jest@29.7.0(@babel/core@7.19.3): - resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@babel/core': ^7.8.0 - dependencies: - '@babel/core': 7.19.3 - '@jest/transform': 29.7.0 - '@types/babel__core': 7.1.19 - babel-plugin-istanbul: 6.1.1 - babel-preset-jest: 29.6.3(@babel/core@7.19.3) - chalk: 4.1.2 - graceful-fs: 4.2.10 - slash: 3.0.0 - transitivePeerDependencies: - - supports-color - dev: true - - /babel-plugin-istanbul@6.1.1: - resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} - engines: {node: '>=8'} - dependencies: - '@babel/helper-plugin-utils': 7.19.0 - '@istanbuljs/load-nyc-config': 1.1.0 - '@istanbuljs/schema': 0.1.3 - istanbul-lib-instrument: 5.2.1 - test-exclude: 6.0.0 - transitivePeerDependencies: - - supports-color - dev: true - - /babel-plugin-jest-hoist@29.6.3: - resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@babel/template': 7.18.10 - '@babel/types': 7.19.3 - '@types/babel__core': 7.1.19 - '@types/babel__traverse': 7.18.2 - dev: true - - /babel-preset-current-node-syntax@1.0.1(@babel/core@7.19.3): - resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.19.3 - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.19.3) - '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.19.3) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.19.3) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.19.3) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.19.3) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.19.3) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.19.3) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.19.3) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.19.3) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.19.3) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.19.3) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.19.3) - dev: true - - /babel-preset-jest@29.6.3(@babel/core@7.19.3): - resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.19.3 - babel-plugin-jest-hoist: 29.6.3 - babel-preset-current-node-syntax: 1.0.1(@babel/core@7.19.3) - dev: true - - /balanced-match@1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - dev: true - - /brace-expansion@1.1.11: - resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} - dependencies: - balanced-match: 1.0.2 - concat-map: 0.0.1 - dev: true - - /braces@3.0.2: - resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} - engines: {node: '>=8'} - dependencies: - fill-range: 7.0.1 - - /browserslist@4.21.4: - resolution: {integrity: sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true - dependencies: - caniuse-lite: 1.0.30001418 - electron-to-chromium: 1.4.275 - node-releases: 2.0.6 - update-browserslist-db: 1.0.10(browserslist@4.21.4) - dev: true - - /browserslist@4.23.0: - resolution: {integrity: sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true - dependencies: - caniuse-lite: 1.0.30001607 - electron-to-chromium: 1.4.730 - node-releases: 2.0.14 - update-browserslist-db: 1.0.13(browserslist@4.23.0) - dev: true - - /bs-logger@0.2.6: - resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} - engines: {node: '>= 6'} - dependencies: - fast-json-stable-stringify: 2.1.0 - dev: true - - /bser@2.1.1: - resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} - dependencies: - node-int64: 0.4.0 - dev: true - - /buffer-from@1.1.2: - resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - dev: true - - /callsites@3.1.0: - resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} - engines: {node: '>=6'} - dev: true - - /camelcase@5.3.1: - resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} - engines: {node: '>=6'} - dev: true - - /camelcase@6.3.0: - resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} - engines: {node: '>=10'} - dev: true - - /caniuse-lite@1.0.30001418: - resolution: {integrity: sha512-oIs7+JL3K9JRQ3jPZjlH6qyYDp+nBTCais7hjh0s+fuBwufc7uZ7hPYMXrDOJhV360KGMTcczMRObk0/iMqZRg==} - dev: true - - /caniuse-lite@1.0.30001607: - resolution: {integrity: sha512-WcvhVRjXLKFB/kmOFVwELtMxyhq3iM/MvmXcyCe2PNf166c39mptscOc/45TTS96n2gpNV2z7+NakArTWZCQ3w==} - dev: true - - /chalk@2.4.2: - resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} - engines: {node: '>=4'} - dependencies: - ansi-styles: 3.2.1 - escape-string-regexp: 1.0.5 - supports-color: 5.5.0 - dev: true - - /chalk@4.1.2: - resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} - engines: {node: '>=10'} - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 - dev: true - - /chalk@5.3.0: - resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} - engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - dev: false - - /char-regex@1.0.2: - resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} - engines: {node: '>=10'} - dev: true - - /ci-info@3.4.0: - resolution: {integrity: sha512-t5QdPT5jq3o262DOQ8zA6E1tlH2upmUc4Hlvrbx1pGYJuiiHl7O7rvVNI+l8HTVhd/q3Qc9vqimkNk5yiXsAug==} - dev: true - - /cjs-module-lexer@1.2.2: - resolution: {integrity: sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==} - dev: true - - /cliui@8.0.1: - resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} - engines: {node: '>=12'} - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 7.0.0 - dev: true - - /co@4.6.0: - resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} - engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} - dev: true - - /collect-v8-coverage@1.0.1: - resolution: {integrity: sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==} - dev: true - - /color-convert@1.9.3: - resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} - dependencies: - color-name: 1.1.3 - dev: true - - /color-convert@2.0.1: - resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} - engines: {node: '>=7.0.0'} - dependencies: - color-name: 1.1.4 - dev: true - - /color-name@1.1.3: - resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} - dev: true - - /color-name@1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - dev: true - - /concat-map@0.0.1: - resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=} - dev: true - - /convert-source-map@1.8.0: - resolution: {integrity: sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==} - dependencies: - safe-buffer: 5.1.2 - dev: true - - /convert-source-map@2.0.0: - resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - dev: true - - /create-jest@29.7.0(@types/node@18.19.32)(ts-node@10.9.2): - resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - hasBin: true - dependencies: - '@jest/types': 29.6.3 - chalk: 4.1.2 - exit: 0.1.2 - graceful-fs: 4.2.10 - jest-config: 29.7.0(@types/node@18.19.32)(ts-node@10.9.2) - jest-util: 29.7.0 - prompts: 2.4.2 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - dev: true - - /create-require@1.1.1: - resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} - - /cross-spawn@7.0.3: - resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} - engines: {node: '>= 8'} - dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 - dev: true - - /data-uri-to-buffer@4.0.0: - resolution: {integrity: sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA==} - engines: {node: '>= 12'} - dev: false - - /debug@4.3.4: - resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - dependencies: - ms: 2.1.2 - dev: true - - /dedent@1.5.1: - resolution: {integrity: sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==} - peerDependencies: - babel-plugin-macros: ^3.1.0 - peerDependenciesMeta: - babel-plugin-macros: - optional: true - dev: true - - /deepmerge@4.2.2: - resolution: {integrity: sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==} - engines: {node: '>=0.10.0'} - dev: true - - /detect-newline@3.1.0: - resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} - engines: {node: '>=8'} - dev: true - - /diff-sequences@29.0.0: - resolution: {integrity: sha512-7Qe/zd1wxSDL4D/X/FPjOMB+ZMDt71W94KYaq05I2l0oQqgXgs7s4ftYYmV38gBSrPz2vcygxfs1xn0FT+rKNA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dev: true - - /diff-sequences@29.6.3: - resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dev: true - - /diff@4.0.2: - resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} - engines: {node: '>=0.3.1'} - - /dir-glob@3.0.1: - resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} - engines: {node: '>=8'} - dependencies: - path-type: 4.0.0 - dev: false - - /duplexer@0.1.2: - resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} - dev: false - - /electron-to-chromium@1.4.275: - resolution: {integrity: sha512-aJeQQ+Hl9Jyyzv4chBqYJwmVRY46N5i2BEX5Cuyk/5gFCUZ5F3i7Hnba6snZftWla7Gglwc5pIgcd+E7cW+rPg==} - dev: true - - /electron-to-chromium@1.4.730: - resolution: {integrity: sha512-oJRPo82XEqtQAobHpJIR3zW5YO3sSRRkPz2an4yxi1UvqhsGm54vR/wzTFV74a3soDOJ8CKW7ajOOX5ESzddwg==} - dev: true - - /emittery@0.13.1: - resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} - engines: {node: '>=12'} - dev: true - - /emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - dev: true - - /error-ex@1.3.2: - resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} - dependencies: - is-arrayish: 0.2.1 - dev: true - - /escalade@3.1.1: - resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} - engines: {node: '>=6'} - dev: true - - /escape-string-regexp@1.0.5: - resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} - engines: {node: '>=0.8.0'} - dev: true - - /escape-string-regexp@2.0.0: - resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} - engines: {node: '>=8'} - dev: true - - /esprima@4.0.1: - resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} - engines: {node: '>=4'} - hasBin: true - dev: true - - /event-stream@3.3.4: - resolution: {integrity: sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=} - dependencies: - duplexer: 0.1.2 - from: 0.1.7 - map-stream: 0.1.0 - pause-stream: 0.0.11 - split: 0.3.3 - stream-combiner: 0.0.4 - through: 2.3.8 - dev: false - - /execa@5.1.1: - resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} - engines: {node: '>=10'} - dependencies: - cross-spawn: 7.0.3 - get-stream: 6.0.1 - human-signals: 2.1.0 - is-stream: 2.0.1 - merge-stream: 2.0.0 - npm-run-path: 4.0.1 - onetime: 5.1.2 - signal-exit: 3.0.7 - strip-final-newline: 2.0.0 - dev: true - - /exit@0.1.2: - resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} - engines: {node: '>= 0.8.0'} - dev: true - - /expect@29.1.2: - resolution: {integrity: sha512-AuAGn1uxva5YBbBlXb+2JPxJRuemZsmlGcapPXWNSBNsQtAULfjioREGBWuI0EOvYUKjDnrCy8PW5Zlr1md5mw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/expect-utils': 29.1.2 - jest-get-type: 29.0.0 - jest-matcher-utils: 29.1.2 - jest-message-util: 29.1.2 - jest-util: 29.1.2 - dev: true - - /expect@29.7.0: - resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/expect-utils': 29.7.0 - jest-get-type: 29.6.3 - jest-matcher-utils: 29.7.0 - jest-message-util: 29.7.0 - jest-util: 29.7.0 - dev: true - - /fast-glob@3.3.2: - resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} - engines: {node: '>=8.6.0'} - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.5 - dev: false - - /fast-json-stable-stringify@2.1.0: - resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - dev: true - - /fastq@1.13.0: - resolution: {integrity: sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==} - dependencies: - reusify: 1.0.4 - dev: false - - /fb-watchman@2.0.2: - resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} - dependencies: - bser: 2.1.1 - dev: true - - /fetch-blob@3.2.0: - resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} - engines: {node: ^12.20 || >= 14.13} - dependencies: - node-domexception: 1.0.0 - web-streams-polyfill: 3.2.1 - dev: false - - /fill-range@7.0.1: - resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} - engines: {node: '>=8'} - dependencies: - to-regex-range: 5.0.1 - - /find-up@4.1.0: - resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} - engines: {node: '>=8'} - dependencies: - locate-path: 5.0.0 - path-exists: 4.0.0 - dev: true - - /formdata-polyfill@4.0.10: - resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} - engines: {node: '>=12.20.0'} - dependencies: - fetch-blob: 3.2.0 - dev: false - - /from@0.1.7: - resolution: {integrity: sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==} - dev: false - - /fs-extra@11.2.0: - resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==} - engines: {node: '>=14.14'} - dependencies: - graceful-fs: 4.2.10 - jsonfile: 6.1.0 - universalify: 2.0.0 - dev: false - - /fs.realpath@1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - dev: true - - /fsevents@2.3.2: - resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - requiresBuild: true - dev: true - optional: true - - /function-bind@1.1.1: - resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} - dev: true - - /fx@34.0.0: - resolution: {integrity: sha512-/fZih3/WLsrtlaj2mahjWxAmyuikmcl3D5kKPqLtFmEilLsy9wp0+/vEmfvYXXhwJc+ajtCFDCf+yttXmPMHSQ==} - hasBin: true - dev: false - - /gensync@1.0.0-beta.2: - resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} - engines: {node: '>=6.9.0'} - dev: true - - /get-caller-file@2.0.5: - resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} - engines: {node: 6.* || 8.* || >= 10.*} - dev: true - - /get-package-type@0.1.0: - resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} - engines: {node: '>=8.0.0'} - dev: true - - /get-stream@6.0.1: - resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} - engines: {node: '>=10'} - dev: true - - /glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} - dependencies: - is-glob: 4.0.3 - dev: false - - /glob@7.2.3: - resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.2 - once: 1.4.0 - path-is-absolute: 1.0.1 - dev: true - - /globals@11.12.0: - resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} - engines: {node: '>=4'} - dev: true - - /globby@13.2.2: - resolution: {integrity: sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dependencies: - dir-glob: 3.0.1 - fast-glob: 3.3.2 - ignore: 5.3.1 - merge2: 1.4.1 - slash: 4.0.0 - dev: false - - /graceful-fs@4.2.10: - resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} - - /has-flag@3.0.0: - resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} - engines: {node: '>=4'} - dev: true - - /has-flag@4.0.0: - resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} - engines: {node: '>=8'} - dev: true - - /has@1.0.3: - resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} - engines: {node: '>= 0.4.0'} - dependencies: - function-bind: 1.1.1 - dev: true - - /html-escaper@2.0.2: - resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} - dev: true - - /human-signals@2.1.0: - resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} - engines: {node: '>=10.17.0'} - dev: true - - /ignore@5.3.1: - resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} - engines: {node: '>= 4'} - dev: false - - /import-local@3.1.0: - resolution: {integrity: sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==} - engines: {node: '>=8'} - hasBin: true - dependencies: - pkg-dir: 4.2.0 - resolve-cwd: 3.0.0 - dev: true - - /imurmurhash@0.1.4: - resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} - engines: {node: '>=0.8.19'} - dev: true - - /inflight@1.0.6: - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - dependencies: - once: 1.4.0 - wrappy: 1.0.2 - dev: true - - /inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - dev: true - - /is-arrayish@0.2.1: - resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - dev: true - - /is-core-module@2.10.0: - resolution: {integrity: sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==} - dependencies: - has: 1.0.3 - dev: true - - /is-extglob@2.1.1: - resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} - engines: {node: '>=0.10.0'} - dev: false - - /is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} - dev: true - - /is-generator-fn@2.1.0: - resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} - engines: {node: '>=6'} - dev: true - - /is-glob@4.0.3: - resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} - engines: {node: '>=0.10.0'} - dependencies: - is-extglob: 2.1.1 - dev: false - - /is-number@7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} - - /is-stream@2.0.1: - resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} - engines: {node: '>=8'} - dev: true - - /isexe@2.0.0: - resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - - /istanbul-lib-coverage@3.2.0: - resolution: {integrity: sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==} - engines: {node: '>=8'} - dev: true - - /istanbul-lib-instrument@5.2.1: - resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} - engines: {node: '>=8'} - dependencies: - '@babel/core': 7.19.3 - '@babel/parser': 7.19.3 - '@istanbuljs/schema': 0.1.3 - istanbul-lib-coverage: 3.2.0 - semver: 6.3.0 - transitivePeerDependencies: - - supports-color - dev: true - - /istanbul-lib-instrument@6.0.2: - resolution: {integrity: sha512-1WUsZ9R1lA0HtBSohTkm39WTPlNKSJ5iFk7UwqXkBLoHQT+hfqPsfsTDVuZdKGaBwn7din9bS7SsnoAr943hvw==} - engines: {node: '>=10'} - dependencies: - '@babel/core': 7.24.4 - '@babel/parser': 7.24.4 - '@istanbuljs/schema': 0.1.3 - istanbul-lib-coverage: 3.2.0 - semver: 7.6.0 - transitivePeerDependencies: - - supports-color - dev: true - - /istanbul-lib-report@3.0.0: - resolution: {integrity: sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==} - engines: {node: '>=8'} - dependencies: - istanbul-lib-coverage: 3.2.0 - make-dir: 3.1.0 - supports-color: 7.2.0 - dev: true - - /istanbul-lib-source-maps@4.0.1: - resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} - engines: {node: '>=10'} - dependencies: - debug: 4.3.4 - istanbul-lib-coverage: 3.2.0 - source-map: 0.6.1 - transitivePeerDependencies: - - supports-color - dev: true - - /istanbul-reports@3.1.5: - resolution: {integrity: sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==} - engines: {node: '>=8'} - dependencies: - html-escaper: 2.0.2 - istanbul-lib-report: 3.0.0 - dev: true - - /jest-changed-files@29.7.0: - resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - execa: 5.1.1 - jest-util: 29.7.0 - p-limit: 3.1.0 - dev: true - - /jest-circus@29.7.0: - resolution: {integrity: sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/environment': 29.7.0 - '@jest/expect': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 18.19.32 - chalk: 4.1.2 - co: 4.6.0 - dedent: 1.5.1 - is-generator-fn: 2.1.0 - jest-each: 29.7.0 - jest-matcher-utils: 29.7.0 - jest-message-util: 29.7.0 - jest-runtime: 29.7.0 - jest-snapshot: 29.7.0 - jest-util: 29.7.0 - p-limit: 3.1.0 - pretty-format: 29.7.0 - pure-rand: 6.1.0 - slash: 3.0.0 - stack-utils: 2.0.5 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - dev: true - - /jest-cli@29.7.0(@types/node@18.19.32)(ts-node@10.9.2): - resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - hasBin: true - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2) - '@jest/test-result': 29.7.0 - '@jest/types': 29.6.3 - chalk: 4.1.2 - create-jest: 29.7.0(@types/node@18.19.32)(ts-node@10.9.2) - exit: 0.1.2 - import-local: 3.1.0 - jest-config: 29.7.0(@types/node@18.19.32)(ts-node@10.9.2) - jest-util: 29.7.0 - jest-validate: 29.7.0 - yargs: 17.6.0 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - dev: true - - /jest-config@29.7.0(@types/node@18.19.32)(ts-node@10.9.2): - resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@types/node': '*' - ts-node: '>=9.0.0' - peerDependenciesMeta: - '@types/node': - optional: true - ts-node: - optional: true - dependencies: - '@babel/core': 7.19.3 - '@jest/test-sequencer': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 18.19.32 - babel-jest: 29.7.0(@babel/core@7.19.3) - chalk: 4.1.2 - ci-info: 3.4.0 - deepmerge: 4.2.2 - glob: 7.2.3 - graceful-fs: 4.2.10 - jest-circus: 29.7.0 - jest-environment-node: 29.7.0 - jest-get-type: 29.6.3 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-runner: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - micromatch: 4.0.5 - parse-json: 5.2.0 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-json-comments: 3.1.1 - ts-node: 10.9.2(@types/node@18.19.32)(typescript@5.4.5) - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - dev: true - - /jest-diff@29.1.2: - resolution: {integrity: sha512-4GQts0aUopVvecIT4IwD/7xsBaMhKTYoM4/njE/aVw9wpw+pIUVp8Vab/KnSzSilr84GnLBkaP3JLDnQYCKqVQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - chalk: 4.1.2 - diff-sequences: 29.0.0 - jest-get-type: 29.0.0 - pretty-format: 29.1.2 - dev: true - - /jest-diff@29.7.0: - resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - chalk: 4.1.2 - diff-sequences: 29.6.3 - jest-get-type: 29.6.3 - pretty-format: 29.7.0 - dev: true - - /jest-docblock@29.7.0: - resolution: {integrity: sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - detect-newline: 3.1.0 - dev: true - - /jest-each@29.7.0: - resolution: {integrity: sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.6.3 - chalk: 4.1.2 - jest-get-type: 29.6.3 - jest-util: 29.7.0 - pretty-format: 29.7.0 - dev: true - - /jest-environment-node@29.7.0: - resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/environment': 29.7.0 - '@jest/fake-timers': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 18.19.32 - jest-mock: 29.7.0 - jest-util: 29.7.0 - dev: true - - /jest-get-type@29.0.0: - resolution: {integrity: sha512-83X19z/HuLKYXYHskZlBAShO7UfLFXu/vWajw9ZNJASN32li8yHMaVGAQqxFW1RCFOkB7cubaL6FaJVQqqJLSw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dev: true - - /jest-get-type@29.6.3: - resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dev: true - - /jest-haste-map@29.7.0: - resolution: {integrity: sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.6.3 - '@types/graceful-fs': 4.1.5 - '@types/node': 18.19.32 - anymatch: 3.1.2 - fb-watchman: 2.0.2 - graceful-fs: 4.2.10 - jest-regex-util: 29.6.3 - jest-util: 29.7.0 - jest-worker: 29.7.0 - micromatch: 4.0.5 - walker: 1.0.8 - optionalDependencies: - fsevents: 2.3.2 - dev: true - - /jest-leak-detector@29.7.0: - resolution: {integrity: sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - jest-get-type: 29.6.3 - pretty-format: 29.7.0 - dev: true - - /jest-matcher-utils@29.1.2: - resolution: {integrity: sha512-MV5XrD3qYSW2zZSHRRceFzqJ39B2z11Qv0KPyZYxnzDHFeYZGJlgGi0SW+IXSJfOewgJp/Km/7lpcFT+cgZypw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - chalk: 4.1.2 - jest-diff: 29.1.2 - jest-get-type: 29.0.0 - pretty-format: 29.1.2 - dev: true - - /jest-matcher-utils@29.7.0: - resolution: {integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - chalk: 4.1.2 - jest-diff: 29.7.0 - jest-get-type: 29.6.3 - pretty-format: 29.7.0 - dev: true - - /jest-message-util@29.1.2: - resolution: {integrity: sha512-9oJ2Os+Qh6IlxLpmvshVbGUiSkZVc2FK+uGOm6tghafnB2RyjKAxMZhtxThRMxfX1J1SOMhTn9oK3/MutRWQJQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@babel/code-frame': 7.18.6 - '@jest/types': 29.1.2 - '@types/stack-utils': 2.0.1 - chalk: 4.1.2 - graceful-fs: 4.2.10 - micromatch: 4.0.5 - pretty-format: 29.1.2 - slash: 3.0.0 - stack-utils: 2.0.5 - dev: true - - /jest-message-util@29.7.0: - resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@babel/code-frame': 7.18.6 - '@jest/types': 29.6.3 - '@types/stack-utils': 2.0.1 - chalk: 4.1.2 - graceful-fs: 4.2.10 - micromatch: 4.0.5 - pretty-format: 29.7.0 - slash: 3.0.0 - stack-utils: 2.0.5 - dev: true - - /jest-mock@29.7.0: - resolution: {integrity: sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.6.3 - '@types/node': 18.19.32 - jest-util: 29.7.0 - dev: true - - /jest-pnp-resolver@1.2.2(jest-resolve@29.7.0): - resolution: {integrity: sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==} - engines: {node: '>=6'} - peerDependencies: - jest-resolve: '*' - peerDependenciesMeta: - jest-resolve: - optional: true - dependencies: - jest-resolve: 29.7.0 - dev: true - - /jest-regex-util@29.6.3: - resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dev: true - - /jest-resolve-dependencies@29.7.0: - resolution: {integrity: sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - jest-regex-util: 29.6.3 - jest-snapshot: 29.7.0 - transitivePeerDependencies: - - supports-color - dev: true - - /jest-resolve@29.7.0: - resolution: {integrity: sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - chalk: 4.1.2 - graceful-fs: 4.2.10 - jest-haste-map: 29.7.0 - jest-pnp-resolver: 1.2.2(jest-resolve@29.7.0) - jest-util: 29.7.0 - jest-validate: 29.7.0 - resolve: 1.22.1 - resolve.exports: 2.0.2 - slash: 3.0.0 - dev: true - - /jest-runner@29.7.0: - resolution: {integrity: sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/console': 29.7.0 - '@jest/environment': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 18.19.32 - chalk: 4.1.2 - emittery: 0.13.1 - graceful-fs: 4.2.10 - jest-docblock: 29.7.0 - jest-environment-node: 29.7.0 - jest-haste-map: 29.7.0 - jest-leak-detector: 29.7.0 - jest-message-util: 29.7.0 - jest-resolve: 29.7.0 - jest-runtime: 29.7.0 - jest-util: 29.7.0 - jest-watcher: 29.7.0 - jest-worker: 29.7.0 - p-limit: 3.1.0 - source-map-support: 0.5.13 - transitivePeerDependencies: - - supports-color - dev: true - - /jest-runtime@29.7.0: - resolution: {integrity: sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/environment': 29.7.0 - '@jest/fake-timers': 29.7.0 - '@jest/globals': 29.7.0 - '@jest/source-map': 29.6.3 - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 18.19.32 - chalk: 4.1.2 - cjs-module-lexer: 1.2.2 - collect-v8-coverage: 1.0.1 - glob: 7.2.3 - graceful-fs: 4.2.10 - jest-haste-map: 29.7.0 - jest-message-util: 29.7.0 - jest-mock: 29.7.0 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-snapshot: 29.7.0 - jest-util: 29.7.0 - slash: 3.0.0 - strip-bom: 4.0.0 - transitivePeerDependencies: - - supports-color - dev: true - - /jest-snapshot@29.7.0: - resolution: {integrity: sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@babel/core': 7.19.3 - '@babel/generator': 7.19.3 - '@babel/plugin-syntax-jsx': 7.18.6(@babel/core@7.19.3) - '@babel/plugin-syntax-typescript': 7.18.6(@babel/core@7.19.3) - '@babel/types': 7.19.3 - '@jest/expect-utils': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - babel-preset-current-node-syntax: 1.0.1(@babel/core@7.19.3) - chalk: 4.1.2 - expect: 29.7.0 - graceful-fs: 4.2.10 - jest-diff: 29.7.0 - jest-get-type: 29.6.3 - jest-matcher-utils: 29.7.0 - jest-message-util: 29.7.0 - jest-util: 29.7.0 - natural-compare: 1.4.0 - pretty-format: 29.7.0 - semver: 7.6.0 - transitivePeerDependencies: - - supports-color - dev: true - - /jest-util@29.1.2: - resolution: {integrity: sha512-vPCk9F353i0Ymx3WQq3+a4lZ07NXu9Ca8wya6o4Fe4/aO1e1awMMprZ3woPFpKwghEOW+UXgd15vVotuNN9ONQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.1.2 - '@types/node': 18.19.32 - chalk: 4.1.2 - ci-info: 3.4.0 - graceful-fs: 4.2.10 - picomatch: 2.3.1 - dev: true - - /jest-util@29.7.0: - resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.6.3 - '@types/node': 18.19.32 - chalk: 4.1.2 - ci-info: 3.4.0 - graceful-fs: 4.2.10 - picomatch: 2.3.1 - dev: true - - /jest-validate@29.7.0: - resolution: {integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.6.3 - camelcase: 6.3.0 - chalk: 4.1.2 - jest-get-type: 29.6.3 - leven: 3.1.0 - pretty-format: 29.7.0 - dev: true - - /jest-watcher@29.7.0: - resolution: {integrity: sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/test-result': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 18.19.32 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - emittery: 0.13.1 - jest-util: 29.7.0 - string-length: 4.0.2 - dev: true - - /jest-worker@29.7.0: - resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@types/node': 18.19.32 - jest-util: 29.7.0 - merge-stream: 2.0.0 - supports-color: 8.1.1 - dev: true - - /jest@29.7.0(@types/node@18.19.32)(ts-node@10.9.2): - resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - hasBin: true - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2) - '@jest/types': 29.6.3 - import-local: 3.1.0 - jest-cli: 29.7.0(@types/node@18.19.32)(ts-node@10.9.2) - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - dev: true - - /js-tokens@4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - dev: true - - /js-yaml@3.14.1: - resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} - hasBin: true - dependencies: - argparse: 1.0.10 - esprima: 4.0.1 - dev: true - - /jsesc@2.5.2: - resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} - engines: {node: '>=4'} - hasBin: true - dev: true - - /json-parse-even-better-errors@2.3.1: - resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - dev: true - - /json5@2.2.3: - resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} - engines: {node: '>=6'} - hasBin: true - dev: true - - /jsonfile@6.1.0: - resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} - dependencies: - universalify: 2.0.0 - optionalDependencies: - graceful-fs: 4.2.10 - dev: false - - /kleur@3.0.3: - resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} - engines: {node: '>=6'} - dev: true - - /leven@3.1.0: - resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} - engines: {node: '>=6'} - dev: true - - /lines-and-columns@1.2.4: - resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - dev: true - - /locate-path@5.0.0: - resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} - engines: {node: '>=8'} - dependencies: - p-locate: 4.1.0 - dev: true - - /lodash.memoize@4.1.2: - resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} - dev: true - - /lru-cache@5.1.1: - resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - dependencies: - yallist: 3.1.1 - dev: true - - /lru-cache@6.0.0: - resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} - engines: {node: '>=10'} - dependencies: - yallist: 4.0.0 - dev: true - - /make-dir@3.1.0: - resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} - engines: {node: '>=8'} - dependencies: - semver: 6.3.0 - dev: true - - /make-error@1.3.6: - resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} - - /makeerror@1.0.12: - resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} - dependencies: - tmpl: 1.0.5 - dev: true - - /map-stream@0.1.0: - resolution: {integrity: sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==} - dev: false - - /merge-stream@2.0.0: - resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - dev: true - - /merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - dev: false - - /micromatch@4.0.5: - resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} - engines: {node: '>=8.6'} - dependencies: - braces: 3.0.2 - picomatch: 2.3.1 - - /mimic-fn@2.1.0: - resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} - engines: {node: '>=6'} - dev: true - - /minimatch@3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - dependencies: - brace-expansion: 1.1.11 - dev: true - - /minimist@1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - dev: false - - /ms@2.1.2: - resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} - dev: true - - /natural-compare@1.4.0: - resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - dev: true - - /node-domexception@1.0.0: - resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} - engines: {node: '>=10.5.0'} - dev: false - - /node-fetch@3.3.1: - resolution: {integrity: sha512-cRVc/kyto/7E5shrWca1Wsea4y6tL9iYJE5FBCius3JQfb/4P4I295PfhgbJQBLTx6lATE4z+wK0rPM4VS2uow==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dependencies: - data-uri-to-buffer: 4.0.0 - fetch-blob: 3.2.0 - formdata-polyfill: 4.0.10 - dev: false - - /node-int64@0.4.0: - resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} - dev: true - - /node-releases@2.0.14: - resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} - dev: true - - /node-releases@2.0.6: - resolution: {integrity: sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==} - dev: true - - /normalize-path@3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} - engines: {node: '>=0.10.0'} - dev: true - - /npm-run-path@4.0.1: - resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} - engines: {node: '>=8'} - dependencies: - path-key: 3.1.1 - dev: true - - /once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - dependencies: - wrappy: 1.0.2 - dev: true - - /onetime@5.1.2: - resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} - engines: {node: '>=6'} - dependencies: - mimic-fn: 2.1.0 - dev: true - - /p-limit@2.3.0: - resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} - engines: {node: '>=6'} - dependencies: - p-try: 2.2.0 - dev: true - - /p-limit@3.1.0: - resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} - engines: {node: '>=10'} - dependencies: - yocto-queue: 0.1.0 - dev: true - - /p-locate@4.1.0: - resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} - engines: {node: '>=8'} - dependencies: - p-limit: 2.3.0 - dev: true - - /p-try@2.2.0: - resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} - engines: {node: '>=6'} - dev: true - - /parse-json@5.2.0: - resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} - engines: {node: '>=8'} - dependencies: - '@babel/code-frame': 7.18.6 - error-ex: 1.3.2 - json-parse-even-better-errors: 2.3.1 - lines-and-columns: 1.2.4 - dev: true - - /path-exists@4.0.0: - resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} - engines: {node: '>=8'} - dev: true - - /path-is-absolute@1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} - dev: true - - /path-key@3.1.1: - resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} - engines: {node: '>=8'} - dev: true - - /path-parse@1.0.7: - resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - dev: true - - /path-type@4.0.0: - resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} - engines: {node: '>=8'} - dev: false - - /pause-stream@0.0.11: - resolution: {integrity: sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=} - dependencies: - through: 2.3.8 - dev: false - - /picocolors@1.0.0: - resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} - dev: true - - /picomatch@2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} - engines: {node: '>=8.6'} - - /pirates@4.0.5: - resolution: {integrity: sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==} - engines: {node: '>= 6'} - dev: true - - /pkg-dir@4.2.0: - resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} - engines: {node: '>=8'} - dependencies: - find-up: 4.1.0 - dev: true - - /pretty-format@29.1.2: - resolution: {integrity: sha512-CGJ6VVGXVRP2o2Dorl4mAwwvDWT25luIsYhkyVQW32E4nL+TgW939J7LlKT/npq5Cpq6j3s+sy+13yk7xYpBmg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/schemas': 29.0.0 - ansi-styles: 5.2.0 - react-is: 18.2.0 - dev: true - - /pretty-format@29.7.0: - resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/schemas': 29.6.3 - ansi-styles: 5.2.0 - react-is: 18.2.0 - dev: true - - /prompts@2.4.2: - resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} - engines: {node: '>= 6'} - dependencies: - kleur: 3.0.3 - sisteransi: 1.0.5 - dev: true - - /ps-tree@1.2.0: - resolution: {integrity: sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==} - engines: {node: '>= 0.10'} - hasBin: true - dependencies: - event-stream: 3.3.4 - dev: false - - /pure-rand@6.1.0: - resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} - dev: true - - /queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - dev: false - - /react-is@18.2.0: - resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} - dev: true - - /require-directory@2.1.1: - resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} - engines: {node: '>=0.10.0'} - dev: true - - /resolve-cwd@3.0.0: - resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} - engines: {node: '>=8'} - dependencies: - resolve-from: 5.0.0 - dev: true - - /resolve-from@5.0.0: - resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} - engines: {node: '>=8'} - dev: true - - /resolve.exports@2.0.2: - resolution: {integrity: sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==} - engines: {node: '>=10'} - dev: true - - /resolve@1.22.1: - resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==} - hasBin: true - dependencies: - is-core-module: 2.10.0 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 - dev: true - - /reusify@1.0.4: - resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - dev: false - - /run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - dependencies: - queue-microtask: 1.2.3 - dev: false - - /safe-buffer@5.1.2: - resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} - dev: true - - /semver@6.3.0: - resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==} - hasBin: true - dev: true - - /semver@6.3.1: - resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} - hasBin: true - dev: true - - /semver@7.6.0: - resolution: {integrity: sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==} - engines: {node: '>=10'} - hasBin: true - dependencies: - lru-cache: 6.0.0 - dev: true - - /shebang-command@2.0.0: - resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} - engines: {node: '>=8'} - dependencies: - shebang-regex: 3.0.0 - dev: true - - /shebang-regex@3.0.0: - resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} - engines: {node: '>=8'} - dev: true - - /signal-exit@3.0.7: - resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} - dev: true - - /sisteransi@1.0.5: - resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} - dev: true - - /slash@3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} - engines: {node: '>=8'} - dev: true - - /slash@4.0.0: - resolution: {integrity: sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==} - engines: {node: '>=12'} - dev: false - - /source-map-support@0.5.13: - resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} - dependencies: - buffer-from: 1.1.2 - source-map: 0.6.1 - dev: true - - /source-map@0.6.1: - resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} - engines: {node: '>=0.10.0'} - dev: true - - /split@0.3.3: - resolution: {integrity: sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==} - dependencies: - through: 2.3.8 - dev: false - - /sprintf-js@1.0.3: - resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - dev: true - - /stack-utils@2.0.5: - resolution: {integrity: sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==} - engines: {node: '>=10'} - dependencies: - escape-string-regexp: 2.0.0 - dev: true - - /stream-combiner@0.0.4: - resolution: {integrity: sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==} - dependencies: - duplexer: 0.1.2 - dev: false - - /string-length@4.0.2: - resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} - engines: {node: '>=10'} - dependencies: - char-regex: 1.0.2 - strip-ansi: 6.0.1 - dev: true - - /string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} - dependencies: - emoji-regex: 8.0.0 - is-fullwidth-code-point: 3.0.0 - strip-ansi: 6.0.1 - dev: true - - /strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} - dependencies: - ansi-regex: 5.0.1 - dev: true - - /strip-bom@4.0.0: - resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} - engines: {node: '>=8'} - dev: true - - /strip-final-newline@2.0.0: - resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} - engines: {node: '>=6'} - dev: true - - /strip-json-comments@3.1.1: - resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} - engines: {node: '>=8'} - dev: true - - /supports-color@5.5.0: - resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} - engines: {node: '>=4'} - dependencies: - has-flag: 3.0.0 - dev: true - - /supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} - dependencies: - has-flag: 4.0.0 - dev: true - - /supports-color@8.1.1: - resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} - engines: {node: '>=10'} - dependencies: - has-flag: 4.0.0 - dev: true - - /supports-preserve-symlinks-flag@1.0.0: - resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} - engines: {node: '>= 0.4'} - dev: true - - /test-exclude@6.0.0: - resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} - engines: {node: '>=8'} - dependencies: - '@istanbuljs/schema': 0.1.3 - glob: 7.2.3 - minimatch: 3.1.2 - dev: true - - /through@2.3.8: - resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} - dev: false - - /tmpl@1.0.5: - resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} - dev: true - - /to-fast-properties@2.0.0: - resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} - engines: {node: '>=4'} - dev: true - - /to-regex-range@5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} - dependencies: - is-number: 7.0.0 - - /ts-jest@29.1.2(@babel/core@7.19.3)(jest@29.7.0)(typescript@5.4.5): - resolution: {integrity: sha512-br6GJoH/WUX4pu7FbZXuWGKGNDuU7b8Uj77g/Sp7puZV6EXzuByl6JrECvm0MzVzSTkSHWTihsXt+5XYER5b+g==} - engines: {node: ^16.10.0 || ^18.0.0 || >=20.0.0} - hasBin: true - peerDependencies: - '@babel/core': '>=7.0.0-beta.0 <8' - '@jest/types': ^29.0.0 - babel-jest: ^29.0.0 - esbuild: '*' - jest: ^29.0.0 - typescript: '>=4.3 <6' - peerDependenciesMeta: - '@babel/core': - optional: true - '@jest/types': - optional: true - babel-jest: - optional: true - esbuild: - optional: true - dependencies: - '@babel/core': 7.19.3 - bs-logger: 0.2.6 - fast-json-stable-stringify: 2.1.0 - jest: 29.7.0(@types/node@18.19.32)(ts-node@10.9.2) - jest-util: 29.1.2 - json5: 2.2.3 - lodash.memoize: 4.1.2 - make-error: 1.3.6 - semver: 7.6.0 - typescript: 5.4.5 - yargs-parser: 21.1.1 - dev: true - - /ts-node@10.9.2(@types/node@18.19.32)(typescript@5.4.5): - resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} - hasBin: true - peerDependencies: - '@swc/core': '>=1.2.50' - '@swc/wasm': '>=1.2.50' - '@types/node': '*' - typescript: '>=2.7' - peerDependenciesMeta: - '@swc/core': - optional: true - '@swc/wasm': - optional: true - dependencies: - '@cspotcode/source-map-support': 0.8.1 - '@tsconfig/node10': 1.0.9 - '@tsconfig/node12': 1.0.11 - '@tsconfig/node14': 1.0.3 - '@tsconfig/node16': 1.0.3 - '@types/node': 18.19.32 - acorn: 8.8.0 - acorn-walk: 8.2.0 - arg: 4.1.3 - create-require: 1.1.1 - diff: 4.0.2 - make-error: 1.3.6 - typescript: 5.4.5 - v8-compile-cache-lib: 3.0.1 - yn: 3.1.1 - - /tunnel@0.0.6: - resolution: {integrity: sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==} - engines: {node: '>=0.6.11 <=0.7.0 || >=0.7.3'} - dev: false - - /type-detect@4.0.8: - resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} - engines: {node: '>=4'} - dev: true - - /type-fest@0.21.3: - resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} - engines: {node: '>=10'} - dev: true - - /typescript@5.4.5: - resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==} - engines: {node: '>=14.17'} - hasBin: true - - /undici-types@5.26.5: - resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} - - /universalify@2.0.0: - resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==} - engines: {node: '>= 10.0.0'} - dev: false - - /update-browserslist-db@1.0.10(browserslist@4.21.4): - resolution: {integrity: sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' - dependencies: - browserslist: 4.21.4 - escalade: 3.1.1 - picocolors: 1.0.0 - dev: true - - /update-browserslist-db@1.0.13(browserslist@4.23.0): - resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' - dependencies: - browserslist: 4.23.0 - escalade: 3.1.1 - picocolors: 1.0.0 - dev: true - - /uuid@8.3.2: - resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} - hasBin: true - dev: false - - /v8-compile-cache-lib@3.0.1: - resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} - - /v8-to-istanbul@9.0.1: - resolution: {integrity: sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w==} - engines: {node: '>=10.12.0'} - dependencies: - '@jridgewell/trace-mapping': 0.3.25 - '@types/istanbul-lib-coverage': 2.0.4 - convert-source-map: 1.8.0 - dev: true - - /walker@1.0.8: - resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} - dependencies: - makeerror: 1.0.12 - dev: true - - /web-streams-polyfill@3.2.1: - resolution: {integrity: sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==} - engines: {node: '>= 8'} - dev: false - - /webpod@0.0.2: - resolution: {integrity: sha512-cSwwQIeg8v4i3p4ajHhwgR7N6VyxAf+KYSSsY6Pd3aETE+xEU4vbitz7qQkB0I321xnhDdgtxuiSfk5r/FVtjg==} - hasBin: true - dev: false - - /which@2.0.2: - resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} - engines: {node: '>= 8'} - hasBin: true - dependencies: - isexe: 2.0.0 - dev: true - - /which@3.0.1: - resolution: {integrity: sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - hasBin: true - dependencies: - isexe: 2.0.0 - dev: false - - /wrap-ansi@7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - dev: true - - /wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - dev: true - - /write-file-atomic@4.0.2: - resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - dependencies: - imurmurhash: 0.1.4 - signal-exit: 3.0.7 - dev: true - - /y18n@5.0.8: - resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} - engines: {node: '>=10'} - dev: true - - /yallist@3.1.1: - resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - dev: true - - /yallist@4.0.0: - resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - dev: true - - /yaml@2.4.1: - resolution: {integrity: sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg==} - engines: {node: '>= 14'} - hasBin: true - dev: false - - /yargs-parser@21.1.1: - resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} - engines: {node: '>=12'} - dev: true - - /yargs@17.6.0: - resolution: {integrity: sha512-8H/wTDqlSwoSnScvV2N/JHfLWOKuh5MVla9hqLjK3nsfyy6Y4kDSYSvkU5YCUEPOSnRXfIyx3Sq+B/IWudTo4g==} - engines: {node: '>=12'} - dependencies: - cliui: 8.0.1 - escalade: 3.1.1 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - string-width: 4.2.3 - y18n: 5.0.8 - yargs-parser: 21.1.1 - dev: true - - /yn@3.1.1: - resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} - engines: {node: '>=6'} - - /yocto-queue@0.1.0: - resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} - engines: {node: '>=10'} - dev: true - - /zx@7.2.3: - resolution: {integrity: sha512-QODu38nLlYXg/B/Gw7ZKiZrvPkEsjPN3LQ5JFXM7h0JvwhEdPNNl+4Ao1y4+o3CLNiDUNcwzQYZ4/Ko7kKzCMA==} - engines: {node: '>= 16.0.0'} - hasBin: true - dependencies: - '@types/fs-extra': 11.0.4 - '@types/minimist': 1.2.2 - '@types/node': 18.19.32 - '@types/ps-tree': 1.1.2 - '@types/which': 3.0.3 - chalk: 5.3.0 - fs-extra: 11.2.0 - fx: 34.0.0 - globby: 13.2.2 - minimist: 1.2.8 - node-fetch: 3.3.1 - ps-tree: 1.2.0 - webpod: 0.0.2 - which: 3.0.1 - yaml: 2.4.1 - dev: false diff --git a/.github/actions/split-tests/src/index.mts b/.github/actions/split-tests/src/index.mts deleted file mode 100644 index 43f9eb1316..0000000000 --- a/.github/actions/split-tests/src/index.mts +++ /dev/null @@ -1,74 +0,0 @@ -import { $, cd, glob, fs } from "zx"; -import path from "node:path"; -import { setOutput } from "@actions/core"; -import { SolidityConfig, SoliditySplit } from "./types.mjs"; -import { sieveSlowTests } from "./sieve.mjs"; -import { simpleSplit } from "./splitter.mjs"; - -/** - * Get a JSON formatted config file - * - * @param path The path to the config relative to the git root - */ -function getConfigFrom(path?: string): SolidityConfig { - if (!path) { - throw Error("No config path given, specify a path via $CONFIG"); - } - try { - const config = fs.readJsonSync(path); - return config; - } catch (e: unknown) { - throw Error( - `Could not find config file at path: ${path}. ${(e as Error).message}` - ); - } -} - -async function main() { - $.verbose = false; - await runAtGitRoot(); - const configPath = process.env.CONFIG; - const config = getConfigFrom(configPath); - if (config.type === "solidity") { - await handleSolidity(config); - } else { - throw Error(`Invalid config given`); - } -} -main(); - -async function handleSolidity(config: SolidityConfig) { - const { basePath, splits: configBySplit } = config; - const splits = await Promise.all( - configBySplit.map( - async ({ dir, numOfSplits, slowTests: slowTestMatchers }) => { - const globPath = path.join(basePath, dir, "/**/*.test.ts"); - const rawTests = await glob(globPath); - const pathMappedTests = rawTests.map((r) => - r.replace("contracts/", "") - ); - const { filteredTests, slowTests } = sieveSlowTests( - pathMappedTests, - slowTestMatchers - ); - const testsBySplit = simpleSplit(filteredTests, slowTests, numOfSplits); - const splits: SoliditySplit[] = testsBySplit.map((tests, i) => ({ - idx: `${dir}_${i + 1}`, - id: `${dir} ${i + 1}/${numOfSplits}`, - tests: tests.join(" "), - coverageTests: - tests.length === 1 ? tests.join(",") : `{${tests.join(",")}}`, - })); - return splits; - } - ) - ); - - const serializedSplits = JSON.stringify(splits.flat()); - setOutput("splits", serializedSplits); -} - -async function runAtGitRoot() { - const gitRoot = await $`git rev-parse --show-toplevel`; - cd(gitRoot.stdout.trimEnd()); -} diff --git a/.github/actions/split-tests/src/sieve.mts b/.github/actions/split-tests/src/sieve.mts deleted file mode 100644 index 93573669a7..0000000000 --- a/.github/actions/split-tests/src/sieve.mts +++ /dev/null @@ -1,27 +0,0 @@ -import {Tests} from "./types.mjs"; - -export function sieveSlowTests(tests: Tests, slowTestMatchers?: string[]) { - const slowTests: Tests = []; - const filteredTests: Tests = []; - - if (!slowTestMatchers) { - return {slowTests, filteredTests: tests}; - } - - // If the user supplies slow test matchers - // then we go through each test to see if we get a case sensitive match - - tests.forEach((t) => { - const isSlow = slowTestMatchers.reduce( - (isSlow, matcher) => t.includes(matcher) || isSlow, - false - ); - if (isSlow) { - slowTests.push(t); - } else { - filteredTests.push(t); - } - }); - - return {slowTests, filteredTests}; -} diff --git a/.github/actions/split-tests/src/splitter.mts b/.github/actions/split-tests/src/splitter.mts deleted file mode 100644 index f924df5508..0000000000 --- a/.github/actions/split-tests/src/splitter.mts +++ /dev/null @@ -1,43 +0,0 @@ -import {Tests, TestsBySplit} from "./types.mjs"; - -/** - * Split tests by first prioritizing slow tests being spread over each split, then filling each split by test list order. - * - * @example - * Given the following arguments: - * tests: ['foo.test', 'bar.test', 'baz.test', 'yup.test', 'nope.test'] - * slowTests: ['bonk.test', 'bop.test', 'ouch.test.ts'] - * numOfSplits: 2 - * - * We get the following output: - * 1. Spread slow tests across splits: [['bonk.test', 'ouch.test.ts'], ['bop.test']] - * 2. Insert list of tests: [['bonk.test', 'ouch.test.ts', 'foo.test', 'bar.test'], ['bop.test', 'baz.test', 'yup.test', 'nope.test']] - * - * @param tests A list of tests to distribute across splits by the test list order - * @param slowTests A list of slow tests, where the list of tests is evenly distributed across all splits before inserting regular tests - * @param numOfSplits The number of splits to spread tests across - */ -export function simpleSplit( - tests: Tests, - slowTests: Tests, - numOfSplits: number -): TestsBySplit { - const maxTestsPerSplit = Math.max(tests.length / numOfSplits); - - const testsBySplit: TestsBySplit = new Array(numOfSplits) - .fill(null) - .map(() => []); - - // Evenly distribute slow tests over each split - slowTests.forEach((test, i) => { - const splitIndex = i % numOfSplits; - testsBySplit[splitIndex].push(test); - }); - - tests.forEach((test, i) => { - const splitIndex = Math.floor(i / maxTestsPerSplit); - testsBySplit[splitIndex].push(test); - }); - - return testsBySplit; -} diff --git a/.github/actions/split-tests/src/types.mts b/.github/actions/split-tests/src/types.mts deleted file mode 100644 index 3eae2f0eb9..0000000000 --- a/.github/actions/split-tests/src/types.mts +++ /dev/null @@ -1,75 +0,0 @@ -/** - * An array of all tests - */ -export type Tests = string[]; - -/** - * An array of tests, indexed by split - */ -export type TestsBySplit = string[][]; - -export interface Split { - /** - * The split index - * @example "4" - */ - idx: string; - - /** - * The split index in the context of all splits - * @example "4/10" - */ - id: string; -} - -export interface SoliditySplit extends Split { - /** - * A string that contains a whitespace delimited list of tests to run - * - * This format is to support the `hardhat test` command. - * @example test/foo.test.ts test/bar.test.ts - */ - tests: string; - - /** - * A string that contains a glob that expresses the list of tests to run. - * - * This format is used to conform to the --testfiles flag of solidity-coverage - * @example {test/foo.test.ts,test/bar.test.ts} - */ - coverageTests: string; -} - -/** - * Configuration file for solidity tests - */ -export interface SolidityConfig { - type: "solidity"; - /** - * The path to the contracts tests directory, relative to the git root - */ - basePath: string; - splits: { - /** - * The number of sub-splits to run across - */ - numOfSplits: number; - /** - * The directory of the tests to create sub-splits across, relative to the basePath - */ - dir: string; - /** - * An array of known slow tests, to better distribute across sub-splits - * - * Each string is a case-sensitive matcher that will match against any substring within the list of test file paths within the `dir` configuration. - * - * @example - * Given the dir `v0.8`, we get the following tests: ['v0.8/Foo1.test.ts','v0.8/bar.test.ts','v0.8/dev/eolpe/Foo.test.ts'] - * - * If we supply the following `slowTests` argument: ['Foo'] - * - * Then it'll match against both 'v0.8/Foo1.test.ts' and 'v0.8/dev/eolpe/Foo.test.ts'. - */ - slowTests?: string[]; - }[]; -} diff --git a/.github/actions/split-tests/test/__snapshots__/sieve.test.ts.snap b/.github/actions/split-tests/test/__snapshots__/sieve.test.ts.snap deleted file mode 100644 index d55bc175b8..0000000000 --- a/.github/actions/split-tests/test/__snapshots__/sieve.test.ts.snap +++ /dev/null @@ -1,99 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`sieveSlowTests works 1`] = ` -{ - "filteredTests": [], - "slowTests": [], -} -`; - -exports[`sieveSlowTests works 2`] = ` -{ - "filteredTests": [], - "slowTests": [], -} -`; - -exports[`sieveSlowTests works 3`] = ` -{ - "filteredTests": [ - "keepme", - ], - "slowTests": [], -} -`; - -exports[`sieveSlowTests works 4`] = ` -{ - "filteredTests": [ - "keepme", - ], - "slowTests": [], -} -`; - -exports[`sieveSlowTests works 5`] = ` -{ - "filteredTests": [ - "foo.test", - "bar.test", - "baz.test", - "yup.test", - "nope.test", - "bonk.test", - "bop.test", - "ouch.test.ts", - ], - "slowTests": [], -} -`; - -exports[`sieveSlowTests works 6`] = ` -{ - "filteredTests": [ - "foo.test", - "bar.test", - "baz.test", - "yup.test", - "nope.test", - "bonk.test", - "bop.test", - "ouch.test.ts", - ], - "slowTests": [], -} -`; - -exports[`sieveSlowTests works 7`] = ` -{ - "filteredTests": [ - "foo.test", - "bar.test", - "baz.test", - "yup.test", - "nope.test", - "bonk.test", - "bop.test", - ], - "slowTests": [ - "ouch.test.ts", - ], -} -`; - -exports[`sieveSlowTests works 8`] = ` -{ - "filteredTests": [ - "foo.test", - "bar.test", - "baz.test", - "yup.test", - "nope.test", - ], - "slowTests": [ - "bonk.test", - "bop.test", - "ouch.test.ts", - ], -} -`; diff --git a/.github/actions/split-tests/test/__snapshots__/splitter.test.ts.snap b/.github/actions/split-tests/test/__snapshots__/splitter.test.ts.snap deleted file mode 100644 index 70dfe70ec1..0000000000 --- a/.github/actions/split-tests/test/__snapshots__/splitter.test.ts.snap +++ /dev/null @@ -1,119 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`simpleSplit doesn't error on empty arrays 1`] = ` -[ - [], -] -`; - -exports[`simpleSplit doesn't error on empty arrays 2`] = ` -[ - [], - [], - [], - [], - [], -] -`; - -exports[`simpleSplit handles no slow test splitting 1`] = ` -[ - [ - "foo.test", - "bar.test", - "baz.test", - "yup.test", - "nope.test", - "bonk.test", - "bop.test", - "ouch.test.ts", - ], -] -`; - -exports[`simpleSplit handles no slow test splitting 2`] = ` -[ - [ - "foo.test", - "bar.test", - "baz.test", - "yup.test", - ], - [ - "nope.test", - "bonk.test", - "bop.test", - "ouch.test.ts", - ], -] -`; - -exports[`simpleSplit handles no slow test splitting 3`] = ` -[ - [ - "foo.test", - "bar.test", - "baz.test", - ], - [ - "yup.test", - "nope.test", - "bonk.test", - ], - [ - "bop.test", - "ouch.test.ts", - ], -] -`; - -exports[`simpleSplit handles slow test splitting 1`] = ` -[ - [ - "bonk.test", - "bop.test", - "ouch.test.ts", - "foo.test", - "bar.test", - "baz.test", - "yup.test", - "nope.test", - ], -] -`; - -exports[`simpleSplit handles slow test splitting 2`] = ` -[ - [ - "bonk.test", - "ouch.test.ts", - "foo.test", - "bar.test", - "baz.test", - ], - [ - "bop.test", - "yup.test", - "nope.test", - ], -] -`; - -exports[`simpleSplit handles slow test splitting 3`] = ` -[ - [ - "bonk.test", - "foo.test", - "bar.test", - ], - [ - "bop.test", - "baz.test", - "yup.test", - ], - [ - "ouch.test.ts", - "nope.test", - ], -] -`; diff --git a/.github/actions/split-tests/test/fixtures.mts b/.github/actions/split-tests/test/fixtures.mts deleted file mode 100644 index aa87ba4c35..0000000000 --- a/.github/actions/split-tests/test/fixtures.mts +++ /dev/null @@ -1,20 +0,0 @@ -export const testArr: string[] = [ - "foo.test", - "bar.test", - "baz.test", - "yup.test", - "nope.test", - "bonk.test", - "bop.test", - "ouch.test.ts", -]; - -export const testSievedArr: string[] = [ - "foo.test", - "bar.test", - "baz.test", - "yup.test", - "nope.test", -]; - -export const testSlowArr: string[] = ["bonk.test", "bop.test", "ouch.test.ts"]; diff --git a/.github/actions/split-tests/test/sieve.test.ts b/.github/actions/split-tests/test/sieve.test.ts deleted file mode 100644 index bc296a435f..0000000000 --- a/.github/actions/split-tests/test/sieve.test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import {sieveSlowTests} from "../src/sieve.mjs"; -import {testArr} from "./fixtures.mjs"; - -describe("sieveSlowTests", () => { - it("works", () => { - expect(sieveSlowTests([])).toMatchSnapshot(); - expect(sieveSlowTests([], [])).toMatchSnapshot(); - expect(sieveSlowTests(["keepme"], [])).toMatchSnapshot(); - expect(sieveSlowTests(["keepme"])).toMatchSnapshot(); - expect(sieveSlowTests(testArr, [])).toMatchSnapshot(); - expect(sieveSlowTests(testArr, ["noself"])).toMatchSnapshot(); - expect(sieveSlowTests(testArr, ["ouch.test.ts"])).toMatchSnapshot(); - expect(sieveSlowTests(testArr, ["bo", "ouch.test.ts"])).toMatchSnapshot(); - }); -}); diff --git a/.github/actions/split-tests/test/splitter.test.ts b/.github/actions/split-tests/test/splitter.test.ts deleted file mode 100644 index 85ae7726fe..0000000000 --- a/.github/actions/split-tests/test/splitter.test.ts +++ /dev/null @@ -1,21 +0,0 @@ -import {simpleSplit} from "../src/splitter.mjs"; -import {testArr, testSievedArr, testSlowArr} from "./fixtures.mjs"; - -describe("simpleSplit", () => { - it("doesn't error on empty arrays", () => { - expect(simpleSplit([], [], 1)).toMatchSnapshot(); - expect(simpleSplit([], [], 5)).toMatchSnapshot(); - }); - - it("handles no slow test splitting", () => { - expect(simpleSplit(testArr, [], 1)).toMatchSnapshot(); - expect(simpleSplit(testArr, [], 2)).toMatchSnapshot(); - expect(simpleSplit(testArr, [], 3)).toMatchSnapshot(); - }); - - it("handles slow test splitting", () => { - expect(simpleSplit(testSievedArr, testSlowArr, 1)).toMatchSnapshot(); - expect(simpleSplit(testSievedArr, testSlowArr, 2)).toMatchSnapshot(); - expect(simpleSplit(testSievedArr, testSlowArr, 3)).toMatchSnapshot(); - }); -}); diff --git a/.github/actions/split-tests/tsconfig.json b/.github/actions/split-tests/tsconfig.json deleted file mode 100644 index 4b36d4a178..0000000000 --- a/.github/actions/split-tests/tsconfig.json +++ /dev/null @@ -1,104 +0,0 @@ -{ - "compilerOptions": { - /* Visit https://aka.ms/tsconfig to read more about this file */ - - /* Projects */ - // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ - // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ - // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ - // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ - // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ - // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ - - /* Language and Environment */ - "target": "ESNext" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, - // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ - // "jsx": "preserve", /* Specify what JSX code is generated. */ - // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ - // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ - // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ - // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ - // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ - // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ - // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ - // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ - // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ - - /* Modules */ - "module": "NodeNext" /* Specify what module code is generated. */, - // "rootDir": "./", /* Specify the root folder within your source files. */ - "moduleResolution": "NodeNext" /* Specify how TypeScript looks up a file from a given module specifier. */, - // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ - // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ - // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ - // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ - // "types": [], /* Specify type package names to be included without being referenced in a source file. */ - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ - // "resolveJsonModule": true, /* Enable importing .json files. */ - // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ - - /* JavaScript Support */ - // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ - // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ - // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ - - /* Emit */ - // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ - // "declarationMap": true, /* Create sourcemaps for d.ts files. */ - // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ - // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ - // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ - // "outDir": "./", /* Specify an output folder for all emitted files. */ - // "removeComments": true, /* Disable emitting comments. */ - "noEmit": true /* Disable emitting files from a compilation. */, - // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ - // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ - // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ - // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ - // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ - // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ - // "newLine": "crlf", /* Set the newline character for emitting files. */ - // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ - // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ - // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ - // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ - // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ - // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ - - /* Interop Constraints */ - // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ - // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ - "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */, - // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ - "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, - - /* Type Checking */ - "strict": true /* Enable all strict type-checking options. */, - // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ - // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ - // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ - // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ - // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ - // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ - // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ - // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ - // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ - // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ - // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ - // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ - // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ - // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ - // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ - // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ - // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ - // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ - - /* Completeness */ - // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ - "skipLibCheck": false /* Skip type checking all .d.ts files. */ - }, - "include": ["src", "test"] -} diff --git a/.github/workflows/ci-protobuf.yml b/.github/workflows/ci-protobuf.yml new file mode 100644 index 0000000000..d832939ded --- /dev/null +++ b/.github/workflows/ci-protobuf.yml @@ -0,0 +1,36 @@ +name: CI ProtoBuf + +on: + pull_request: + +jobs: + buf-breaking: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + + - name: Setup buf + uses: bufbuild/buf-setup-action@35c243d7f2a909b1d4e40399b348a7fdab27d78d # v1.34.0 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + + - name: Run buf breaking + uses: bufbuild/buf-breaking-action@c57b3d842a5c3f3b454756ef65305a50a587c5ba # v1.1.4 + env: + REPO_URL: https://github.com/${{ github.repository }} + BASE_BRANCH: ${{ github.base_ref }} + with: + against: "${REPO_URL}.git#branch=${BASE_BRANCH}" + + - name: Collect Metrics + if: always() + id: collect-gha-metrics + uses: smartcontractkit/push-gha-metrics-action@d9da21a2747016b3e13de58c7d4115a3d5c97935 # v3.0.1 + with: + id: ci-protobuf + org-id: ${{ secrets.GRAFANA_INTERNAL_TENANT_ID }} + basic-auth: ${{ secrets.GRAFANA_INTERNAL_BASIC_AUTH }} + hostname: ${{ secrets.GRAFANA_INTERNAL_HOST }} + this-job-name: buf-breaking + continue-on-error: true diff --git a/.github/workflows/client-compatibility-tests.yml b/.github/workflows/client-compatibility-tests.yml index b46336dd2a..2b7b8341fa 100644 --- a/.github/workflows/client-compatibility-tests.yml +++ b/.github/workflows/client-compatibility-tests.yml @@ -157,7 +157,11 @@ jobs: fi IFS=',' eth_implementations="${implementations_arr[*]}" - echo "Found new releases for: $eth_implementations" + if [ -n "$eth_implementations" ]; then + echo "Found new releases for: $eth_implementations" + else + echo "No new releases found" + fi echo "evm_implementations=$eth_implementations" >> $GITHUB_OUTPUT elif [ "$GITHUB_EVENT_NAME" = "workflow_dispatch" ]; then if [ -n "${{ github.event.inputs.base64TestList }}" ]; then @@ -229,7 +233,7 @@ jobs: check-ecr-images-exist: name: Check images used as test dependencies exist in ECR - if: always() && needs.should-run.outputs.should_run == 'true' + if: always() && needs.should-run.outputs.should_run == 'true' && (needs.select-versions.outputs.evm_implementations != '' || github.event.inputs.base64TestList != '') environment: integration permissions: id-token: write @@ -263,7 +267,7 @@ jobs: page_size: ${{matrix.mirror.page_size}} build-chainlink: - if: always() && needs.should-run.outputs.should_run == 'true' + if: always() && needs.should-run.outputs.should_run == 'true' && (needs.select-versions.outputs.evm_implementations != '' || github.event.inputs.base64TestList != '') environment: integration permissions: id-token: write @@ -298,7 +302,7 @@ jobs: get-latest-available-images: name: Get Latest EVM Implementation's Images - if: always() && needs.should-run.outputs.should_run == 'true' + if: always() && needs.should-run.outputs.should_run == 'true' && (needs.select-versions.outputs.evm_implementations != '' || github.event.inputs.base64TestList != '') environment: integration runs-on: ubuntu-latest needs: [check-ecr-images-exist, should-run, select-versions] @@ -371,7 +375,7 @@ jobs: prepare-compatibility-matrix: name: Prepare Compatibility Matrix - if: always() && needs.should-run.outputs.should_run == 'true' + if: always() && needs.should-run.outputs.should_run == 'true' && (needs.select-versions.outputs.evm_implementations != '' || github.event.inputs.base64TestList != '') environment: integration permissions: checks: write @@ -501,7 +505,7 @@ jobs: run-client-compatibility-matrix: name: ${{ matrix.evm_node.product }} compatibility with ${{ matrix.evm_node.docker_image }} - if: always() && needs.should-run.outputs.should_run == 'true' && needs.build-chainlink.result == 'success' + if: always() && needs.should-run.outputs.should_run == 'true' && needs.build-chainlink.result == 'success' && needs.prepare-compatibility-matrix.outputs.matrix != '' environment: integration permissions: checks: write @@ -605,7 +609,7 @@ jobs: start-slack-thread: name: Start Slack Thread - if: always() && needs.*.result != 'skipped' && needs.*.result != 'cancelled' && needs.should-run.outputs.should_run == 'true' + if: always() && needs.*.result != 'skipped' && needs.*.result != 'cancelled' && needs.should-run.outputs.should_run == 'true' && (needs.select-versions.outputs.evm_implementations != '' || github.event.inputs.base64TestList != '') environment: integration outputs: thread_ts: ${{ steps.slack.outputs.thread_ts }} @@ -664,7 +668,7 @@ jobs: parse-test-results: name: Parse Test Results - if: always() && needs.*.result != 'skipped' && needs.*.result != 'cancelled' && needs.should-run.outputs.should_run == 'true' + if: always() && needs.*.result != 'skipped' && needs.*.result != 'cancelled' && needs.should-run.outputs.should_run == 'true' && (needs.select-versions.outputs.evm_implementations != '' || github.event.inputs.base64TestList != '') environment: integration permissions: checks: write @@ -750,7 +754,7 @@ jobs: post-test-results-to-slack: name: Post Test Results for ${{matrix.product}} - if: ${{ always() && needs.*.result != 'skipped' && needs.*.result != 'cancelled' && needs.should-run.outputs.should_run == 'true' }} + if: always() && needs.*.result != 'skipped' && needs.*.result != 'cancelled' && needs.should-run.outputs.should_run == 'true' && needs.parse-test-results.result == 'success' environment: integration permissions: checks: write diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 8bf9542f7e..56b123735d 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -258,6 +258,7 @@ jobs: fi - name: Setup GAP for Grafana uses: smartcontractkit/.github/actions/setup-gap@d316f66b2990ea4daa479daa3de6fc92b00f863e # setup-gap@0.3.2 + id: setup-gap with: # aws inputs aws-region: ${{ secrets.AWS_REGION }} @@ -521,11 +522,13 @@ jobs: -v $PWD/../../integration-tests/smoke/traces:/tracing \ --user "$(id -u):$(id -g)" \ -p 4317:4317 otel/opentelemetry-collector:0.88.0 --config=/etc/otel-collector.yaml + - name: Locate Docker Volume id: locate-volume if: false run: | echo "VOLUME_PATH=$(docker volume inspect --format '{{ .Mountpoint }}' otel-traces)" >> $GITHUB_OUTPUT + - name: Show Otel-Collector Logs if: steps.check-label.outputs.trace == 'true' && matrix.product.name == 'ocr2' && matrix.product.tag_suffix == '-plugins' run: | @@ -539,6 +542,7 @@ jobs: fi - name: Setup GAP for Grafana uses: smartcontractkit/.github/actions/setup-gap@d316f66b2990ea4daa479daa3de6fc92b00f863e # setup-gap@0.3.2 + id: setup-gap with: # aws inputs aws-region: ${{ secrets.AWS_REGION }} @@ -637,23 +641,27 @@ jobs: QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} QA_KUBECONFIG: "" should_tidy: "false" + - name: Show Otel-Collector Logs if: steps.check-label.outputs.trace == 'true' && matrix.product.name == 'ocr2' && matrix.product.tag_suffix == '-plugins' run: | docker logs otel-collector + - name: Permissions on traces if: steps.check-label.outputs.trace == 'true' && matrix.product.name == 'ocr2' && matrix.product.tag_suffix == '-plugins' run: | ls -l ./integration-tests/smoke/traces + - name: Upload Trace Data if: steps.check-label.outputs.trace == 'true' && matrix.product.name == 'ocr2' && matrix.product.tag_suffix == '-plugins' uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 with: name: trace-data path: ./integration-tests/smoke/traces/trace-data.json + - name: Print failed test summary if: always() - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@1587f59bfd626b668d303abbc90fee41b12397e6 # v2.3.23 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@a8afe9cb511125685ce02bb8694298ca89685028 # v2.3.23 with: test_directories: ./integration-tests/smoke/,./integration-tests/ccip-tests/smoke/ @@ -737,7 +745,6 @@ jobs: # Run the setup if the matrix finishes but this time save the cache if we have a cache hit miss # this will also only run if both of the matrix jobs pass eth-smoke-go-mod-cache: - environment: integration needs: [eth-smoke-tests] runs-on: ubuntu-latest diff --git a/.github/workflows/on-demand-ocr-soak-test.yml b/.github/workflows/on-demand-ocr-soak-test.yml index ae24786b43..c4415ac85d 100644 --- a/.github/workflows/on-demand-ocr-soak-test.yml +++ b/.github/workflows/on-demand-ocr-soak-test.yml @@ -8,7 +8,10 @@ on: default: TestOCRSoak type: choice options: - - TestOCRSoak + - TestOCRv1Soak + - TestOCRv2Soak + - TestForwarderOCRv1Soak + - TestForwarderOCRv2Soak - TestOCRSoak_GethReorgBelowFinality_FinalityTagDisabled - TestOCRSoak_GethReorgBelowFinality_FinalityTagEnabled - TestOCRSoak_GasSpike @@ -18,7 +21,7 @@ on: base64Config: description: base64-ed config required: true - type: string + type: string slackMemberID: description: Slack Member ID (Not your @) required: true @@ -78,7 +81,7 @@ jobs: echo "### chainlink-tests image tag for this test run :ship:" >>$GITHUB_STEP_SUMMARY echo "\`${GITHUB_SHA}\`" >>$GITHUB_STEP_SUMMARY echo "### Networks on which test was run" >>$GITHUB_STEP_SUMMARY - echo "\`${{ env.NETWORKS }}\`" >>$GITHUB_STEP_SUMMARY + echo "\`${{ env.NETWORKS }}\`" >>$GITHUB_STEP_SUMMARY - name: Build Image uses: ./.github/actions/build-test-image with: diff --git a/.github/workflows/solidity-foundry.yml b/.github/workflows/solidity-foundry.yml index b2756c4ac3..e43a4c1320 100644 --- a/.github/workflows/solidity-foundry.yml +++ b/.github/workflows/solidity-foundry.yml @@ -18,16 +18,12 @@ jobs: - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 id: changes with: - # Foundry is only used for Solidity v0.8 contracts, therefore we can ignore - # changes to older contracts. filters: | src: - 'contracts/src/v0.8/**/*' - '.github/workflows/solidity-foundry.yml' - 'contracts/foundry.toml' - 'contracts/gas-snapshots/*.gas-snapshot' - - 'contracts/foundry-lib/**/*' - - '.gitmodules' tests: strategy: diff --git a/.tool-versions b/.tool-versions index ca10fd5f6a..f660f2ac11 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,5 +1,5 @@ golang 1.21.7 -mockery 2.42.2 +mockery 2.43.2 nodejs 20.13.1 pnpm 9.4.0 postgres 15.1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 7662247a36..f212445503 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,99 +1,136 @@ # Changelog Chainlink Core -## 2.13.0 - UNRELEASED +## 2.14.0 - UNRELEASED ### Minor Changes -- [#13235](https://github.com/smartcontractkit/chainlink/pull/13235) [`5374a577dd`](https://github.com/smartcontractkit/chainlink/commit/5374a577dd6396e149c26168a2f093b3204dfddb) Thanks [@krehermann](https://github.com/krehermann)! - #internal move workflow validation to common repo +- [#13356](https://github.com/smartcontractkit/chainlink/pull/13356) [`0228243f20`](https://github.com/smartcontractkit/chainlink/commit/0228243f20bb02d40c7ad2c9cf1002599436ff1d) Thanks [@ilija42](https://github.com/ilija42)! - #internal Add Log Poller support to Chain Reader through setting them in config. All filters should be part of the contract wide filter unless an event needs specific polling configuration, which can be set on a per event basis.. -- [#12970](https://github.com/smartcontractkit/chainlink/pull/12970) [`1eb7180205`](https://github.com/smartcontractkit/chainlink/commit/1eb71802053fcde7e809ef58918110cf03046f3a) Thanks [@dimriou](https://github.com/dimriou)! - Decouple client tests from core #internal +- [#13718](https://github.com/smartcontractkit/chainlink/pull/13718) [`f33cd1915c`](https://github.com/smartcontractkit/chainlink/commit/f33cd1915c03bef9bfe110481eb13094c36ce454) Thanks [@nickcorin](https://github.com/nickcorin)! - #internal Added a chain writer constructor onto the evm relayer. -- [#13354](https://github.com/smartcontractkit/chainlink/pull/13354) [`58d73ecf61`](https://github.com/smartcontractkit/chainlink/commit/58d73ecf618ac39c37f767e70c4e6d6a51eaba59) Thanks [@friedemannf](https://github.com/friedemannf)! - #breaking_change Remove the `xdai` `ChainType` config option. Moving forward, only `gnosis` can be used. +- [#13040](https://github.com/smartcontractkit/chainlink/pull/13040) [`0ac790b37f`](https://github.com/smartcontractkit/chainlink/commit/0ac790b37fca951dfb4b4093c00c5adbd6987668) Thanks [@amit-momin](https://github.com/amit-momin)! - Added API for products to query a transaction's status in the TXM #internal -- [#12971](https://github.com/smartcontractkit/chainlink/pull/12971) [`a2441ba8cc`](https://github.com/smartcontractkit/chainlink/commit/a2441ba8cc44b6455338410b739d394a3bf3557e) Thanks [@dimriou](https://github.com/dimriou)! - Decouple evm config tests from core #internal +- [#13413](https://github.com/smartcontractkit/chainlink/pull/13413) [`9e733a07fe`](https://github.com/smartcontractkit/chainlink/commit/9e733a07fe2082207e6d7884abee26d82d37e808) Thanks [@silaslenihan](https://github.com/silaslenihan)! - #added EVM implementation of GetFeeComponents function for ChainWriter -- [#13396](https://github.com/smartcontractkit/chainlink/pull/13396) [`5ff32bde25`](https://github.com/smartcontractkit/chainlink/commit/5ff32bde2575d2b2ef626a69d54b9f6eed0d21c5) Thanks [@ettec](https://github.com/ettec)! - #internal keystone: handle local target transmission logic in capability wrapper +- [#13478](https://github.com/smartcontractkit/chainlink/pull/13478) [`9f6e454bc5`](https://github.com/smartcontractkit/chainlink/commit/9f6e454bc51c07cf817c90a9a11fa44ae3708be5) Thanks [@snehaagni](https://github.com/snehaagni)! - Bump to start the next version -- [#13357](https://github.com/smartcontractkit/chainlink/pull/13357) [`21f38e52d1`](https://github.com/smartcontractkit/chainlink/commit/21f38e52d1d2401f58da83f143043e3f4561e21c) Thanks [@ettec](https://github.com/ettec)! - #internal remote target capability and transmission protocol +- [#13501](https://github.com/smartcontractkit/chainlink/pull/13501) [`15f02f65c1`](https://github.com/smartcontractkit/chainlink/commit/15f02f65c1ac3cfc89e726597a981ddf206eea2b) Thanks [@ettec](https://github.com/ettec)! - #internal remove shared secret from transmission schedule -- [#13397](https://github.com/smartcontractkit/chainlink/pull/13397) [`daf8cf6cfe`](https://github.com/smartcontractkit/chainlink/commit/daf8cf6cfe1c0b8d2a70fb476a4e815af5025ebe) Thanks [@dimriou](https://github.com/dimriou)! - Remove multiple core dependencies from evm head tracker tests #internal" +- [#13691](https://github.com/smartcontractkit/chainlink/pull/13691) [`f2630b280d`](https://github.com/smartcontractkit/chainlink/commit/f2630b280d917313c6d3af468d9a7c039d5e179e) Thanks [@nickcorin](https://github.com/nickcorin)! - #internal Implemented the `GetTransactionStatus` method on the EVM implementation of the `ChainWriter`. -- [#13312](https://github.com/smartcontractkit/chainlink/pull/13312) [`c3829cac20`](https://github.com/smartcontractkit/chainlink/commit/c3829cac20e158f805d0efb9a8a2183e8d7b0515) Thanks [@amit-momin](https://github.com/amit-momin)! - #internal Added new Geth InsufficientEth client error for internal TXM classification +- [#13551](https://github.com/smartcontractkit/chainlink/pull/13551) [`f7e036244c`](https://github.com/smartcontractkit/chainlink/commit/f7e036244c1bf019964704734a96be5e5699ca2c) Thanks [@ettec](https://github.com/ettec)! - #internal capability dispatcher threading and context usage -- [#13381](https://github.com/smartcontractkit/chainlink/pull/13381) [`0a62f024e1`](https://github.com/smartcontractkit/chainlink/commit/0a62f024e10398cdd6451ef0d15db2501de11538) Thanks [@nickcorin](https://github.com/nickcorin)! - #internal Pre-process contract abis in the evm chainwriter. +- [#13564](https://github.com/smartcontractkit/chainlink/pull/13564) [`2c2ca6a278`](https://github.com/smartcontractkit/chainlink/commit/2c2ca6a27899295dc87e48e4378671dbbe06ac48) Thanks [@yongkangchia](https://github.com/yongkangchia)! - #changed Added Aptos Keystore to Core. This includes Aptos Key which uses ED25519, Keystore, Relevant tests -- [#13221](https://github.com/smartcontractkit/chainlink/pull/13221) [`0b100ad3db`](https://github.com/smartcontractkit/chainlink/commit/0b100ad3dbf0a3c2fbd6e55c539046f6f3c9e5f6) Thanks [@ilija42](https://github.com/ilija42)! - Added a mechanism to validate forwarders for OCR2 and fallback to EOA if necessary #added +- [#13427](https://github.com/smartcontractkit/chainlink/pull/13427) [`66f154745d`](https://github.com/smartcontractkit/chainlink/commit/66f154745d3d973aa8b629a99a65a660a1d1b3bc) Thanks [@cedric-cordenier](https://github.com/cedric-cordenier)! - #internal Add RegistrySyncer -- [#13416](https://github.com/smartcontractkit/chainlink/pull/13416) [`68e00b3abf`](https://github.com/smartcontractkit/chainlink/commit/68e00b3abf566a222ff6775b0fb2575ff73f735e) Thanks [@ettec](https://github.com/ettec)! - #internal fix for workflow step persistence +- [#13661](https://github.com/smartcontractkit/chainlink/pull/13661) [`363e8290bb`](https://github.com/smartcontractkit/chainlink/commit/363e8290bb5ea2f7bf07528ba19cc64dca8e87a8) Thanks [@amit-momin](https://github.com/amit-momin)! - Updated L1 gas price calculations for Optimism Ecotone and Fjord upgrades #internal -- [#13259](https://github.com/smartcontractkit/chainlink/pull/13259) [`76dbe19282`](https://github.com/smartcontractkit/chainlink/commit/76dbe192822c7e9f289c98e33ebb6693a07046a0) Thanks [@archseer](https://github.com/archseer)! - #internal Added a configuration option to chain writer to set the tx send strategy. +- [#13297](https://github.com/smartcontractkit/chainlink/pull/13297) [`e55e0424a2`](https://github.com/smartcontractkit/chainlink/commit/e55e0424a276416cf849a83a1488a287872824c5) Thanks [@amit-momin](https://github.com/amit-momin)! - #changed Refactored the BlockHistoryEstimator check to prevent excessively bumping transactions. Check no longer waits for CheckInclusionBlocks to pass before assessing an attempt. + #bugfix Fixed a bug that would use the oldest blocks in the cached history instead of the latest to perform gas estimations. -- [#13384](https://github.com/smartcontractkit/chainlink/pull/13384) [`bc087f1de2`](https://github.com/smartcontractkit/chainlink/commit/bc087f1de2014cce1027341d14e0917c4351fb21) Thanks [@augustbleeds](https://github.com/augustbleeds)! - bump chainlink-starknet so it builds reports with median gas price #updated +- [#13524](https://github.com/smartcontractkit/chainlink/pull/13524) [`d736d9e083`](https://github.com/smartcontractkit/chainlink/commit/d736d9e0838983a021677bc608556b3994f46690) Thanks [@ettec](https://github.com/ettec)! - #internal remote target wait until initiated threads exit on close -- [#13353](https://github.com/smartcontractkit/chainlink/pull/13353) [`7a86103474`](https://github.com/smartcontractkit/chainlink/commit/7a861034740a44ebb5d3f1da2d271637691c0bd3) Thanks [@pavel-raykov](https://github.com/pavel-raykov)! - #updated Remove deprecated app.shortcut links +- [#13169](https://github.com/smartcontractkit/chainlink/pull/13169) [`3f56b3eecd`](https://github.com/smartcontractkit/chainlink/commit/3f56b3eecd2d5fd3e325325d9d1e82d8a749d9eb) Thanks [@HenryNguyen5](https://github.com/HenryNguyen5)! - #internal rework operator_ui installer -- [#13181](https://github.com/smartcontractkit/chainlink/pull/13181) [`c14576a945`](https://github.com/smartcontractkit/chainlink/commit/c14576a945fdc6b5099ffd31f467a0712afe6fd6) Thanks [@krehermann](https://github.com/krehermann)! - #added workflow spec auto-approval via CLO +- [#13601](https://github.com/smartcontractkit/chainlink/pull/13601) [`c3f6b704f1`](https://github.com/smartcontractkit/chainlink/commit/c3f6b704f1c510dbfb28b421ee3a8f63416b18c1) Thanks [@cedric-cordenier](https://github.com/cedric-cordenier)! - #internal Allow outputs to be passed directly to the inputs -- [#13455](https://github.com/smartcontractkit/chainlink/pull/13455) [`066afc0877`](https://github.com/smartcontractkit/chainlink/commit/066afc0877a9e953bbda25a4ff09009d7f1c1e2d) Thanks [@krehermann](https://github.com/krehermann)! - #bugfix use correct internal id in workflow auto-approval +- [#13621](https://github.com/smartcontractkit/chainlink/pull/13621) [`3eb0a3736f`](https://github.com/smartcontractkit/chainlink/commit/3eb0a3736ff1cc885dd7ff9c978c09218f61e7a6) Thanks [@cedric-cordenier](https://github.com/cedric-cordenier)! - #internal Add script to provision capability registry -- [#13259](https://github.com/smartcontractkit/chainlink/pull/13259) [`76dbe19282`](https://github.com/smartcontractkit/chainlink/commit/76dbe192822c7e9f289c98e33ebb6693a07046a0) Thanks [@archseer](https://github.com/archseer)! - #internal added tests for Chainwriter +- [#13522](https://github.com/smartcontractkit/chainlink/pull/13522) [`90924dcc26`](https://github.com/smartcontractkit/chainlink/commit/90924dcc2667807f57e31992aac9f674935680b8) Thanks [@krehermann](https://github.com/krehermann)! - #internal moves workflow name and owner to the yaml spec -- [#12881](https://github.com/smartcontractkit/chainlink/pull/12881) [`d675d864f0`](https://github.com/smartcontractkit/chainlink/commit/d675d864f0e6f33c783bfed17fe31b2c127eb51d) Thanks [@amit-momin](https://github.com/amit-momin)! - #added Added an auto-purge feature to the EVM TXM that identifies terminally stuck transactions either through a chain specific method or heurisitic then purges them to unblock the nonce. Included 4 new toml configs under Transactions.AutoPurge to configure this new feature: Enabled, Threshold, MinAttempts, and DetectionApiUrl. +- [#13668](https://github.com/smartcontractkit/chainlink/pull/13668) [`e5dc01e844`](https://github.com/smartcontractkit/chainlink/commit/e5dc01e844064e2fdab976369cf83195f9659216) Thanks [@cedric-cordenier](https://github.com/cedric-cordenier)! - Add registry syncer package #internal -- [#13401](https://github.com/smartcontractkit/chainlink/pull/13401) [`905830c3ff`](https://github.com/smartcontractkit/chainlink/commit/905830c3ff16c670c3fbf3d83a0c2ca3a15e83a8) Thanks [@krehermann](https://github.com/krehermann)! - #db_update add persistence for DON-2-DON discovery announcements +### Patch Changes -- [#13466](https://github.com/smartcontractkit/chainlink/pull/13466) [`4fdfffdb4c`](https://github.com/smartcontractkit/chainlink/commit/4fdfffdb4c4a6f16fcdb388fe38642398ce130da) Thanks [@dimriou](https://github.com/dimriou)! - Move chaintype #internal +- [#13498](https://github.com/smartcontractkit/chainlink/pull/13498) [`c6f1b30f34`](https://github.com/smartcontractkit/chainlink/commit/c6f1b30f346c0a8a64ed39b8317e247c928a368e) Thanks [@simsonraj](https://github.com/simsonraj)! - #added zkSync L1 GasPrice calculation -- [#13200](https://github.com/smartcontractkit/chainlink/pull/13200) [`4718aa7ec2`](https://github.com/smartcontractkit/chainlink/commit/4718aa7ec20e2ef70dff7fb72095d357f3725a80) Thanks [@augustbleeds](https://github.com/augustbleeds)! - Add option to include GasPriceSubunits pipeline to include gasPriceSubunits in median ocr2 transmission (only to be used with Starknet chain for now) #added #nops #updated +- [#13487](https://github.com/smartcontractkit/chainlink/pull/13487) [`5e27da95f0`](https://github.com/smartcontractkit/chainlink/commit/5e27da95f09f21272e93f086bc2de5a9bc2ae399) Thanks [@bolekk](https://github.com/bolekk)! - #internal Use audited version of OCR2Base.sol in OCR3Capability.sol + +- [#13612](https://github.com/smartcontractkit/chainlink/pull/13612) [`98108568dc`](https://github.com/smartcontractkit/chainlink/commit/98108568dc3ea54537a6867d36f3e48ee456daae) Thanks [@nickcorin](https://github.com/nickcorin)! - #internal bump `chainlink-common` version to commit 6d926be950a6f6ca289a84edad938d4eef2ee337. -- [#13215](https://github.com/smartcontractkit/chainlink/pull/13215) [`66d8d16638`](https://github.com/smartcontractkit/chainlink/commit/66d8d16638f16dc863cbc6d24356c3cf3c9724f0) Thanks [@ettec](https://github.com/ettec)! - #internal standard capability support +- [#13726](https://github.com/smartcontractkit/chainlink/pull/13726) [`2ecf45d381`](https://github.com/smartcontractkit/chainlink/commit/2ecf45d381b98714f4f673221bfc74577fcfeb04) Thanks [@ferglor](https://github.com/ferglor)! - Only encode non nil block numbers for eth_call #changed -- [#12995](https://github.com/smartcontractkit/chainlink/pull/12995) [`322f3b9616`](https://github.com/smartcontractkit/chainlink/commit/322f3b96162a4e0738d76ba00f949c5b19d09909) Thanks [@dimriou](https://github.com/dimriou)! - Decouple monitoring tests from core #internal +- [#13048](https://github.com/smartcontractkit/chainlink/pull/13048) [`3f8c00a6f1`](https://github.com/smartcontractkit/chainlink/commit/3f8c00a6f1884f765bbe9e4b70e0dc4fb94a0088) Thanks [@dhaidashenko](https://github.com/dhaidashenko)! - Fixed local finality violation caused by an RPC lagging behind on latest finalized block. -- [#12972](https://github.com/smartcontractkit/chainlink/pull/12972) [`1196df4684`](https://github.com/smartcontractkit/chainlink/commit/1196df4684c812361b8f0994ade6414a1f214c2c) Thanks [@dimriou](https://github.com/dimriou)! - Decouple gas tests from core #internal + Added `EVM.FinalizedBlockOffset` and `EVM.NodePool.EnforceRepeatableRead` config options. + With `EnforceRepeatableRead = true`, RPC is considered healthy only if its most recent finalized block is larger or equal to the highest finalized block observed by the Node minus `FinalizedBlockOffset`. + #bugfix -- [#12993](https://github.com/smartcontractkit/chainlink/pull/12993) [`2a8d1b150a`](https://github.com/smartcontractkit/chainlink/commit/2a8d1b150a07c5dfd036559a35c8f83bb3e4f757) Thanks [@dimriou](https://github.com/dimriou)! - Decouple utils tests from core #internal +- [#13554](https://github.com/smartcontractkit/chainlink/pull/13554) [`22cab6c152`](https://github.com/smartcontractkit/chainlink/commit/22cab6c1526371c898bde52e7836cfd01ba7daf5) Thanks [@nickcorin](https://github.com/nickcorin)! - #internal Bumped `chainlink-common` package version to commit `1fb0b48758af25d689b6957ebfb76598c9fb27ea`. -- [#13445](https://github.com/smartcontractkit/chainlink/pull/13445) [`3ff5128026`](https://github.com/smartcontractkit/chainlink/commit/3ff51280266dd979cbe2a8fe779c669a4e12fe22) Thanks [@ettec](https://github.com/ettec)! - #internal update operator ui versioun +- [#13670](https://github.com/smartcontractkit/chainlink/pull/13670) [`253a962fa3`](https://github.com/smartcontractkit/chainlink/commit/253a962fa3997765d5df40cf5ca19f5471ed5976) Thanks [@nickcorin](https://github.com/nickcorin)! - #internal bumped chainlink-common version to commit 1eff5dedc9857ed8811186dd2996603942dc1107 -- [#13259](https://github.com/smartcontractkit/chainlink/pull/13259) [`76dbe19282`](https://github.com/smartcontractkit/chainlink/commit/76dbe192822c7e9f289c98e33ebb6693a07046a0) Thanks [@archseer](https://github.com/archseer)! - #added A ChainWriter implementation in the EVM relay. +- [#13459](https://github.com/smartcontractkit/chainlink/pull/13459) [`8fdc77f6a7`](https://github.com/smartcontractkit/chainlink/commit/8fdc77f6a7c30be5254bd9d0cb75fd6b2736242e) Thanks [@makramkd](https://github.com/makramkd)! - #db_update ccip capability specs migration -- [#13265](https://github.com/smartcontractkit/chainlink/pull/13265) [`5db47b63b3`](https://github.com/smartcontractkit/chainlink/commit/5db47b63b3f2d0addf521904940d780caf9f57eb) Thanks [@krehermann](https://github.com/krehermann)! - #db_update Add name to workflow spec. Add unique constraint to (owner,name) for workflow spec +- [#13494](https://github.com/smartcontractkit/chainlink/pull/13494) [`a830fe093f`](https://github.com/smartcontractkit/chainlink/commit/a830fe093f4642236a89ac73bf9eefe5008898bc) Thanks [@HelloKashif](https://github.com/HelloKashif)! - #internal removed HistoryDepth to FinalityDepth validation -- [#13089](https://github.com/smartcontractkit/chainlink/pull/13089) [`b00ad69095`](https://github.com/smartcontractkit/chainlink/commit/b00ad6909571042947f615b8740c31c304bbf7ec) Thanks [@silaslenihan](https://github.com/silaslenihan)! - #internal Switched finality check in HeadTracker to use the underlying finality type +- [#13580](https://github.com/smartcontractkit/chainlink/pull/13580) [`0d4a3b2cd8`](https://github.com/smartcontractkit/chainlink/commit/0d4a3b2cd8ff938ba018d982ef514c754a7df345) Thanks [@DeividasK](https://github.com/DeividasK)! - #internal -### Patch Changes +- [#13569](https://github.com/smartcontractkit/chainlink/pull/13569) [`f5a70eb09a`](https://github.com/smartcontractkit/chainlink/commit/f5a70eb09abc9a4d859442c9bd062a74a7ec9c54) Thanks [@DeividasK](https://github.com/DeividasK)! - #internal -- [#13377](https://github.com/smartcontractkit/chainlink/pull/13377) [`390ee1990e`](https://github.com/smartcontractkit/chainlink/commit/390ee1990e2545d4f47e64a521ee61ee0b200786) Thanks [@bolekk](https://github.com/bolekk)! - #internal handle new metadata fields +- [#13528](https://github.com/smartcontractkit/chainlink/pull/13528) [`95502ad269`](https://github.com/smartcontractkit/chainlink/commit/95502ad2699d63891662594f70e82e76682f2ed8) Thanks [@DeividasK](https://github.com/DeividasK)! - #internal -- [#13315](https://github.com/smartcontractkit/chainlink/pull/13315) [`3af83ed014`](https://github.com/smartcontractkit/chainlink/commit/3af83ed01439648354ac6b348d61b0f9594b99ec) Thanks [@mateusz-sekara](https://github.com/mateusz-sekara)! - Reducing the scope of 0233 migration to include only 5th word index which is required for CCIP #db_update +- [#13563](https://github.com/smartcontractkit/chainlink/pull/13563) [`99fa0e68d1`](https://github.com/smartcontractkit/chainlink/commit/99fa0e68d11f4bbc5bc54c6c65edb1db024b21a9) Thanks [@HenryNguyen5](https://github.com/HenryNguyen5)! - #internal Clean up workflow engine logging -- [#13139](https://github.com/smartcontractkit/chainlink/pull/13139) [`15fab1daa8`](https://github.com/smartcontractkit/chainlink/commit/15fab1daa84348e96a7895280209cb73e456a1c5) Thanks [@jmank88](https://github.com/jmank88)! - core/services: fix ocrWrapper saveError contexts #internal +- [#13443](https://github.com/smartcontractkit/chainlink/pull/13443) [`955566fb9b`](https://github.com/smartcontractkit/chainlink/commit/955566fb9b9a4f930069d497a81bb6c37fac0125) Thanks [@Tofel](https://github.com/Tofel)! - #changed Expand EVM implementation compatibility pipeline -- [#13144](https://github.com/smartcontractkit/chainlink/pull/13144) [`49f1bf3ba2`](https://github.com/smartcontractkit/chainlink/commit/49f1bf3ba296f0e3dfc01d5a3d371f82f159dc4a) Thanks [@jmank88](https://github.com/jmank88)! - improve handling of postgres connection settings and driver versions #db +- [#13602](https://github.com/smartcontractkit/chainlink/pull/13602) [`fc3a291d6c`](https://github.com/smartcontractkit/chainlink/commit/fc3a291d6c75e8045c3f8e2458ec7513ffdb3882) Thanks [@nickcorin](https://github.com/nickcorin)! - #internal fixed a bug where we weren't sending the value param to the txm -- [#13286](https://github.com/smartcontractkit/chainlink/pull/13286) [`6139126034`](https://github.com/smartcontractkit/chainlink/commit/61391260340ba74f3510e6ded4fdace6829630b7) Thanks [@EasterTheBunny](https://github.com/EasterTheBunny)! - enforce proper result indexing on pipeline results #breaking_change +- [#13714](https://github.com/smartcontractkit/chainlink/pull/13714) [`4b19e37553`](https://github.com/smartcontractkit/chainlink/commit/4b19e37553ecf60c9d98209bc29b4079ae64cbe3) Thanks [@shileiwill](https://github.com/shileiwill)! - add native billing in smoke test #added -- [#13279](https://github.com/smartcontractkit/chainlink/pull/13279) [`5a87f4a59e`](https://github.com/smartcontractkit/chainlink/commit/5a87f4a59e3c6c92b08ebefc5090017693785729) Thanks [@DylanTinianov](https://github.com/DylanTinianov)! - #changed Remove ClientErrors interface from common +- [#13426](https://github.com/smartcontractkit/chainlink/pull/13426) [`592b2bb5a8`](https://github.com/smartcontractkit/chainlink/commit/592b2bb5a84a0e8858f77c5faa99e881f911878c) Thanks [@archseer](https://github.com/archseer)! - #internal + +- [#13546](https://github.com/smartcontractkit/chainlink/pull/13546) [`10ddafaebe`](https://github.com/smartcontractkit/chainlink/commit/10ddafaebedb94ad5a59968d19256b8c4592857f) Thanks [@cds95](https://github.com/cds95)! - #internal upgrade keystone contracts to 0.8.24 + +- [#13504](https://github.com/smartcontractkit/chainlink/pull/13504) [`815c5ea871`](https://github.com/smartcontractkit/chainlink/commit/815c5ea8715462e00f6ea10cdc0b93ec3e1ba505) Thanks [@shileiwill](https://github.com/shileiwill)! - move v23 contracts #bugfix + +- [#13583](https://github.com/smartcontractkit/chainlink/pull/13583) [`8ccaa140ae`](https://github.com/smartcontractkit/chainlink/commit/8ccaa140aebdafbb760569a1d2b7cabe8ac1bf61) Thanks [@samsondav](https://github.com/samsondav)! - Add new relayer type "dummy" for testing. -- [#13190](https://github.com/smartcontractkit/chainlink/pull/13190) [`c93e83ab99`](https://github.com/smartcontractkit/chainlink/commit/c93e83ab992c5238ec79f9581f2eb370d9862492) Thanks [@HenryNguyen5](https://github.com/HenryNguyen5)! - #internal Add Keystone CRIB Provisioning + #added -- [#13008](https://github.com/smartcontractkit/chainlink/pull/13008) [`841fe61daa`](https://github.com/smartcontractkit/chainlink/commit/841fe61daa90b980f1e1622d2f7bd8f850b55462) Thanks [@HenryNguyen5](https://github.com/HenryNguyen5)! - #internal Keystone - rename type -> id +- [#13671](https://github.com/smartcontractkit/chainlink/pull/13671) [`4493b96f62`](https://github.com/smartcontractkit/chainlink/commit/4493b96f62d18376d89ae152cb0df3eab87ab5fd) Thanks [@poopoothegorilla](https://github.com/poopoothegorilla)! - #bugfix fixed ureachable code bug which could result in stuck txns -- [#13436](https://github.com/smartcontractkit/chainlink/pull/13436) [`f37afb9eba`](https://github.com/smartcontractkit/chainlink/commit/f37afb9ebaeda10f8b3873b069b8a824e60a81c3) Thanks [@bolekk](https://github.com/bolekk)! - #internal #bugfix keystone bugfixes +- [#13376](https://github.com/smartcontractkit/chainlink/pull/13376) [`bb40d51502`](https://github.com/smartcontractkit/chainlink/commit/bb40d51502487b010a1d6621db42458356dbbdc0) Thanks [@shileiwill](https://github.com/shileiwill)! - add events, add getter and add comments #bugfix -- [#13214](https://github.com/smartcontractkit/chainlink/pull/13214) [`921a015792`](https://github.com/smartcontractkit/chainlink/commit/921a015792570ad3fa3b7700bdd3ec8f0590b383) Thanks [@momentmaker](https://github.com/momentmaker)! - Add to CI changeset workflow an additional step to update the Jira issue associated with this PR and set the `fixVersions` for the issue with the upcoming core release version. #internal #wip +- [#13495](https://github.com/smartcontractkit/chainlink/pull/13495) [`483ee6ae06`](https://github.com/smartcontractkit/chainlink/commit/483ee6ae06f608d150ab360e0a3ffe6895b39d52) Thanks [@pavel-raykov](https://github.com/pavel-raykov)! - #updated Fix verb formatting in the log outputs. -- [#13449](https://github.com/smartcontractkit/chainlink/pull/13449) [`69a95d8262`](https://github.com/smartcontractkit/chainlink/commit/69a95d82626290858219250e746fd51c8c7c4093) Thanks [@dimriou](https://github.com/dimriou)! - Cleanup txm tests #internal +- [#13599](https://github.com/smartcontractkit/chainlink/pull/13599) [`e0ce0795b4`](https://github.com/smartcontractkit/chainlink/commit/e0ce0795b44f27539611327efce7c7c004511daa) Thanks [@pavel-raykov](https://github.com/pavel-raykov)! - #internal Add loggercheck linter to verify that \*w logging methods have even number of args. -- [#13183](https://github.com/smartcontractkit/chainlink/pull/13183) [`96304756a7`](https://github.com/smartcontractkit/chainlink/commit/96304756a77cdb2acf251d21d59b6aa8b55bf61a) Thanks [@cds95](https://github.com/cds95)! - #internal add modify DON function to capability registry +## 2.13.0 - 2024-07-01 -- [#13453](https://github.com/smartcontractkit/chainlink/pull/13453) [`8c98c80376`](https://github.com/smartcontractkit/chainlink/commit/8c98c80376c3b6d72bffeab62ee45a74449b6ef5) Thanks [@cds95](https://github.com/cds95)! - #internal return hashed capability ids +### Minor Changes + +- [#13354](https://github.com/smartcontractkit/chainlink/pull/13354) [`58d73ecf61`](https://github.com/smartcontractkit/chainlink/commit/58d73ecf618ac39c37f767e70c4e6d6a51eaba59) Thanks [@friedemannf](https://github.com/friedemannf)! - #breaking_change Remove the `xdai` `ChainType` config option. Moving forward, only `gnosis` can be used. + +- [#13221](https://github.com/smartcontractkit/chainlink/pull/13221) [`0b100ad3db`](https://github.com/smartcontractkit/chainlink/commit/0b100ad3dbf0a3c2fbd6e55c539046f6f3c9e5f6) Thanks [@ilija42](https://github.com/ilija42)! - Added a mechanism to validate forwarders for OCR2 and fallback to EOA if necessary #added + +- [#13384](https://github.com/smartcontractkit/chainlink/pull/13384) [`bc087f1de2`](https://github.com/smartcontractkit/chainlink/commit/bc087f1de2014cce1027341d14e0917c4351fb21) Thanks [@augustbleeds](https://github.com/augustbleeds)! - bump chainlink-starknet so it builds reports with median gas price #updated + +- [#13353](https://github.com/smartcontractkit/chainlink/pull/13353) [`7a86103474`](https://github.com/smartcontractkit/chainlink/commit/7a861034740a44ebb5d3f1da2d271637691c0bd3) Thanks [@pavel-raykov](https://github.com/pavel-raykov)! - #updated Remove deprecated app.shortcut links + +- [#13181](https://github.com/smartcontractkit/chainlink/pull/13181) [`c14576a945`](https://github.com/smartcontractkit/chainlink/commit/c14576a945fdc6b5099ffd31f467a0712afe6fd6) Thanks [@krehermann](https://github.com/krehermann)! - #added workflow spec auto-approval via CLO -- [#13368](https://github.com/smartcontractkit/chainlink/pull/13368) [`000f2cb36b`](https://github.com/smartcontractkit/chainlink/commit/000f2cb36b7d9b6d046d383c85996ae1ae7a606e) Thanks [@cds95](https://github.com/cds95)! - #internal remove update capabilities from capability registry +- [#13455](https://github.com/smartcontractkit/chainlink/pull/13455) [`066afc0877`](https://github.com/smartcontractkit/chainlink/commit/066afc0877a9e953bbda25a4ff09009d7f1c1e2d) Thanks [@krehermann](https://github.com/krehermann)! - #bugfix use correct internal id in workflow auto-approval + +- [#12881](https://github.com/smartcontractkit/chainlink/pull/12881) [`d675d864f0`](https://github.com/smartcontractkit/chainlink/commit/d675d864f0e6f33c783bfed17fe31b2c127eb51d) Thanks [@amit-momin](https://github.com/amit-momin)! - #added Added an auto-purge feature to the EVM TXM that identifies terminally stuck transactions either through a chain specific method or heurisitic then purges them to unblock the nonce. Included 4 new toml configs under Transactions.AutoPurge to configure this new feature: Enabled, Threshold, MinAttempts, and DetectionApiUrl. -- [#13183](https://github.com/smartcontractkit/chainlink/pull/13183) [`96304756a7`](https://github.com/smartcontractkit/chainlink/commit/96304756a77cdb2acf251d21d59b6aa8b55bf61a) Thanks [@cds95](https://github.com/cds95)! - #internal add getters in capability registry +- [#13401](https://github.com/smartcontractkit/chainlink/pull/13401) [`905830c3ff`](https://github.com/smartcontractkit/chainlink/commit/905830c3ff16c670c3fbf3d83a0c2ca3a15e83a8) Thanks [@krehermann](https://github.com/krehermann)! - #db_update add persistence for DON-2-DON discovery announcements + +- [#13200](https://github.com/smartcontractkit/chainlink/pull/13200) [`4718aa7ec2`](https://github.com/smartcontractkit/chainlink/commit/4718aa7ec20e2ef70dff7fb72095d357f3725a80) Thanks [@augustbleeds](https://github.com/augustbleeds)! - Add option to include GasPriceSubunits pipeline to include gasPriceSubunits in median ocr2 transmission (only to be used with Starknet chain for now) #added #nops #updated +- [#13259](https://github.com/smartcontractkit/chainlink/pull/13259) [`76dbe19282`](https://github.com/smartcontractkit/chainlink/commit/76dbe192822c7e9f289c98e33ebb6693a07046a0) Thanks [@archseer](https://github.com/archseer)! - #added A ChainWriter implementation in the EVM relay. + +- [#13265](https://github.com/smartcontractkit/chainlink/pull/13265) [`5db47b63b3`](https://github.com/smartcontractkit/chainlink/commit/5db47b63b3f2d0addf521904940d780caf9f57eb) Thanks [@krehermann](https://github.com/krehermann)! - #db_update Add name to workflow spec. Add unique constraint to (owner,name) for workflow spec + +### Patch Changes +- [#13315](https://github.com/smartcontractkit/chainlink/pull/13315) [`3af83ed014`](https://github.com/smartcontractkit/chainlink/commit/3af83ed01439648354ac6b348d61b0f9594b99ec) Thanks [@mateusz-sekara](https://github.com/mateusz-sekara)! - Reducing the scope of 0233 migration to include only 5th word index which is required for CCIP #db_update + +- [#13144](https://github.com/smartcontractkit/chainlink/pull/13144) [`49f1bf3ba2`](https://github.com/smartcontractkit/chainlink/commit/49f1bf3ba296f0e3dfc01d5a3d371f82f159dc4a) Thanks [@jmank88](https://github.com/jmank88)! - improve handling of postgres connection settings and driver versions #db + +- [#13286](https://github.com/smartcontractkit/chainlink/pull/13286) [`6139126034`](https://github.com/smartcontractkit/chainlink/commit/61391260340ba74f3510e6ded4fdace6829630b7) Thanks [@EasterTheBunny](https://github.com/EasterTheBunny)! - enforce proper result indexing on pipeline results #breaking_change + +- [#13279](https://github.com/smartcontractkit/chainlink/pull/13279) [`5a87f4a59e`](https://github.com/smartcontractkit/chainlink/commit/5a87f4a59e3c6c92b08ebefc5090017693785729) Thanks [@DylanTinianov](https://github.com/DylanTinianov)! - #changed Remove ClientErrors interface from common - [#13230](https://github.com/smartcontractkit/chainlink/pull/13230) [`6f1ebca197`](https://github.com/smartcontractkit/chainlink/commit/6f1ebca1970d4a970be64c581800ab781c6c3c7c) Thanks [@dhaidashenko](https://github.com/dhaidashenko)! - Fixed CPU usage issues caused by inefficiencies in HeadTracker. HeadTracker's support of finality tags caused a drastic increase in the number of tracked blocks on the Arbitrum chain (from 50 to 12,000), which has led to a 30% increase in CPU usage. @@ -106,66 +143,30 @@ - [#13364](https://github.com/smartcontractkit/chainlink/pull/13364) [`fc007a9484`](https://github.com/smartcontractkit/chainlink/commit/fc007a94846c178bc9d5203dbff6b6b8c7546a71) Thanks [@FelixFan1992](https://github.com/FelixFan1992)! - #bugfix fix a funding bug in LinkAvailableBalanceMonitor -- [#13368](https://github.com/smartcontractkit/chainlink/pull/13368) [`000f2cb36b`](https://github.com/smartcontractkit/chainlink/commit/000f2cb36b7d9b6d046d383c85996ae1ae7a606e) Thanks [@cds95](https://github.com/cds95)! - #internal capability registry informational findings - -- [#13368](https://github.com/smartcontractkit/chainlink/pull/13368) [`000f2cb36b`](https://github.com/smartcontractkit/chainlink/commit/000f2cb36b7d9b6d046d383c85996ae1ae7a606e) Thanks [@cds95](https://github.com/cds95)! - #internal return don capability config contract config from capability registry - -- [#13368](https://github.com/smartcontractkit/chainlink/pull/13368) [`000f2cb36b`](https://github.com/smartcontractkit/chainlink/commit/000f2cb36b7d9b6d046d383c85996ae1ae7a606e) Thanks [@cds95](https://github.com/cds95)! - #internal add capability registry comment explaining why we do not validate node operator name - -- [#13425](https://github.com/smartcontractkit/chainlink/pull/13425) [`eeb363f123`](https://github.com/smartcontractkit/chainlink/commit/eeb363f1230415dde573607a095b177c612d3bef) Thanks [@DeividasK](https://github.com/DeividasK)! - #internal - -- [#13328](https://github.com/smartcontractkit/chainlink/pull/13328) [`0d95942ad4`](https://github.com/smartcontractkit/chainlink/commit/0d95942ad414a3ecefb17bd8166fe28f474018d0) Thanks [@HenryNguyen5](https://github.com/HenryNguyen5)! - #internal [Keystone] Merge version field with ID - - [#13174](https://github.com/smartcontractkit/chainlink/pull/13174) [`e778a3202b`](https://github.com/smartcontractkit/chainlink/commit/e778a3202b4d8761ffc44b790196d9a580fede1c) Thanks [@FelixFan1992](https://github.com/FelixFan1992)! - #changed: AUTO-10539: adjust logging for offchain config and gas control - [#12952](https://github.com/smartcontractkit/chainlink/pull/12952) [`7572a50a78`](https://github.com/smartcontractkit/chainlink/commit/7572a50a78a270188344786937f68233df82f65b) Thanks [@FelixFan1992](https://github.com/FelixFan1992)! - #added compare user-defined max gas price with current gas price in automation simulation pipeline -- [#13183](https://github.com/smartcontractkit/chainlink/pull/13183) [`96304756a7`](https://github.com/smartcontractkit/chainlink/commit/96304756a77cdb2acf251d21d59b6aa8b55bf61a) Thanks [@cds95](https://github.com/cds95)! - #internal update ICapabilityConfiguration interface - - [#13216](https://github.com/smartcontractkit/chainlink/pull/13216) [`6099abbdbf`](https://github.com/smartcontractkit/chainlink/commit/6099abbdbfb3ad396ca1ed5138ecd7a13159de19) Thanks [@ibrajer](https://github.com/ibrajer)! - Added Base Sepolia to ChainUtils #changed - [#13177](https://github.com/smartcontractkit/chainlink/pull/13177) [`0d58a8d5db`](https://github.com/smartcontractkit/chainlink/commit/0d58a8d5db24f42720226e73328e501637ba59c5) Thanks [@shileiwill](https://github.com/shileiwill)! - link transfer status check #bugfix -- [#13118](https://github.com/smartcontractkit/chainlink/pull/13118) [`6008d730bf`](https://github.com/smartcontractkit/chainlink/commit/6008d730bf1fcfc4a9dd1e46497c3db75cf390fe) Thanks [@bolekk](https://github.com/bolekk)! - #internal Pass MercuryTriggerService to Mercury Transmitter - - [#13058](https://github.com/smartcontractkit/chainlink/pull/13058) [`a34a17ae9d`](https://github.com/smartcontractkit/chainlink/commit/a34a17ae9d62679a1ff15a7703f5cbcf6dfd1d0f) Thanks [@shileiwill](https://github.com/shileiwill)! - withdraw in offchain mode #bugfix -- [#13328](https://github.com/smartcontractkit/chainlink/pull/13328) [`0d95942ad4`](https://github.com/smartcontractkit/chainlink/commit/0d95942ad414a3ecefb17bd8166fe28f474018d0) Thanks [@HenryNguyen5](https://github.com/HenryNguyen5)! - #internal Add workflow validation - -- [#13456](https://github.com/smartcontractkit/chainlink/pull/13456) [`b09c14d0ca`](https://github.com/smartcontractkit/chainlink/commit/b09c14d0ca85678799cb108500687d0e8456205a) Thanks [@bolekk](https://github.com/bolekk)! - #internal [Keystone] Add remote target to syncer - -- [#13176](https://github.com/smartcontractkit/chainlink/pull/13176) [`d2cd916e46`](https://github.com/smartcontractkit/chainlink/commit/d2cd916e46c51d76f7e1ea61a0cc86b1415f4036) Thanks [@ferglor](https://github.com/ferglor)! - Add logs for when the assumptions of how the log buffer will be used are violated #internal - - [#13213](https://github.com/smartcontractkit/chainlink/pull/13213) [`1b1e31ebfc`](https://github.com/smartcontractkit/chainlink/commit/1b1e31ebfc5198ab7e43291110b6f5d54e467a01) Thanks [@FelixFan1992](https://github.com/FelixFan1992)! - #bugfix fix an automation smoke test flake -- [#13368](https://github.com/smartcontractkit/chainlink/pull/13368) [`000f2cb36b`](https://github.com/smartcontractkit/chainlink/commit/000f2cb36b7d9b6d046d383c85996ae1ae7a606e) Thanks [@cds95](https://github.com/cds95)! - #internal verify valid node operator when adding nodes to capability registry - -- [#13218](https://github.com/smartcontractkit/chainlink/pull/13218) [`4938ef3961`](https://github.com/smartcontractkit/chainlink/commit/4938ef396112a6de6f9822cfcc116fd5cdfb92be) Thanks [@bolekk](https://github.com/bolekk)! - #internal ReportCodec for Streams trigger - -- [#13094](https://github.com/smartcontractkit/chainlink/pull/13094) [`a0d1ce5e9c`](https://github.com/smartcontractkit/chainlink/commit/a0d1ce5e9cddc540bba8eb193865646cf0ebc0a8) Thanks [@momentmaker](https://github.com/momentmaker)! - Refactor changesets release preview workflow #internal - -- [#13175](https://github.com/smartcontractkit/chainlink/pull/13175) [`fbd94c4351`](https://github.com/smartcontractkit/chainlink/commit/fbd94c43511dabd272d7fd990dfb76de66c30a16) Thanks [@erikburt](https://github.com/erikburt)! - bump chainlink-solana dependency #internal - -- [#13183](https://github.com/smartcontractkit/chainlink/pull/13183) [`96304756a7`](https://github.com/smartcontractkit/chainlink/commit/96304756a77cdb2acf251d21d59b6aa8b55bf61a) Thanks [@cds95](https://github.com/cds95)! - #internal implement remove DONs in capability registry - - [#12813](https://github.com/smartcontractkit/chainlink/pull/12813) [`ac893364e6`](https://github.com/smartcontractkit/chainlink/commit/ac893364e6c6ede08e9bf04da7dc64e0da94ab6e) Thanks [@matYang](https://github.com/matYang)! - #db_update created 2 new CCIP tables in migration 0236, one for observed gas prices, one for observed token prices; setup indexing for these tables. #added ORM for CCIP gas prices and token prices -- [#13080](https://github.com/smartcontractkit/chainlink/pull/13080) [`36cc95f625`](https://github.com/smartcontractkit/chainlink/commit/36cc95f6256b5ba418a916de2c9dc9597508147a) Thanks [@cds95](https://github.com/cds95)! - #internal Generate gethwrappers for capability registry changes - - [#13173](https://github.com/smartcontractkit/chainlink/pull/13173) [`a9717f05e9`](https://github.com/smartcontractkit/chainlink/commit/a9717f05e9af0fa07746c6b95b7f1625089a860f) Thanks [@ferglor](https://github.com/ferglor)! - Revert block number tracking #changed -- [#13453](https://github.com/smartcontractkit/chainlink/pull/13453) [`8c98c80376`](https://github.com/smartcontractkit/chainlink/commit/8c98c80376c3b6d72bffeab62ee45a74449b6ef5) Thanks [@cds95](https://github.com/cds95)! - #internal update error message when node does not exist - - [#12952](https://github.com/smartcontractkit/chainlink/pull/12952) [`7572a50a78`](https://github.com/smartcontractkit/chainlink/commit/7572a50a78a270188344786937f68233df82f65b) Thanks [@FelixFan1992](https://github.com/FelixFan1992)! - #added pass a gas estimator to registry 2.1 pipeline -- [#13128](https://github.com/smartcontractkit/chainlink/pull/13128) [`a0e7b7cdd6`](https://github.com/smartcontractkit/chainlink/commit/a0e7b7cdd63ecb3f4d8e0ca3f5a4111703760c9b) Thanks [@samsondav](https://github.com/samsondav)! - #internal improve mercury tranmission debugging - - [#13132](https://github.com/smartcontractkit/chainlink/pull/13132) [`eed5668e3c`](https://github.com/smartcontractkit/chainlink/commit/eed5668e3c83cb680d2915f89d097fcb1b74a4f9) Thanks [@akuzni2](https://github.com/akuzni2)! - #nops fix metric description on mercury_transmit_queue_load - [#13084](https://github.com/smartcontractkit/chainlink/pull/13084) [`d79bdf16c5`](https://github.com/smartcontractkit/chainlink/commit/d79bdf16c5129cf7bc7cc5114f92eb07fd3fbf02) Thanks [@austinborn](https://github.com/austinborn)! - #updated Add gethwrappers for operatorforwarder contracts @@ -176,56 +177,20 @@ - [#13133](https://github.com/smartcontractkit/chainlink/pull/13133) [`2e668372ac`](https://github.com/smartcontractkit/chainlink/commit/2e668372ac54e71fd357feba427ffacf0613bda2) Thanks [@matYang](https://github.com/matYang)! - #changed CCIP price cache to use DB timestamp -- [#13380](https://github.com/smartcontractkit/chainlink/pull/13380) [`21c4cde066`](https://github.com/smartcontractkit/chainlink/commit/21c4cde066f3d6a49072ca338c966265b910583e) Thanks [@dimriou](https://github.com/dimriou)! - Removed deprecated evm client code #internal - - [#13096](https://github.com/smartcontractkit/chainlink/pull/13096) [`2c08c8c1a5`](https://github.com/smartcontractkit/chainlink/commit/2c08c8c1a58ea4b7c09b0d5a5ca3b8a677beb9f4) Thanks [@shileiwill](https://github.com/shileiwill)! - add upkeepCharged event #bugfix -- [#13368](https://github.com/smartcontractkit/chainlink/pull/13368) [`000f2cb36b`](https://github.com/smartcontractkit/chainlink/commit/000f2cb36b7d9b6d046d383c85996ae1ae7a606e) Thanks [@cds95](https://github.com/cds95)! - #internal verify that node is not part of a DON when removing - -- [#13202](https://github.com/smartcontractkit/chainlink/pull/13202) [`eb6b50d313`](https://github.com/smartcontractkit/chainlink/commit/eb6b50d31323c324aaa2bf8d1cf465f97a7893fd) Thanks [@bolekk](https://github.com/bolekk)! - #internal [Keystone] EVM encoder support for tuples - - [#13078](https://github.com/smartcontractkit/chainlink/pull/13078) [`0917394a46`](https://github.com/smartcontractkit/chainlink/commit/0917394a4625c3e97b17e348dd473199a15402bf) Thanks [@finleydecker](https://github.com/finleydecker)! - bumpThreshold config setting for chains using suggestPrice estimator #updated -- [#13368](https://github.com/smartcontractkit/chainlink/pull/13368) [`000f2cb36b`](https://github.com/smartcontractkit/chainlink/commit/000f2cb36b7d9b6d046d383c85996ae1ae7a606e) Thanks [@cds95](https://github.com/cds95)! - #internal remove tracking deprecated arrays - -- [#13368](https://github.com/smartcontractkit/chainlink/pull/13368) [`000f2cb36b`](https://github.com/smartcontractkit/chainlink/commit/000f2cb36b7d9b6d046d383c85996ae1ae7a606e) Thanks [@cds95](https://github.com/cds95)! - #internal capability registry internal review - - [#13336](https://github.com/smartcontractkit/chainlink/pull/13336) [`4c7e5a0efa`](https://github.com/smartcontractkit/chainlink/commit/4c7e5a0efa90aed5d5454b5a68753076eea67f55) Thanks [@dhaidashenko](https://github.com/dhaidashenko)! - Added config option `HeadTracker.FinalityTagBypass` to force `HeadTracker` to track blocks up to `FinalityDepth` even if `FinalityTagsEnabled = true`. This option is a temporary measure to address high CPU usage on chains with extremely large actual finality depth (gap between the current head and the latest finalized block). #added Added config option `HeadTracker.MaxAllowedFinalityDepth` maximum gap between current head to the latest finalized block that `HeadTracker` considers healthy. #added -- [#13368](https://github.com/smartcontractkit/chainlink/pull/13368) [`000f2cb36b`](https://github.com/smartcontractkit/chainlink/commit/000f2cb36b7d9b6d046d383c85996ae1ae7a606e) Thanks [@cds95](https://github.com/cds95)! - #internal internal-review-fixes-for-capability-registry - -- [#13199](https://github.com/smartcontractkit/chainlink/pull/13199) [`4f502c9fd0`](https://github.com/smartcontractkit/chainlink/commit/4f502c9fd0fea458647bb345f5c0da995f3b6cb1) Thanks [@bolekk](https://github.com/bolekk)! - #internal Use Aggregator factory for OCR capability - - [#13263](https://github.com/smartcontractkit/chainlink/pull/13263) [`14ec6c4a91`](https://github.com/smartcontractkit/chainlink/commit/14ec6c4a912eeb65753703c363d1e90cbcf88328) Thanks [@shileiwill](https://github.com/shileiwill)! - tune debugging script #bugfix - [#13088](https://github.com/smartcontractkit/chainlink/pull/13088) [`29b16360fb`](https://github.com/smartcontractkit/chainlink/commit/29b16360fb41e4372f72fe744aaf3ee8234a9b67) Thanks [@shileiwill](https://github.com/shileiwill)! - get available erc20 for payment #bugfix - [#13165](https://github.com/smartcontractkit/chainlink/pull/13165) [`143741012c`](https://github.com/smartcontractkit/chainlink/commit/143741012c4d0b148ada9d5aa237ff932cd3005b) Thanks [@cedric-cordenier](https://github.com/cedric-cordenier)! - #db_update Add ON DELETE CASCADE to workflow tables -- [#13103](https://github.com/smartcontractkit/chainlink/pull/13103) [`54f7c9c8f5`](https://github.com/smartcontractkit/chainlink/commit/54f7c9c8f5508d0d0a063eb435404b4164723300) Thanks [@DeividasK](https://github.com/DeividasK)! - #internal regen geth wrappers for capability registry - -- [#13112](https://github.com/smartcontractkit/chainlink/pull/13112) [`80590662bd`](https://github.com/smartcontractkit/chainlink/commit/80590662bd9956d3c93449ca4703a2430e0613b7) Thanks [@HenryNguyen5](https://github.com/HenryNguyen5)! - #internal Normalize keystone workflow ref regex property to match id regex - -- [#13183](https://github.com/smartcontractkit/chainlink/pull/13183) [`96304756a7`](https://github.com/smartcontractkit/chainlink/commit/96304756a77cdb2acf251d21d59b6aa8b55bf61a) Thanks [@cds95](https://github.com/cds95)! - #internal track config count in DON struct - -- [#13102](https://github.com/smartcontractkit/chainlink/pull/13102) [`700a827194`](https://github.com/smartcontractkit/chainlink/commit/700a82719451611381ab5dbb94fe00547660440b) Thanks [@cds95](https://github.com/cds95)! - #internal generate geth wrappers for capability registry remove nodes - -- [#13406](https://github.com/smartcontractkit/chainlink/pull/13406) [`a63569c9a3`](https://github.com/smartcontractkit/chainlink/commit/a63569c9a3893a7dc431459e08a4b08bb3a91231) Thanks [@bolekk](https://github.com/bolekk)! - #internal #bugfix Fix target wrapper init problems - -- [#13368](https://github.com/smartcontractkit/chainlink/pull/13368) [`000f2cb36b`](https://github.com/smartcontractkit/chainlink/commit/000f2cb36b7d9b6d046d383c85996ae1ae7a606e) Thanks [@cds95](https://github.com/cds95)! - #internal allow updating capabilities and to add/remove multiple capabilities at once from the capability registry - -- [#13189](https://github.com/smartcontractkit/chainlink/pull/13189) [`1451b2b632`](https://github.com/smartcontractkit/chainlink/commit/1451b2b6321f997c2df2c0b7fd05d6ba1eac30e4) Thanks [@samsondav](https://github.com/samsondav)! - Performance improvements for mercury single insert for multiple mercury servers #internal - -- [#13366](https://github.com/smartcontractkit/chainlink/pull/13366) [`d53d6d08da`](https://github.com/smartcontractkit/chainlink/commit/d53d6d08dac5d3ee27ae89012669c6c2455295c8) Thanks [@bolekk](https://github.com/bolekk)! - #internal keystone report context - -- [#13389](https://github.com/smartcontractkit/chainlink/pull/13389) [`3959091d4f`](https://github.com/smartcontractkit/chainlink/commit/3959091d4f3925b64cb6b0b55b7f7c72a4f924b9) Thanks [@bolekk](https://github.com/bolekk)! - #internal Update metadata passed to Forwarder and Receiver - -- [#13183](https://github.com/smartcontractkit/chainlink/pull/13183) [`96304756a7`](https://github.com/smartcontractkit/chainlink/commit/96304756a77cdb2acf251d21d59b6aa8b55bf61a) Thanks [@cds95](https://github.com/cds95)! - #internal update node signer type - -- [#13368](https://github.com/smartcontractkit/chainlink/pull/13368) [`000f2cb36b`](https://github.com/smartcontractkit/chainlink/commit/000f2cb36b7d9b6d046d383c85996ae1ae7a606e) Thanks [@cds95](https://github.com/cds95)! - #internal update uint256 to uint32 for donId declaration in capability config interface - - [#12952](https://github.com/smartcontractkit/chainlink/pull/12952) [`7572a50a78`](https://github.com/smartcontractkit/chainlink/commit/7572a50a78a270188344786937f68233df82f65b) Thanks [@FelixFan1992](https://github.com/FelixFan1992)! - #added an integration test for max gas price check - [#13272](https://github.com/smartcontractkit/chainlink/pull/13272) [`c7a6356f49`](https://github.com/smartcontractkit/chainlink/commit/c7a6356f4903e919964ca91493f18e0ebf4eb08b) Thanks [@friedemannf](https://github.com/friedemannf)! - #bugfix allow ChainType to be set to xdai diff --git a/GNUmakefile b/GNUmakefile index 4db64002f6..8224aa4745 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -135,7 +135,7 @@ gomods: ## Install gomods .PHONY: mockery mockery: $(mockery) ## Install mockery. - go install github.com/vektra/mockery/v2@v2.42.2 + go install github.com/vektra/mockery/v2@v2.43.2 .PHONY: codecgen codecgen: $(codecgen) ## Install codecgen @@ -179,6 +179,10 @@ goreleaser-dev-release: ## run goreleaser snapshot release modgraph: ./tools/bin/modgraph > go.md +.PHONY: test-short +test-short: ## Run 'go test -short' and suppress uninteresting output + go test -short ./... | grep -v "[no test files]" | grep -v "\(cached\)" + help: @echo "" @echo " .__ .__ .__ .__ __" diff --git a/common/client/ctx.go b/common/client/ctx.go new file mode 100644 index 0000000000..57b2fc8a86 --- /dev/null +++ b/common/client/ctx.go @@ -0,0 +1,17 @@ +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 new file mode 100644 index 0000000000..822b36c3f8 --- /dev/null +++ b/common/client/ctx_test.go @@ -0,0 +1,16 @@ +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_head_test.go b/common/client/mock_head_test.go index 9a66e164aa..64c6dbcb0a 100644 --- a/common/client/mock_head_test.go +++ b/common/client/mock_head_test.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package client diff --git a/common/client/mock_node_client_test.go b/common/client/mock_node_client_test.go index ec83158a5f..a7c0e4dbdb 100644 --- a/common/client/mock_node_client_test.go +++ b/common/client/mock_node_client_test.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package client @@ -116,6 +116,34 @@ func (_m *mockNodeClient[CHAIN_ID, HEAD]) DisconnectAll() { _m.Called() } +// GetInterceptedChainInfo provides a mock function with given fields: +func (_m *mockNodeClient[CHAIN_ID, HEAD]) GetInterceptedChainInfo() (ChainInfo, ChainInfo) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for GetInterceptedChainInfo") + } + + var r0 ChainInfo + var r1 ChainInfo + if rf, ok := ret.Get(0).(func() (ChainInfo, ChainInfo)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() ChainInfo); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(ChainInfo) + } + + if rf, ok := ret.Get(1).(func() ChainInfo); ok { + r1 = rf() + } else { + r1 = ret.Get(1).(ChainInfo) + } + + return r0, r1 +} + // IsSyncing provides a mock function with given fields: ctx func (_m *mockNodeClient[CHAIN_ID, HEAD]) IsSyncing(ctx context.Context) (bool, error) { ret := _m.Called(ctx) @@ -177,32 +205,29 @@ func (_m *mockNodeClient[CHAIN_ID, HEAD]) SetAliveLoopSub(_a0 types.Subscription _m.Called(_a0) } -// Subscribe provides a mock function with given fields: ctx, channel, args -func (_m *mockNodeClient[CHAIN_ID, HEAD]) Subscribe(ctx context.Context, channel chan<- HEAD, args ...interface{}) (types.Subscription, error) { - var _ca []interface{} - _ca = append(_ca, ctx, channel) - _ca = append(_ca, args...) - ret := _m.Called(_ca...) +// SubscribeNewHead provides a mock function with given fields: ctx, channel +func (_m *mockNodeClient[CHAIN_ID, HEAD]) SubscribeNewHead(ctx context.Context, channel chan<- HEAD) (types.Subscription, error) { + ret := _m.Called(ctx, channel) if len(ret) == 0 { - panic("no return value specified for Subscribe") + panic("no return value specified for SubscribeNewHead") } var r0 types.Subscription var r1 error - if rf, ok := ret.Get(0).(func(context.Context, chan<- HEAD, ...interface{}) (types.Subscription, error)); ok { - return rf(ctx, channel, args...) + if rf, ok := ret.Get(0).(func(context.Context, chan<- HEAD) (types.Subscription, error)); ok { + return rf(ctx, channel) } - if rf, ok := ret.Get(0).(func(context.Context, chan<- HEAD, ...interface{}) types.Subscription); ok { - r0 = rf(ctx, channel, args...) + if rf, ok := ret.Get(0).(func(context.Context, chan<- HEAD) types.Subscription); ok { + r0 = rf(ctx, channel) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(types.Subscription) } } - if rf, ok := ret.Get(1).(func(context.Context, chan<- HEAD, ...interface{}) error); ok { - r1 = rf(ctx, channel, args...) + if rf, ok := ret.Get(1).(func(context.Context, chan<- HEAD) error); ok { + r1 = rf(ctx, channel) } else { r1 = ret.Error(1) } diff --git a/common/client/mock_node_selector_test.go b/common/client/mock_node_selector_test.go index 996d064daa..783fc50c29 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package client diff --git a/common/client/mock_node_test.go b/common/client/mock_node_test.go index ee2cacb927..5109eb6bb9 100644 --- a/common/client/mock_node_test.go +++ b/common/client/mock_node_test.go @@ -1,14 +1,12 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package client import ( context "context" - big "math/big" - - mock "github.com/stretchr/testify/mock" types "github.com/smartcontractkit/chainlink/v2/common/types" + mock "github.com/stretchr/testify/mock" ) // mockNode is an autogenerated mock type for the Node type @@ -52,6 +50,24 @@ func (_m *mockNode[CHAIN_ID, HEAD, RPC]) ConfiguredChainID() CHAIN_ID { return r0 } +// HighestUserObservations provides a mock function with given fields: +func (_m *mockNode[CHAIN_ID, HEAD, 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 +} + // Name provides a mock function with given fields: func (_m *mockNode[CHAIN_ID, HEAD, RPC]) Name() string { ret := _m.Called() @@ -106,6 +122,11 @@ func (_m *mockNode[CHAIN_ID, HEAD, RPC]) RPC() RPC { return r0 } +// SetPoolChainInfoProvider provides a mock function with given fields: _a0 +func (_m *mockNode[CHAIN_ID, HEAD, RPC]) SetPoolChainInfoProvider(_a0 PoolChainInfoProvider) { + _m.Called(_a0) +} + // Start provides a mock function with given fields: _a0 func (_m *mockNode[CHAIN_ID, HEAD, RPC]) Start(_a0 context.Context) error { ret := _m.Called(_a0) @@ -143,7 +164,7 @@ func (_m *mockNode[CHAIN_ID, HEAD, RPC]) State() nodeState { } // StateAndLatest provides a mock function with given fields: -func (_m *mockNode[CHAIN_ID, HEAD, RPC]) StateAndLatest() (nodeState, int64, *big.Int) { +func (_m *mockNode[CHAIN_ID, HEAD, RPC]) StateAndLatest() (nodeState, ChainInfo) { ret := _m.Called() if len(ret) == 0 { @@ -151,9 +172,8 @@ func (_m *mockNode[CHAIN_ID, HEAD, RPC]) StateAndLatest() (nodeState, int64, *bi } var r0 nodeState - var r1 int64 - var r2 *big.Int - if rf, ok := ret.Get(0).(func() (nodeState, int64, *big.Int)); ok { + var r1 ChainInfo + if rf, ok := ret.Get(0).(func() (nodeState, ChainInfo)); ok { return rf() } if rf, ok := ret.Get(0).(func() nodeState); ok { @@ -162,21 +182,13 @@ func (_m *mockNode[CHAIN_ID, HEAD, RPC]) StateAndLatest() (nodeState, int64, *bi r0 = ret.Get(0).(nodeState) } - if rf, ok := ret.Get(1).(func() int64); ok { + if rf, ok := ret.Get(1).(func() ChainInfo); ok { r1 = rf() } else { - r1 = ret.Get(1).(int64) - } - - if rf, ok := ret.Get(2).(func() *big.Int); ok { - r2 = rf() - } else { - if ret.Get(2) != nil { - r2 = ret.Get(2).(*big.Int) - } + r1 = ret.Get(1).(ChainInfo) } - return r0, r1, r2 + return r0, r1 } // String provides a mock function with given fields: diff --git a/common/client/mock_pool_chain_info_provider_test.go b/common/client/mock_pool_chain_info_provider_test.go new file mode 100644 index 0000000000..4e4955e738 --- /dev/null +++ b/common/client/mock_pool_chain_info_provider_test.go @@ -0,0 +1,70 @@ +// Code generated by mockery v2.43.2. 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 +} + +// HighestUserObservations provides a mock function with given 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 +} + +// LatestChainInfo provides a mock function with given 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 +} + +// 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_test.go b/common/client/mock_rpc_test.go index 54f57e81f6..81bac04547 100644 --- a/common/client/mock_rpc_test.go +++ b/common/client/mock_rpc_test.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package client @@ -366,6 +366,34 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS return r0, r1 } +// GetInterceptedChainInfo provides a mock function with given fields: +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) GetInterceptedChainInfo() (ChainInfo, ChainInfo) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for GetInterceptedChainInfo") + } + + var r0 ChainInfo + var r1 ChainInfo + if rf, ok := ret.Get(0).(func() (ChainInfo, ChainInfo)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() ChainInfo); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(ChainInfo) + } + + if rf, ok := ret.Get(1).(func() ChainInfo); ok { + r1 = rf() + } else { + r1 = ret.Get(1).(ChainInfo) + } + + return r0, r1 +} + // IsSyncing provides a mock function with given fields: ctx func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) IsSyncing(ctx context.Context) (bool, error) { ret := _m.Called(ctx) @@ -637,32 +665,29 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS return r0 } -// Subscribe provides a mock function with given fields: ctx, channel, args -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Subscribe(ctx context.Context, channel chan<- HEAD, args ...interface{}) (types.Subscription, error) { - var _ca []interface{} - _ca = append(_ca, ctx, channel) - _ca = append(_ca, args...) - ret := _m.Called(_ca...) +// SubscribeNewHead provides a mock function with given fields: ctx, channel +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) SubscribeNewHead(ctx context.Context, channel chan<- HEAD) (types.Subscription, error) { + ret := _m.Called(ctx, channel) if len(ret) == 0 { - panic("no return value specified for Subscribe") + panic("no return value specified for SubscribeNewHead") } var r0 types.Subscription var r1 error - if rf, ok := ret.Get(0).(func(context.Context, chan<- HEAD, ...interface{}) (types.Subscription, error)); ok { - return rf(ctx, channel, args...) + if rf, ok := ret.Get(0).(func(context.Context, chan<- HEAD) (types.Subscription, error)); ok { + return rf(ctx, channel) } - if rf, ok := ret.Get(0).(func(context.Context, chan<- HEAD, ...interface{}) types.Subscription); ok { - r0 = rf(ctx, channel, args...) + if rf, ok := ret.Get(0).(func(context.Context, chan<- HEAD) types.Subscription); ok { + r0 = rf(ctx, channel) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(types.Subscription) } } - if rf, ok := ret.Get(1).(func(context.Context, chan<- HEAD, ...interface{}) error); ok { - r1 = rf(ctx, channel, args...) + if rf, ok := ret.Get(1).(func(context.Context, chan<- HEAD) error); ok { + r1 = rf(ctx, channel) } else { r1 = ret.Error(1) } diff --git a/common/client/mock_send_only_client_test.go b/common/client/mock_send_only_client_test.go index b07e10ed8c..606506e657 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package client diff --git a/common/client/mock_send_only_node_test.go b/common/client/mock_send_only_node_test.go index 4822c2620b..a39df992ae 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package client diff --git a/common/client/mocks/config.go b/common/client/mocks/config.go index 306965a9f5..d1007f39f0 100644 --- a/common/client/mocks/config.go +++ b/common/client/mocks/config.go @@ -3,9 +3,10 @@ package mocks import "time" type ChainConfig struct { - IsFinalityTagEnabled bool - FinalityDepthVal uint32 - NoNewHeadsThresholdVal time.Duration + IsFinalityTagEnabled bool + FinalityDepthVal uint32 + NoNewHeadsThresholdVal time.Duration + FinalizedBlockOffsetVal uint32 } func (t ChainConfig) NodeNoNewHeadsThreshold() time.Duration { @@ -19,3 +20,7 @@ func (t ChainConfig) FinalityDepth() uint32 { func (t ChainConfig) FinalityTagEnabled() bool { return t.IsFinalityTagEnabled } + +func (t ChainConfig) FinalizedBlockOffset() uint32 { + return t.FinalizedBlockOffsetVal +} diff --git a/common/client/multi_node.go b/common/client/multi_node.go index 0fc095c293..c53e5d33b7 100644 --- a/common/client/multi_node.go +++ b/common/client/multi_node.go @@ -90,18 +90,19 @@ type multiNode[ BATCH_ELEM any, ] struct { services.StateMachine - nodes []Node[CHAIN_ID, HEAD, RPC_CLIENT] - sendonlys []SendOnlyNode[CHAIN_ID, RPC_CLIENT] - chainID CHAIN_ID - lggr logger.SugaredLogger - selectionMode string - noNewHeadsThreshold time.Duration - nodeSelector NodeSelector[CHAIN_ID, HEAD, RPC_CLIENT] - leaseDuration time.Duration - leaseTicker *time.Ticker - chainFamily string - reportInterval time.Duration - sendTxSoftTimeout time.Duration // defines max waiting time from first response til responses evaluation + nodes []Node[CHAIN_ID, HEAD, RPC_CLIENT] + sendonlys []SendOnlyNode[CHAIN_ID, RPC_CLIENT] + chainID CHAIN_ID + lggr logger.SugaredLogger + selectionMode string + noNewHeadsThreshold time.Duration + nodeSelector NodeSelector[CHAIN_ID, HEAD, RPC_CLIENT] + leaseDuration time.Duration + leaseTicker *time.Ticker + chainFamily string + reportInterval time.Duration + deathDeclarationDelay time.Duration + sendTxSoftTimeout time.Duration // defines max waiting time from first response til responses evaluation activeMu sync.RWMutex activeNode Node[CHAIN_ID, HEAD, RPC_CLIENT] @@ -137,6 +138,7 @@ func NewMultiNode[ chainFamily string, classifySendTxError func(tx TX, err error) SendTxReturnCode, sendTxSoftTimeout time.Duration, + deathDeclarationDelay time.Duration, ) MultiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM] { nodeSelector := newNodeSelector(selectionMode, nodes) // Prometheus' default interval is 15s, set this to under 7.5s to avoid @@ -146,19 +148,20 @@ func NewMultiNode[ sendTxSoftTimeout = QueryTimeout / 2 } c := &multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]{ - nodes: nodes, - sendonlys: sendonlys, - chainID: chainID, - lggr: logger.Sugared(lggr).Named("MultiNode").With("chainID", chainID.String()), - selectionMode: selectionMode, - noNewHeadsThreshold: noNewHeadsThreshold, - nodeSelector: nodeSelector, - chStop: make(services.StopChan), - leaseDuration: leaseDuration, - chainFamily: chainFamily, - classifySendTxError: classifySendTxError, - reportInterval: reportInterval, - sendTxSoftTimeout: sendTxSoftTimeout, + nodes: nodes, + sendonlys: sendonlys, + chainID: chainID, + lggr: logger.Sugared(lggr).Named("MultiNode").With("chainID", chainID.String()), + selectionMode: selectionMode, + noNewHeadsThreshold: noNewHeadsThreshold, + nodeSelector: nodeSelector, + chStop: make(services.StopChan), + leaseDuration: leaseDuration, + chainFamily: chainFamily, + classifySendTxError: classifySendTxError, + reportInterval: reportInterval, + deathDeclarationDelay: deathDeclarationDelay, + sendTxSoftTimeout: sendTxSoftTimeout, } c.lggr.Debugf("The MultiNode is configured to use NodeSelectionMode: %s", selectionMode) @@ -180,14 +183,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP 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())) } - rawNode, ok := n.(*node[CHAIN_ID, HEAD, RPC_CLIENT]) - if ok { - // This is a bit hacky but it allows the node to be aware of - // pool state and prevent certain state transitions that might - // otherwise leave no nodes available. It is better to have one - // node in a degraded state than no nodes at all. - rawNode.nLiveNodes = c.nLiveNodes - } + n.SetPoolChainInfoProvider(c) // node will handle its own redialing and automatic recovery if err := ms.Start(ctx, n); err != nil { return err @@ -253,6 +249,9 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return // another goroutine beat us here } + if c.activeNode != nil { + c.activeNode.UnsubscribeAllExceptAliveLoop() + } c.activeNode = c.nodeSelector.Select() if c.activeNode == nil { @@ -265,22 +264,37 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return c.activeNode, err } -// nLiveNodes returns the number of currently alive nodes, as well as the highest block number and greatest total difficulty. -// totalDifficulty will be 0 if all nodes return nil. -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) nLiveNodes() (nLiveNodes int, blockNumber int64, totalDifficulty *big.Int) { - totalDifficulty = big.NewInt(0) +// LatestChainInfo - returns number of live nodes available in the pool, so we can prevent the last alive node in a pool from being marked as out-of-sync. +// Return highest ChainInfo most recently received by the alive nodes. +// E.g. If Node A's the most recent block is 10 and highest 15 and for Node B it's - 12 and 14. This method will return 12. +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) LatestChainInfo() (int, ChainInfo) { + var nLiveNodes int + ch := ChainInfo{ + TotalDifficulty: big.NewInt(0), + } for _, n := range c.nodes { - if s, num, td := n.StateAndLatest(); s == nodeStateAlive { + if s, nodeChainInfo := n.StateAndLatest(); s == nodeStateAlive { nLiveNodes++ - if num > blockNumber { - blockNumber = num - } - if td != nil && td.Cmp(totalDifficulty) > 0 { - totalDifficulty = td - } + ch.BlockNumber = max(ch.BlockNumber, nodeChainInfo.BlockNumber) + ch.FinalizedBlockNumber = max(ch.FinalizedBlockNumber, nodeChainInfo.FinalizedBlockNumber) + ch.TotalDifficulty = MaxTotalDifficulty(ch.TotalDifficulty, nodeChainInfo.TotalDifficulty) } } - return + return nLiveNodes, ch +} + +// HighestUserObservations - returns highest ChainInfo ever observed by any user of the MultiNode +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) HighestUserObservations() ChainInfo { + ch := ChainInfo{ + TotalDifficulty: big.NewInt(0), + } + for _, n := range c.nodes { + 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, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) checkLease() { @@ -295,10 +309,13 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP } c.activeMu.Lock() + defer c.activeMu.Unlock() if bestNode != c.activeNode { + if c.activeNode != nil { + c.activeNode.UnsubscribeAllExceptAliveLoop() + } c.activeNode = bestNode } - c.activeMu.Unlock() } func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) checkLeaseLoop() { @@ -319,7 +336,16 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) runLoop() { defer c.wg.Done() - c.report() + nodeStates := make([]nodeWithState, len(c.nodes)) + for i, n := range c.nodes { + nodeStates[i] = nodeWithState{ + Node: n.String(), + State: n.State().String(), + DeadSince: nil, + } + } + + c.report(nodeStates) monitor := time.NewTicker(utils.WithJitter(c.reportInterval)) defer monitor.Stop() @@ -327,44 +353,54 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP for { select { case <-monitor.C: - c.report() + c.report(nodeStates) case <-c.chStop: return } } } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) report() { - type nodeWithState struct { - Node string - State string - } +type nodeWithState struct { + Node string + State string + DeadSince *time.Time +} - var total, dead int +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) report(nodesStateInfo []nodeWithState) { + start := time.Now() + var dead int counts := make(map[nodeState]int) - nodeStates := make([]nodeWithState, len(c.nodes)) for i, n := range c.nodes { state := n.State() - nodeStates[i] = nodeWithState{n.String(), state.String()} - total++ - if state != nodeStateAlive { + 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++ } - counts[state]++ } for _, state := range allNodeStates { count := counts[state] PromMultiNodeRPCNodeStates.WithLabelValues(c.chainFamily, c.chainID.String(), state.String()).Set(float64(count)) } + total := len(c.nodes) live := total - dead - c.lggr.Tracew(fmt.Sprintf("MultiNode state: %d/%d nodes are alive", live, total), "nodeStates", nodeStates) + c.lggr.Tracew(fmt.Sprintf("MultiNode state: %d/%d nodes are alive", live, total), "nodeStates", nodesStateInfo) if total == dead { rerr := fmt.Errorf("no primary nodes available: 0/%d nodes are alive", total) - c.lggr.Criticalw(rerr.Error(), "nodeStates", nodeStates) + c.lggr.Criticalw(rerr.Error(), "nodeStates", nodesStateInfo) c.SvcErrBuffer.Append(rerr) } else if dead > 0 { - c.lggr.Errorw(fmt.Sprintf("At least one primary node is dead: %d/%d nodes are alive", live, total), "nodeStates", nodeStates) + c.lggr.Errorw(fmt.Sprintf("At least one primary node is dead: %d/%d nodes are alive", live, total), "nodeStates", nodesStateInfo) } } @@ -779,12 +815,12 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return n.RPC().SimulateTransaction(ctx, tx) } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) Subscribe(ctx context.Context, channel chan<- HEAD, args ...interface{}) (s types.Subscription, err error) { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) SubscribeNewHead(ctx context.Context, channel chan<- HEAD) (s types.Subscription, err error) { n, err := c.selectNode() if err != nil { return s, err } - return n.RPC().Subscribe(ctx, channel, args...) + return n.RPC().SubscribeNewHead(ctx, channel) } func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) TokenBalance(ctx context.Context, account ADDR, tokenAddr ADDR) (b *big.Int, err error) { diff --git a/common/client/multi_node_test.go b/common/client/multi_node_test.go index 3076d99b61..ffef0c29d5 100644 --- a/common/client/multi_node_test.go +++ b/common/client/multi_node_test.go @@ -29,16 +29,17 @@ type testMultiNode struct { } type multiNodeOpts struct { - logger logger.Logger - selectionMode string - leaseDuration time.Duration - noNewHeadsThreshold time.Duration - nodes []Node[types.ID, types.Head[Hashable], multiNodeRPCClient] - sendonlys []SendOnlyNode[types.ID, multiNodeRPCClient] - chainID types.ID - chainFamily string - classifySendTxError func(tx any, err error) SendTxReturnCode - sendTxSoftTimeout time.Duration + logger logger.Logger + selectionMode string + leaseDuration time.Duration + noNewHeadsThreshold time.Duration + nodes []Node[types.ID, types.Head[Hashable], multiNodeRPCClient] + sendonlys []SendOnlyNode[types.ID, multiNodeRPCClient] + chainID types.ID + chainFamily string + classifySendTxError func(tx any, err error) SendTxReturnCode + sendTxSoftTimeout time.Duration + deathDeclarationDelay time.Duration } func newTestMultiNode(t *testing.T, opts multiNodeOpts) testMultiNode { @@ -49,7 +50,7 @@ func newTestMultiNode(t *testing.T, opts multiNodeOpts) testMultiNode { result := NewMultiNode[types.ID, *big.Int, Hashable, Hashable, any, Hashable, any, any, types.Receipt[Hashable, Hashable], Hashable, types.Head[Hashable], multiNodeRPCClient, any](opts.logger, opts.selectionMode, opts.leaseDuration, opts.noNewHeadsThreshold, opts.nodes, opts.sendonlys, - opts.chainID, opts.chainFamily, opts.classifySendTxError, opts.sendTxSoftTimeout) + opts.chainID, opts.chainFamily, opts.classifySendTxError, opts.sendTxSoftTimeout, opts.deathDeclarationDelay) return testMultiNode{ result.(*multiNode[types.ID, *big.Int, Hashable, Hashable, any, Hashable, any, any, types.Receipt[Hashable, Hashable], Hashable, types.Head[Hashable], multiNodeRPCClient, any]), @@ -67,14 +68,21 @@ func newHealthyNode(t *testing.T, chainID types.ID) *mockNode[types.ID, types.He } func newNodeWithState(t *testing.T, chainID types.ID, state nodeState) *mockNode[types.ID, types.Head[Hashable], multiNodeRPCClient] { + node := newDialableNode(t, chainID) + node.On("State").Return(state).Maybe() + return node +} + +func newDialableNode(t *testing.T, chainID types.ID) *mockNode[types.ID, types.Head[Hashable], multiNodeRPCClient] { node := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) node.On("ConfiguredChainID").Return(chainID).Once() node.On("Start", mock.Anything).Return(nil).Once() node.On("Close").Return(nil).Once() - node.On("State").Return(state).Maybe() node.On("String").Return(fmt.Sprintf("healthy_node_%d", rand.Int())).Maybe() + node.On("SetPoolChainInfoProvider", mock.Anything).Once() return node } + func TestMultiNode_Dial(t *testing.T) { t.Parallel() @@ -111,6 +119,7 @@ func TestMultiNode_Dial(t *testing.T) { node := newMockNode(t) chainID := types.RandomID() node.On("ConfiguredChainID").Return(chainID).Once() + node.On("SetPoolChainInfoProvider", mock.Anything).Once() expectedError := errors.New("failed to start node") node.On("Start", mock.Anything).Return(expectedError).Once() mn := newTestMultiNode(t, multiNodeOpts{ @@ -128,6 +137,7 @@ func TestMultiNode_Dial(t *testing.T) { node1 := newHealthyNode(t, chainID) node2 := newMockNode(t) node2.On("ConfiguredChainID").Return(chainID).Once() + node2.On("SetPoolChainInfoProvider", mock.Anything).Once() expectedError := errors.New("failed to start node") node2.On("Start", mock.Anything).Return(expectedError).Once() @@ -219,6 +229,7 @@ func TestMultiNode_Report(t *testing.T) { logger: lggr, }) mn.reportInterval = tests.TestInterval + mn.deathDeclarationDelay = tests.TestInterval defer func() { assert.NoError(t, mn.Close()) }() err := mn.Dial(tests.Context(t)) require.NoError(t, err) @@ -236,6 +247,7 @@ func TestMultiNode_Report(t *testing.T) { logger: lggr, }) mn.reportInterval = tests.TestInterval + mn.deathDeclarationDelay = tests.TestInterval defer func() { assert.NoError(t, mn.Close()) }() err := mn.Dial(tests.Context(t)) require.NoError(t, err) @@ -377,6 +389,7 @@ func TestMultiNode_selectNode(t *testing.T) { chainID := types.RandomID() oldBest := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) oldBest.On("String").Return("oldBest").Maybe() + oldBest.On("UnsubscribeAllExceptAliveLoop").Once() newBest := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) newBest.On("String").Return("newBest").Maybe() mn := newTestMultiNode(t, multiNodeOpts{ @@ -417,49 +430,94 @@ func TestMultiNode_selectNode(t *testing.T) { }) } -func TestMultiNode_nLiveNodes(t *testing.T) { +func TestMultiNode_ChainInfo(t *testing.T) { t.Parallel() type nodeParams struct { - BlockNumber int64 - TotalDifficulty *big.Int - State nodeState + LatestChainInfo ChainInfo + HighestUserObservations ChainInfo + State nodeState } testCases := []struct { - Name string - ExpectedNLiveNodes int - ExpectedBlockNumber int64 - ExpectedTotalDifficulty *big.Int - NodeParams []nodeParams + Name string + ExpectedNLiveNodes int + ExpectedLatestChainInfo ChainInfo + ExpectedHighestUserObservations ChainInfo + NodeParams []nodeParams }{ { - Name: "no nodes", - ExpectedTotalDifficulty: big.NewInt(0), + Name: "no nodes", + ExpectedLatestChainInfo: ChainInfo{ + TotalDifficulty: big.NewInt(0), + }, + ExpectedHighestUserObservations: ChainInfo{ + TotalDifficulty: big.NewInt(0), + }, }, { - Name: "Best node is not healthy", - ExpectedTotalDifficulty: big.NewInt(10), - ExpectedBlockNumber: 20, - ExpectedNLiveNodes: 3, + 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, - BlockNumber: 1000, - TotalDifficulty: big.NewInt(2000), + State: nodeStateOutOfSync, + LatestChainInfo: ChainInfo{ + BlockNumber: 1000, + FinalizedBlockNumber: 990, + TotalDifficulty: big.NewInt(2000), + }, + HighestUserObservations: ChainInfo{ + BlockNumber: 1005, + FinalizedBlockNumber: 995, + TotalDifficulty: big.NewInt(2005), + }, }, { - State: nodeStateAlive, - BlockNumber: 20, - TotalDifficulty: big.NewInt(9), + State: nodeStateAlive, + LatestChainInfo: ChainInfo{ + BlockNumber: 20, + FinalizedBlockNumber: 10, + TotalDifficulty: big.NewInt(9), + }, + HighestUserObservations: ChainInfo{ + BlockNumber: 25, + FinalizedBlockNumber: 15, + TotalDifficulty: big.NewInt(14), + }, }, { - State: nodeStateAlive, - BlockNumber: 19, - TotalDifficulty: big.NewInt(10), + State: nodeStateAlive, + LatestChainInfo: ChainInfo{ + BlockNumber: 19, + FinalizedBlockNumber: 9, + TotalDifficulty: big.NewInt(10), + }, + HighestUserObservations: ChainInfo{ + BlockNumber: 24, + FinalizedBlockNumber: 14, + TotalDifficulty: big.NewInt(15), + }, }, { - State: nodeStateAlive, - BlockNumber: 11, - TotalDifficulty: nil, + State: nodeStateAlive, + LatestChainInfo: ChainInfo{ + BlockNumber: 11, + FinalizedBlockNumber: 1, + TotalDifficulty: nil, + }, + HighestUserObservations: ChainInfo{ + BlockNumber: 16, + FinalizedBlockNumber: 6, + TotalDifficulty: nil, + }, }, }, }, @@ -475,14 +533,17 @@ func TestMultiNode_nLiveNodes(t *testing.T) { t.Run(tc.Name, func(t *testing.T) { for _, params := range tc.NodeParams { node := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) - node.On("StateAndLatest").Return(params.State, params.BlockNumber, params.TotalDifficulty) + node.On("StateAndLatest").Return(params.State, params.LatestChainInfo) + node.On("HighestUserObservations").Return(params.HighestUserObservations) mn.nodes = append(mn.nodes, node) } - nNodes, blockNum, td := mn.nLiveNodes() + nNodes, latestChainInfo := mn.LatestChainInfo() assert.Equal(t, tc.ExpectedNLiveNodes, nNodes) - assert.Equal(t, tc.ExpectedTotalDifficulty, td) - assert.Equal(t, tc.ExpectedBlockNumber, blockNum) + 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 index 869ea89c03..7871c622eb 100644 --- a/common/client/node.go +++ b/common/client/node.go @@ -4,7 +4,6 @@ import ( "context" "errors" "fmt" - "math/big" "net/url" "sync" "time" @@ -44,12 +43,15 @@ type NodeConfig interface { SyncThreshold() uint32 NodeIsSyncingEnabled() bool FinalizedBlockPollInterval() time.Duration + EnforceRepeatableRead() bool + DeathDeclarationDelay() time.Duration } type ChainConfig interface { NodeNoNewHeadsThreshold() time.Duration FinalityDepth() uint32 FinalityTagEnabled() bool + FinalizedBlockOffset() uint32 } //go:generate mockery --quiet --name Node --structname mockNode --filename "mock_node_test.go" --inpackage --case=underscore @@ -58,15 +60,21 @@ type Node[ HEAD Head, RPC NodeClient[CHAIN_ID, HEAD], ] interface { - // State returns nodeState + // 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 received block number & total difficulty. - StateAndLatest() (nodeState, int64, *big.Int) + // 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() string RPC() RPC SubscribersCount() int32 + // UnsubscribeAllExceptAliveLoop - closes all subscriptions except the aliveLoop subscription UnsubscribeAllExceptAliveLoop() ConfiguredChainID() CHAIN_ID Order() int32 @@ -96,20 +104,12 @@ type node[ stateMu sync.RWMutex // protects state* fields state nodeState - // Each node is tracking the last received head number and total difficulty - stateLatestBlockNumber int64 - stateLatestTotalDifficulty *big.Int - stateLatestFinalizedBlockNumber int64 + + poolInfoProvider PoolChainInfoProvider stopCh services.StopChan // wg waits for subsidiary goroutines wg sync.WaitGroup - - // nLiveNodes is a passed in function that allows this node to: - // 1. see how many live nodes there are in total, 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. - // 2. compare against the highest head (by number or difficulty) to ensure we don't fall behind too far. - nLiveNodes func() (count int, blockNumber int64, totalDifficulty *big.Int) } func NewNode[ @@ -150,7 +150,6 @@ func NewNode[ "nodeOrder", n.order, ) n.lfcLog = logger.Named(lggr, "Lifecycle") - n.stateLatestBlockNumber = -1 n.rpc = rpc n.chainFamily = chainFamily return n @@ -243,7 +242,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) verifyChainID(callerCtx context.Context, lgg promPoolRPCNodeVerifiesFailed.WithLabelValues(n.chainFamily, n.chainID.String(), n.name).Inc() } - st := n.State() + st := n.getCachedState() switch st { case nodeStateClosed: // The node is already closed, and any subsequent transition is invalid. @@ -258,7 +257,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) verifyChainID(callerCtx context.Context, lgg 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.State()) + lggr.Errorw("Failed to verify chain ID for node", "err", err, "nodeState", n.getCachedState()) return nodeStateUnreachable } else if chainID.String() != n.chainID.String() { promFailed() @@ -269,7 +268,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) verifyChainID(callerCtx context.Context, lgg n.name, errInvalidChainID, ) - lggr.Errorw("Failed to verify RPC node; remote endpoint returned the wrong chain ID", "err", err, "nodeState", n.State()) + lggr.Errorw("Failed to verify RPC node; remote endpoint returned the wrong chain ID", "err", err, "nodeState", n.getCachedState()) return nodeStateInvalidChainID } @@ -282,7 +281,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) verifyChainID(callerCtx context.Context, lgg // 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.State()) + n.lfcLog.Errorw("Dial failed: Node is unreachable", "err", err, "nodeState", n.getCachedState()) return nodeStateUnreachable } @@ -300,12 +299,12 @@ func (n *node[CHAIN_ID, HEAD, RPC]) verifyConn(ctx context.Context, lggr logger. 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.State()) + 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.State()) + lggr.Errorw("Verification failed: Node is syncing", "nodeState", n.getCachedState()) return nodeStateSyncing } } @@ -323,3 +322,9 @@ func (n *node[CHAIN_ID, HEAD, RPC]) disconnectAll() { 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 index e9105dcc06..5a5e255443 100644 --- a/common/client/node_fsm.go +++ b/common/client/node_fsm.go @@ -63,6 +63,8 @@ func (n nodeState) String() string { return "Closed" case nodeStateSyncing: return "Syncing" + case nodeStateFinalizedBlockOutOfSync: + return "FinalizedBlockOutOfSync" default: return fmt.Sprintf("nodeState(%d)", n) } @@ -98,6 +100,8 @@ const ( // 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 ) @@ -115,15 +119,59 @@ func init() { // 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]) StateAndLatest() (nodeState, int64, *big.Int) { +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, _ := n.rpc.GetInterceptedChainInfo() + if n.chainCfg.FinalityTagEnabled() { + return latest.FinalizedBlockNumber < highestObservedByCaller.FinalizedBlockNumber-int64(n.chainCfg.FinalizedBlockOffset()) + } + + return latest.BlockNumber < highestObservedByCaller.BlockNumber-int64(n.chainCfg.FinalizedBlockOffset()) +} + +// 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() - return n.state, n.stateLatestBlockNumber, n.stateLatestTotalDifficulty + 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. @@ -243,7 +291,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) transitionToUnreachable(fn func()) { } func (n *node[CHAIN_ID, HEAD, RPC]) declareState(state nodeState) { - if n.State() == nodeStateClosed { + if n.getCachedState() == nodeStateClosed { return } switch state { diff --git a/common/client/node_lifecycle.go b/common/client/node_lifecycle.go index 5947774e20..39e17bb497 100644 --- a/common/client/node_lifecycle.go +++ b/common/client/node_lifecycle.go @@ -56,20 +56,11 @@ func zombieNodeCheckInterval(noNewHeadsThreshold time.Duration) time.Duration { return utils.WithJitter(interval) } -func (n *node[CHAIN_ID, HEAD, RPC]) setLatestReceived(blockNumber int64, totalDifficulty *big.Int) { - n.stateMu.Lock() - defer n.stateMu.Unlock() - n.stateLatestBlockNumber = blockNumber - n.stateLatestTotalDifficulty = totalDifficulty -} - 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" ) -const rpcSubscriptionMethodNewHeads = "newHeads" - // 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. @@ -79,12 +70,12 @@ const rpcSubscriptionMethodNewHeads = "newHeads" // 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.stopCh.NewCtx() + ctx, cancel := n.newCtx() defer cancel() { // sanity check - state := n.State() + state := n.getCachedState() switch state { case nodeStateAlive: case nodeStateClosed: @@ -99,12 +90,12 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { 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.State()) + lggr.Tracew("Alive loop starting", "nodeState", n.getCachedState()) headsC := make(chan HEAD) - sub, err := n.rpc.Subscribe(ctx, headsC, rpcSubscriptionMethodNewHeads) + sub, err := n.rpc.SubscribeNewHead(ctx, headsC) if err != nil { - lggr.Errorw("Initial subscribe for heads failed", "nodeState", n.State()) + lggr.Errorw("Initial subscribe for heads failed", "nodeState", n.getCachedState()) n.declareUnreachable() return } @@ -116,7 +107,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { var outOfSyncT *time.Ticker var outOfSyncTC <-chan time.Time if noNewHeadsTimeoutThreshold > 0 { - lggr.Debugw("Head liveness checking enabled", "nodeState", n.State()) + lggr.Debugw("Head liveness checking enabled", "nodeState", n.getCachedState()) outOfSyncT = time.NewTicker(noNewHeadsTimeoutThreshold) defer outOfSyncT.Stop() outOfSyncTC = outOfSyncT.C @@ -148,7 +139,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { pollFinalizedHeadCh = pollT.C } - _, highestReceivedBlockNumber, _ := n.StateAndLatest() + localHighestChainInfo, _ := n.rpc.GetInterceptedChainInfo() var pollFailures uint32 for { @@ -157,7 +148,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { return case <-pollCh: promPoolRPCNodePolls.WithLabelValues(n.chainID.String(), n.name).Inc() - lggr.Tracew("Polling for version", "nodeState", n.State(), "pollFailures", pollFailures) + lggr.Tracew("Polling for version", "nodeState", n.getCachedState(), "pollFailures", pollFailures) version, err := func(ctx context.Context) (string, error) { ctx, cancel := context.WithTimeout(ctx, pollInterval) defer cancel() @@ -169,16 +160,16 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { 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.State()) + lggr.Warnw(fmt.Sprintf("Poll failure, RPC endpoint %s failed to respond properly", n.String()), "err", err, "pollFailures", pollFailures, "nodeState", n.getCachedState()) } else { - lggr.Debugw("Version poll successful", "nodeState", n.State(), "clientVersion", version) + lggr.Debugw("Version poll successful", "nodeState", n.getCachedState(), "clientVersion", version) 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.State()) - if n.nLiveNodes != nil { - if l, _, _ := n.nLiveNodes(); l < 2 { + 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 } @@ -186,10 +177,10 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { n.declareUnreachable() return } - _, num, td := n.StateAndLatest() - if outOfSync, liveNodes := n.syncStatus(num, td); outOfSync { + _, ci := n.StateAndLatest() + if outOfSync, liveNodes := n.syncStatus(ci.BlockNumber, ci.TotalDifficulty); outOfSync { // note: there must be another live node for us to be out of sync - lggr.Errorw("RPC endpoint has fallen behind", "blockNumber", num, "totalDifficulty", td, "nodeState", n.State()) + lggr.Errorw("RPC endpoint has fallen behind", "blockNumber", ci.BlockNumber, "totalDifficulty", ci.TotalDifficulty, "nodeState", n.getCachedState()) if liveNodes < 2 { lggr.Criticalf("RPC endpoint has fallen behind; %s %s", msgCannotDisable, msgDegradedState) continue @@ -199,40 +190,39 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { } case bh, open := <-headsC: if !open { - lggr.Errorw("Subscription channel unexpectedly closed", "nodeState", n.State()) + lggr.Errorw("Subscription channel unexpectedly closed", "nodeState", n.getCachedState()) n.declareUnreachable() return } promPoolRPCNodeNumSeenBlocks.WithLabelValues(n.chainID.String(), n.name).Inc() lggr.Tracew("Got head", "head", bh) - if bh.BlockNumber() > highestReceivedBlockNumber { + if bh.BlockNumber() > localHighestChainInfo.BlockNumber { promPoolRPCNodeHighestSeenBlock.WithLabelValues(n.chainID.String(), n.name).Set(float64(bh.BlockNumber())) - lggr.Tracew("Got higher block number, resetting timer", "latestReceivedBlockNumber", highestReceivedBlockNumber, "blockNumber", bh.BlockNumber(), "nodeState", n.State()) - highestReceivedBlockNumber = bh.BlockNumber() + lggr.Tracew("Got higher block number, resetting timer", "latestReceivedBlockNumber", localHighestChainInfo.BlockNumber, "blockNumber", bh.BlockNumber(), "nodeState", n.getCachedState()) + localHighestChainInfo.BlockNumber = bh.BlockNumber() } else { - lggr.Tracew("Ignoring previously seen block number", "latestReceivedBlockNumber", highestReceivedBlockNumber, "blockNumber", bh.BlockNumber(), "nodeState", n.State()) + lggr.Tracew("Ignoring previously seen block number", "latestReceivedBlockNumber", localHighestChainInfo.BlockNumber, "blockNumber", bh.BlockNumber(), "nodeState", n.getCachedState()) } if outOfSyncT != nil { outOfSyncT.Reset(noNewHeadsTimeoutThreshold) } - n.setLatestReceived(bh.BlockNumber(), bh.BlockDifficulty()) if !n.chainCfg.FinalityTagEnabled() { latestFinalizedBN := max(bh.BlockNumber()-int64(n.chainCfg.FinalityDepth()), 0) - if latestFinalizedBN > n.stateLatestFinalizedBlockNumber { + if latestFinalizedBN > localHighestChainInfo.FinalizedBlockNumber { promPoolRPCNodeHighestFinalizedBlock.WithLabelValues(n.chainID.String(), n.name).Set(float64(latestFinalizedBN)) - n.stateLatestFinalizedBlockNumber = latestFinalizedBN + localHighestChainInfo.FinalizedBlockNumber = latestFinalizedBN } } case err := <-sub.Err(): - lggr.Errorw("Subscription was terminated", "err", err, "nodeState", n.State()) + lggr.Errorw("Subscription was terminated", "err", err, "nodeState", n.getCachedState()) n.declareUnreachable() return case <-outOfSyncTC: // 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, highestReceivedBlockNumber), "nodeState", n.State(), "latestReceivedBlockNumber", highestReceivedBlockNumber, "noNewHeadsTimeoutThreshold", noNewHeadsTimeoutThreshold) - if n.nLiveNodes != nil { - if l, _, _ := n.nLiveNodes(); l < 2 { + 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 @@ -240,7 +230,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { continue } } - n.declareOutOfSync(func(num int64, td *big.Int) bool { return num < highestReceivedBlockNumber }) + n.declareOutOfSync(func(num int64, td *big.Int) bool { return num < localHighestChainInfo.BlockNumber }) return case <-pollFinalizedHeadCh: latestFinalized, err := func(ctx context.Context) (HEAD, error) { @@ -259,9 +249,9 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { } latestFinalizedBN := latestFinalized.BlockNumber() - if latestFinalizedBN > n.stateLatestFinalizedBlockNumber { + if latestFinalizedBN > localHighestChainInfo.FinalizedBlockNumber { promPoolRPCNodeHighestFinalizedBlock.WithLabelValues(n.chainID.String(), n.name).Set(float64(latestFinalizedBN)) - n.stateLatestFinalizedBlockNumber = latestFinalizedBN + localHighestChainInfo.FinalizedBlockNumber = latestFinalizedBN } } } @@ -276,7 +266,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) isOutOfSync(num int64, td *big.Int) (outOfSy // Always returns outOfSync false for SyncThreshold 0. // liveNodes is only included when outOfSync is true. func (n *node[CHAIN_ID, HEAD, RPC]) syncStatus(num int64, td *big.Int) (outOfSync bool, liveNodes int) { - if n.nLiveNodes == nil { + if n.poolInfoProvider == nil { return // skip for tests } threshold := n.nodePoolCfg.SyncThreshold() @@ -284,14 +274,14 @@ func (n *node[CHAIN_ID, HEAD, RPC]) syncStatus(num int64, td *big.Int) (outOfSyn return // disabled } // Check against best node - ln, highest, greatest := n.nLiveNodes() + ln, ci := n.poolInfoProvider.LatestChainInfo() mode := n.nodePoolCfg.SelectionMode() switch mode { case NodeSelectionModeHighestHead, NodeSelectionModeRoundRobin, NodeSelectionModePriorityLevel: - return num < highest-int64(threshold), ln + return num < ci.BlockNumber-int64(threshold), ln case NodeSelectionModeTotalDifficulty: bigThreshold := big.NewInt(int64(threshold)) - return td.Cmp(bigmath.Sub(greatest, bigThreshold)) < 0, ln + return td.Cmp(bigmath.Sub(ci.TotalDifficulty, bigThreshold)) < 0, ln default: panic("unrecognized NodeSelectionMode: " + mode) } @@ -305,12 +295,12 @@ const ( // 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(isOutOfSync func(num int64, td *big.Int) bool) { defer n.wg.Done() - ctx, cancel := n.stopCh.NewCtx() + ctx, cancel := n.newCtx() defer cancel() { // sanity check - state := n.State() + state := n.getCachedState() switch state { case nodeStateOutOfSync: case nodeStateClosed: @@ -323,7 +313,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) outOfSyncLoop(isOutOfSync func(num int64, td outOfSyncAt := time.Now() lggr := logger.Sugared(logger.Named(n.lfcLog, "OutOfSync")) - lggr.Debugw("Trying to revive out-of-sync RPC node", "nodeState", n.State()) + lggr.Debugw("Trying to revive out-of-sync RPC node", "nodeState", n.getCachedState()) // Need to redial since out-of-sync nodes are automatically disconnected state := n.createVerifiedConn(ctx, lggr) @@ -332,12 +322,12 @@ func (n *node[CHAIN_ID, HEAD, RPC]) outOfSyncLoop(isOutOfSync func(num int64, td return } - lggr.Tracew("Successfully subscribed to heads feed on out-of-sync RPC node", "nodeState", n.State()) + lggr.Tracew("Successfully subscribed to heads feed on out-of-sync RPC node", "nodeState", n.getCachedState()) ch := make(chan HEAD) - sub, err := n.rpc.Subscribe(ctx, ch, rpcSubscriptionMethodNewHeads) + sub, err := n.rpc.SubscribeNewHead(ctx, ch) if err != nil { - lggr.Errorw("Failed to subscribe heads on out-of-sync RPC node", "nodeState", n.State(), "err", err) + lggr.Errorw("Failed to subscribe heads on out-of-sync RPC node", "nodeState", n.getCachedState(), "err", err) n.declareUnreachable() return } @@ -349,28 +339,27 @@ func (n *node[CHAIN_ID, HEAD, RPC]) outOfSyncLoop(isOutOfSync func(num int64, td return case head, open := <-ch: if !open { - lggr.Error("Subscription channel unexpectedly closed", "nodeState", n.State()) + lggr.Error("Subscription channel unexpectedly closed", "nodeState", n.getCachedState()) n.declareUnreachable() return } - n.setLatestReceived(head.BlockNumber(), head.BlockDifficulty()) if !isOutOfSync(head.BlockNumber(), head.BlockDifficulty()) { // 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)), "blockNumber", head.BlockNumber(), "blockDifficulty", head.BlockDifficulty(), "nodeState", n.State()) + lggr.Infow(fmt.Sprintf("%s: %s. Node was out-of-sync for %s", msgInSync, n.String(), time.Since(outOfSyncAt)), "blockNumber", head.BlockNumber(), "blockDifficulty", head.BlockDifficulty(), "nodeState", n.getCachedState()) n.declareInSync() return } - lggr.Debugw(msgReceivedBlock, "blockNumber", head.BlockNumber(), "blockDifficulty", head.BlockDifficulty(), "nodeState", n.State()) + lggr.Debugw(msgReceivedBlock, "blockNumber", head.BlockNumber(), "blockDifficulty", head.BlockDifficulty(), "nodeState", n.getCachedState()) case <-time.After(zombieNodeCheckInterval(n.chainCfg.NodeNoNewHeadsThreshold())): - if n.nLiveNodes != nil { - if l, _, _ := n.nLiveNodes(); l < 1 { + if n.poolInfoProvider != nil { + if l, _ := n.poolInfoProvider.LatestChainInfo(); l < 1 { lggr.Critical("RPC endpoint is still out of sync, but there are no other available nodes. This RPC node will be forcibly moved back into the live pool in a degraded state") n.declareInSync() return } } case err := <-sub.Err(): - lggr.Errorw("Subscription was terminated", "nodeState", n.State(), "err", err) + lggr.Errorw("Subscription was terminated", "nodeState", n.getCachedState(), "err", err) n.declareUnreachable() return } @@ -379,12 +368,12 @@ func (n *node[CHAIN_ID, HEAD, RPC]) outOfSyncLoop(isOutOfSync func(num int64, td func (n *node[CHAIN_ID, HEAD, RPC]) unreachableLoop() { defer n.wg.Done() - ctx, cancel := n.stopCh.NewCtx() + ctx, cancel := n.newCtx() defer cancel() { // sanity check - state := n.State() + state := n.getCachedState() switch state { case nodeStateUnreachable: case nodeStateClosed: @@ -397,7 +386,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) unreachableLoop() { unreachableAt := time.Now() lggr := logger.Sugared(logger.Named(n.lfcLog, "Unreachable")) - lggr.Debugw("Trying to revive unreachable RPC node", "nodeState", n.State()) + lggr.Debugw("Trying to revive unreachable RPC node", "nodeState", n.getCachedState()) dialRetryBackoff := iutils.NewRedialBackoff() @@ -406,11 +395,11 @@ func (n *node[CHAIN_ID, HEAD, RPC]) unreachableLoop() { case <-ctx.Done(): return case <-time.After(dialRetryBackoff.Duration()): - lggr.Tracew("Trying to re-dial RPC node", "nodeState", n.State()) + 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.State()) + lggr.Errorw(fmt.Sprintf("Failed to redial RPC node; still unreachable: %v", err), "err", err, "nodeState", n.getCachedState()) continue } @@ -422,7 +411,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) unreachableLoop() { 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.State()) + 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) @@ -434,12 +423,12 @@ func (n *node[CHAIN_ID, HEAD, RPC]) unreachableLoop() { func (n *node[CHAIN_ID, HEAD, RPC]) invalidChainIDLoop() { defer n.wg.Done() - ctx, cancel := n.stopCh.NewCtx() + ctx, cancel := n.newCtx() defer cancel() { // sanity check - state := n.State() + state := n.getCachedState() switch state { case nodeStateInvalidChainID: case nodeStateClosed: @@ -460,7 +449,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) invalidChainIDLoop() { return } - lggr.Debugw(fmt.Sprintf("Periodically re-checking RPC node %s with invalid chain ID", n.String()), "nodeState", n.State()) + lggr.Debugw(fmt.Sprintf("Periodically re-checking RPC node %s with invalid chain ID", n.String()), "nodeState", n.getCachedState()) chainIDRecheckBackoff := iutils.NewRedialBackoff() @@ -474,7 +463,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) invalidChainIDLoop() { case nodeStateInvalidChainID: continue case nodeStateAlive: - lggr.Infow(fmt.Sprintf("Successfully verified RPC node. Node was offline for %s", time.Since(invalidAt)), "nodeState", n.State()) + lggr.Infow(fmt.Sprintf("Successfully verified RPC node. Node was offline for %s", time.Since(invalidAt)), "nodeState", n.getCachedState()) fallthrough default: n.declareState(state) @@ -486,12 +475,12 @@ func (n *node[CHAIN_ID, HEAD, RPC]) invalidChainIDLoop() { func (n *node[CHAIN_ID, HEAD, RPC]) syncingLoop() { defer n.wg.Done() - ctx, cancel := n.stopCh.NewCtx() + ctx, cancel := n.newCtx() defer cancel() { // sanity check - state := n.State() + state := n.getCachedState() switch state { case nodeStateSyncing: case nodeStateClosed: @@ -504,7 +493,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) syncingLoop() { 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.State()) + 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 { @@ -519,20 +508,20 @@ func (n *node[CHAIN_ID, HEAD, RPC]) syncingLoop() { case <-ctx.Done(): return case <-time.After(recheckBackoff.Duration()): - lggr.Tracew("Trying to recheck if the node is still syncing", "nodeState", n.State()) + 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.State()) + 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.State()) + 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.State()) + 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 index 4bdfd698f7..863a15a1fa 100644 --- a/common/client/node_lifecycle_test.go +++ b/common/client/node_lifecycle_test.go @@ -15,7 +15,6 @@ import ( "go.uber.org/zap" "github.com/smartcontractkit/chainlink-common/pkg/logger" - bigmath "github.com/smartcontractkit/chainlink-common/pkg/utils/big_math" "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" clientMocks "github.com/smartcontractkit/chainlink/v2/common/client/mocks" @@ -49,8 +48,8 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { defer func() { assert.NoError(t, node.close()) }() expectedError := errors.New("failed to subscribe to rpc") - rpc.On("Subscribe", mock.Anything, mock.Anything, rpcSubscriptionMethodNewHeads).Return(nil, expectedError).Once() rpc.On("DisconnectAll").Once() + rpc.On("SubscribeNewHead", mock.Anything, mock.Anything).Return(nil, expectedError).Once() // might be called in unreachable loop rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() node.declareAlive() @@ -67,6 +66,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { rpc: rpc, lggr: lggr, }) + rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() defer func() { assert.NoError(t, node.close()) }() sub := mocks.NewSubscription(t) @@ -74,7 +74,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { close(errChan) sub.On("Err").Return((<-chan error)(errChan)).Once() sub.On("Unsubscribe").Once() - rpc.On("Subscribe", mock.Anything, mock.Anything, rpcSubscriptionMethodNewHeads).Return(sub, nil).Once() + rpc.On("SubscribeNewHead", mock.Anything, mock.Anything).Return(sub, nil).Once() rpc.On("SetAliveLoopSub", sub).Once() // disconnects all on transfer to unreachable rpc.On("DisconnectAll").Once() @@ -89,13 +89,14 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { sub := mocks.NewSubscription(t) sub.On("Err").Return((<-chan error)(nil)) sub.On("Unsubscribe").Once() - opts.rpc.On("Subscribe", mock.Anything, mock.Anything, rpcSubscriptionMethodNewHeads).Return(sub, nil).Once() + opts.rpc.On("SubscribeNewHead", mock.Anything, mock.Anything).Return(sub, nil).Once() opts.rpc.On("SetAliveLoopSub", sub).Once() return newDialedNode(t, opts) } t.Run("Stays alive and waits for signal", func(t *testing.T) { t.Parallel() rpc := newMockNodeClient[types.ID, Head](t) + rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newSubscribedNode(t, testNodeOpts{ config: testNodeConfig{}, @@ -111,6 +112,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { t.Run("stays alive while below pollFailureThreshold and resets counter on success", func(t *testing.T) { t.Parallel() rpc := newMockNodeClient[types.ID, Head](t) + rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}) lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) const pollFailureThreshold = 3 node := newSubscribedNode(t, testNodeOpts{ @@ -152,6 +154,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { t.Run("with threshold poll failures, transitions to unreachable", func(t *testing.T) { t.Parallel() rpc := newMockNodeClient[types.ID, Head](t) + rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}) lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) const pollFailureThreshold = 3 node := newSubscribedNode(t, testNodeOpts{ @@ -189,9 +192,12 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { lggr: lggr, }) defer func() { assert.NoError(t, node.close()) }() - node.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *big.Int) { - return 1, 20, big.NewInt(10) - } + 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("ClientVersion", mock.Anything).Return("", pollError) node.declareAlive() @@ -213,10 +219,14 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { lggr: lggr, }) defer func() { assert.NoError(t, node.close()) }() - node.stateLatestBlockNumber = 20 - node.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *big.Int) { - return 10, syncThreshold + node.stateLatestBlockNumber + 1, big.NewInt(10) - } + 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), + }).Once() + node.SetPoolChainInfoProvider(poolInfo) rpc.On("ClientVersion", mock.Anything).Return("", nil) // tries to redial in outOfSync rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Run(func(_ mock.Arguments) { @@ -246,10 +256,14 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { lggr: lggr, }) defer func() { assert.NoError(t, node.close()) }() - node.stateLatestBlockNumber = 20 - node.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *big.Int) { - return 1, syncThreshold + node.stateLatestBlockNumber + 1, big.NewInt(10) - } + 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), + }).Once() + node.SetPoolChainInfoProvider(poolInfo) rpc.On("ClientVersion", mock.Anything).Return("", nil) node.declareAlive() tests.AssertLogEventually(t, observedLogs, fmt.Sprintf("RPC endpoint has fallen behind; %s %s", msgCannotDisable, msgDegradedState)) @@ -268,19 +282,17 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { lggr: lggr, }) defer func() { assert.NoError(t, node.close()) }() - node.stateLatestBlockNumber = 20 - node.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *big.Int) { - return 1, node.stateLatestBlockNumber + 100, big.NewInt(10) - } + const mostRecentBlock = 20 + rpc.On("GetInterceptedChainInfo").Return(ChainInfo{BlockNumber: mostRecentBlock}, ChainInfo{BlockNumber: 30}) rpc.On("ClientVersion", mock.Anything).Return("", nil) node.declareAlive() tests.AssertLogCountEventually(t, observedLogs, "Version poll successful", 2) assert.Equal(t, nodeStateAlive, node.State()) }) - t.Run("when no new heads received for threshold, transitions to out of sync", func(t *testing.T) { t.Parallel() rpc := newMockNodeClient[types.ID, Head](t) + rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() node := newSubscribedNode(t, testNodeOpts{ config: testNodeConfig{}, chainConfig: clientMocks.ChainConfig{ @@ -307,6 +319,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { t.Run("when no new heads received for threshold but we are the last live node, forcibly stays alive", func(t *testing.T) { t.Parallel() rpc := newMockNodeClient[types.ID, Head](t) + rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newSubscribedNode(t, testNodeOpts{ config: testNodeConfig{}, @@ -317,21 +330,24 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { rpc: rpc, }) defer func() { assert.NoError(t, node.close()) }() - node.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *big.Int) { - return 1, 20, big.NewInt(10) - } + 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 := newMockNodeClient[types.ID, Head](t) + rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() sub := mocks.NewSubscription(t) sub.On("Err").Return((<-chan error)(nil)) sub.On("Unsubscribe").Once() - rpc.On("Subscribe", mock.Anything, mock.Anything, rpcSubscriptionMethodNewHeads).Run(func(args mock.Arguments) { + rpc.On("SubscribeNewHead", mock.Anything, mock.Anything).Run(func(args mock.Arguments) { ch := args.Get(1).(chan<- Head) close(ch) }).Return(sub, nil).Once() @@ -354,30 +370,6 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { tests.AssertLogEventually(t, observedLogs, "Subscription channel unexpectedly closed") assert.Equal(t, nodeStateUnreachable, node.State()) }) - t.Run("updates block number and difficulty on new head", func(t *testing.T) { - t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) - sub := mocks.NewSubscription(t) - sub.On("Err").Return((<-chan error)(nil)) - sub.On("Unsubscribe").Once() - expectedBlockNumber := rand.Int64() - expectedDiff := big.NewInt(rand.Int64()) - rpc.On("Subscribe", mock.Anything, mock.Anything, rpcSubscriptionMethodNewHeads).Run(func(args mock.Arguments) { - ch := args.Get(1).(chan<- Head) - go writeHeads(t, ch, head{BlockNumber: expectedBlockNumber, BlockDifficulty: expectedDiff}) - }).Return(sub, nil).Once() - rpc.On("SetAliveLoopSub", sub).Once() - node := newDialedNode(t, testNodeOpts{ - config: testNodeConfig{}, - rpc: rpc, - }) - defer func() { assert.NoError(t, node.close()) }() - node.declareAlive() - tests.AssertEventually(t, func() bool { - state, block, diff := node.StateAndLatest() - return state == nodeStateAlive && block == expectedBlockNumber == bigmath.Equal(diff, expectedDiff) - }) - }) t.Run("If finality tag is not enabled updates finalized block metric using finality depth and latest head", func(t *testing.T) { t.Parallel() rpc := newMockNodeClient[types.ID, Head](t) @@ -387,7 +379,8 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { const blockNumber = 1000 const finalityDepth = 10 const expectedBlock = 990 - rpc.On("Subscribe", mock.Anything, mock.Anything, rpcSubscriptionMethodNewHeads).Run(func(args mock.Arguments) { + rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() + rpc.On("SubscribeNewHead", mock.Anything, mock.Anything).Run(func(args mock.Arguments) { ch := args.Get(1).(chan<- Head) go writeHeads(t, ch, head{BlockNumber: blockNumber - 1}, head{BlockNumber: blockNumber}, head{BlockNumber: blockNumber - 1}) }).Return(sub, nil).Once() @@ -413,11 +406,12 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { t.Run("Logs warning if failed to get finalized block", func(t *testing.T) { t.Parallel() rpc := newMockNodeClient[types.ID, Head](t) + rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() rpc.On("LatestFinalizedBlock", mock.Anything).Return(newMockHead(t), errors.New("failed to get finalized block")) sub := mocks.NewSubscription(t) sub.On("Err").Return((<-chan error)(nil)) sub.On("Unsubscribe").Once() - rpc.On("Subscribe", mock.Anything, mock.Anything, rpcSubscriptionMethodNewHeads).Return(sub, nil).Once() + rpc.On("SubscribeNewHead", mock.Anything, mock.Anything).Return(sub, nil).Once() rpc.On("SetAliveLoopSub", sub).Once() lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newDialedNode(t, testNodeOpts{ @@ -440,10 +434,11 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { head := newMockHead(t) head.On("IsValid").Return(false) rpc.On("LatestFinalizedBlock", mock.Anything).Return(head, nil) + rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() sub := mocks.NewSubscription(t) sub.On("Err").Return((<-chan error)(nil)) sub.On("Unsubscribe").Once() - rpc.On("Subscribe", mock.Anything, mock.Anything, rpcSubscriptionMethodNewHeads).Return(sub, nil).Once() + rpc.On("SubscribeNewHead", mock.Anything, mock.Anything).Return(sub, nil).Once() rpc.On("SetAliveLoopSub", sub).Once() lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newDialedNode(t, testNodeOpts{ @@ -470,12 +465,13 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { sub := mocks.NewSubscription(t) sub.On("Err").Return((<-chan error)(nil)) sub.On("Unsubscribe").Once() - rpc.On("Subscribe", mock.Anything, mock.Anything, rpcSubscriptionMethodNewHeads).Run(func(args mock.Arguments) { + rpc.On("SubscribeNewHead", mock.Anything, mock.Anything).Run(func(args mock.Arguments) { ch := args.Get(1).(chan<- Head) // ensure that "calculated" finalized head is larger than actual, to ensure we are correctly setting // the metric go writeHeads(t, ch, head{BlockNumber: expectedBlock*2 + finalityDepth}) }).Return(sub, nil).Once() + rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() rpc.On("SetAliveLoopSub", sub).Once() name := "node-" + rand.Str(5) node := newDialedNode(t, testNodeOpts{ @@ -531,8 +527,9 @@ func setupRPCForAliveLoop(t *testing.T, rpc *mockNodeClient[types.ID, Head]) { aliveSubscription := mocks.NewSubscription(t) aliveSubscription.On("Err").Return((<-chan error)(nil)).Maybe() aliveSubscription.On("Unsubscribe").Maybe() - rpc.On("Subscribe", mock.Anything, mock.Anything, rpcSubscriptionMethodNewHeads).Return(aliveSubscription, nil).Maybe() + rpc.On("SubscribeNewHead", mock.Anything, mock.Anything).Return(aliveSubscription, nil).Maybe() rpc.On("SetAliveLoopSub", mock.Anything).Maybe() + rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Maybe() } func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { @@ -577,7 +574,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { outOfSyncSubscription.On("Err").Return((<-chan error)(nil)) outOfSyncSubscription.On("Unsubscribe").Once() heads := []head{{BlockNumber: 7}, {BlockNumber: 11}, {BlockNumber: 13}} - rpc.On("Subscribe", mock.Anything, mock.Anything, rpcSubscriptionMethodNewHeads).Run(func(args mock.Arguments) { + rpc.On("SubscribeNewHead", mock.Anything, mock.Anything).Run(func(args mock.Arguments) { ch := args.Get(1).(chan<- Head) go writeHeads(t, ch, heads...) }).Return(outOfSyncSubscription, nil).Once() @@ -701,7 +698,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { rpc.On("Dial", mock.Anything).Return(nil).Once() rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once() expectedError := errors.New("failed to subscribe") - rpc.On("Subscribe", mock.Anything, mock.Anything, rpcSubscriptionMethodNewHeads).Return(nil, expectedError) + rpc.On("SubscribeNewHead", mock.Anything, mock.Anything).Return(nil, expectedError) rpc.On("Dial", mock.Anything).Return(errors.New("failed to redial")).Maybe() node.declareOutOfSync(stubIsOutOfSync) tests.AssertEventually(t, func() bool { @@ -728,7 +725,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { errChan <- errors.New("subscription was terminate") sub.On("Err").Return((<-chan error)(errChan)) sub.On("Unsubscribe").Once() - rpc.On("Subscribe", mock.Anything, mock.Anything, rpcSubscriptionMethodNewHeads).Return(sub, nil).Once() + rpc.On("SubscribeNewHead", mock.Anything, mock.Anything).Return(sub, nil).Once() rpc.On("Dial", mock.Anything).Return(errors.New("failed to redial")).Maybe() node.declareOutOfSync(stubIsOutOfSync) tests.AssertLogEventually(t, observedLogs, "Subscription was terminated") @@ -754,7 +751,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { sub := mocks.NewSubscription(t) sub.On("Err").Return((<-chan error)(nil)) sub.On("Unsubscribe").Once() - rpc.On("Subscribe", mock.Anything, mock.Anything, rpcSubscriptionMethodNewHeads).Run(func(args mock.Arguments) { + rpc.On("SubscribeNewHead", mock.Anything, mock.Anything).Run(func(args mock.Arguments) { ch := args.Get(1).(chan<- Head) close(ch) }).Return(sub, nil).Once() @@ -785,10 +782,11 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { outOfSyncSubscription.On("Err").Return((<-chan error)(nil)) outOfSyncSubscription.On("Unsubscribe").Once() const highestBlock = 1000 - rpc.On("Subscribe", mock.Anything, mock.Anything, rpcSubscriptionMethodNewHeads).Run(func(args mock.Arguments) { + rpc.On("SubscribeNewHead", mock.Anything, mock.Anything).Run(func(args mock.Arguments) { ch := args.Get(1).(chan<- Head) go writeHeads(t, ch, head{BlockNumber: highestBlock - 1}, head{BlockNumber: highestBlock}) }).Return(outOfSyncSubscription, nil).Once() + rpc.On("GetInterceptedChainInfo").Return(ChainInfo{BlockNumber: highestBlock}, ChainInfo{BlockNumber: highestBlock}) setupRPCForAliveLoop(t, rpc) @@ -815,9 +813,13 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { lggr: lggr, }) defer func() { assert.NoError(t, node.close()) }() - node.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *big.Int) { - return 0, 100, big.NewInt(200) - } + poolInfo := newMockPoolChainInfoProvider(t) + poolInfo.On("LatestChainInfo").Return(0, ChainInfo{ + BlockNumber: 100, + TotalDifficulty: big.NewInt(200), + }) + node.SetPoolChainInfoProvider(poolInfo) + rpc.On("GetInterceptedChainInfo").Return(ChainInfo{BlockNumber: 0}, ChainInfo{BlockNumber: 0}) rpc.On("Dial", mock.Anything).Return(nil).Once() rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once() @@ -825,7 +827,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { outOfSyncSubscription := mocks.NewSubscription(t) outOfSyncSubscription.On("Err").Return((<-chan error)(nil)) outOfSyncSubscription.On("Unsubscribe").Once() - rpc.On("Subscribe", mock.Anything, mock.Anything, rpcSubscriptionMethodNewHeads).Return(outOfSyncSubscription, nil).Once() + rpc.On("SubscribeNewHead", mock.Anything, mock.Anything).Return(outOfSyncSubscription, nil).Once() setupRPCForAliveLoop(t, rpc) @@ -1304,9 +1306,8 @@ func TestUnit_NodeLifecycle_syncStatus(t *testing.T) { }) t.Run("skip if syncThreshold is not configured", func(t *testing.T) { node := newTestNode(t, testNodeOpts{}) - node.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *big.Int) { - return - } + poolInfo := newMockPoolChainInfoProvider(t) + node.SetPoolChainInfoProvider(poolInfo) outOfSync, liveNodes := node.syncStatus(0, nil) assert.Equal(t, false, outOfSync) assert.Equal(t, 0, liveNodes) @@ -1315,9 +1316,9 @@ func TestUnit_NodeLifecycle_syncStatus(t *testing.T) { node := newTestNode(t, testNodeOpts{ config: testNodeConfig{syncThreshold: 1}, }) - node.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *big.Int) { - return - } + poolInfo := newMockPoolChainInfoProvider(t) + poolInfo.On("LatestChainInfo").Return(1, ChainInfo{}).Once() + node.SetPoolChainInfoProvider(poolInfo) assert.Panics(t, func() { _, _ = node.syncStatus(0, nil) }) @@ -1361,9 +1362,12 @@ func TestUnit_NodeLifecycle_syncStatus(t *testing.T) { selectionMode: selectionMode, }, }) - node.nLiveNodes = func() (int, int64, *big.Int) { - return nodesNum, highestBlock, big.NewInt(totalDifficulty) - } + 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) { @@ -1413,9 +1417,13 @@ func TestUnit_NodeLifecycle_syncStatus(t *testing.T) { selectionMode: NodeSelectionModeTotalDifficulty, }, }) - node.nLiveNodes = func() (int, int64, *big.Int) { - return nodesNum, highestBlock, big.NewInt(totalDifficulty) - } + + 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) { @@ -1573,3 +1581,98 @@ func TestUnit_NodeLifecycle_SyncingLoop(t *testing.T) { }) }) } + +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 := newMockNodeClient[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_highest_head.go b/common/client/node_selector_highest_head.go index 99a130004a..25a931fc01 100644 --- a/common/client/node_selector_highest_head.go +++ b/common/client/node_selector_highest_head.go @@ -24,7 +24,8 @@ func (s highestHeadNodeSelector[CHAIN_ID, HEAD, RPC]) Select() Node[CHAIN_ID, HE var highestHeadNumber int64 = math.MinInt64 var highestHeadNodes []Node[CHAIN_ID, HEAD, RPC] for _, n := range s { - state, currentHeadNumber, _ := n.StateAndLatest() + state, currentChainInfo := n.StateAndLatest() + currentHeadNumber := currentChainInfo.BlockNumber if state == nodeStateAlive && currentHeadNumber >= highestHeadNumber { if highestHeadNumber < currentHeadNumber { highestHeadNumber = currentHeadNumber diff --git a/common/client/node_selector_highest_head_test.go b/common/client/node_selector_highest_head_test.go index 6e47bbedca..e245924589 100644 --- a/common/client/node_selector_highest_head_test.go +++ b/common/client/node_selector_highest_head_test.go @@ -24,13 +24,13 @@ func TestHighestHeadNodeSelector(t *testing.T) { node := newMockNode[types.ID, Head, nodeClient](t) if i == 0 { // first node is out of sync - node.On("StateAndLatest").Return(nodeStateOutOfSync, int64(-1), nil) + node.On("StateAndLatest").Return(nodeStateOutOfSync, ChainInfo{BlockNumber: int64(-1)}) } else if i == 1 { // second node is alive, LatestReceivedBlockNumber = 1 - node.On("StateAndLatest").Return(nodeStateAlive, int64(1), nil) + node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(1)}) } else { // third node is alive, LatestReceivedBlockNumber = 2 (best node) - node.On("StateAndLatest").Return(nodeStateAlive, int64(2), nil) + node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(2)}) } node.On("Order").Maybe().Return(int32(1)) nodes = append(nodes, node) @@ -42,7 +42,7 @@ func TestHighestHeadNodeSelector(t *testing.T) { t.Run("stick to the same node", func(t *testing.T) { node := newMockNode[types.ID, Head, nodeClient](t) // fourth node is alive, LatestReceivedBlockNumber = 2 (same as 3rd) - node.On("StateAndLatest").Return(nodeStateAlive, int64(2), nil) + node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(2)}) node.On("Order").Return(int32(1)) nodes = append(nodes, node) @@ -53,7 +53,7 @@ func TestHighestHeadNodeSelector(t *testing.T) { t.Run("another best node", func(t *testing.T) { node := newMockNode[types.ID, Head, nodeClient](t) // fifth node is alive, LatestReceivedBlockNumber = 3 (better than 3rd and 4th) - node.On("StateAndLatest").Return(nodeStateAlive, int64(3), nil) + node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(3)}) node.On("Order").Return(int32(1)) nodes = append(nodes, node) @@ -63,10 +63,10 @@ func TestHighestHeadNodeSelector(t *testing.T) { t.Run("nodes never update latest block number", func(t *testing.T) { node1 := newMockNode[types.ID, Head, nodeClient](t) - node1.On("StateAndLatest").Return(nodeStateAlive, int64(-1), nil) + node1.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(-1)}) node1.On("Order").Return(int32(1)) node2 := newMockNode[types.ID, Head, nodeClient](t) - node2.On("StateAndLatest").Return(nodeStateAlive, int64(-1), nil) + node2.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(-1)}) node2.On("Order").Return(int32(1)) selector := newNodeSelector(NodeSelectionModeHighestHead, []Node[types.ID, Head, nodeClient]{node1, node2}) assert.Same(t, node1, selector.Select()) @@ -83,10 +83,10 @@ func TestHighestHeadNodeSelector_None(t *testing.T) { node := newMockNode[types.ID, Head, nodeClient](t) if i == 0 { // first node is out of sync - node.On("StateAndLatest").Return(nodeStateOutOfSync, int64(-1), nil) + node.On("StateAndLatest").Return(nodeStateOutOfSync, ChainInfo{BlockNumber: int64(-1)}) } else { // others are unreachable - node.On("StateAndLatest").Return(nodeStateUnreachable, int64(1), nil) + node.On("StateAndLatest").Return(nodeStateUnreachable, ChainInfo{BlockNumber: int64(-1)}) } nodes = append(nodes, node) } @@ -104,7 +104,7 @@ func TestHighestHeadNodeSelectorWithOrder(t *testing.T) { t.Run("same head and order", func(t *testing.T) { for i := 0; i < 3; i++ { node := newMockNode[types.ID, Head, nodeClient](t) - node.On("StateAndLatest").Return(nodeStateAlive, int64(1), nil) + node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(1)}) node.On("Order").Return(int32(2)) nodes = append(nodes, node) } @@ -115,15 +115,15 @@ func TestHighestHeadNodeSelectorWithOrder(t *testing.T) { t.Run("same head but different order", func(t *testing.T) { node1 := newMockNode[types.ID, Head, nodeClient](t) - node1.On("StateAndLatest").Return(nodeStateAlive, int64(3), nil) + node1.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(3)}) node1.On("Order").Return(int32(3)) node2 := newMockNode[types.ID, Head, nodeClient](t) - node2.On("StateAndLatest").Return(nodeStateAlive, int64(3), nil) + node2.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(3)}) node2.On("Order").Return(int32(1)) node3 := newMockNode[types.ID, Head, nodeClient](t) - node3.On("StateAndLatest").Return(nodeStateAlive, int64(3), nil) + node3.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(3)}) node3.On("Order").Return(int32(2)) nodes := []Node[types.ID, Head, nodeClient]{node1, node2, node3} @@ -134,15 +134,15 @@ func TestHighestHeadNodeSelectorWithOrder(t *testing.T) { t.Run("different head but same order", func(t *testing.T) { node1 := newMockNode[types.ID, Head, nodeClient](t) - node1.On("StateAndLatest").Return(nodeStateAlive, int64(1), nil) + node1.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(1)}) node1.On("Order").Maybe().Return(int32(3)) node2 := newMockNode[types.ID, Head, nodeClient](t) - node2.On("StateAndLatest").Return(nodeStateAlive, int64(2), nil) + node2.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(2)}) node2.On("Order").Maybe().Return(int32(3)) node3 := newMockNode[types.ID, Head, nodeClient](t) - node3.On("StateAndLatest").Return(nodeStateAlive, int64(3), nil) + node3.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(3)}) node3.On("Order").Return(int32(3)) nodes := []Node[types.ID, Head, nodeClient]{node1, node2, node3} @@ -153,19 +153,19 @@ func TestHighestHeadNodeSelectorWithOrder(t *testing.T) { t.Run("different head and different order", func(t *testing.T) { node1 := newMockNode[types.ID, Head, nodeClient](t) - node1.On("StateAndLatest").Return(nodeStateAlive, int64(10), nil) + node1.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(10)}) node1.On("Order").Maybe().Return(int32(3)) node2 := newMockNode[types.ID, Head, nodeClient](t) - node2.On("StateAndLatest").Return(nodeStateAlive, int64(11), nil) + node2.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(11)}) node2.On("Order").Maybe().Return(int32(4)) node3 := newMockNode[types.ID, Head, nodeClient](t) - node3.On("StateAndLatest").Return(nodeStateAlive, int64(11), nil) + node3.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(11)}) node3.On("Order").Maybe().Return(int32(3)) node4 := newMockNode[types.ID, Head, nodeClient](t) - node4.On("StateAndLatest").Return(nodeStateAlive, int64(10), nil) + node4.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(10)}) node4.On("Order").Maybe().Return(int32(1)) nodes := []Node[types.ID, Head, nodeClient]{node1, node2, node3, node4} diff --git a/common/client/node_selector_total_difficulty.go b/common/client/node_selector_total_difficulty.go index 35491503bc..6b45e75528 100644 --- a/common/client/node_selector_total_difficulty.go +++ b/common/client/node_selector_total_difficulty.go @@ -27,11 +27,12 @@ func (s totalDifficultyNodeSelector[CHAIN_ID, HEAD, RPC]) Select() Node[CHAIN_ID var aliveNodes []Node[CHAIN_ID, HEAD, RPC] for _, n := range s { - state, _, currentTD := n.StateAndLatest() + 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 { diff --git a/common/client/node_selector_total_difficulty_test.go b/common/client/node_selector_total_difficulty_test.go index 5c43cdd847..0bc214918d 100644 --- a/common/client/node_selector_total_difficulty_test.go +++ b/common/client/node_selector_total_difficulty_test.go @@ -1,7 +1,7 @@ package client import ( - big "math/big" + "math/big" "testing" "github.com/smartcontractkit/chainlink/v2/common/types" @@ -24,13 +24,13 @@ func TestTotalDifficultyNodeSelector(t *testing.T) { node := newMockNode[types.ID, Head, nodeClient](t) if i == 0 { // first node is out of sync - node.On("StateAndLatest").Return(nodeStateOutOfSync, int64(-1), nil) + node.On("StateAndLatest").Return(nodeStateOutOfSync, ChainInfo{BlockNumber: -1}) } else if i == 1 { // second node is alive - node.On("StateAndLatest").Return(nodeStateAlive, int64(1), big.NewInt(7)) + node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(7)}) } else { // third node is alive and best - node.On("StateAndLatest").Return(nodeStateAlive, int64(2), big.NewInt(8)) + node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 2, TotalDifficulty: big.NewInt(8)}) } node.On("Order").Maybe().Return(int32(1)) nodes = append(nodes, node) @@ -42,7 +42,7 @@ func TestTotalDifficultyNodeSelector(t *testing.T) { t.Run("stick to the same node", func(t *testing.T) { node := newMockNode[types.ID, Head, nodeClient](t) // fourth node is alive (same as 3rd) - node.On("StateAndLatest").Return(nodeStateAlive, int64(2), big.NewInt(8)) + node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 2, TotalDifficulty: big.NewInt(8)}) node.On("Order").Maybe().Return(int32(1)) nodes = append(nodes, node) @@ -53,7 +53,7 @@ func TestTotalDifficultyNodeSelector(t *testing.T) { t.Run("another best node", func(t *testing.T) { node := newMockNode[types.ID, Head, nodeClient](t) // fifth node is alive (better than 3rd and 4th) - node.On("StateAndLatest").Return(nodeStateAlive, int64(3), big.NewInt(11)) + node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 3, TotalDifficulty: big.NewInt(11)}) node.On("Order").Maybe().Return(int32(1)) nodes = append(nodes, node) @@ -63,10 +63,10 @@ func TestTotalDifficultyNodeSelector(t *testing.T) { t.Run("nodes never update latest block number", func(t *testing.T) { node1 := newMockNode[types.ID, Head, nodeClient](t) - node1.On("StateAndLatest").Return(nodeStateAlive, int64(-1), nil) + node1.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: -1, TotalDifficulty: nil}) node1.On("Order").Maybe().Return(int32(1)) node2 := newMockNode[types.ID, Head, nodeClient](t) - node2.On("StateAndLatest").Return(nodeStateAlive, int64(-1), nil) + node2.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: -1, TotalDifficulty: nil}) node2.On("Order").Maybe().Return(int32(1)) nodes := []Node[types.ID, Head, nodeClient]{node1, node2} @@ -85,10 +85,10 @@ func TestTotalDifficultyNodeSelector_None(t *testing.T) { node := newMockNode[types.ID, Head, nodeClient](t) if i == 0 { // first node is out of sync - node.On("StateAndLatest").Return(nodeStateOutOfSync, int64(-1), nil) + node.On("StateAndLatest").Return(nodeStateOutOfSync, ChainInfo{BlockNumber: -1, TotalDifficulty: nil}) } else { // others are unreachable - node.On("StateAndLatest").Return(nodeStateUnreachable, int64(1), big.NewInt(7)) + node.On("StateAndLatest").Return(nodeStateUnreachable, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(7)}) } nodes = append(nodes, node) } @@ -106,7 +106,7 @@ func TestTotalDifficultyNodeSelectorWithOrder(t *testing.T) { t.Run("same td and order", func(t *testing.T) { for i := 0; i < 3; i++ { node := newMockNode[types.ID, Head, nodeClient](t) - node.On("StateAndLatest").Return(nodeStateAlive, int64(1), big.NewInt(10)) + node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(10)}) node.On("Order").Return(int32(2)) nodes = append(nodes, node) } @@ -117,15 +117,15 @@ func TestTotalDifficultyNodeSelectorWithOrder(t *testing.T) { t.Run("same td but different order", func(t *testing.T) { node1 := newMockNode[types.ID, Head, nodeClient](t) - node1.On("StateAndLatest").Return(nodeStateAlive, int64(3), big.NewInt(10)) + node1.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 3, TotalDifficulty: big.NewInt(10)}) node1.On("Order").Return(int32(3)) node2 := newMockNode[types.ID, Head, nodeClient](t) - node2.On("StateAndLatest").Return(nodeStateAlive, int64(3), big.NewInt(10)) + node2.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 3, TotalDifficulty: big.NewInt(10)}) node2.On("Order").Return(int32(1)) node3 := newMockNode[types.ID, Head, nodeClient](t) - node3.On("StateAndLatest").Return(nodeStateAlive, int64(3), big.NewInt(10)) + node3.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 3, TotalDifficulty: big.NewInt(10)}) node3.On("Order").Return(int32(2)) nodes := []Node[types.ID, Head, nodeClient]{node1, node2, node3} @@ -136,15 +136,15 @@ func TestTotalDifficultyNodeSelectorWithOrder(t *testing.T) { t.Run("different td but same order", func(t *testing.T) { node1 := newMockNode[types.ID, Head, nodeClient](t) - node1.On("StateAndLatest").Return(nodeStateAlive, int64(1), big.NewInt(10)) + node1.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(10)}) node1.On("Order").Maybe().Return(int32(3)) node2 := newMockNode[types.ID, Head, nodeClient](t) - node2.On("StateAndLatest").Return(nodeStateAlive, int64(1), big.NewInt(11)) + node2.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(11)}) node2.On("Order").Maybe().Return(int32(3)) node3 := newMockNode[types.ID, Head, nodeClient](t) - node3.On("StateAndLatest").Return(nodeStateAlive, int64(1), big.NewInt(12)) + node3.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(12)}) node3.On("Order").Return(int32(3)) nodes := []Node[types.ID, Head, nodeClient]{node1, node2, node3} @@ -155,19 +155,19 @@ func TestTotalDifficultyNodeSelectorWithOrder(t *testing.T) { t.Run("different head and different order", func(t *testing.T) { node1 := newMockNode[types.ID, Head, nodeClient](t) - node1.On("StateAndLatest").Return(nodeStateAlive, int64(1), big.NewInt(100)) + node1.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(100)}) node1.On("Order").Maybe().Return(int32(4)) node2 := newMockNode[types.ID, Head, nodeClient](t) - node2.On("StateAndLatest").Return(nodeStateAlive, int64(1), big.NewInt(110)) + node2.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(110)}) node2.On("Order").Maybe().Return(int32(5)) node3 := newMockNode[types.ID, Head, nodeClient](t) - node3.On("StateAndLatest").Return(nodeStateAlive, int64(1), big.NewInt(110)) + node3.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(110)}) node3.On("Order").Maybe().Return(int32(1)) node4 := newMockNode[types.ID, Head, nodeClient](t) - node4.On("StateAndLatest").Return(nodeStateAlive, int64(1), big.NewInt(105)) + node4.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(105)}) node4.On("Order").Maybe().Return(int32(2)) nodes := []Node[types.ID, Head, nodeClient]{node1, node2, node3, node4} diff --git a/common/client/node_test.go b/common/client/node_test.go index a97f26555a..3b971e8490 100644 --- a/common/client/node_test.go +++ b/common/client/node_test.go @@ -17,7 +17,9 @@ type testNodeConfig struct { selectionMode string syncThreshold uint32 nodeIsSyncingEnabled bool + enforceRepeatableRead bool finalizedBlockPollInterval time.Duration + deathDeclarationDelay time.Duration } func (n testNodeConfig) PollFailureThreshold() uint32 { @@ -44,6 +46,14 @@ 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, NodeClient[types.ID, Head]] } diff --git a/common/client/types.go b/common/client/types.go index a27e6a50b7..3d548b9deb 100644 --- a/common/client/types.go +++ b/common/client/types.go @@ -66,6 +66,7 @@ type NodeClient[ connection[CHAIN_ID, HEAD] DialHTTP() error + // DisconnectAll - cancels all inflight requests, terminates all subscriptions and resets latest ChainInfo. DisconnectAll() Close() ClientVersion(context.Context) (string, error) @@ -74,6 +75,17 @@ type NodeClient[ UnsubscribeAllExceptAliveLoop() IsSyncing(ctx context.Context) (bool, error) LatestFinalizedBlock(ctx context.Context) (HEAD, error) + // 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) } // clientAPI includes all the direct RPC methods required by the generalized common client to implement its own. @@ -145,5 +157,41 @@ type connection[ ] interface { ChainID(ctx context.Context) (CHAIN_ID, error) Dial(ctx context.Context) error - Subscribe(ctx context.Context, channel chan<- HEAD, args ...interface{}) (types.Subscription, error) + SubscribeNewHead(ctx context.Context, channel chan<- HEAD) (types.Subscription, error) +} + +// PoolChainInfoProvider - provides aggregation of nodes pool ChainInfo +// +//go:generate mockery --quiet --name PoolChainInfoProvider --structname mockPoolChainInfoProvider --filename "mock_pool_chain_info_provider_test.go" --inpackage --case=underscore +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 new file mode 100644 index 0000000000..68d7a3fe78 --- /dev/null +++ b/common/client/types_test.go @@ -0,0 +1,34 @@ +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/headtracker/head_listener.go b/common/headtracker/head_listener.go index 15977c4dfe..25715b3528 100644 --- a/common/headtracker/head_listener.go +++ b/common/headtracker/head_listener.go @@ -36,7 +36,7 @@ type headHandler[H types.Head[BLOCK_HASH], BLOCK_HASH types.Hashable] func(ctx c type HeadListener[H types.Head[BLOCK_HASH], BLOCK_HASH types.Hashable] interface { // ListenForNewHeads kicks off the listen loop (not thread safe) // done() must be executed upon leaving ListenForNewHeads() - ListenForNewHeads(handleNewHead headHandler[H, BLOCK_HASH], done func()) + ListenForNewHeads(onSubscribe func(), handleNewHead headHandler[H, BLOCK_HASH], done func()) // ReceivingHeads returns true if the listener is receiving heads (thread safe) ReceivingHeads() bool @@ -88,7 +88,7 @@ func (hl *headListener[HTH, S, ID, BLOCK_HASH]) Name() string { return hl.logger.Name() } -func (hl *headListener[HTH, S, ID, BLOCK_HASH]) ListenForNewHeads(handleNewHead headHandler[HTH, BLOCK_HASH], done func()) { +func (hl *headListener[HTH, S, ID, BLOCK_HASH]) ListenForNewHeads(onSubscription func(), handleNewHead headHandler[HTH, BLOCK_HASH], done func()) { defer done() defer hl.unsubscribe() @@ -99,6 +99,8 @@ func (hl *headListener[HTH, S, ID, BLOCK_HASH]) ListenForNewHeads(handleNewHead if !hl.subscribe(ctx) { break } + + onSubscription() err := hl.receiveHeaders(ctx, handleNewHead) if ctx.Err() != nil { break diff --git a/common/headtracker/head_tracker.go b/common/headtracker/head_tracker.go index 48c4859a64..bc5f5274c0 100644 --- a/common/headtracker/head_tracker.go +++ b/common/headtracker/head_tracker.go @@ -40,8 +40,11 @@ const HeadsBufferSize = 10 type HeadTracker[H types.Head[BLOCK_HASH], BLOCK_HASH types.Hashable] interface { services.Service // Backfill given a head will fill in any missing heads up to latestFinalized - Backfill(ctx context.Context, headWithChain, latestFinalized H) (err error) + Backfill(ctx context.Context, headWithChain H) (err error) LatestChain() H + // LatestAndFinalizedBlock - returns latest and latest finalized blocks. + // NOTE: Returns latest finalized block as is, ignoring the FinalityTagBypass feature flag. + LatestAndFinalizedBlock(ctx context.Context) (latest, finalized H, err error) } type headTracker[ @@ -114,16 +117,15 @@ func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) Start(ctx context.Context) error // anyway when we connect (but we should not rely on this because it is // not specced). If it happens this is fine, and the head will be // ignored as a duplicate. - err := ht.handleInitialHead(ctx) - if err != nil { - if ctx.Err() != nil { - return ctx.Err() + onSubscribe := func() { + err := ht.handleInitialHead(ctx) + if err != nil { + ht.log.Errorw("Error handling initial head", "err", err.Error()) } - ht.log.Errorw("Error handling initial head", "err", err.Error()) } ht.wgDone.Add(3) - go ht.headListener.ListenForNewHeads(ht.handleNewHead, ht.wgDone.Done) + go ht.headListener.ListenForNewHeads(onSubscribe, ht.handleNewHead, ht.wgDone.Done) go ht.backfillLoop() go ht.broadcastLoop() @@ -145,7 +147,7 @@ func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) handleInitialHead(ctx context.Con } ht.log.Debugw("Got initial head", "head", initialHead, "blockNumber", initialHead.BlockNumber(), "blockHash", initialHead.BlockHash()) - latestFinalized, err := ht.calculateLatestFinalized(ctx, initialHead) + latestFinalized, err := ht.calculateLatestFinalized(ctx, initialHead, ht.htConfig.FinalityTagBypass()) if err != nil { return fmt.Errorf("failed to calculate latest finalized head: %w", err) } @@ -195,7 +197,12 @@ func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) HealthReport() map[string]error { return report } -func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) Backfill(ctx context.Context, headWithChain, latestFinalized HTH) (err error) { +func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) Backfill(ctx context.Context, headWithChain HTH) (err error) { + latestFinalized, err := ht.calculateLatestFinalized(ctx, headWithChain, ht.htConfig.FinalityTagBypass()) + if err != nil { + return fmt.Errorf("failed to calculate finalized block: %w", err) + } + if !latestFinalized.IsValid() { return errors.New("can not perform backfill without a valid latestFinalized head") } @@ -208,6 +215,11 @@ func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) Backfill(ctx context.Context, hea return errors.New(errMsg) } + if headWithChain.BlockNumber()-latestFinalized.BlockNumber() > int64(ht.htConfig.MaxAllowedFinalityDepth()) { + return fmt.Errorf("gap between latest finalized block (%d) and current head (%d) is too large (> %d)", + latestFinalized.BlockNumber(), headWithChain.BlockNumber(), ht.htConfig.MaxAllowedFinalityDepth()) + } + return ht.backfill(ctx, headWithChain, latestFinalized) } @@ -317,13 +329,7 @@ func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) backfillLoop() { break } { - latestFinalized, err := ht.calculateLatestFinalized(ctx, head) - if err != nil { - ht.log.Warnw("Failed to calculate finalized block", "err", err) - continue - } - - err = ht.Backfill(ctx, head, latestFinalized) + err := ht.Backfill(ctx, head) if err != nil { ht.log.Warnw("Unexpected error while backfilling heads", "err", err) } else if ctx.Err() != nil { @@ -335,12 +341,58 @@ func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) backfillLoop() { } } +// LatestAndFinalizedBlock - returns latest and latest finalized blocks. +// NOTE: Returns latest finalized block as is, ignoring the FinalityTagBypass feature flag. +// TODO: BCI-3321 use cached values instead of making RPC requests +func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) LatestAndFinalizedBlock(ctx context.Context) (latest, finalized HTH, err error) { + latest, err = ht.client.HeadByNumber(ctx, nil) + if err != nil { + err = fmt.Errorf("failed to get latest block: %w", err) + return + } + + if !latest.IsValid() { + err = fmt.Errorf("expected latest block to be valid") + return + } + + finalized, err = ht.calculateLatestFinalized(ctx, latest, false) + if err != nil { + err = fmt.Errorf("failed to calculate latest finalized block: %w", err) + return + } + if !finalized.IsValid() { + err = fmt.Errorf("expected finalized block to be valid") + return + } + + return +} + +func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) getHeadAtHeight(ctx context.Context, chainHeadHash BLOCK_HASH, blockHeight int64) (HTH, error) { + chainHead := ht.headSaver.Chain(chainHeadHash) + if chainHead.IsValid() { + // check if provided chain contains a block of specified height + headAtHeight, err := chainHead.HeadAtHeight(blockHeight) + if err == nil { + // we are forced to reload the block due to type mismatched caused by generics + hthAtHeight := ht.headSaver.Chain(headAtHeight.BlockHash()) + // ensure that the block was not removed from the chain by another goroutine + if hthAtHeight.IsValid() { + return hthAtHeight, nil + } + } + } + + return ht.client.HeadByNumber(ctx, big.NewInt(blockHeight)) +} + // calculateLatestFinalized - returns latest finalized block. It's expected that currentHeadNumber - is the head of // canonical chain. There is no guaranties that returned block belongs to the canonical chain. Additional verification // must be performed before usage. -func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) calculateLatestFinalized(ctx context.Context, currentHead HTH) (latestFinalized HTH, err error) { - if ht.config.FinalityTagEnabled() && !ht.htConfig.FinalityTagBypass() { - latestFinalized, err = ht.client.LatestFinalizedBlock(ctx) +func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) calculateLatestFinalized(ctx context.Context, currentHead HTH, finalityTagBypass bool) (HTH, error) { + if ht.config.FinalityTagEnabled() && !finalityTagBypass { + latestFinalized, err := ht.client.LatestFinalizedBlock(ctx) if err != nil { return latestFinalized, fmt.Errorf("failed to get latest finalized block: %w", err) } @@ -349,22 +401,22 @@ func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) calculateLatestFinalized(ctx cont return latestFinalized, fmt.Errorf("failed to get valid latest finalized block") } - if currentHead.BlockNumber()-latestFinalized.BlockNumber() > int64(ht.htConfig.MaxAllowedFinalityDepth()) { - return latestFinalized, fmt.Errorf("gap between latest finalized block (%d) and current head (%d) is too large (> %d)", - latestFinalized.BlockNumber(), currentHead.BlockNumber(), ht.htConfig.MaxAllowedFinalityDepth()) + if ht.config.FinalizedBlockOffset() == 0 { + return latestFinalized, nil } - return latestFinalized, nil + finalizedBlockNumber := max(latestFinalized.BlockNumber()-int64(ht.config.FinalizedBlockOffset()), 0) + return ht.getHeadAtHeight(ctx, latestFinalized.BlockHash(), finalizedBlockNumber) } // no need to make an additional RPC call on chains with instant finality - if ht.config.FinalityDepth() == 0 { + if ht.config.FinalityDepth() == 0 && ht.config.FinalizedBlockOffset() == 0 { return currentHead, nil } - finalizedBlockNumber := currentHead.BlockNumber() - int64(ht.config.FinalityDepth()) + finalizedBlockNumber := currentHead.BlockNumber() - int64(ht.config.FinalityDepth()) - int64(ht.config.FinalizedBlockOffset()) if finalizedBlockNumber <= 0 { finalizedBlockNumber = 0 } - return ht.client.HeadByNumber(ctx, big.NewInt(finalizedBlockNumber)) + return ht.getHeadAtHeight(ctx, currentHead.BlockHash(), finalizedBlockNumber) } // backfill fetches all missing heads up until the latestFinalizedHead diff --git a/common/headtracker/mocks/head_broadcaster.go b/common/headtracker/mocks/head_broadcaster.go index c3a9fba97b..e11adb715f 100644 --- a/common/headtracker/mocks/head_broadcaster.go +++ b/common/headtracker/mocks/head_broadcaster.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/common/headtracker/mocks/head_trackable.go b/common/headtracker/mocks/head_trackable.go index 417152d8cb..9b70938512 100644 --- a/common/headtracker/mocks/head_trackable.go +++ b/common/headtracker/mocks/head_trackable.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/common/headtracker/mocks/head_tracker.go b/common/headtracker/mocks/head_tracker.go index 2321c3bfdc..b02c605f53 100644 --- a/common/headtracker/mocks/head_tracker.go +++ b/common/headtracker/mocks/head_tracker.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks @@ -15,17 +15,17 @@ type HeadTracker[H types.Head[BLOCK_HASH], BLOCK_HASH types.Hashable] struct { mock.Mock } -// Backfill provides a mock function with given fields: ctx, headWithChain, latestFinalized -func (_m *HeadTracker[H, BLOCK_HASH]) Backfill(ctx context.Context, headWithChain H, latestFinalized H) error { - ret := _m.Called(ctx, headWithChain, latestFinalized) +// Backfill provides a mock function with given fields: ctx, headWithChain +func (_m *HeadTracker[H, BLOCK_HASH]) Backfill(ctx context.Context, headWithChain H) error { + ret := _m.Called(ctx, headWithChain) if len(ret) == 0 { panic("no return value specified for Backfill") } var r0 error - if rf, ok := ret.Get(0).(func(context.Context, H, H) error); ok { - r0 = rf(ctx, headWithChain, latestFinalized) + if rf, ok := ret.Get(0).(func(context.Context, H) error); ok { + r0 = rf(ctx, headWithChain) } else { r0 = ret.Error(0) } @@ -71,6 +71,41 @@ func (_m *HeadTracker[H, BLOCK_HASH]) HealthReport() map[string]error { return r0 } +// LatestAndFinalizedBlock provides a mock function with given fields: ctx +func (_m *HeadTracker[H, BLOCK_HASH]) LatestAndFinalizedBlock(ctx context.Context) (H, H, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for LatestAndFinalizedBlock") + } + + var r0 H + var r1 H + var r2 error + if rf, ok := ret.Get(0).(func(context.Context) (H, H, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) H); ok { + r0 = rf(ctx) + } else { + 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 rf, ok := ret.Get(2).(func(context.Context) error); ok { + r2 = rf(ctx) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + // LatestChain provides a mock function with given fields: func (_m *HeadTracker[H, BLOCK_HASH]) LatestChain() H { ret := _m.Called() diff --git a/common/headtracker/types/config.go b/common/headtracker/types/config.go index e0eb422672..06ad93d39d 100644 --- a/common/headtracker/types/config.go +++ b/common/headtracker/types/config.go @@ -6,6 +6,7 @@ type Config interface { BlockEmissionIdleWarningThreshold() time.Duration FinalityDepth() uint32 FinalityTagEnabled() bool + FinalizedBlockOffset() uint32 } type HeadTrackerConfig interface { diff --git a/common/txmgr/mocks/tx_manager.go b/common/txmgr/mocks/tx_manager.go index 502f6fe8a2..c7380b7687 100644 --- a/common/txmgr/mocks/tx_manager.go +++ b/common/txmgr/mocks/tx_manager.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/common/txmgr/types/mocks/forwarder_manager.go b/common/txmgr/types/mocks/forwarder_manager.go index b2cf9bc9d3..366fd27015 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/common/txmgr/types/mocks/key_store.go b/common/txmgr/types/mocks/key_store.go index f6f572a35f..0a14acfbed 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/common/txmgr/types/mocks/reaper_chain_config.go b/common/txmgr/types/mocks/reaper_chain_config.go index 716b3e5175..30f58c1000 100644 --- a/common/txmgr/types/mocks/reaper_chain_config.go +++ b/common/txmgr/types/mocks/reaper_chain_config.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. 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 4761467433..692b71e739 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/common/txmgr/types/mocks/tx_store.go b/common/txmgr/types/mocks/tx_store.go index bb8fabf1a7..9c2edfa963 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/common/txmgr/types/mocks/tx_strategy.go b/common/txmgr/types/mocks/tx_strategy.go index 414fdb44cb..1034e00e54 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/common/types/head.go b/common/types/head.go index 9d927d4f5e..77a2a7a0dc 100644 --- a/common/types/head.go +++ b/common/types/head.go @@ -33,6 +33,9 @@ type Head[BLOCK_HASH Hashable] interface { // If not in chain, returns the zero hash HashAtHeight(blockNum int64) BLOCK_HASH + // HeadAtHeight returns head at specified height or an error, if one does not exist in provided chain. + HeadAtHeight(blockNum int64) (Head[BLOCK_HASH], error) + // Returns the total difficulty of the block. For chains who do not have a concept of block // difficulty, return 0. BlockDifficulty() *big.Int diff --git a/common/types/mocks/head.go b/common/types/mocks/head.go index 8fcd57a33c..102368199a 100644 --- a/common/types/mocks/head.go +++ b/common/types/mocks/head.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks @@ -184,6 +184,36 @@ func (_m *Head[BLOCK_HASH]) HashAtHeight(blockNum int64) BLOCK_HASH { return r0 } +// 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 +} + // IsValid provides a mock function with given fields: func (_m *Head[BLOCK_HASH]) IsValid() bool { ret := _m.Called() diff --git a/common/types/mocks/subscription.go b/common/types/mocks/subscription.go index e52fafa7ca..6abf2acdb5 100644 --- a/common/types/mocks/subscription.go +++ b/common/types/mocks/subscription.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/contracts/.changeset/tricky-comics-scream.md b/contracts/.changeset/tricky-comics-scream.md new file mode 100644 index 0000000000..f7cd1ceeab --- /dev/null +++ b/contracts/.changeset/tricky-comics-scream.md @@ -0,0 +1,5 @@ +--- +"@chainlink/contracts": patch +--- + +remove stale automation HH tests diff --git a/contracts/.prettierignore b/contracts/.prettierignore index 5059ce7025..4b0b1887fd 100644 --- a/contracts/.prettierignore +++ b/contracts/.prettierignore @@ -15,7 +15,6 @@ abi artifacts foundry-artifacts foundry-cache -foundry-lib cache node_modules solc diff --git a/contracts/GNUmakefile b/contracts/GNUmakefile index 009724d448..026a5f47a4 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.42.2 + go install github.com/vektra/mockery/v2@v2.43.2 .PHONY: foundry foundry: ## Install foundry. diff --git a/contracts/ci.json b/contracts/ci.json deleted file mode 100644 index 668c85fd4d..0000000000 --- a/contracts/ci.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "type": "solidity", - "basePath": "./contracts/test/", - "splits": [ - { - "dir": "v0.8", - "numOfSplits": 5, - "slowTests": [ - "CronUpkeepFactory", - "AutomationRegistry2_2", - "AutomationRegistry2_3" - ] - } - ] -} diff --git a/contracts/foundry.toml b/contracts/foundry.toml index 1605b3130c..c755ba6437 100644 --- a/contracts/foundry.toml +++ b/contracts/foundry.toml @@ -7,7 +7,7 @@ src = 'src/v0.8' test = 'test/v0.8' out = 'foundry-artifacts' cache_path = 'foundry-cache' -libs = ['node_modules', 'foundry-lib'] +libs = ['node_modules'] bytecode_hash = "none" ffi = false diff --git a/contracts/gas-snapshots/ccip.gas-snapshot b/contracts/gas-snapshots/ccip.gas-snapshot index 54443aa4e2..d76d62a606 100644 --- a/contracts/gas-snapshots/ccip.gas-snapshot +++ b/contracts/gas-snapshots/ccip.gas-snapshot @@ -116,213 +116,229 @@ CommitStore_verify:test_TooManyLeaves_Revert() (gas: 36785) DefensiveExampleTest:test_HappyPath_Success() (gas: 200018) DefensiveExampleTest:test_Recovery() (gas: 424253) E2E:test_E2E_3MessagesSuccess_gas() (gas: 1104821) -EVM2EVMMultiOffRamp__releaseOrMintSingleToken:test__releaseOrMintSingleToken_NotACompatiblePool_Revert() (gas: 38361) -EVM2EVMMultiOffRamp__releaseOrMintSingleToken:test__releaseOrMintSingleToken_Success() (gas: 108455) -EVM2EVMMultiOffRamp__releaseOrMintSingleToken:test__releaseOrMintSingleToken_TokenHandlingError_revert_Revert() (gas: 116907) -EVM2EVMMultiOffRamp_applySourceChainConfigUpdates:test_AddMultipleChains_Success() (gas: 460360) -EVM2EVMMultiOffRamp_applySourceChainConfigUpdates:test_AddNewChain_Success() (gas: 95427) -EVM2EVMMultiOffRamp_applySourceChainConfigUpdates:test_ApplyZeroUpdates_Success() (gas: 12395) -EVM2EVMMultiOffRamp_applySourceChainConfigUpdates:test_ReplaceExistingChainOnRamp_Revert() (gas: 90249) -EVM2EVMMultiOffRamp_applySourceChainConfigUpdates:test_ReplaceExistingChain_Success() (gas: 105403) -EVM2EVMMultiOffRamp_applySourceChainConfigUpdates:test_ZeroOnRampAddress_Revert() (gas: 15651) -EVM2EVMMultiOffRamp_applySourceChainConfigUpdates:test_ZeroSourceChainSelector_Revert() (gas: 12989) -EVM2EVMMultiOffRamp_batchExecute:test_MultipleReportsDifferentChains_Success() (gas: 305737) -EVM2EVMMultiOffRamp_batchExecute:test_MultipleReportsSameChain_Success() (gas: 247076) -EVM2EVMMultiOffRamp_batchExecute:test_MultipleReportsSkipDuplicate_Success() (gas: 162376) -EVM2EVMMultiOffRamp_batchExecute:test_OutOfBoundsGasLimitsAccess_Revert() (gas: 195244) -EVM2EVMMultiOffRamp_batchExecute:test_SingleReport_Success() (gas: 150011) -EVM2EVMMultiOffRamp_batchExecute:test_Unhealthy_Revert() (gas: 535435) -EVM2EVMMultiOffRamp_batchExecute:test_ZeroReports_Revert() (gas: 10525) -EVM2EVMMultiOffRamp_ccipReceive:test_Reverts() (gas: 16096) -EVM2EVMMultiOffRamp_commit:test_InvalidIntervalMinLargerThanMax_Revert() (gas: 67080) -EVM2EVMMultiOffRamp_commit:test_InvalidInterval_Revert() (gas: 59630) -EVM2EVMMultiOffRamp_commit:test_InvalidRootRevert() (gas: 58710) -EVM2EVMMultiOffRamp_commit:test_NoConfigWithOtherConfigPresent_Revert() (gas: 6574764) -EVM2EVMMultiOffRamp_commit:test_NoConfig_Revert() (gas: 6157992) -EVM2EVMMultiOffRamp_commit:test_OnlyGasPriceUpdates_Success() (gas: 108329) -EVM2EVMMultiOffRamp_commit:test_OnlyPriceUpdateStaleReport_Revert() (gas: 118244) -EVM2EVMMultiOffRamp_commit:test_OnlyTokenPriceUpdates_Success() (gas: 108372) -EVM2EVMMultiOffRamp_commit:test_PriceSequenceNumberCleared_Success() (gas: 353646) -EVM2EVMMultiOffRamp_commit:test_ReportAndPriceUpdate_Success() (gas: 161185) -EVM2EVMMultiOffRamp_commit:test_ReportOnlyRootSuccess_gas() (gas: 136112) -EVM2EVMMultiOffRamp_commit:test_RootAlreadyCommitted_Revert() (gas: 136695) -EVM2EVMMultiOffRamp_commit:test_SourceChainNotEnabled_Revert() (gas: 58978) -EVM2EVMMultiOffRamp_commit:test_StaleReportWithRoot_Success() (gas: 227569) -EVM2EVMMultiOffRamp_commit:test_UnauthorizedTransmitter_Revert() (gas: 119611) -EVM2EVMMultiOffRamp_commit:test_Unhealthy_Revert() (gas: 77537) -EVM2EVMMultiOffRamp_commit:test_ValidPriceUpdateThenStaleReportWithRoot_Success() (gas: 209029) -EVM2EVMMultiOffRamp_commit:test_WrongConfigWithoutSigners_Revert() (gas: 6569153) -EVM2EVMMultiOffRamp_commit:test_ZeroEpochAndRound_Revert() (gas: 47717) -EVM2EVMMultiOffRamp_constructor:test_Constructor_Success() (gas: 6158331) -EVM2EVMMultiOffRamp_constructor:test_SourceChainSelector_Revert() (gas: 104384) -EVM2EVMMultiOffRamp_constructor:test_ZeroChainSelector_Revert() (gas: 100309) -EVM2EVMMultiOffRamp_constructor:test_ZeroNonceManager_Revert() (gas: 98180) -EVM2EVMMultiOffRamp_constructor:test_ZeroOnRampAddress_Revert() (gas: 106890) -EVM2EVMMultiOffRamp_constructor:test_ZeroRMNProxy_Revert() (gas: 98079) -EVM2EVMMultiOffRamp_constructor:test_ZeroTokenAdminRegistry_Revert() (gas: 98146) -EVM2EVMMultiOffRamp_execute:test_IncorrectArrayType_Revert() (gas: 17299) -EVM2EVMMultiOffRamp_execute:test_LargeBatch_Success() (gas: 1640074) -EVM2EVMMultiOffRamp_execute:test_MultipleReportsWithPartialValidationFailures_Success() (gas: 350683) -EVM2EVMMultiOffRamp_execute:test_MultipleReports_Success() (gas: 267647) -EVM2EVMMultiOffRamp_execute:test_NoConfigWithOtherConfigPresent_Revert() (gas: 6627625) -EVM2EVMMultiOffRamp_execute:test_NoConfig_Revert() (gas: 6210572) -EVM2EVMMultiOffRamp_execute:test_NonArray_Revert() (gas: 28435) -EVM2EVMMultiOffRamp_execute:test_SingleReport_Success() (gas: 167667) -EVM2EVMMultiOffRamp_execute:test_UnauthorizedTransmitter_Revert() (gas: 151623) -EVM2EVMMultiOffRamp_execute:test_WrongConfigWithSigners_Revert() (gas: 6989700) -EVM2EVMMultiOffRamp_execute:test_ZeroReports_Revert() (gas: 17174) -EVM2EVMMultiOffRamp_executeSingleMessage:test_MessageSender_Revert() (gas: 19548) -EVM2EVMMultiOffRamp_executeSingleMessage:test_NonContractWithTokens_Success() (gas: 254458) -EVM2EVMMultiOffRamp_executeSingleMessage:test_NonContract_Success() (gas: 21806) -EVM2EVMMultiOffRamp_executeSingleMessage:test_TokenHandlingError_Revert() (gas: 206750) -EVM2EVMMultiOffRamp_executeSingleMessage:test_ZeroGasDONExecution_Revert() (gas: 50035) -EVM2EVMMultiOffRamp_executeSingleMessage:test_executeSingleMessage_NoTokens_Success() (gas: 49556) -EVM2EVMMultiOffRamp_executeSingleMessage:test_executeSingleMessage_WithFailingValidationNoRouterCall_Revert() (gas: 234116) -EVM2EVMMultiOffRamp_executeSingleMessage:test_executeSingleMessage_WithFailingValidation_Revert() (gas: 90376) -EVM2EVMMultiOffRamp_executeSingleMessage:test_executeSingleMessage_WithTokens_Success() (gas: 287302) -EVM2EVMMultiOffRamp_executeSingleMessage:test_executeSingleMessage_WithValidation_Success() (gas: 94638) -EVM2EVMMultiOffRamp_executeSingleReport:test_DisabledSourceChain_Revert() (gas: 36249) -EVM2EVMMultiOffRamp_executeSingleReport:test_EmptyReport_Revert() (gas: 23921) -EVM2EVMMultiOffRamp_executeSingleReport:test_InvalidSourcePoolAddress_Success() (gas: 462794) -EVM2EVMMultiOffRamp_executeSingleReport:test_ManualExecutionNotYetEnabled_Revert() (gas: 55975) -EVM2EVMMultiOffRamp_executeSingleReport:test_MismatchingDestChainSelector_Revert() (gas: 37072) -EVM2EVMMultiOffRamp_executeSingleReport:test_MismatchingOnRampRoot_Revert() (gas: 156143) -EVM2EVMMultiOffRamp_executeSingleReport:test_NonExistingSourceChain_Revert() (gas: 36483) -EVM2EVMMultiOffRamp_executeSingleReport:test_ReceiverError_Success() (gas: 184243) -EVM2EVMMultiOffRamp_executeSingleReport:test_RetryFailedMessageWithoutManualExecution_Revert() (gas: 194605) -EVM2EVMMultiOffRamp_executeSingleReport:test_RootNotCommitted_Revert() (gas: 49553) -EVM2EVMMultiOffRamp_executeSingleReport:test_RouterYULCall_Revert() (gas: 439123) -EVM2EVMMultiOffRamp_executeSingleReport:test_SingleMessageNoTokensOtherChain_Success() (gas: 256572) -EVM2EVMMultiOffRamp_executeSingleReport:test_SingleMessageNoTokensUnordered_Success() (gas: 179039) -EVM2EVMMultiOffRamp_executeSingleReport:test_SingleMessageNoTokens_Success() (gas: 198372) -EVM2EVMMultiOffRamp_executeSingleReport:test_SingleMessageToNonCCIPReceiver_Success() (gas: 262344) -EVM2EVMMultiOffRamp_executeSingleReport:test_SingleMessagesNoTokensSuccess_gas() (gas: 131587) -EVM2EVMMultiOffRamp_executeSingleReport:test_SkippedIncorrectNonceStillExecutes_Success() (gas: 399804) -EVM2EVMMultiOffRamp_executeSingleReport:test_SkippedIncorrectNonce_Success() (gas: 67753) -EVM2EVMMultiOffRamp_executeSingleReport:test_TokenDataMismatch_Revert() (gas: 82478) -EVM2EVMMultiOffRamp_executeSingleReport:test_TwoMessagesWithTokensAndGE_Success() (gas: 546583) -EVM2EVMMultiOffRamp_executeSingleReport:test_TwoMessagesWithTokensSuccess_gas() (gas: 486844) -EVM2EVMMultiOffRamp_executeSingleReport:test_UnexpectedTokenData_Revert() (gas: 36976) -EVM2EVMMultiOffRamp_executeSingleReport:test_UnhealthySingleChainCurse_Revert() (gas: 534156) -EVM2EVMMultiOffRamp_executeSingleReport:test_Unhealthy_Revert() (gas: 531524) -EVM2EVMMultiOffRamp_executeSingleReport:test_WithCurseOnAnotherSourceChain_Success() (gas: 498454) -EVM2EVMMultiOffRamp_executeSingleReport:test__execute_SkippedAlreadyExecutedMessageUnordered_Success() (gas: 131751) -EVM2EVMMultiOffRamp_executeSingleReport:test__execute_SkippedAlreadyExecutedMessage_Success() (gas: 160626) -EVM2EVMMultiOffRamp_getExecutionState:test_FillExecutionState_Success() (gas: 3633818) -EVM2EVMMultiOffRamp_getExecutionState:test_GetDifferentChainExecutionState_Success() (gas: 118144) -EVM2EVMMultiOffRamp_getExecutionState:test_GetExecutionState_Success() (gas: 87253) -EVM2EVMMultiOffRamp_manuallyExecute:test_ManualExecGasLimitMismatchSingleReport_Revert() (gas: 81957) -EVM2EVMMultiOffRamp_manuallyExecute:test_ManualExecInvalidGasLimit_Revert() (gas: 27558) -EVM2EVMMultiOffRamp_manuallyExecute:test_manuallyExecute_DoesNotRevertIfUntouched_Success() (gas: 165891) -EVM2EVMMultiOffRamp_manuallyExecute:test_manuallyExecute_FailedTx_Revert() (gas: 212381) -EVM2EVMMultiOffRamp_manuallyExecute:test_manuallyExecute_ForkedChain_Revert() (gas: 27086) -EVM2EVMMultiOffRamp_manuallyExecute:test_manuallyExecute_GasLimitMismatchMultipleReports_Revert() (gas: 166703) -EVM2EVMMultiOffRamp_manuallyExecute:test_manuallyExecute_LowGasLimit_Success() (gas: 512307) -EVM2EVMMultiOffRamp_manuallyExecute:test_manuallyExecute_ReentrancyFails() (gas: 2397801) -EVM2EVMMultiOffRamp_manuallyExecute:test_manuallyExecute_Success() (gas: 214474) -EVM2EVMMultiOffRamp_manuallyExecute:test_manuallyExecute_WithGasOverride_Success() (gas: 215037) -EVM2EVMMultiOffRamp_manuallyExecute:test_manuallyExecute_WithMultiReportGasOverride_Success() (gas: 692864) -EVM2EVMMultiOffRamp_manuallyExecute:test_manuallyExecute_WithPartialMessages_Success() (gas: 309081) -EVM2EVMMultiOffRamp_releaseOrMintTokens:test_TokenHandlingError_Reverts() (gas: 165102) -EVM2EVMMultiOffRamp_releaseOrMintTokens:test__releaseOrMintTokens_PoolIsNotAPool_Reverts() (gas: 26890) -EVM2EVMMultiOffRamp_releaseOrMintTokens:test_releaseOrMintTokens_InvalidDataLengthReturnData_Revert() (gas: 63495) -EVM2EVMMultiOffRamp_releaseOrMintTokens:test_releaseOrMintTokens_InvalidEVMAddress_Revert() (gas: 44612) -EVM2EVMMultiOffRamp_releaseOrMintTokens:test_releaseOrMintTokens_PoolDoesNotSupportDest_Reverts() (gas: 80519) -EVM2EVMMultiOffRamp_releaseOrMintTokens:test_releaseOrMintTokens_Success() (gas: 185299) -EVM2EVMMultiOffRamp_releaseOrMintTokens:test_releaseOrMintTokens_destDenominatedDecimals_Success() (gas: 283897) -EVM2EVMMultiOffRamp_resetUnblessedRoots:test_OnlyOwner_Revert() (gas: 11420) -EVM2EVMMultiOffRamp_resetUnblessedRoots:test_ResetUnblessedRoots_Success() (gas: 215247) -EVM2EVMMultiOffRamp_setDynamicConfig:test_NonOwner_Revert() (gas: 14223) -EVM2EVMMultiOffRamp_setDynamicConfig:test_PriceRegistryZeroAddress_Revert() (gas: 11729) -EVM2EVMMultiOffRamp_setDynamicConfig:test_RouterZeroAddress_Revert() (gas: 13885) -EVM2EVMMultiOffRamp_setDynamicConfig:test_SetDynamicConfigWithValidator_Success() (gas: 55608) -EVM2EVMMultiOffRamp_setDynamicConfig:test_SetDynamicConfig_Success() (gas: 33618) -EVM2EVMMultiOffRamp_trialExecute:test_RateLimitError_Success() (gas: 242740) -EVM2EVMMultiOffRamp_trialExecute:test_TokenHandlingErrorIsCaught_Success() (gas: 251403) -EVM2EVMMultiOffRamp_trialExecute:test_TokenPoolIsNotAContract_Success() (gas: 308206) -EVM2EVMMultiOffRamp_trialExecute:test_trialExecute_Success() (gas: 285105) -EVM2EVMMultiOffRamp_verify:test_Blessed_Success() (gas: 176538) -EVM2EVMMultiOffRamp_verify:test_NotBlessedWrongChainSelector_Success() (gas: 178609) -EVM2EVMMultiOffRamp_verify:test_NotBlessed_Success() (gas: 141470) -EVM2EVMMultiOffRamp_verify:test_TooManyLeaves_Revert() (gas: 51507) -EVM2EVMMultiOnRamp_applyDestChainConfigUpdates:test_InvalidDestBytesOverhead_Revert() (gas: 33833) -EVM2EVMMultiOnRamp_applyDestChainConfigUpdates:test_InvalidDestChainConfigDestChainSelectorEqZero_Revert() (gas: 16646) -EVM2EVMMultiOnRamp_applyDestChainConfigUpdates:test_InvalidDestChainConfigNewPrevOnRampOnExistingChain_Revert() (gas: 30796) -EVM2EVMMultiOnRamp_applyDestChainConfigUpdates:test_applyDestChainConfigUpdatesDefaultTxGasLimitEqZero() (gas: 16696) -EVM2EVMMultiOnRamp_applyDestChainConfigUpdates:test_applyDestChainConfigUpdatesZeroIntput() (gas: 12313) -EVM2EVMMultiOnRamp_applyDestChainConfigUpdates:test_applyDestChainConfigUpdates_Success() (gas: 174714) -EVM2EVMMultiOnRamp_applyTokenTransferFeeConfigUpdates:test_ApplyTokenTransferFeeConfig_Success() (gas: 86785) -EVM2EVMMultiOnRamp_applyTokenTransferFeeConfigUpdates:test_ApplyTokenTransferFeeZeroInput() (gas: 13089) -EVM2EVMMultiOnRamp_applyTokenTransferFeeConfigUpdates:test_InvalidDestBytesOverhead_Revert() (gas: 17084) -EVM2EVMMultiOnRamp_applyTokenTransferFeeConfigUpdates:test_OnlyCallableByOwnerOrAdmin_Revert() (gas: 12268) -EVM2EVMMultiOnRamp_constructor:test_Constructor_InvalidConfigLinkChainSelectorEqZero_Revert() (gas: 142392) -EVM2EVMMultiOnRamp_constructor:test_Constructor_InvalidConfigLinkTokenEqAddressZero_Revert() (gas: 138007) -EVM2EVMMultiOnRamp_constructor:test_Constructor_InvalidConfigNonceManagerEqAddressZero_Revert() (gas: 140293) -EVM2EVMMultiOnRamp_constructor:test_Constructor_InvalidConfigRMNProxyEqAddressZero_Revert() (gas: 145314) -EVM2EVMMultiOnRamp_constructor:test_Constructor_InvalidConfigTokenAdminRegistryEqAddressZero_Revert() (gas: 140360) -EVM2EVMMultiOnRamp_constructor:test_Constructor_Success() (gas: 4895404) -EVM2EVMMultiOnRamp_forwardFromRouter:test_CannotSendZeroTokens_Revert() (gas: 96382) -EVM2EVMMultiOnRamp_forwardFromRouter:test_EnforceOutOfOrder_Revert() (gas: 102566) -EVM2EVMMultiOnRamp_forwardFromRouter:test_ForwardFromRouterExtraArgsV2AllowOutOfOrderTrue_Success() (gas: 125845) -EVM2EVMMultiOnRamp_forwardFromRouter:test_ForwardFromRouterExtraArgsV2_Success() (gas: 156591) -EVM2EVMMultiOnRamp_forwardFromRouter:test_ForwardFromRouterSuccessCustomExtraArgs() (gas: 156102) -EVM2EVMMultiOnRamp_forwardFromRouter:test_ForwardFromRouterSuccessEmptyExtraArgs() (gas: 156332) -EVM2EVMMultiOnRamp_forwardFromRouter:test_ForwardFromRouterSuccessLegacyExtraArgs() (gas: 156357) -EVM2EVMMultiOnRamp_forwardFromRouter:test_ForwardFromRouter_Success() (gas: 155802) -EVM2EVMMultiOnRamp_forwardFromRouter:test_InvalidAddressEncodePacked_Revert() (gas: 35086) -EVM2EVMMultiOnRamp_forwardFromRouter:test_InvalidAddress_Revert() (gas: 35347) +EVM2EVMMultiOffRamp__releaseOrMintSingleToken:test__releaseOrMintSingleToken_NotACompatiblePool_Revert() (gas: 38220) +EVM2EVMMultiOffRamp__releaseOrMintSingleToken:test__releaseOrMintSingleToken_Success() (gas: 108400) +EVM2EVMMultiOffRamp__releaseOrMintSingleToken:test__releaseOrMintSingleToken_TokenHandlingError_revert_Revert() (gas: 116868) +EVM2EVMMultiOffRamp_applySourceChainConfigUpdates:test_AddMultipleChains_Success() (gas: 460557) +EVM2EVMMultiOffRamp_applySourceChainConfigUpdates:test_AddNewChain_Success() (gas: 95536) +EVM2EVMMultiOffRamp_applySourceChainConfigUpdates:test_ApplyZeroUpdates_Success() (gas: 12460) +EVM2EVMMultiOffRamp_applySourceChainConfigUpdates:test_ReplaceExistingChainOnRamp_Revert() (gas: 90379) +EVM2EVMMultiOffRamp_applySourceChainConfigUpdates:test_ReplaceExistingChain_Success() (gas: 105577) +EVM2EVMMultiOffRamp_applySourceChainConfigUpdates:test_ZeroOnRampAddress_Revert() (gas: 15716) +EVM2EVMMultiOffRamp_applySourceChainConfigUpdates:test_ZeroSourceChainSelector_Revert() (gas: 13054) +EVM2EVMMultiOffRamp_batchExecute:test_MultipleReportsDifferentChains_Success() (gas: 298930) +EVM2EVMMultiOffRamp_batchExecute:test_MultipleReportsSameChain_Success() (gas: 240268) +EVM2EVMMultiOffRamp_batchExecute:test_MultipleReportsSkipDuplicate_Success() (gas: 158987) +EVM2EVMMultiOffRamp_batchExecute:test_OutOfBoundsGasLimitsAccess_Revert() (gas: 189548) +EVM2EVMMultiOffRamp_batchExecute:test_SingleReport_Success() (gas: 147703) +EVM2EVMMultiOffRamp_batchExecute:test_Unhealthy_Revert() (gas: 521990) +EVM2EVMMultiOffRamp_batchExecute:test_ZeroReports_Revert() (gas: 10456) +EVM2EVMMultiOffRamp_ccipReceive:test_Reverts() (gas: 15659) +EVM2EVMMultiOffRamp_commit:test_InvalidIntervalMinLargerThanMax_Revert() (gas: 67189) +EVM2EVMMultiOffRamp_commit:test_InvalidInterval_Revert() (gas: 59695) +EVM2EVMMultiOffRamp_commit:test_InvalidRootRevert() (gas: 58775) +EVM2EVMMultiOffRamp_commit:test_NoConfigWithOtherConfigPresent_Revert() (gas: 6544263) +EVM2EVMMultiOffRamp_commit:test_NoConfig_Revert() (gas: 6127493) +EVM2EVMMultiOffRamp_commit:test_OnlyGasPriceUpdates_Success() (gas: 108372) +EVM2EVMMultiOffRamp_commit:test_OnlyPriceUpdateStaleReport_Revert() (gas: 118374) +EVM2EVMMultiOffRamp_commit:test_OnlyTokenPriceUpdates_Success() (gas: 108415) +EVM2EVMMultiOffRamp_commit:test_PriceSequenceNumberCleared_Success() (gas: 353706) +EVM2EVMMultiOffRamp_commit:test_ReportAndPriceUpdate_Success() (gas: 161272) +EVM2EVMMultiOffRamp_commit:test_ReportOnlyRootSuccess_gas() (gas: 136244) +EVM2EVMMultiOffRamp_commit:test_RootAlreadyCommitted_Revert() (gas: 136825) +EVM2EVMMultiOffRamp_commit:test_SourceChainNotEnabled_Revert() (gas: 59043) +EVM2EVMMultiOffRamp_commit:test_StaleReportWithRoot_Success() (gas: 227655) +EVM2EVMMultiOffRamp_commit:test_UnauthorizedTransmitter_Revert() (gas: 119676) +EVM2EVMMultiOffRamp_commit:test_Unhealthy_Revert() (gas: 77602) +EVM2EVMMultiOffRamp_commit:test_ValidPriceUpdateThenStaleReportWithRoot_Success() (gas: 209115) +EVM2EVMMultiOffRamp_commit:test_WrongConfigWithoutSigners_Revert() (gas: 6538652) +EVM2EVMMultiOffRamp_commit:test_ZeroEpochAndRound_Revert() (gas: 47782) +EVM2EVMMultiOffRamp_constructor:test_Constructor_Success() (gas: 6127677) +EVM2EVMMultiOffRamp_constructor:test_SourceChainSelector_Revert() (gas: 104335) +EVM2EVMMultiOffRamp_constructor:test_ZeroChainSelector_Revert() (gas: 100260) +EVM2EVMMultiOffRamp_constructor:test_ZeroNonceManager_Revert() (gas: 98131) +EVM2EVMMultiOffRamp_constructor:test_ZeroOnRampAddress_Revert() (gas: 106841) +EVM2EVMMultiOffRamp_constructor:test_ZeroRMNProxy_Revert() (gas: 98030) +EVM2EVMMultiOffRamp_constructor:test_ZeroTokenAdminRegistry_Revert() (gas: 98097) +EVM2EVMMultiOffRamp_execute:test_IncorrectArrayType_Revert() (gas: 17277) +EVM2EVMMultiOffRamp_execute:test_LargeBatch_Success() (gas: 1563033) +EVM2EVMMultiOffRamp_execute:test_MultipleReportsWithPartialValidationFailures_Success() (gas: 343670) +EVM2EVMMultiOffRamp_execute:test_MultipleReports_Success() (gas: 260538) +EVM2EVMMultiOffRamp_execute:test_NoConfigWithOtherConfigPresent_Revert() (gas: 6594735) +EVM2EVMMultiOffRamp_execute:test_NoConfig_Revert() (gas: 6177684) +EVM2EVMMultiOffRamp_execute:test_NonArray_Revert() (gas: 27678) +EVM2EVMMultiOffRamp_execute:test_SingleReport_Success() (gas: 165299) +EVM2EVMMultiOffRamp_execute:test_UnauthorizedTransmitter_Revert() (gas: 149255) +EVM2EVMMultiOffRamp_execute:test_WrongConfigWithSigners_Revert() (gas: 6956810) +EVM2EVMMultiOffRamp_execute:test_ZeroReports_Revert() (gas: 17151) +EVM2EVMMultiOffRamp_executeSingleMessage:test_MessageSender_Revert() (gas: 18413) +EVM2EVMMultiOffRamp_executeSingleMessage:test_NonContractWithTokens_Success() (gas: 249488) +EVM2EVMMultiOffRamp_executeSingleMessage:test_NonContract_Success() (gas: 20672) +EVM2EVMMultiOffRamp_executeSingleMessage:test_TokenHandlingError_Revert() (gas: 201787) +EVM2EVMMultiOffRamp_executeSingleMessage:test_ZeroGasDONExecution_Revert() (gas: 48860) +EVM2EVMMultiOffRamp_executeSingleMessage:test_executeSingleMessage_NoTokens_Success() (gas: 48381) +EVM2EVMMultiOffRamp_executeSingleMessage:test_executeSingleMessage_WithFailingValidationNoRouterCall_Revert() (gas: 232668) +EVM2EVMMultiOffRamp_executeSingleMessage:test_executeSingleMessage_WithFailingValidation_Revert() (gas: 89262) +EVM2EVMMultiOffRamp_executeSingleMessage:test_executeSingleMessage_WithTokens_Success() (gas: 278263) +EVM2EVMMultiOffRamp_executeSingleMessage:test_executeSingleMessage_WithValidation_Success() (gas: 93485) +EVM2EVMMultiOffRamp_executeSingleReport:test_DisabledSourceChain_Revert() (gas: 35083) +EVM2EVMMultiOffRamp_executeSingleReport:test_EmptyReport_Revert() (gas: 23910) +EVM2EVMMultiOffRamp_executeSingleReport:test_InvalidSourcePoolAddress_Success() (gas: 451951) +EVM2EVMMultiOffRamp_executeSingleReport:test_ManualExecutionNotYetEnabled_Revert() (gas: 54472) +EVM2EVMMultiOffRamp_executeSingleReport:test_MismatchingDestChainSelector_Revert() (gas: 35906) +EVM2EVMMultiOffRamp_executeSingleReport:test_MismatchingOnRampRoot_Revert() (gas: 154363) +EVM2EVMMultiOffRamp_executeSingleReport:test_NonExistingSourceChain_Revert() (gas: 35317) +EVM2EVMMultiOffRamp_executeSingleReport:test_ReceiverError_Success() (gas: 181628) +EVM2EVMMultiOffRamp_executeSingleReport:test_RetryFailedMessageWithoutManualExecution_Revert() (gas: 190908) +EVM2EVMMultiOffRamp_executeSingleReport:test_RootNotCommitted_Revert() (gas: 48050) +EVM2EVMMultiOffRamp_executeSingleReport:test_RouterYULCall_Revert() (gas: 436547) +EVM2EVMMultiOffRamp_executeSingleReport:test_SingleMessageNoTokensOtherChain_Success() (gas: 252012) +EVM2EVMMultiOffRamp_executeSingleReport:test_SingleMessageNoTokensUnordered_Success() (gas: 174226) +EVM2EVMMultiOffRamp_executeSingleReport:test_SingleMessageNoTokens_Success() (gas: 193899) +EVM2EVMMultiOffRamp_executeSingleReport:test_SingleMessageToNonCCIPReceiver_Success() (gas: 259769) +EVM2EVMMultiOffRamp_executeSingleReport:test_SingleMessagesNoTokensSuccess_gas() (gas: 129695) +EVM2EVMMultiOffRamp_executeSingleReport:test_SkippedIncorrectNonceStillExecutes_Success() (gas: 391966) +EVM2EVMMultiOffRamp_executeSingleReport:test_SkippedIncorrectNonce_Success() (gas: 65914) +EVM2EVMMultiOffRamp_executeSingleReport:test_TokenDataMismatch_Revert() (gas: 80970) +EVM2EVMMultiOffRamp_executeSingleReport:test_TwoMessagesWithTokensAndGE_Success() (gas: 535883) +EVM2EVMMultiOffRamp_executeSingleReport:test_TwoMessagesWithTokensSuccess_gas() (gas: 480816) +EVM2EVMMultiOffRamp_executeSingleReport:test_UnexpectedTokenData_Revert() (gas: 35763) +EVM2EVMMultiOffRamp_executeSingleReport:test_UnhealthySingleChainCurse_Revert() (gas: 520826) +EVM2EVMMultiOffRamp_executeSingleReport:test_Unhealthy_Revert() (gas: 518194) +EVM2EVMMultiOffRamp_executeSingleReport:test_WithCurseOnAnotherSourceChain_Success() (gas: 488330) +EVM2EVMMultiOffRamp_executeSingleReport:test__execute_SkippedAlreadyExecutedMessageUnordered_Success() (gas: 128053) +EVM2EVMMultiOffRamp_executeSingleReport:test__execute_SkippedAlreadyExecutedMessage_Success() (gas: 157265) +EVM2EVMMultiOffRamp_getExecutionState:test_FillExecutionState_Success() (gas: 3650714) +EVM2EVMMultiOffRamp_getExecutionState:test_GetDifferentChainExecutionState_Success() (gas: 118188) +EVM2EVMMultiOffRamp_getExecutionState:test_GetExecutionState_Success() (gas: 87407) +EVM2EVMMultiOffRamp_manuallyExecute:test_ManualExecGasLimitMismatchSingleReport_Revert() (gas: 75594) +EVM2EVMMultiOffRamp_manuallyExecute:test_ManualExecInvalidGasLimit_Revert() (gas: 26473) +EVM2EVMMultiOffRamp_manuallyExecute:test_manuallyExecute_DoesNotRevertIfUntouched_Success() (gas: 163355) +EVM2EVMMultiOffRamp_manuallyExecute:test_manuallyExecute_FailedTx_Revert() (gas: 207956) +EVM2EVMMultiOffRamp_manuallyExecute:test_manuallyExecute_ForkedChain_Revert() (gas: 26001) +EVM2EVMMultiOffRamp_manuallyExecute:test_manuallyExecute_GasLimitMismatchMultipleReports_Revert() (gas: 152913) +EVM2EVMMultiOffRamp_manuallyExecute:test_manuallyExecute_LowGasLimit_Success() (gas: 507883) +EVM2EVMMultiOffRamp_manuallyExecute:test_manuallyExecute_ReentrancyFails() (gas: 2308282) +EVM2EVMMultiOffRamp_manuallyExecute:test_manuallyExecute_Success() (gas: 210050) +EVM2EVMMultiOffRamp_manuallyExecute:test_manuallyExecute_WithGasOverride_Success() (gas: 210613) +EVM2EVMMultiOffRamp_manuallyExecute:test_manuallyExecute_WithMultiReportGasOverride_Success() (gas: 670625) +EVM2EVMMultiOffRamp_manuallyExecute:test_manuallyExecute_WithPartialMessages_Success() (gas: 300122) +EVM2EVMMultiOffRamp_releaseOrMintTokens:test_TokenHandlingError_Reverts() (gas: 160712) +EVM2EVMMultiOffRamp_releaseOrMintTokens:test__releaseOrMintTokens_PoolIsNotAPool_Reverts() (gas: 24152) +EVM2EVMMultiOffRamp_releaseOrMintTokens:test_releaseOrMintTokens_InvalidDataLengthReturnData_Revert() (gas: 59156) +EVM2EVMMultiOffRamp_releaseOrMintTokens:test_releaseOrMintTokens_InvalidEVMAddress_Revert() (gas: 40402) +EVM2EVMMultiOffRamp_releaseOrMintTokens:test_releaseOrMintTokens_PoolDoesNotSupportDest_Reverts() (gas: 76181) +EVM2EVMMultiOffRamp_releaseOrMintTokens:test_releaseOrMintTokens_Success() (gas: 179068) +EVM2EVMMultiOffRamp_releaseOrMintTokens:test_releaseOrMintTokens_destDenominatedDecimals_Success() (gas: 278906) +EVM2EVMMultiOffRamp_resetUnblessedRoots:test_OnlyOwner_Revert() (gas: 11376) +EVM2EVMMultiOffRamp_resetUnblessedRoots:test_ResetUnblessedRoots_Success() (gas: 215403) +EVM2EVMMultiOffRamp_setDynamicConfig:test_NonOwner_Revert() (gas: 14289) +EVM2EVMMultiOffRamp_setDynamicConfig:test_PriceRegistryZeroAddress_Revert() (gas: 11795) +EVM2EVMMultiOffRamp_setDynamicConfig:test_RouterZeroAddress_Revert() (gas: 13951) +EVM2EVMMultiOffRamp_setDynamicConfig:test_SetDynamicConfigWithValidator_Success() (gas: 55630) +EVM2EVMMultiOffRamp_setDynamicConfig:test_SetDynamicConfig_Success() (gas: 33640) +EVM2EVMMultiOffRamp_trialExecute:test_RateLimitError_Success() (gas: 238383) +EVM2EVMMultiOffRamp_trialExecute:test_TokenHandlingErrorIsCaught_Success() (gas: 247046) +EVM2EVMMultiOffRamp_trialExecute:test_TokenPoolIsNotAContract_Success() (gas: 300337) +EVM2EVMMultiOffRamp_trialExecute:test_trialExecute_Success() (gas: 280699) +EVM2EVMMultiOffRamp_verify:test_Blessed_Success() (gas: 176601) +EVM2EVMMultiOffRamp_verify:test_NotBlessedWrongChainSelector_Success() (gas: 178672) +EVM2EVMMultiOffRamp_verify:test_NotBlessed_Success() (gas: 141533) +EVM2EVMMultiOffRamp_verify:test_TooManyLeaves_Revert() (gas: 51505) +EVM2EVMMultiOnRamp_applyDestChainConfigUpdates:test_InvalidChainFamilySelector_Revert() (gas: 16917) +EVM2EVMMultiOnRamp_applyDestChainConfigUpdates:test_InvalidDestBytesOverhead_Revert() (gas: 34341) +EVM2EVMMultiOnRamp_applyDestChainConfigUpdates:test_InvalidDestChainConfigDestChainSelectorEqZero_Revert() (gas: 16770) +EVM2EVMMultiOnRamp_applyDestChainConfigUpdates:test_InvalidDestChainConfigNewPrevOnRampOnExistingChain_Revert() (gas: 31306) +EVM2EVMMultiOnRamp_applyDestChainConfigUpdates:test_applyDestChainConfigUpdatesDefaultTxGasLimitEqZero_Revert() (gas: 16842) +EVM2EVMMultiOnRamp_applyDestChainConfigUpdates:test_applyDestChainConfigUpdatesZeroIntput() (gas: 12336) +EVM2EVMMultiOnRamp_applyDestChainConfigUpdates:test_applyDestChainConfigUpdates_Success() (gas: 177813) +EVM2EVMMultiOnRamp_applyPremiumMultiplierWeiPerEthUpdates:test_OnlyCallableByOwnerOrAdmin_Revert() (gas: 11449) +EVM2EVMMultiOnRamp_applyPremiumMultiplierWeiPerEthUpdates:test_applyPremiumMultiplierWeiPerEthUpdatesMultipleTokens_Success() (gas: 54003) +EVM2EVMMultiOnRamp_applyPremiumMultiplierWeiPerEthUpdates:test_applyPremiumMultiplierWeiPerEthUpdatesSingleToken_Success() (gas: 44785) +EVM2EVMMultiOnRamp_applyPremiumMultiplierWeiPerEthUpdates:test_applyPremiumMultiplierWeiPerEthUpdatesZeroInput() (gas: 12302) +EVM2EVMMultiOnRamp_applyTokenTransferFeeConfigUpdates:test_ApplyTokenTransferFeeConfig_Success() (gas: 86917) +EVM2EVMMultiOnRamp_applyTokenTransferFeeConfigUpdates:test_ApplyTokenTransferFeeZeroInput() (gas: 13111) +EVM2EVMMultiOnRamp_applyTokenTransferFeeConfigUpdates:test_InvalidDestBytesOverhead_Revert() (gas: 17106) +EVM2EVMMultiOnRamp_applyTokenTransferFeeConfigUpdates:test_OnlyCallableByOwnerOrAdmin_Revert() (gas: 12290) +EVM2EVMMultiOnRamp_constructor:test_Constructor_InvalidConfigLinkChainSelectorEqZero_Revert() (gas: 143541) +EVM2EVMMultiOnRamp_constructor:test_Constructor_InvalidConfigLinkTokenEqAddressZero_Revert() (gas: 139156) +EVM2EVMMultiOnRamp_constructor:test_Constructor_InvalidConfigNonceManagerEqAddressZero_Revert() (gas: 141442) +EVM2EVMMultiOnRamp_constructor:test_Constructor_InvalidConfigRMNProxyEqAddressZero_Revert() (gas: 146463) +EVM2EVMMultiOnRamp_constructor:test_Constructor_InvalidConfigTokenAdminRegistryEqAddressZero_Revert() (gas: 141509) +EVM2EVMMultiOnRamp_constructor:test_Constructor_Success() (gas: 5447902) +EVM2EVMMultiOnRamp_convertParsedExtraArgs:test_EVMExtraArgsDefault_Success() (gas: 18235) +EVM2EVMMultiOnRamp_convertParsedExtraArgs:test_EVMExtraArgsEnforceOutOfOrder_Revert() (gas: 22580) +EVM2EVMMultiOnRamp_convertParsedExtraArgs:test_EVMExtraArgsGasLimitTooHigh_Revert() (gas: 19677) +EVM2EVMMultiOnRamp_convertParsedExtraArgs:test_EVMExtraArgsInvalidExtraArgsTag_Revert() (gas: 19202) +EVM2EVMMultiOnRamp_convertParsedExtraArgs:test_EVMExtraArgsV1_Success() (gas: 19292) +EVM2EVMMultiOnRamp_convertParsedExtraArgs:test_EVMExtraArgsV2_Success() (gas: 19431) +EVM2EVMMultiOnRamp_convertParsedExtraArgs:test_EmptyExtraArgs_Success() (gas: 19657) +EVM2EVMMultiOnRamp_forwardFromRouter:test_CannotSendZeroTokens_Revert() (gas: 105130) +EVM2EVMMultiOnRamp_forwardFromRouter:test_EnforceOutOfOrder_Revert() (gas: 109612) +EVM2EVMMultiOnRamp_forwardFromRouter:test_ForwardFromRouterExtraArgsV2AllowOutOfOrderTrue_Success() (gas: 138405) +EVM2EVMMultiOnRamp_forwardFromRouter:test_ForwardFromRouterExtraArgsV2_Success() (gas: 169217) +EVM2EVMMultiOnRamp_forwardFromRouter:test_ForwardFromRouterSuccessCustomExtraArgs() (gas: 168640) +EVM2EVMMultiOnRamp_forwardFromRouter:test_ForwardFromRouterSuccessEmptyExtraArgs() (gas: 168046) +EVM2EVMMultiOnRamp_forwardFromRouter:test_ForwardFromRouterSuccessLegacyExtraArgs() (gas: 168914) +EVM2EVMMultiOnRamp_forwardFromRouter:test_ForwardFromRouter_Success() (gas: 168275) +EVM2EVMMultiOnRamp_forwardFromRouter:test_InvalidAddressEncodePacked_Revert() (gas: 31992) EVM2EVMMultiOnRamp_forwardFromRouter:test_InvalidChainSelector_Revert() (gas: 27934) -EVM2EVMMultiOnRamp_forwardFromRouter:test_InvalidExtraArgsTag_Revert() (gas: 29913) -EVM2EVMMultiOnRamp_forwardFromRouter:test_MessageGasLimitTooHigh_Revert() (gas: 31675) -EVM2EVMMultiOnRamp_forwardFromRouter:test_MessageTooLarge_Revert() (gas: 110363) -EVM2EVMMultiOnRamp_forwardFromRouter:test_MessageValidationError_Revert() (gas: 152538) -EVM2EVMMultiOnRamp_forwardFromRouter:test_MesssageFeeTooHigh_Revert() (gas: 34127) -EVM2EVMMultiOnRamp_forwardFromRouter:test_OriginalSender_Revert() (gas: 23037) -EVM2EVMMultiOnRamp_forwardFromRouter:test_Paused_Revert() (gas: 41465) -EVM2EVMMultiOnRamp_forwardFromRouter:test_Permissions_Revert() (gas: 25882) -EVM2EVMMultiOnRamp_forwardFromRouter:test_ShouldIncrementNonceOnlyOnOrdered_Success() (gas: 204962) -EVM2EVMMultiOnRamp_forwardFromRouter:test_ShouldIncrementSeqNumAndNonce_Success() (gas: 231235) -EVM2EVMMultiOnRamp_forwardFromRouter:test_ShouldStoreLinkFees() (gas: 142548) -EVM2EVMMultiOnRamp_forwardFromRouter:test_ShouldStoreNonLinkFees() (gas: 169489) -EVM2EVMMultiOnRamp_forwardFromRouter:test_SourceTokenDataTooLarge_Revert() (gas: 3778765) -EVM2EVMMultiOnRamp_forwardFromRouter:test_TooManyTokens_Revert() (gas: 32857) -EVM2EVMMultiOnRamp_forwardFromRouter:test_Unhealthy_Revert() (gas: 44213) -EVM2EVMMultiOnRamp_forwardFromRouter:test_UnsupportedToken_Revert() (gas: 135415) -EVM2EVMMultiOnRamp_forwardFromRouter:test_ZeroAddressReceiver_Revert() (gas: 281847) -EVM2EVMMultiOnRamp_forwardFromRouter:test_forwardFromRouter_UnsupportedToken_Revert() (gas: 98476) -EVM2EVMMultiOnRamp_forwardFromRouter:test_forwardFromRouter_WithValidation_Success() (gas: 274979) -EVM2EVMMultiOnRamp_getDataAvailabilityCost:test_EmptyMessageCalculatesDataAvailabilityCost_Success() (gas: 124845) -EVM2EVMMultiOnRamp_getDataAvailabilityCost:test_SimpleMessageCalculatesDataAvailabilityCostUnsupportedDestChainSelector_Success() (gas: 11969) -EVM2EVMMultiOnRamp_getDataAvailabilityCost:test_SimpleMessageCalculatesDataAvailabilityCost_Success() (gas: 24662) -EVM2EVMMultiOnRamp_getFee:test_DestinationChainNotEnabled_Revert() (gas: 15458) -EVM2EVMMultiOnRamp_getFee:test_EmptyMessage_Success() (gas: 92893) -EVM2EVMMultiOnRamp_getFee:test_HighGasMessage_Success() (gas: 249487) -EVM2EVMMultiOnRamp_getFee:test_MessageGasLimitTooHigh_Revert() (gas: 19150) -EVM2EVMMultiOnRamp_getFee:test_MessageTooLarge_Revert() (gas: 97779) -EVM2EVMMultiOnRamp_getFee:test_MessageWithDataAndTokenTransfer_Success() (gas: 173747) -EVM2EVMMultiOnRamp_getFee:test_NotAFeeToken_Revert() (gas: 22291) -EVM2EVMMultiOnRamp_getFee:test_SingleTokenMessage_Success() (gas: 132322) -EVM2EVMMultiOnRamp_getFee:test_TooManyTokens_Revert() (gas: 20287) -EVM2EVMMultiOnRamp_getFee:test_ZeroDataAvailabilityMultiplier_Success() (gas: 78672) -EVM2EVMMultiOnRamp_getSupportedTokens:test_GetSupportedTokens_Revert() (gas: 10438) -EVM2EVMMultiOnRamp_getTokenPool:test_GetTokenPool_Success() (gas: 35261) -EVM2EVMMultiOnRamp_getTokenTransferCost:test_CustomTokenBpsFee_Success() (gas: 47297) -EVM2EVMMultiOnRamp_getTokenTransferCost:test_FeeTokenBpsFee_Success() (gas: 35287) -EVM2EVMMultiOnRamp_getTokenTransferCost:test_LargeTokenTransferChargesMaxFeeAndGas_Success() (gas: 28361) -EVM2EVMMultiOnRamp_getTokenTransferCost:test_MixedTokenTransferFee_Success() (gas: 129897) -EVM2EVMMultiOnRamp_getTokenTransferCost:test_NoTokenTransferChargesZeroFee_Success() (gas: 15253) -EVM2EVMMultiOnRamp_getTokenTransferCost:test_SmallTokenTransferChargesMinFeeAndGas_Success() (gas: 28169) -EVM2EVMMultiOnRamp_getTokenTransferCost:test_UnsupportedToken_Revert() (gas: 21263) -EVM2EVMMultiOnRamp_getTokenTransferCost:test_WETHTokenBpsFee_Success() (gas: 41222) -EVM2EVMMultiOnRamp_getTokenTransferCost:test_ZeroAmountTokenTransferChargesMinFeeAndGas_Success() (gas: 28192) -EVM2EVMMultiOnRamp_getTokenTransferCost:test_ZeroFeeConfigChargesMinFee_Success() (gas: 40577) -EVM2EVMMultiOnRamp_getTokenTransferCost:test_getTokenTransferCost_selfServeUsesDefaults_Success() (gas: 31690) -EVM2EVMMultiOnRamp_setDynamicConfig:test_SetConfigInvalidConfigFeeAggregatorEqAddressZero_Revert() (gas: 11334) -EVM2EVMMultiOnRamp_setDynamicConfig:test_SetConfigInvalidConfigPriceRegistryEqAddressZero_Revert() (gas: 12934) -EVM2EVMMultiOnRamp_setDynamicConfig:test_SetConfigInvalidConfig_Revert() (gas: 11291) -EVM2EVMMultiOnRamp_setDynamicConfig:test_SetConfigOnlyOwner_Revert() (gas: 16243) -EVM2EVMMultiOnRamp_setDynamicConfig:test_SetDynamicConfig_Success() (gas: 60038) -EVM2EVMMultiOnRamp_withdrawFeeTokens:test_WithdrawFeeTokens_Success() (gas: 97129) +EVM2EVMMultiOnRamp_forwardFromRouter:test_InvalidEVMAddressDestToken_Revert() (gas: 3525966) +EVM2EVMMultiOnRamp_forwardFromRouter:test_InvalidEVMAddress_Revert() (gas: 32232) +EVM2EVMMultiOnRamp_forwardFromRouter:test_InvalidExtraArgsTag_Revert() (gas: 37388) +EVM2EVMMultiOnRamp_forwardFromRouter:test_MessageGasLimitTooHigh_Revert() (gas: 38351) +EVM2EVMMultiOnRamp_forwardFromRouter:test_MessageTooLarge_Revert() (gas: 107541) +EVM2EVMMultiOnRamp_forwardFromRouter:test_MessageValidationError_Revert() (gas: 152145) +EVM2EVMMultiOnRamp_forwardFromRouter:test_MesssageFeeTooHigh_Revert() (gas: 33678) +EVM2EVMMultiOnRamp_forwardFromRouter:test_OriginalSender_Revert() (gas: 23015) +EVM2EVMMultiOnRamp_forwardFromRouter:test_Paused_Revert() (gas: 41487) +EVM2EVMMultiOnRamp_forwardFromRouter:test_Permissions_Revert() (gas: 25860) +EVM2EVMMultiOnRamp_forwardFromRouter:test_ShouldIncrementNonceOnlyOnOrdered_Success() (gas: 244399) +EVM2EVMMultiOnRamp_forwardFromRouter:test_ShouldIncrementSeqNumAndNonce_Success() (gas: 270405) +EVM2EVMMultiOnRamp_forwardFromRouter:test_ShouldStoreLinkFees() (gas: 152708) +EVM2EVMMultiOnRamp_forwardFromRouter:test_ShouldStoreNonLinkFees() (gas: 179642) +EVM2EVMMultiOnRamp_forwardFromRouter:test_SourceTokenDataTooLarge_Revert() (gas: 3821022) +EVM2EVMMultiOnRamp_forwardFromRouter:test_TooManyTokens_Revert() (gas: 29961) +EVM2EVMMultiOnRamp_forwardFromRouter:test_Unhealthy_Revert() (gas: 44169) +EVM2EVMMultiOnRamp_forwardFromRouter:test_UnsupportedToken_Revert() (gas: 144163) +EVM2EVMMultiOnRamp_forwardFromRouter:test_ZeroAddressReceiver_Revert() (gas: 250642) +EVM2EVMMultiOnRamp_forwardFromRouter:test_forwardFromRouter_UnsupportedToken_Revert() (gas: 107224) +EVM2EVMMultiOnRamp_forwardFromRouter:test_forwardFromRouter_WithValidation_Success() (gas: 283983) +EVM2EVMMultiOnRamp_getDataAvailabilityCost:test_EmptyMessageCalculatesDataAvailabilityCost_Success() (gas: 126323) +EVM2EVMMultiOnRamp_getDataAvailabilityCost:test_SimpleMessageCalculatesDataAvailabilityCostUnsupportedDestChainSelector_Success() (gas: 12013) +EVM2EVMMultiOnRamp_getDataAvailabilityCost:test_SimpleMessageCalculatesDataAvailabilityCost_Success() (gas: 24974) +EVM2EVMMultiOnRamp_getFee:test_DestinationChainNotEnabled_Revert() (gas: 15422) +EVM2EVMMultiOnRamp_getFee:test_EmptyMessage_Success() (gas: 102285) +EVM2EVMMultiOnRamp_getFee:test_HighGasMessage_Success() (gas: 258923) +EVM2EVMMultiOnRamp_getFee:test_MessageGasLimitTooHigh_Revert() (gas: 42468) +EVM2EVMMultiOnRamp_getFee:test_MessageTooLarge_Revert() (gas: 95014) +EVM2EVMMultiOnRamp_getFee:test_MessageWithDataAndTokenTransfer_Success() (gas: 183629) +EVM2EVMMultiOnRamp_getFee:test_NotAFeeToken_Revert() (gas: 21863) +EVM2EVMMultiOnRamp_getFee:test_SingleTokenMessage_Success() (gas: 141949) +EVM2EVMMultiOnRamp_getFee:test_TooManyTokens_Revert() (gas: 17439) +EVM2EVMMultiOnRamp_getFee:test_ZeroDataAvailabilityMultiplier_Success() (gas: 84431) +EVM2EVMMultiOnRamp_getSupportedTokens:test_GetSupportedTokens_Revert() (gas: 10460) +EVM2EVMMultiOnRamp_getTokenPool:test_GetTokenPool_Success() (gas: 35195) +EVM2EVMMultiOnRamp_getTokenTransferCost:test_CustomTokenBpsFee_Success() (gas: 47357) +EVM2EVMMultiOnRamp_getTokenTransferCost:test_FeeTokenBpsFee_Success() (gas: 35335) +EVM2EVMMultiOnRamp_getTokenTransferCost:test_LargeTokenTransferChargesMaxFeeAndGas_Success() (gas: 28409) +EVM2EVMMultiOnRamp_getTokenTransferCost:test_MixedTokenTransferFee_Success() (gas: 130389) +EVM2EVMMultiOnRamp_getTokenTransferCost:test_NoTokenTransferChargesZeroFee_Success() (gas: 15255) +EVM2EVMMultiOnRamp_getTokenTransferCost:test_SmallTokenTransferChargesMinFeeAndGas_Success() (gas: 28217) +EVM2EVMMultiOnRamp_getTokenTransferCost:test_UnsupportedToken_Revert() (gas: 21265) +EVM2EVMMultiOnRamp_getTokenTransferCost:test_WETHTokenBpsFee_Success() (gas: 41270) +EVM2EVMMultiOnRamp_getTokenTransferCost:test_ZeroAmountTokenTransferChargesMinFeeAndGas_Success() (gas: 28240) +EVM2EVMMultiOnRamp_getTokenTransferCost:test_ZeroFeeConfigChargesMinFee_Success() (gas: 40601) +EVM2EVMMultiOnRamp_getTokenTransferCost:test_getTokenTransferCost_selfServeUsesDefaults_Success() (gas: 31720) +EVM2EVMMultiOnRamp_setDynamicConfig:test_SetConfigInvalidConfigFeeAggregatorEqAddressZero_Revert() (gas: 11378) +EVM2EVMMultiOnRamp_setDynamicConfig:test_SetConfigInvalidConfigPriceRegistryEqAddressZero_Revert() (gas: 12978) +EVM2EVMMultiOnRamp_setDynamicConfig:test_SetConfigInvalidConfig_Revert() (gas: 11335) +EVM2EVMMultiOnRamp_setDynamicConfig:test_SetConfigOnlyOwner_Revert() (gas: 16331) +EVM2EVMMultiOnRamp_setDynamicConfig:test_SetDynamicConfig_Success() (gas: 60104) +EVM2EVMMultiOnRamp_validateDestFamilyAddress:test_InvalidEVMAddress_Revert() (gas: 10851) +EVM2EVMMultiOnRamp_validateDestFamilyAddress:test_ValidEVMAddress_Success() (gas: 6711) +EVM2EVMMultiOnRamp_validateDestFamilyAddress:test_ValidNonEVMAddress_Success() (gas: 6417) +EVM2EVMMultiOnRamp_withdrawFeeTokens:test_WithdrawFeeTokens_Success() (gas: 97211) EVM2EVMOffRamp__releaseOrMintToken:test__releaseOrMintToken_NotACompatiblePool_Revert() (gas: 38076) EVM2EVMOffRamp__releaseOrMintToken:test__releaseOrMintToken_Success() (gas: 108251) EVM2EVMOffRamp__releaseOrMintToken:test__releaseOrMintToken_TokenHandlingError_revert_Revert() (gas: 116792) @@ -402,10 +418,6 @@ EVM2EVMOffRamp_setDynamicConfig:test_SetDynamicConfig_Success() (gas: 146778) EVM2EVMOffRamp_updateRateLimitTokens:test_updateRateLimitTokens_AddsAndRemoves_Success() (gas: 162464) EVM2EVMOffRamp_updateRateLimitTokens:test_updateRateLimitTokens_NonOwner_Revert() (gas: 16667) EVM2EVMOffRamp_updateRateLimitTokens:test_updateRateLimitTokens_Success() (gas: 197660) -EVM2EVMOnRamp_applyPremiumMultiplierWeiPerEthUpdates:test_OnlyCallableByOwnerOrAdmin_Revert() (gas: 11471) -EVM2EVMOnRamp_applyPremiumMultiplierWeiPerEthUpdates:test_applyPremiumMultiplierWeiPerEthUpdatesMultipleTokens_Success() (gas: 54025) -EVM2EVMOnRamp_applyPremiumMultiplierWeiPerEthUpdates:test_applyPremiumMultiplierWeiPerEthUpdatesSingleToken_Success() (gas: 44807) -EVM2EVMOnRamp_applyPremiumMultiplierWeiPerEthUpdates:test_applyPremiumMultiplierWeiPerEthUpdatesZeroInput() (gas: 12324) EVM2EVMOnRamp_constructor:test_Constructor_Success() (gas: 5635586) EVM2EVMOnRamp_forwardFromRouter:test_CannotSendZeroTokens_Revert() (gas: 35788) EVM2EVMOnRamp_forwardFromRouter:test_EnforceOutOfOrder_Revert() (gas: 99488) @@ -637,22 +649,22 @@ MultiOCR3Base_transmit:test_UnAuthorizedTransmitter_Revert() (gas: 24191) MultiOCR3Base_transmit:test_UnauthorizedSigner_Revert() (gas: 61409) MultiOCR3Base_transmit:test_UnconfiguredPlugin_Revert() (gas: 39890) MultiOCR3Base_transmit:test_ZeroSignatures_Revert() (gas: 32973) -MultiRampsE2E:test_E2E_3MessagesSuccess_gas() (gas: 1435700) +MultiRampsE2E:test_E2E_3MessagesSuccess_gas() (gas: 1457196) NonceManager_NonceIncrementation:test_getIncrementedOutboundNonce_Success() (gas: 37907) NonceManager_NonceIncrementation:test_incrementInboundNonce_Skip() (gas: 23694) NonceManager_NonceIncrementation:test_incrementInboundNonce_Success() (gas: 38763) NonceManager_NonceIncrementation:test_incrementNoncesInboundAndOutbound_Success() (gas: 71847) -NonceManager_OffRampUpgrade:test_NoPrevOffRampForChain_Success() (gas: 255244) -NonceManager_OffRampUpgrade:test_UpgradedNonceNewSenderStartsAtZero_Success() (gas: 257899) -NonceManager_OffRampUpgrade:test_UpgradedNonceStartsAtV1Nonce_Success() (gas: 313263) -NonceManager_OffRampUpgrade:test_UpgradedOffRampNonceSkipsIfMsgInFlight_Success() (gas: 295481) -NonceManager_OffRampUpgrade:test_UpgradedSenderNoncesReadsPreviousRampTransitive_Success() (gas: 248932) -NonceManager_OffRampUpgrade:test_UpgradedSenderNoncesReadsPreviousRamp_Success() (gas: 236918) -NonceManager_OffRampUpgrade:test_Upgraded_Success() (gas: 147192) -NonceManager_OnRampUpgrade:test_UpgradeNonceNewSenderStartsAtZero_Success() (gas: 173337) -NonceManager_OnRampUpgrade:test_UpgradeNonceStartsAtV1Nonce_Success() (gas: 217372) -NonceManager_OnRampUpgrade:test_UpgradeSenderNoncesReadsPreviousRamp_Success() (gas: 125707) -NonceManager_OnRampUpgrade:test_Upgrade_Success() (gas: 120818) +NonceManager_OffRampUpgrade:test_NoPrevOffRampForChain_Success() (gas: 252899) +NonceManager_OffRampUpgrade:test_UpgradedNonceNewSenderStartsAtZero_Success() (gas: 255229) +NonceManager_OffRampUpgrade:test_UpgradedNonceStartsAtV1Nonce_Success() (gas: 308391) +NonceManager_OffRampUpgrade:test_UpgradedOffRampNonceSkipsIfMsgInFlight_Success() (gas: 291386) +NonceManager_OffRampUpgrade:test_UpgradedSenderNoncesReadsPreviousRampTransitive_Success() (gas: 248762) +NonceManager_OffRampUpgrade:test_UpgradedSenderNoncesReadsPreviousRamp_Success() (gas: 236748) +NonceManager_OffRampUpgrade:test_Upgraded_Success() (gas: 144907) +NonceManager_OnRampUpgrade:test_UpgradeNonceNewSenderStartsAtZero_Success() (gas: 185811) +NonceManager_OnRampUpgrade:test_UpgradeNonceStartsAtV1Nonce_Success() (gas: 242327) +NonceManager_OnRampUpgrade:test_UpgradeSenderNoncesReadsPreviousRamp_Success() (gas: 125653) +NonceManager_OnRampUpgrade:test_Upgrade_Success() (gas: 133309) NonceManager_applyPreviousRampsUpdates:test_MultipleRampsUpdates() (gas: 122899) NonceManager_applyPreviousRampsUpdates:test_PreviousRampAlreadySetOffRamp_Revert() (gas: 42959) NonceManager_applyPreviousRampsUpdates:test_PreviousRampAlreadySetOnRampAndOffRamp_Revert() (gas: 64282) diff --git a/contracts/src/v0.8/ccip/libraries/Internal.sol b/contracts/src/v0.8/ccip/libraries/Internal.sol index 9068ca3a10..4b6d3daa16 100644 --- a/contracts/src/v0.8/ccip/libraries/Internal.sol +++ b/contracts/src/v0.8/ccip/libraries/Internal.sol @@ -58,7 +58,7 @@ library Internal { // The source pool address, abi encoded. This value is trusted as it was obtained through the onRamp. It can be // relied upon by the destination pool to validate the source pool. bytes sourcePoolAddress; - // The address of the destination token pool, abi encoded in the case of EVM chains + // The address of the destination token, abi encoded in the case of EVM chains // This value is UNTRUSTED as any pool owner can return whatever value they want. bytes destTokenAddress; // Optional pool data to be transferred to the destination chain. Be default this is capped at @@ -106,7 +106,6 @@ library Internal { bytes32 messageId; // a hash of the message data } - // TODO: create new const for EVM2AnyMessage /// @dev EVM2EVMMessage struct has 13 fields, including 3 variable arrays. /// Each variable array takes 1 more slot to store its length. /// When abi encoded, excluding array contents, @@ -114,11 +113,24 @@ library Internal { /// For structs that contain arrays, 1 more slot is added to the front, reaching a total of 17. uint256 public constant MESSAGE_FIXED_BYTES = 32 * 17; - // TODO: create new const for EVM2AnyMessage /// @dev Each token transfer adds 1 EVMTokenAmount and 1 bytes. /// When abiEncoded, each EVMTokenAmount takes 2 slots, each bytes takes 2 slots, excl bytes contents uint256 public constant MESSAGE_FIXED_BYTES_PER_TOKEN = 32 * 4; + /// @dev Any2EVMRampMessage struct has 10 fields, including 3 variable unnested arrays (data, receiver and tokenAmounts). + /// Each variable array takes 1 more slot to store its length. + /// When abi encoded, excluding array contents, + /// Any2EVMMessage takes up a fixed number of 13 slots, 32 bytes each. + /// For structs that contain arrays, 1 more slot is added to the front, reaching a total of 14. + /// The fixed bytes does not cover struct data (this is represented by ANY_2_EVM_MESSAGE_FIXED_BYTES_PER_TOKEN) + uint256 public constant ANY_2_EVM_MESSAGE_FIXED_BYTES = 32 * 14; + + /// @dev Each token transfer adds 1 RampTokenAmount + /// RampTokenAmount has 4 fields, including 3 bytes. + /// Each bytes takes 1 more slot to store its length. + /// When abi encoded, each token transfer takes up 7 slots, excl bytes contents. + uint256 public constant ANY_2_EVM_MESSAGE_FIXED_BYTES_PER_TOKEN = 32 * 7; + function _toAny2EVMMessage( EVM2EVMMessage memory original, Client.EVMTokenAmount[] memory destTokenAmounts @@ -167,6 +179,7 @@ library Internal { } bytes32 internal constant ANY_2_EVM_MESSAGE_HASH = keccak256("Any2EVMMessageHashV1"); + bytes32 internal constant EVM_2_ANY_MESSAGE_HASH = keccak256("EVM2AnyMessageHashV1"); /// @dev Used to hash messages for multi-lane family-agnostic OffRamps. /// OnRamp hash(EVM2AnyMessage) != Any2EVMRampMessage.messageId @@ -197,8 +210,31 @@ library Internal { ) ), keccak256(original.data), + keccak256(abi.encode(original.tokenAmounts)) + ) + ); + } + + function _hash(EVM2AnyRampMessage memory original, bytes32 metadataHash) internal pure returns (bytes32) { + // Fixed-size message fields are included in nested hash to reduce stack pressure. + // This hashing scheme is also used by RMN. If changing it, please notify the RMN maintainers. + return keccak256( + abi.encode( + MerkleMultiProof.LEAF_DOMAIN_SEPARATOR, + metadataHash, + keccak256( + abi.encode( + original.sender, + original.receiver, + original.header.sequenceNumber, + original.header.nonce, + original.feeToken, + original.feeTokenAmount + ) + ), + keccak256(original.data), keccak256(abi.encode(original.tokenAmounts)), - keccak256(abi.encode(original.sourceTokenData)) + keccak256(original.extraArgs) ) ); } @@ -246,9 +282,23 @@ library Internal { Execution } + /// @notice Family-agnostic token amounts used for both OnRamp & OffRamp messages + struct RampTokenAmount { + // The source pool address, abi encoded. This value is trusted as it was obtained through the onRamp. It can be + // relied upon by the destination pool to validate the source pool. + bytes sourcePoolAddress; + // The address of the destination token, abi encoded in the case of EVM chains + // This value is UNTRUSTED as any pool owner can return whatever value they want. + bytes destTokenAddress; + // Optional pool data to be transferred to the destination chain. Be default this is capped at + // CCIP_LOCK_OR_BURN_V1_RET_BYTES bytes. If more data is required, the TokenTransferFeeConfig.destBytesOverhead + // has to be set for the specific token. + bytes extraData; + uint256 amount; // Amount of tokens. + } + /// @notice Family-agnostic header for OnRamp & OffRamp messages. /// The messageId is not expected to match hash(message), since it may originate from another ramp family - // TODO: revisit if destChainSelector is required (likely sufficient to have it implicitly in the commit roots) struct RampMessageHeader { bytes32 messageId; // Unique identifier for the message, generated with the source chain's encoding scheme (i.e. not necessarily abi.encoded) uint64 sourceChainSelector; // ───────╮ the chain selector of the source chain, note: not chainId @@ -266,8 +316,23 @@ library Internal { bytes data; // arbitrary data payload supplied by the message sender address receiver; // receiver address on the destination chain uint256 gasLimit; // user supplied maximum gas amount available for dest chain execution - // TODO: revisit collapsing tokenAmounts + sourceTokenData into one struct array - Client.EVMTokenAmount[] tokenAmounts; // array of tokens and amounts to transfer - bytes[] sourceTokenData; // array of token data, one per token + RampTokenAmount[] tokenAmounts; // array of tokens and amounts to transfer } + + /// @notice Family-agnostic message emitted from the OnRamp + /// Note: hash(Any2EVMRampMessage) != hash(EVM2AnyRampMessage) due to encoding & parameter differences + /// messageId = hash(EVM2AnyRampMessage) using the source EVM chain's encoding format + struct EVM2AnyRampMessage { + RampMessageHeader header; // Message header + address sender; // sender address on the source chain + bytes data; // arbitrary data payload supplied by the message sender + bytes receiver; // receiver address on the destination chain + bytes extraArgs; // destination-chain specific extra args, such as the gasLimit for EVM chains + address feeToken; // fee token + uint256 feeTokenAmount; // fee token amount + RampTokenAmount[] tokenAmounts; // array of tokens and amounts to transfer + } + + // bytes4(keccak256("CCIP ChainFamilySelector EVM")) + bytes4 public constant CHAIN_FAMILY_SELECTOR_EVM = 0x2812d52c; } diff --git a/contracts/src/v0.8/ccip/offRamp/EVM2EVMMultiOffRamp.sol b/contracts/src/v0.8/ccip/offRamp/EVM2EVMMultiOffRamp.sol index 08c8086884..9d1664cd66 100644 --- a/contracts/src/v0.8/ccip/offRamp/EVM2EVMMultiOffRamp.sol +++ b/contracts/src/v0.8/ccip/offRamp/EVM2EVMMultiOffRamp.sol @@ -497,12 +497,7 @@ contract EVM2EVMMultiOffRamp is ITypeAndVersion, MultiOCR3Base { Client.EVMTokenAmount[] memory destTokenAmounts = new Client.EVMTokenAmount[](0); if (message.tokenAmounts.length > 0) { destTokenAmounts = _releaseOrMintTokens( - message.tokenAmounts, - message.sender, - message.receiver, - message.header.sourceChainSelector, - message.sourceTokenData, - offchainTokenData + message.tokenAmounts, message.sender, message.receiver, message.header.sourceChainSelector, offchainTokenData ); } @@ -787,25 +782,22 @@ contract EVM2EVMMultiOffRamp is ITypeAndVersion, MultiOCR3Base { /// @dev The local token address is validated through the TokenAdminRegistry. If, due to some misconfiguration, the /// token is unknown to the registry, the offRamp will revert. The tx, and the tokens, can be retrieved by /// registering the token on this chain, and re-trying the msg. - /// @param sourceAmount The amount of tokens to be released/minted. + /// @param sourceTokenAmount Amount and source data of the token to be released/minted. /// @param originalSender The message sender on the source chain. /// @param receiver The address that will receive the tokens. /// @param sourceChainSelector The remote source chain selector - /// @param sourceTokenData A struct containing the local token address, the source pool address and optional data - /// returned from the source pool. /// @param offchainTokenData Data fetched offchain by the DON. /// @return destTokenAmount local token address with amount function _releaseOrMintSingleToken( - uint256 sourceAmount, + Internal.RampTokenAmount memory sourceTokenAmount, bytes memory originalSender, address receiver, uint64 sourceChainSelector, - Internal.SourceTokenData memory sourceTokenData, bytes memory offchainTokenData ) internal returns (Client.EVMTokenAmount memory destTokenAmount) { // We need to safely decode the token address from the sourceTokenData, as it could be wrong, // in which case it doesn't have to be a valid EVM address. - address localToken = Internal._validateEVMAddress(sourceTokenData.destTokenAddress); + address localToken = Internal._validateEVMAddress(sourceTokenAmount.destTokenAddress); // We check with the token admin registry if the token has a pool on this chain. address localPoolAddress = ITokenAdminRegistry(i_tokenAdminRegistry).getPool(localToken); // This will call the supportsInterface through the ERC165Checker, and not directly on the pool address. @@ -827,11 +819,11 @@ contract EVM2EVMMultiOffRamp is ITypeAndVersion, MultiOCR3Base { Pool.ReleaseOrMintInV1({ originalSender: originalSender, receiver: receiver, - amount: sourceAmount, + amount: sourceTokenAmount.amount, localToken: localToken, remoteChainSelector: sourceChainSelector, - sourcePoolAddress: sourceTokenData.sourcePoolAddress, - sourcePoolData: sourceTokenData.extraData, + sourcePoolAddress: sourceTokenAmount.sourcePoolAddress, + sourcePoolData: sourceTokenAmount.extraData, offchainTokenData: offchainTokenData }) ), @@ -866,34 +858,26 @@ contract EVM2EVMMultiOffRamp is ITypeAndVersion, MultiOCR3Base { } /// @notice Uses pools to release or mint a number of different tokens to a receiver address. - /// @param sourceTokenAmounts List of tokens and amount values to be released/minted. + /// @param sourceTokenAmounts List of token amounts with source data of the tokens to be released/minted. /// @param originalSender The message sender on the source chain. /// @param receiver The address that will receive the tokens. /// @param sourceChainSelector The remote source chain selector - /// @param encodedSourceTokenData Encoded source token data, decoding to Internal.SourceTokenData /// @param offchainTokenData Array of token data fetched offchain by the DON. /// @return destTokenAmounts local token addresses with amounts /// @dev This function wrappes the token pool call in a try catch block to gracefully handle /// any non-rate limiting errors that may occur. If we encounter a rate limiting related error /// we bubble it up. If we encounter a non-rate limiting error we wrap it in a TokenHandlingError. function _releaseOrMintTokens( - Client.EVMTokenAmount[] memory sourceTokenAmounts, + Internal.RampTokenAmount[] memory sourceTokenAmounts, bytes memory originalSender, address receiver, uint64 sourceChainSelector, - bytes[] memory encodedSourceTokenData, bytes[] memory offchainTokenData ) internal returns (Client.EVMTokenAmount[] memory destTokenAmounts) { - // Creating a copy is more gas efficient than initializing a new array. - destTokenAmounts = sourceTokenAmounts; + destTokenAmounts = new Client.EVMTokenAmount[](sourceTokenAmounts.length); for (uint256 i = 0; i < sourceTokenAmounts.length; ++i) { destTokenAmounts[i] = _releaseOrMintSingleToken( - sourceTokenAmounts[i].amount, - originalSender, - receiver, - sourceChainSelector, - abi.decode(encodedSourceTokenData[i], (Internal.SourceTokenData)), - offchainTokenData[i] + sourceTokenAmounts[i], originalSender, receiver, sourceChainSelector, offchainTokenData[i] ); } diff --git a/contracts/src/v0.8/ccip/onRamp/EVM2EVMMultiOnRamp.sol b/contracts/src/v0.8/ccip/onRamp/EVM2EVMMultiOnRamp.sol index 130309a9a6..bcb7a74c5f 100644 --- a/contracts/src/v0.8/ccip/onRamp/EVM2EVMMultiOnRamp.sol +++ b/contracts/src/v0.8/ccip/onRamp/EVM2EVMMultiOnRamp.sol @@ -56,7 +56,7 @@ contract EVM2EVMMultiOnRamp is IEVM2AnyOnRampClient, ITypeAndVersion, OwnerIsCre ); event TokenTransferFeeConfigDeleted(uint64 indexed destChainSelector, address indexed token); /// RMN depends on this event, if changing, please notify the RMN maintainers. - event CCIPSendRequested(uint64 indexed destChainSelector, Internal.EVM2EVMMessage message); + event CCIPSendRequested(uint64 indexed destChainSelector, Internal.EVM2AnyRampMessage message); event DestChainAdded(uint64 indexed destChainSelector, DestChainConfig destChainConfig); event DestChainDynamicConfigUpdated(uint64 indexed destChainSelector, DestChainDynamicConfig dynamicConfig); @@ -140,7 +140,8 @@ contract EVM2EVMMultiOnRamp is IEVM2AnyOnRampClient, ITypeAndVersion, OwnerIsCre uint64 defaultTxGasLimit; // │ Default gas limit for a tx uint64 gasMultiplierWeiPerEth; // │ Multiplier for gas costs, 1e18 based so 11e17 = 10% extra cost. uint32 networkFeeUSDCents; // │ Flat network fee to charge for messages, multiples of 0.01 USD - bool enforceOutOfOrder; // ──────────────────╯ Whether to enforce the allowOutOfOrderExecution extraArg value to be true. + bool enforceOutOfOrder; // │ Whether to enforce the allowOutOfOrderExecution extraArg value to be true. + bytes4 chainFamilySelector; // ──────────────╯ Selector that identifies the destination chain's family. Used to determine the correct validations to perform for the dest chain. } /// @dev Struct to hold the configs for a destination chain @@ -249,7 +250,7 @@ contract EVM2EVMMultiOnRamp is IEVM2AnyOnRampClient, ITypeAndVersion, OwnerIsCre address originalSender ) external returns (bytes32) { DestChainConfig storage destChainConfig = s_destChainConfig[destChainSelector]; - Internal.EVM2EVMMessage memory newMessage = + Internal.EVM2AnyRampMessage memory newMessage = _generateNewMessage(destChainConfig, destChainSelector, message, feeTokenAmount, originalSender); // Lock the tokens as last step. TokenPools may not always be trusted. @@ -287,26 +288,25 @@ contract EVM2EVMMultiOnRamp is IEVM2AnyOnRampClient, ITypeAndVersion, OwnerIsCre ) { revert SourceTokenDataTooLarge(tokenAndAmount.token); } - // We validate the token address to ensure it is a valid EVM address - Internal._validateEVMAddress(poolReturnData.destTokenAddress); - - newMessage.sourceTokenData[i] = abi.encode( - Internal.SourceTokenData({ - sourcePoolAddress: abi.encode(sourcePool), - destTokenAddress: poolReturnData.destTokenAddress, - extraData: poolReturnData.destPoolData - }) - ); + + _validateDestFamilyAddress(destChainConfig.dynamicConfig.chainFamilySelector, poolReturnData.destTokenAddress); + + newMessage.tokenAmounts[i] = Internal.RampTokenAmount({ + sourcePoolAddress: abi.encode(sourcePool), + destTokenAddress: poolReturnData.destTokenAddress, + extraData: poolReturnData.destPoolData, + amount: tokenAndAmount.amount + }); } // Hash only after the sourceTokenData has been set - newMessage.messageId = Internal._hash(newMessage, destChainConfig.metadataHash); + newMessage.header.messageId = Internal._hash(newMessage, destChainConfig.metadataHash); // Emit message request // This must happen after any pool events as some tokens (e.g. USDC) emit events that we expect to precede this // event in the offchain code. emit CCIPSendRequested(destChainSelector, newMessage); - return newMessage.messageId; + return newMessage.header.messageId; } /// @notice Helper function to relieve stack pressure from `forwardFromRouter` @@ -321,7 +321,7 @@ contract EVM2EVMMultiOnRamp is IEVM2AnyOnRampClient, ITypeAndVersion, OwnerIsCre Client.EVM2AnyMessage calldata message, uint256 feeTokenAmount, address originalSender - ) internal returns (Internal.EVM2EVMMessage memory) { + ) internal returns (Internal.EVM2AnyRampMessage memory) { if (IRMN(i_rmnProxy).isCursed(bytes16(uint128(destChainSelector)))) revert CursedByRMN(destChainSelector); // Validate message sender is set and allowed. Not validated in `getFee` since it is not user-driven. if (originalSender == address(0)) revert RouterMustSetOriginalSender(); @@ -329,13 +329,10 @@ contract EVM2EVMMultiOnRamp is IEVM2AnyOnRampClient, ITypeAndVersion, OwnerIsCre if (msg.sender != s_dynamicConfig.router) revert MustBeCalledByRouter(); if (!destChainConfig.dynamicConfig.isEnabled) revert DestinationChainNotEnabled(destChainSelector); - Client.EVMExtraArgsV2 memory extraArgs = - _extraArgsFromBytes(message.extraArgs, destChainConfig.dynamicConfig.defaultTxGasLimit); // Validate the message with various checks uint256 numberOfTokens = message.tokenAmounts.length; - _validateMessage( - destChainSelector, message.data.length, extraArgs.gasLimit, numberOfTokens, extraArgs.allowOutOfOrderExecution - ); + // TODO: optimization - consider removing this call, since the Router calls into getFee -> _validateMessage + _validateMessage(destChainSelector, message.data.length, numberOfTokens, message.receiver); // Only check token value if there are tokens if (numberOfTokens > 0) { @@ -361,48 +358,95 @@ contract EVM2EVMMultiOnRamp is IEVM2AnyOnRampClient, ITypeAndVersion, OwnerIsCre if (msgFeeJuels > i_maxFeeJuelsPerMsg) revert MessageFeeTooHigh(msgFeeJuels, i_maxFeeJuelsPerMsg); - // We need the next available sequence number so we increment before we use the value - return Internal.EVM2EVMMessage({ - sourceChainSelector: i_chainSelector, - sender: originalSender, - // EVM destination addresses should be abi encoded and therefore always 32 bytes long - // Not duplicately validated in `getFee`. Invalid address is uncommon, gas cost outweighs UX gain. - receiver: Internal._validateEVMAddress(message.receiver), - sequenceNumber: ++destChainConfig.sequenceNumber, - gasLimit: extraArgs.gasLimit, - strict: false, + // NOTE: when supporting non-EVM chains, revisit this and parse non-EVM args + // Assumes strict ordering, unless the chain is of the EVM family and the extra args indicate out of order execution + uint64 nonce = 0; + if (!_parseEVMExtraArgsFromBytes(message.extraArgs, destChainConfig.dynamicConfig).allowOutOfOrderExecution) { // Only bump nonce for messages that specify allowOutOfOrderExecution == false. Otherwise, we // may block ordered message nonces, which is not what we want. - nonce: extraArgs.allowOutOfOrderExecution - ? 0 - : INonceManager(i_nonceManager).getIncrementedOutboundNonce(destChainSelector, originalSender), + nonce = INonceManager(i_nonceManager).getIncrementedOutboundNonce(destChainSelector, originalSender); + } + + Internal.EVM2AnyRampMessage memory rampMessage = Internal.EVM2AnyRampMessage({ + header: Internal.RampMessageHeader({ + // Should be generated after the message is complete + messageId: "", + sourceChainSelector: i_chainSelector, + destChainSelector: destChainSelector, + // We need the next available sequence number so we increment before we use the value + sequenceNumber: ++destChainConfig.sequenceNumber, + nonce: nonce + }), + sender: originalSender, + data: message.data, + extraArgs: _convertParsedExtraArgs(message.extraArgs, destChainConfig.dynamicConfig), + receiver: message.receiver, feeToken: message.feeToken, feeTokenAmount: feeTokenAmount, - data: message.data, - tokenAmounts: message.tokenAmounts, - sourceTokenData: new bytes[](numberOfTokens), // will be populated below - messageId: "" + tokenAmounts: new Internal.RampTokenAmount[](numberOfTokens) // should be populated after generation }); + + return rampMessage; + } + + /// @dev Parses extraArgs with dest chain config family tag validation, and re-encodes the args to the latest arguments version. + /// Used to generate an EVM2AnyRampMessage with the accurate representation of the parsed extraArgs. + /// @param extraArgs The extra args bytes + /// @param destChainDynamicConfig Dest chain config to validate against + /// @return encodedExtraArgs the parsed & encoded extra args + function _convertParsedExtraArgs( + bytes calldata extraArgs, + DestChainDynamicConfig memory destChainDynamicConfig + ) internal pure returns (bytes memory encodedExtraArgs) { + bytes4 chainFamilySelector = destChainDynamicConfig.chainFamilySelector; + if (chainFamilySelector == Internal.CHAIN_FAMILY_SELECTOR_EVM) { + return abi.encode(_parseEVMExtraArgsFromBytes(extraArgs, destChainDynamicConfig)); + } + // Invalid chain family selectors cannot be configured - ignore invalid cases + return encodedExtraArgs; + } + + /// @dev Convert the extra args bytes into a struct with validations against the dest chain config + /// @param extraArgs The extra args bytes + /// @param destChainDynamicConfig Dest chain config to validate against + /// @return EVMExtraArgs the extra args struct (latest version) + function _parseEVMExtraArgsFromBytes( + bytes calldata extraArgs, + DestChainDynamicConfig memory destChainDynamicConfig + ) internal pure returns (Client.EVMExtraArgsV2 memory) { + Client.EVMExtraArgsV2 memory evmExtraArgs = + _parseUnvalidatedEVMExtraArgsFromBytes(extraArgs, destChainDynamicConfig.defaultTxGasLimit); + + if (evmExtraArgs.gasLimit > uint256(destChainDynamicConfig.maxPerMsgGasLimit)) revert MessageGasLimitTooHigh(); + if (destChainDynamicConfig.enforceOutOfOrder && !evmExtraArgs.allowOutOfOrderExecution) { + revert ExtraArgOutOfOrderExecutionMustBeTrue(); + } + + return evmExtraArgs; } /// @dev Convert the extra args bytes into a struct /// @param extraArgs The extra args bytes - /// @return EVMExtraArgs the extra args struct - function _extraArgsFromBytes( + /// @param defaultTxGasLimit default tx gas limit to use in the absence of extra args + /// @return EVMExtraArgs the extra args struct (latest version) + function _parseUnvalidatedEVMExtraArgsFromBytes( bytes calldata extraArgs, uint64 defaultTxGasLimit - ) internal pure returns (Client.EVMExtraArgsV2 memory) { + ) private pure returns (Client.EVMExtraArgsV2 memory) { if (extraArgs.length == 0) { + // If extra args are empty, generate default values return Client.EVMExtraArgsV2({gasLimit: defaultTxGasLimit, allowOutOfOrderExecution: false}); } bytes4 extraArgsTag = bytes4(extraArgs); + bytes memory argsData = extraArgs[4:]; + if (extraArgsTag == Client.EVM_EXTRA_ARGS_V2_TAG) { - return abi.decode(extraArgs[4:], (Client.EVMExtraArgsV2)); + return abi.decode(argsData, (Client.EVMExtraArgsV2)); } else if (extraArgsTag == Client.EVM_EXTRA_ARGS_V1_TAG) { // EVMExtraArgsV1 originally included a second boolean (strict) field which has been deprecated. // Clients may still include it but it will be ignored. - return Client.EVMExtraArgsV2({gasLimit: abi.decode(extraArgs[4:], (uint256)), allowOutOfOrderExecution: false}); + return Client.EVMExtraArgsV2({gasLimit: abi.decode(argsData, (uint256)), allowOutOfOrderExecution: false}); } revert InvalidExtraArgsTag(); @@ -413,24 +457,30 @@ contract EVM2EVMMultiOnRamp is IEVM2AnyOnRampClient, ITypeAndVersion, OwnerIsCre /// only common user-driven mistakes are validated here to minimize duplicate validation cost. /// @param destChainSelector The destination chain selector. /// @param dataLength The length of the data field of the message. - /// @param gasLimit The gasLimit set in message for destination execution. /// @param numberOfTokens The number of tokens to be sent. function _validateMessage( uint64 destChainSelector, uint256 dataLength, - uint256 gasLimit, uint256 numberOfTokens, - bool allowOutOfOrderExecution + bytes calldata receiver ) internal view { // Check that payload is formed correctly DestChainDynamicConfig storage destChainDynamicConfig = s_destChainConfig[destChainSelector].dynamicConfig; if (dataLength > uint256(destChainDynamicConfig.maxDataBytes)) { revert MessageTooLarge(uint256(destChainDynamicConfig.maxDataBytes), dataLength); } - if (gasLimit > uint256(destChainDynamicConfig.maxPerMsgGasLimit)) revert MessageGasLimitTooHigh(); if (numberOfTokens > uint256(destChainDynamicConfig.maxNumberOfTokensPerMsg)) revert UnsupportedNumberOfTokens(); - if (destChainDynamicConfig.enforceOutOfOrder && !allowOutOfOrderExecution) { - revert ExtraArgOutOfOrderExecutionMustBeTrue(); + + _validateDestFamilyAddress(destChainDynamicConfig.chainFamilySelector, receiver); + } + + /// @notice Validates that the destAddress matches the expected format of the family. + /// @param chainFamilySelector Tag to identify the target family + /// @param destAddress Dest address to validate + /// @dev precondition - assumes the family tag is correct and validated + function _validateDestFamilyAddress(bytes4 chainFamilySelector, bytes memory destAddress) internal pure { + if (chainFamilySelector == Internal.CHAIN_FAMILY_SELECTOR_EVM) { + Internal._validateEVMAddress(destAddress); } } @@ -515,16 +565,8 @@ contract EVM2EVMMultiOnRamp is IEVM2AnyOnRampClient, ITypeAndVersion, OwnerIsCre if (!destChainDynamicConfig.isEnabled) revert DestinationChainNotEnabled(destChainSelector); - Client.EVMExtraArgsV2 memory extraArgs = - _extraArgsFromBytes(message.extraArgs, destChainDynamicConfig.defaultTxGasLimit); // Validate the message with various checks - _validateMessage( - destChainSelector, - message.data.length, - extraArgs.gasLimit, - message.tokenAmounts.length, - extraArgs.allowOutOfOrderExecution - ); + _validateMessage(destChainSelector, message.data.length, message.tokenAmounts.length, message.receiver); uint64 premiumMultiplierWeiPerEth = s_premiumMultiplierWeiPerEth[message.feeToken]; @@ -569,11 +611,16 @@ contract EVM2EVMMultiOnRamp is IEVM2AnyOnRampClient, ITypeAndVersion, OwnerIsCre // We add the message gas limit, the overhead gas, the gas of passing message data to receiver, and token transfer gas together. // We then multiply this gas total with the gas multiplier and gas price, converting it into USD with 36 decimals. // uint112(packedGasPrice) = executionGasPrice - uint256 executionCost = uint112(packedGasPrice) - * ( - extraArgs.gasLimit + destChainDynamicConfig.destGasOverhead - + (message.data.length * destChainDynamicConfig.destGasPerPayloadByte) + tokenTransferGas - ) * destChainDynamicConfig.gasMultiplierWeiPerEth; + + // TODO: revisit how generic this logic can be for non-EVM chains + + uint256 executionGasCost = destChainDynamicConfig.destGasOverhead + + (message.data.length * destChainDynamicConfig.destGasPerPayloadByte) + tokenTransferGas; + + // NOTE: when supporting non-EVM chains, revisit this and parse non-EVM args + executionGasCost += _parseEVMExtraArgsFromBytes(message.extraArgs, destChainDynamicConfig).gasLimit; + + uint256 executionCost = uint112(packedGasPrice) * executionGasCost * destChainDynamicConfig.gasMultiplierWeiPerEth; // Calculate number of fee tokens to charge. // Total USD fee is in 36 decimals, feeTokenPrice is in 18 decimals USD for 1e18 smallest token denominations. @@ -598,8 +645,8 @@ contract EVM2EVMMultiOnRamp is IEVM2AnyOnRampClient, ITypeAndVersion, OwnerIsCre ) internal view returns (uint256 dataAvailabilityCostUSD36Decimal) { // dataAvailabilityLengthBytes sums up byte lengths of fixed message fields and dynamic message fields. // Fixed message fields do account for the offset and length slot of the dynamic fields. - uint256 dataAvailabilityLengthBytes = Internal.MESSAGE_FIXED_BYTES + messageDataLength - + (numberOfTokens * Internal.MESSAGE_FIXED_BYTES_PER_TOKEN) + tokenTransferBytesOverhead; + uint256 dataAvailabilityLengthBytes = Internal.ANY_2_EVM_MESSAGE_FIXED_BYTES + messageDataLength + + (numberOfTokens * Internal.ANY_2_EVM_MESSAGE_FIXED_BYTES_PER_TOKEN) + tokenTransferBytesOverhead; DestChainDynamicConfig storage destChainDynamicConfig = s_destChainConfig[destChainSelector].dynamicConfig; // destDataAvailabilityOverheadGas is a separate config value for flexibility to be updated independently of message cost. @@ -705,7 +752,11 @@ contract EVM2EVMMultiOnRamp is IEVM2AnyOnRampClient, ITypeAndVersion, OwnerIsCre DestChainConfigArgs memory destChainConfigArg = destChainConfigArgs[i]; uint64 destChainSelector = destChainConfigArgs[i].destChainSelector; - if (destChainSelector == 0 || destChainConfigArg.dynamicConfig.defaultTxGasLimit == 0) { + // NOTE: when supporting non-EVM chains, update chainFamilySelector validations + if ( + destChainSelector == 0 || destChainConfigArg.dynamicConfig.defaultTxGasLimit == 0 + || destChainConfigArg.dynamicConfig.chainFamilySelector != Internal.CHAIN_FAMILY_SELECTOR_EVM + ) { revert InvalidDestChainConfig(destChainSelector); } @@ -723,7 +774,7 @@ contract EVM2EVMMultiOnRamp is IEVM2AnyOnRampClient, ITypeAndVersion, OwnerIsCre if (destChainConfig.metadataHash == 0) { newDestChainConfig.metadataHash = - keccak256(abi.encode(Internal.EVM_2_EVM_MESSAGE_HASH, i_chainSelector, destChainSelector, address(this))); + keccak256(abi.encode(Internal.EVM_2_ANY_MESSAGE_HASH, i_chainSelector, destChainSelector, address(this))); destChainConfig.metadataHash = newDestChainConfig.metadataHash; if (prevOnRamp != address(0)) destChainConfig.prevOnRamp = prevOnRamp; diff --git a/contracts/src/v0.8/ccip/test/NonceManager.t.sol b/contracts/src/v0.8/ccip/test/NonceManager.t.sol index b780f4cfad..a9b248eb72 100644 --- a/contracts/src/v0.8/ccip/test/NonceManager.t.sol +++ b/contracts/src/v0.8/ccip/test/NonceManager.t.sol @@ -402,9 +402,7 @@ contract NonceManager_OffRampUpgrade is EVM2EVMMultiOffRampSetup { _generateSingleLaneSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, SINGLE_LANE_ON_RAMP_ADDRESS_1); uint64 startNonceChain3 = s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_3, abi.encode(messages[0].sender)); - s_prevOffRamp.execute( - _generateSingleLaneRampReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new uint256[](0) - ); + s_prevOffRamp.execute(_generateSingleLaneRampReportFromMessages(messages), new uint256[](0)); // Nonce unchanged for chain 3 assertEq( @@ -436,9 +434,7 @@ contract NonceManager_OffRampUpgrade is EVM2EVMMultiOffRampSetup { uint64 startNonce = s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(messages[0].sender)); for (uint64 i = 1; i < 4; ++i) { - s_prevOffRamp.execute( - _generateSingleLaneRampReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new uint256[](0) - ); + s_prevOffRamp.execute(_generateSingleLaneRampReportFromMessages(messages), new uint256[](0)); // messages contains a single message - update for the next execution messages[0].nonce++; @@ -457,9 +453,7 @@ contract NonceManager_OffRampUpgrade is EVM2EVMMultiOffRampSetup { uint64 startNonce = s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_2, abi.encode(messages[0].sender)); for (uint64 i = 1; i < 4; ++i) { - s_nestedPrevOffRamps[0].execute( - _generateSingleLaneRampReportFromMessages(SOURCE_CHAIN_SELECTOR_2, messages), new uint256[](0) - ); + s_nestedPrevOffRamps[0].execute(_generateSingleLaneRampReportFromMessages(messages), new uint256[](0)); // messages contains a single message - update for the next execution messages[0].nonce++; @@ -478,9 +472,7 @@ contract NonceManager_OffRampUpgrade is EVM2EVMMultiOffRampSetup { _generateSingleLaneSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, SINGLE_LANE_ON_RAMP_ADDRESS_1); uint64 startNonce = s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(messages[0].sender)); - s_prevOffRamp.execute( - _generateSingleLaneRampReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new uint256[](0) - ); + s_prevOffRamp.execute(_generateSingleLaneRampReportFromMessages(messages), new uint256[](0)); assertEq( startNonce + 1, s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(messages[0].sender)) @@ -533,9 +525,7 @@ contract NonceManager_OffRampUpgrade is EVM2EVMMultiOffRampSetup { Internal.EVM2EVMMessage[] memory messages = _generateSingleLaneSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, SINGLE_LANE_ON_RAMP_ADDRESS_1); - s_prevOffRamp.execute( - _generateSingleLaneRampReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new uint256[](0) - ); + s_prevOffRamp.execute(_generateSingleLaneRampReportFromMessages(messages), new uint256[](0)); Internal.Any2EVMRampMessage[] memory messagesMultiRamp = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); @@ -587,9 +577,7 @@ contract NonceManager_OffRampUpgrade is EVM2EVMMultiOffRampSetup { messagesSingleLane[0].messageId = Internal._hash(messagesSingleLane[0], s_prevOffRamp.metadataHash()); // previous offramp executes msg and increases nonce - s_prevOffRamp.execute( - _generateSingleLaneRampReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messagesSingleLane), new uint256[](0) - ); + s_prevOffRamp.execute(_generateSingleLaneRampReportFromMessages(messagesSingleLane), new uint256[](0)); assertEq( startNonce + 1, s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(messagesSingleLane[0].sender)) @@ -612,10 +600,11 @@ contract NonceManager_OffRampUpgrade is EVM2EVMMultiOffRampSetup { assertEq(startNonce + 2, s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender)); } - function _generateSingleLaneRampReportFromMessages( - uint64 sourceChainSelector, - Internal.EVM2EVMMessage[] memory messages - ) internal pure returns (Internal.ExecutionReport memory) { + function _generateSingleLaneRampReportFromMessages(Internal.EVM2EVMMessage[] memory messages) + internal + pure + returns (Internal.ExecutionReport memory) + { bytes[][] memory offchainTokenData = new bytes[][](messages.length); for (uint256 i = 0; i < messages.length; ++i) { diff --git a/contracts/src/v0.8/ccip/test/e2e/MultiRampsEnd2End.sol b/contracts/src/v0.8/ccip/test/e2e/MultiRampsEnd2End.sol index 1d9a99f182..ad42c20d14 100644 --- a/contracts/src/v0.8/ccip/test/e2e/MultiRampsEnd2End.sol +++ b/contracts/src/v0.8/ccip/test/e2e/MultiRampsEnd2End.sol @@ -221,7 +221,7 @@ contract MultiRampsE2E is EVM2EVMMultiOnRampSetup, EVM2EVMMultiOffRampSetup { IERC20(s_sourceTokens[1]).approve(address(router), i_tokenAmount1); message.receiver = abi.encode(address(s_receiver)); - Internal.EVM2EVMMessage memory msgEvent = _messageToEvent( + Internal.EVM2AnyRampMessage memory msgEvent = _messageToEvent( message, sourceChainSelector, DEST_CHAIN_SELECTOR, @@ -240,20 +240,21 @@ contract MultiRampsE2E is EVM2EVMMultiOnRampSetup, EVM2EVMMultiOffRampSetup { router.ccipSend(DEST_CHAIN_SELECTOR, message); vm.pauseGasMetering(); + uint256 gasLimit = abi.decode(msgEvent.extraArgs, (Client.EVMExtraArgsV2)).gasLimit; + return Internal.Any2EVMRampMessage({ header: Internal.RampMessageHeader({ - messageId: msgEvent.messageId, + messageId: msgEvent.header.messageId, sourceChainSelector: sourceChainSelector, destChainSelector: DEST_CHAIN_SELECTOR, - sequenceNumber: msgEvent.sequenceNumber, - nonce: msgEvent.nonce + sequenceNumber: msgEvent.header.sequenceNumber, + nonce: msgEvent.header.nonce }), sender: abi.encode(msgEvent.sender), data: msgEvent.data, - receiver: msgEvent.receiver, - gasLimit: msgEvent.gasLimit, - tokenAmounts: msgEvent.tokenAmounts, - sourceTokenData: msgEvent.sourceTokenData + receiver: abi.decode(msgEvent.receiver, (address)), + gasLimit: gasLimit, + tokenAmounts: msgEvent.tokenAmounts }); } } diff --git a/contracts/src/v0.8/ccip/test/helpers/EVM2EVMMultiOffRampHelper.sol b/contracts/src/v0.8/ccip/test/helpers/EVM2EVMMultiOffRampHelper.sol index 0e2202869e..4ee4144bc9 100644 --- a/contracts/src/v0.8/ccip/test/helpers/EVM2EVMMultiOffRampHelper.sol +++ b/contracts/src/v0.8/ccip/test/helpers/EVM2EVMMultiOffRampHelper.sol @@ -27,29 +27,24 @@ contract EVM2EVMMultiOffRampHelper is EVM2EVMMultiOffRamp, IgnoreContractSize { } function releaseOrMintSingleToken( - uint256 sourceTokenAmount, + Internal.RampTokenAmount memory sourceTokenAmount, bytes calldata originalSender, address receiver, uint64 sourceChainSelector, - Internal.SourceTokenData calldata sourceTokenData, bytes calldata offchainTokenData ) external returns (Client.EVMTokenAmount memory) { - return _releaseOrMintSingleToken( - sourceTokenAmount, originalSender, receiver, sourceChainSelector, sourceTokenData, offchainTokenData - ); + return + _releaseOrMintSingleToken(sourceTokenAmount, originalSender, receiver, sourceChainSelector, offchainTokenData); } function releaseOrMintTokens( - Client.EVMTokenAmount[] memory sourceTokenAmounts, + Internal.RampTokenAmount[] memory sourceTokenAmounts, bytes memory originalSender, address receiver, uint64 sourceChainSelector, - bytes[] calldata sourceTokenData, bytes[] calldata offchainTokenData ) external returns (Client.EVMTokenAmount[] memory) { - return _releaseOrMintTokens( - sourceTokenAmounts, originalSender, receiver, sourceChainSelector, sourceTokenData, offchainTokenData - ); + return _releaseOrMintTokens(sourceTokenAmounts, originalSender, receiver, sourceChainSelector, offchainTokenData); } function trialExecute( diff --git a/contracts/src/v0.8/ccip/test/helpers/EVM2EVMMultiOnRampHelper.sol b/contracts/src/v0.8/ccip/test/helpers/EVM2EVMMultiOnRampHelper.sol index c850de6cd5..6261e6539b 100644 --- a/contracts/src/v0.8/ccip/test/helpers/EVM2EVMMultiOnRampHelper.sol +++ b/contracts/src/v0.8/ccip/test/helpers/EVM2EVMMultiOnRampHelper.sol @@ -42,10 +42,21 @@ contract EVM2EVMMultiOnRampHelper is EVM2EVMMultiOnRamp, IgnoreContractSize { return _getTokenTransferCost(destChainSelector, feeToken, feeTokenPrice, tokenAmounts); } - function extraArgsFromBytes( + function parseEVMExtraArgsFromBytes( bytes calldata extraArgs, uint64 destChainSelector ) external view returns (Client.EVMExtraArgsV2 memory) { - return _extraArgsFromBytes(extraArgs, s_destChainConfig[destChainSelector].dynamicConfig.defaultTxGasLimit); + return _parseEVMExtraArgsFromBytes(extraArgs, s_destChainConfig[destChainSelector].dynamicConfig); + } + + function validateDestFamilyAddress(bytes4 chainFamilySelector, bytes memory destAddress) external pure { + _validateDestFamilyAddress(chainFamilySelector, destAddress); + } + + function convertParsedExtraArgs( + bytes calldata extraArgs, + DestChainDynamicConfig memory destChainDynamicConfig + ) external pure returns (bytes memory encodedExtraArgs) { + return _convertParsedExtraArgs(extraArgs, destChainDynamicConfig); } } diff --git a/contracts/src/v0.8/ccip/test/helpers/MessageHasher.sol b/contracts/src/v0.8/ccip/test/helpers/MessageHasher.sol index 755b326c0d..f0ca898a6c 100644 --- a/contracts/src/v0.8/ccip/test/helpers/MessageHasher.sol +++ b/contracts/src/v0.8/ccip/test/helpers/MessageHasher.sol @@ -11,16 +11,12 @@ contract MessageHasher { return Internal._hash(message, onRamp); } - function encodeTokenAmountsHashPreimage(Client.EVMTokenAmount[] memory tokenAmounts) + function encodeTokenAmountsHashPreimage(Internal.RampTokenAmount[] memory rampTokenAmounts) public pure returns (bytes memory) { - return abi.encode(tokenAmounts); - } - - function encodeSourceTokenDataHashPreimage(bytes[] memory sourceTokenData) public pure returns (bytes memory) { - return abi.encode(sourceTokenData); + return abi.encode(rampTokenAmounts); } function encodeMetadataHashPreimage( @@ -48,11 +44,8 @@ contract MessageHasher { bytes32 implicitMetadataHash, bytes32 fixedSizeFieldsHash, bytes32 dataHash, - bytes32 tokenAmountsHash, - bytes32 sourceTokenDataHash + bytes32 tokenAmountsHash ) public pure returns (bytes memory) { - return abi.encode( - leafDomainSeparator, implicitMetadataHash, fixedSizeFieldsHash, dataHash, tokenAmountsHash, sourceTokenDataHash - ); + return abi.encode(leafDomainSeparator, implicitMetadataHash, fixedSizeFieldsHash, dataHash, tokenAmountsHash); } } diff --git a/contracts/src/v0.8/ccip/test/offRamp/EVM2EVMMultiOffRamp.t.sol b/contracts/src/v0.8/ccip/test/offRamp/EVM2EVMMultiOffRamp.t.sol index c994cf5698..4ab5acf374 100644 --- a/contracts/src/v0.8/ccip/test/offRamp/EVM2EVMMultiOffRamp.t.sol +++ b/contracts/src/v0.8/ccip/test/offRamp/EVM2EVMMultiOffRamp.t.sol @@ -702,13 +702,7 @@ contract EVM2EVMMultiOffRamp_executeSingleReport is EVM2EVMMultiOffRampSetup { Internal.Any2EVMRampMessage[] memory messages = _generateMessagesWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - messages[0].sourceTokenData[0] = abi.encode( - Internal.SourceTokenData({ - sourcePoolAddress: abi.encode(fakePoolAddress), - destTokenAddress: abi.encode(s_destTokenBySourceToken[messages[0].tokenAmounts[0].token]), - extraData: "" - }) - ); + messages[0].tokenAmounts[0].sourcePoolAddress = abi.encode(fakePoolAddress); messages[0].header.messageId = Internal._hash(messages[0], ON_RAMP_ADDRESS_1); messages[1].header.messageId = Internal._hash(messages[1], ON_RAMP_ADDRESS_1); @@ -982,7 +976,6 @@ contract EVM2EVMMultiOffRamp_executeSingleMessage is EVM2EVMMultiOffRampSetup { Internal.Any2EVMRampMessage memory message = _generateMessagesWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1)[0]; bytes[] memory offchainTokenData = new bytes[](message.tokenAmounts.length); - Internal.SourceTokenData memory sourceTokenData = abi.decode(message.sourceTokenData[0], (Internal.SourceTokenData)); vm.expectCall( s_destPoolByToken[s_destTokens[0]], @@ -992,11 +985,11 @@ contract EVM2EVMMultiOffRamp_executeSingleMessage is EVM2EVMMultiOffRampSetup { originalSender: message.sender, receiver: message.receiver, amount: message.tokenAmounts[0].amount, - localToken: s_destTokenBySourceToken[message.tokenAmounts[0].token], + localToken: abi.decode(message.tokenAmounts[0].destTokenAddress, (address)), remoteChainSelector: SOURCE_CHAIN_SELECTOR_1, - sourcePoolAddress: sourceTokenData.sourcePoolAddress, - sourcePoolData: sourceTokenData.extraData, - offchainTokenData: "" + sourcePoolAddress: message.tokenAmounts[0].sourcePoolAddress, + sourcePoolData: message.tokenAmounts[0].extraData, + offchainTokenData: offchainTokenData[0] }) ) ); @@ -1693,16 +1686,13 @@ contract EVM2EVMMultiOffRamp_manuallyExecute is EVM2EVMMultiOffRampSetup { // send the message that they want to replay. Internal.Any2EVMRampMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - messages[0].tokenAmounts = new Client.EVMTokenAmount[](1); - messages[0].tokenAmounts[0] = Client.EVMTokenAmount({token: s_sourceFeeToken, amount: tokenAmount}); - messages[0].sourceTokenData = new bytes[](1); - messages[0].sourceTokenData[0] = abi.encode( - Internal.SourceTokenData({ - sourcePoolAddress: abi.encode(s_sourcePoolByToken[s_sourceFeeToken]), - destTokenAddress: abi.encode(s_destTokenBySourceToken[s_sourceFeeToken]), - extraData: "" - }) - ); + messages[0].tokenAmounts = new Internal.RampTokenAmount[](1); + messages[0].tokenAmounts[0] = Internal.RampTokenAmount({ + sourcePoolAddress: abi.encode(s_sourcePoolByToken[s_sourceFeeToken]), + destTokenAddress: abi.encode(s_destTokenBySourceToken[s_sourceFeeToken]), + extraData: "", + amount: tokenAmount + }); messages[0].receiver = address(receiver); @@ -2257,13 +2247,13 @@ contract EVM2EVMMultiOffRamp_trialExecute is EVM2EVMMultiOffRampSetup { // address 0 has no contract assertEq(address(0).code.length, 0); - message.sourceTokenData[0] = abi.encode( - Internal.SourceTokenData({ - sourcePoolAddress: abi.encode(address(0)), - destTokenAddress: abi.encode(address(0)), - extraData: "" - }) - ); + + message.tokenAmounts[0] = Internal.RampTokenAmount({ + sourcePoolAddress: abi.encode(address(0)), + destTokenAddress: abi.encode(address(0)), + extraData: "", + amount: message.tokenAmounts[0].amount + }); message.header.messageId = Internal._hash(message, ON_RAMP_ADDRESS_1); @@ -2275,13 +2265,12 @@ contract EVM2EVMMultiOffRamp_trialExecute is EVM2EVMMultiOffRampSetup { address notAContract = makeAddr("not_a_contract"); - message.sourceTokenData[0] = abi.encode( - Internal.SourceTokenData({ - sourcePoolAddress: abi.encode(address(0)), - destTokenAddress: abi.encode(notAContract), - extraData: "" - }) - ); + message.tokenAmounts[0] = Internal.RampTokenAmount({ + sourcePoolAddress: abi.encode(address(0)), + destTokenAddress: abi.encode(notAContract), + extraData: "", + amount: message.tokenAmounts[0].amount + }); message.header.messageId = Internal._hash(message, ON_RAMP_ADDRESS_1); @@ -2307,10 +2296,11 @@ contract EVM2EVMMultiOffRamp__releaseOrMintSingleToken is EVM2EVMMultiOffRampSet IERC20 dstToken1 = IERC20(s_destTokenBySourceToken[token]); uint256 startingBalance = dstToken1.balanceOf(OWNER); - Internal.SourceTokenData memory sourceTokenData = Internal.SourceTokenData({ + Internal.RampTokenAmount memory tokenAmount = Internal.RampTokenAmount({ sourcePoolAddress: abi.encode(s_sourcePoolByToken[token]), destTokenAddress: abi.encode(s_destTokenBySourceToken[token]), - extraData: "" + extraData: "", + amount: amount }); vm.expectCall( @@ -2323,16 +2313,14 @@ contract EVM2EVMMultiOffRamp__releaseOrMintSingleToken is EVM2EVMMultiOffRampSet amount: amount, localToken: s_destTokenBySourceToken[token], remoteChainSelector: SOURCE_CHAIN_SELECTOR_1, - sourcePoolAddress: sourceTokenData.sourcePoolAddress, - sourcePoolData: sourceTokenData.extraData, + sourcePoolAddress: tokenAmount.sourcePoolAddress, + sourcePoolData: tokenAmount.extraData, offchainTokenData: offchainTokenData }) ) ); - s_offRamp.releaseOrMintSingleToken( - amount, originalSender, OWNER, SOURCE_CHAIN_SELECTOR_1, sourceTokenData, offchainTokenData - ); + s_offRamp.releaseOrMintSingleToken(tokenAmount, originalSender, OWNER, SOURCE_CHAIN_SELECTOR_1, offchainTokenData); assertEq(startingBalance + amount, dstToken1.balanceOf(OWNER)); } @@ -2345,10 +2333,11 @@ contract EVM2EVMMultiOffRamp__releaseOrMintSingleToken is EVM2EVMMultiOffRampSet bytes memory originalSender = abi.encode(OWNER); bytes memory offchainTokenData = abi.encode(keccak256("offchainTokenData")); - Internal.SourceTokenData memory sourceTokenData = Internal.SourceTokenData({ + Internal.RampTokenAmount memory tokenAmount = Internal.RampTokenAmount({ sourcePoolAddress: abi.encode(s_sourcePoolByToken[token]), destTokenAddress: abi.encode(destToken), - extraData: "" + extraData: "", + amount: amount }); // Address(0) should always revert @@ -2362,9 +2351,7 @@ contract EVM2EVMMultiOffRamp__releaseOrMintSingleToken is EVM2EVMMultiOffRampSet vm.expectRevert(abi.encodeWithSelector(EVM2EVMMultiOffRamp.NotACompatiblePool.selector, returnedPool)); - s_offRamp.releaseOrMintSingleToken( - amount, originalSender, OWNER, SOURCE_CHAIN_SELECTOR_1, sourceTokenData, offchainTokenData - ); + s_offRamp.releaseOrMintSingleToken(tokenAmount, originalSender, OWNER, SOURCE_CHAIN_SELECTOR_1, offchainTokenData); // A contract that doesn't support the interface should also revert returnedPool = address(s_offRamp); @@ -2377,9 +2364,7 @@ contract EVM2EVMMultiOffRamp__releaseOrMintSingleToken is EVM2EVMMultiOffRampSet vm.expectRevert(abi.encodeWithSelector(EVM2EVMMultiOffRamp.NotACompatiblePool.selector, returnedPool)); - s_offRamp.releaseOrMintSingleToken( - amount, originalSender, OWNER, SOURCE_CHAIN_SELECTOR_1, sourceTokenData, offchainTokenData - ); + s_offRamp.releaseOrMintSingleToken(tokenAmount, originalSender, OWNER, SOURCE_CHAIN_SELECTOR_1, offchainTokenData); } function test__releaseOrMintSingleToken_TokenHandlingError_revert_Revert() public { @@ -2390,10 +2375,11 @@ contract EVM2EVMMultiOffRamp__releaseOrMintSingleToken is EVM2EVMMultiOffRampSet bytes memory originalSender = abi.encode(OWNER); bytes memory offchainTokenData = abi.encode(keccak256("offchainTokenData")); - Internal.SourceTokenData memory sourceTokenData = Internal.SourceTokenData({ + Internal.RampTokenAmount memory tokenAmount = Internal.RampTokenAmount({ sourcePoolAddress: abi.encode(s_sourcePoolByToken[token]), destTokenAddress: abi.encode(destToken), - extraData: "" + extraData: "", + amount: amount }); bytes memory revertData = "call reverted :o"; @@ -2402,7 +2388,7 @@ contract EVM2EVMMultiOffRamp__releaseOrMintSingleToken is EVM2EVMMultiOffRampSet vm.expectRevert(abi.encodeWithSelector(EVM2EVMMultiOffRamp.TokenHandlingError.selector, revertData)); s_offRamp.releaseOrMintSingleToken( - amount, originalSender, receiver, SOURCE_CHAIN_SELECTOR_1, sourceTokenData, offchainTokenData + tokenAmount, originalSender, receiver, SOURCE_CHAIN_SELECTOR_1, offchainTokenData ); } } @@ -2423,8 +2409,7 @@ contract EVM2EVMMultiOffRamp_releaseOrMintTokens is EVM2EVMMultiOffRampSetup { bytes[] memory offchainTokenData = new bytes[](srcTokenAmounts.length); offchainTokenData[0] = abi.encode(0x12345678); - bytes[] memory encodedSourceTokenData = _getDefaultSourceTokenData(srcTokenAmounts); - Internal.SourceTokenData memory sourceTokenData = abi.decode(encodedSourceTokenData[0], (Internal.SourceTokenData)); + Internal.RampTokenAmount[] memory sourceTokenAmounts = _getDefaultSourceTokenData(srcTokenAmounts); vm.expectCall( s_destPoolBySourceToken[srcTokenAmounts[0].token], @@ -2436,15 +2421,15 @@ contract EVM2EVMMultiOffRamp_releaseOrMintTokens is EVM2EVMMultiOffRampSetup { amount: srcTokenAmounts[0].amount, localToken: s_destTokenBySourceToken[srcTokenAmounts[0].token], remoteChainSelector: SOURCE_CHAIN_SELECTOR_1, - sourcePoolAddress: sourceTokenData.sourcePoolAddress, - sourcePoolData: sourceTokenData.extraData, + sourcePoolAddress: sourceTokenAmounts[0].sourcePoolAddress, + sourcePoolData: sourceTokenAmounts[0].extraData, offchainTokenData: offchainTokenData[0] }) ) ); s_offRamp.releaseOrMintTokens( - srcTokenAmounts, abi.encode(OWNER), OWNER, SOURCE_CHAIN_SELECTOR_1, encodedSourceTokenData, offchainTokenData + sourceTokenAmounts, abi.encode(OWNER), OWNER, SOURCE_CHAIN_SELECTOR_1, offchainTokenData ); assertEq(startingBalance + amount1, dstToken1.balanceOf(OWNER)); @@ -2458,8 +2443,8 @@ contract EVM2EVMMultiOffRamp_releaseOrMintTokens is EVM2EVMMultiOffRampSetup { srcTokenAmounts[0].amount = amount; bytes[] memory offchainTokenData = new bytes[](srcTokenAmounts.length); - bytes[] memory encodedSourceTokenData = _getDefaultSourceTokenData(srcTokenAmounts); - Internal.SourceTokenData memory sourceTokenData = abi.decode(encodedSourceTokenData[0], (Internal.SourceTokenData)); + + Internal.RampTokenAmount[] memory sourceTokenAmounts = _getDefaultSourceTokenData(srcTokenAmounts); // Since the pool call is mocked, we manually release funds to the offRamp deal(destToken, address(s_offRamp), amount * destinationDenominationMultiplier); @@ -2474,8 +2459,8 @@ contract EVM2EVMMultiOffRamp_releaseOrMintTokens is EVM2EVMMultiOffRampSetup { amount: amount, localToken: s_destTokenBySourceToken[srcTokenAmounts[0].token], remoteChainSelector: SOURCE_CHAIN_SELECTOR_1, - sourcePoolAddress: sourceTokenData.sourcePoolAddress, - sourcePoolData: sourceTokenData.extraData, + sourcePoolAddress: sourceTokenAmounts[0].sourcePoolAddress, + sourcePoolData: sourceTokenAmounts[0].extraData, offchainTokenData: offchainTokenData[0] }) ), @@ -2483,7 +2468,7 @@ contract EVM2EVMMultiOffRamp_releaseOrMintTokens is EVM2EVMMultiOffRampSetup { ); Client.EVMTokenAmount[] memory destTokenAmounts = s_offRamp.releaseOrMintTokens( - srcTokenAmounts, abi.encode(OWNER), OWNER, SOURCE_CHAIN_SELECTOR_1, encodedSourceTokenData, offchainTokenData + sourceTokenAmounts, abi.encode(OWNER), OWNER, SOURCE_CHAIN_SELECTOR_1, offchainTokenData ); assertEq(destTokenAmounts[0].amount, amount * destinationDenominationMultiplier); @@ -2501,11 +2486,10 @@ contract EVM2EVMMultiOffRamp_releaseOrMintTokens is EVM2EVMMultiOffRampSetup { vm.expectRevert(abi.encodeWithSelector(EVM2EVMMultiOffRamp.TokenHandlingError.selector, unknownError)); s_offRamp.releaseOrMintTokens( - srcTokenAmounts, + _getDefaultSourceTokenData(srcTokenAmounts), abi.encode(OWNER), OWNER, SOURCE_CHAIN_SELECTOR_1, - _getDefaultSourceTokenData(srcTokenAmounts), new bytes[](srcTokenAmounts.length) ); } @@ -2516,8 +2500,7 @@ contract EVM2EVMMultiOffRamp_releaseOrMintTokens is EVM2EVMMultiOffRampSetup { srcTokenAmounts[0].amount = amount; bytes[] memory offchainTokenData = new bytes[](srcTokenAmounts.length); - bytes[] memory encodedSourceTokenData = _getDefaultSourceTokenData(srcTokenAmounts); - Internal.SourceTokenData memory sourceTokenData = abi.decode(encodedSourceTokenData[0], (Internal.SourceTokenData)); + Internal.RampTokenAmount[] memory sourceTokenAmounts = _getDefaultSourceTokenData(srcTokenAmounts); vm.mockCall( s_destPoolBySourceToken[srcTokenAmounts[0].token], @@ -2529,8 +2512,8 @@ contract EVM2EVMMultiOffRamp_releaseOrMintTokens is EVM2EVMMultiOffRampSetup { amount: amount, localToken: s_destTokenBySourceToken[srcTokenAmounts[0].token], remoteChainSelector: SOURCE_CHAIN_SELECTOR_1, - sourcePoolAddress: sourceTokenData.sourcePoolAddress, - sourcePoolData: sourceTokenData.extraData, + sourcePoolAddress: sourceTokenAmounts[0].sourcePoolAddress, + sourcePoolData: sourceTokenAmounts[0].extraData, offchainTokenData: offchainTokenData[0] }) ), @@ -2543,7 +2526,7 @@ contract EVM2EVMMultiOffRamp_releaseOrMintTokens is EVM2EVMMultiOffRampSetup { ); s_offRamp.releaseOrMintTokens( - srcTokenAmounts, abi.encode(OWNER), OWNER, SOURCE_CHAIN_SELECTOR_1, encodedSourceTokenData, offchainTokenData + sourceTokenAmounts, abi.encode(OWNER), OWNER, SOURCE_CHAIN_SELECTOR_1, offchainTokenData ); } @@ -2551,21 +2534,15 @@ contract EVM2EVMMultiOffRamp_releaseOrMintTokens is EVM2EVMMultiOffRampSetup { Client.EVMTokenAmount[] memory srcTokenAmounts = getCastedSourceEVMTokenAmountsWithZeroAmounts(); bytes[] memory offchainTokenData = new bytes[](srcTokenAmounts.length); - bytes[] memory sourceTokenData = _getDefaultSourceTokenData(srcTokenAmounts); + Internal.RampTokenAmount[] memory sourceTokenAmounts = _getDefaultSourceTokenData(srcTokenAmounts); bytes memory wrongAddress = abi.encode(address(1000), address(10000), address(10000)); - sourceTokenData[0] = abi.encode( - Internal.SourceTokenData({ - sourcePoolAddress: abi.encode(s_sourcePoolByToken[srcTokenAmounts[0].token]), - destTokenAddress: wrongAddress, - extraData: "" - }) - ); + sourceTokenAmounts[0].destTokenAddress = wrongAddress; vm.expectRevert(abi.encodeWithSelector(Internal.InvalidEVMAddress.selector, wrongAddress)); s_offRamp.releaseOrMintTokens( - srcTokenAmounts, abi.encode(OWNER), OWNER, SOURCE_CHAIN_SELECTOR_1, sourceTokenData, offchainTokenData + sourceTokenAmounts, abi.encode(OWNER), OWNER, SOURCE_CHAIN_SELECTOR_1, offchainTokenData ); } @@ -2573,19 +2550,16 @@ contract EVM2EVMMultiOffRamp_releaseOrMintTokens is EVM2EVMMultiOffRampSetup { // The offRamp is a contract, but not a pool address fakePoolAddress = address(s_offRamp); - bytes[] memory sourceTokenData = new bytes[](1); - sourceTokenData[0] = abi.encode( - Internal.SourceTokenData({ - sourcePoolAddress: abi.encode(fakePoolAddress), - destTokenAddress: abi.encode(s_offRamp), - extraData: "" - }) - ); + Internal.RampTokenAmount[] memory sourceTokenAmounts = new Internal.RampTokenAmount[](1); + sourceTokenAmounts[0] = Internal.RampTokenAmount({ + sourcePoolAddress: abi.encode(fakePoolAddress), + destTokenAddress: abi.encode(s_offRamp), + extraData: "", + amount: 1 + }); vm.expectRevert(abi.encodeWithSelector(EVM2EVMMultiOffRamp.NotACompatiblePool.selector, address(0))); - s_offRamp.releaseOrMintTokens( - new Client.EVMTokenAmount[](1), abi.encode(OWNER), OWNER, SOURCE_CHAIN_SELECTOR_1, sourceTokenData, new bytes[](1) - ); + s_offRamp.releaseOrMintTokens(sourceTokenAmounts, abi.encode(OWNER), OWNER, SOURCE_CHAIN_SELECTOR_1, new bytes[](1)); } function test_releaseOrMintTokens_PoolDoesNotSupportDest_Reverts() public { @@ -2596,8 +2570,7 @@ contract EVM2EVMMultiOffRamp_releaseOrMintTokens is EVM2EVMMultiOffRampSetup { bytes[] memory offchainTokenData = new bytes[](srcTokenAmounts.length); offchainTokenData[0] = abi.encode(0x12345678); - bytes[] memory encodedSourceTokenData = _getDefaultSourceTokenData(srcTokenAmounts); - Internal.SourceTokenData memory sourceTokenData = abi.decode(encodedSourceTokenData[0], (Internal.SourceTokenData)); + Internal.RampTokenAmount[] memory sourceTokenAmounts = _getDefaultSourceTokenData(srcTokenAmounts); vm.expectCall( s_destPoolBySourceToken[srcTokenAmounts[0].token], @@ -2609,15 +2582,15 @@ contract EVM2EVMMultiOffRamp_releaseOrMintTokens is EVM2EVMMultiOffRampSetup { amount: srcTokenAmounts[0].amount, localToken: s_destTokenBySourceToken[srcTokenAmounts[0].token], remoteChainSelector: SOURCE_CHAIN_SELECTOR_3, - sourcePoolAddress: sourceTokenData.sourcePoolAddress, - sourcePoolData: sourceTokenData.extraData, + sourcePoolAddress: sourceTokenAmounts[0].sourcePoolAddress, + sourcePoolData: sourceTokenAmounts[0].extraData, offchainTokenData: offchainTokenData[0] }) ) ); vm.expectRevert(); s_offRamp.releaseOrMintTokens( - srcTokenAmounts, abi.encode(OWNER), OWNER, SOURCE_CHAIN_SELECTOR_3, encodedSourceTokenData, offchainTokenData + sourceTokenAmounts, abi.encode(OWNER), OWNER, SOURCE_CHAIN_SELECTOR_3, offchainTokenData ); } @@ -2629,17 +2602,16 @@ contract EVM2EVMMultiOffRamp_releaseOrMintTokens is EVM2EVMMultiOffRampSetup { // triggers some Create2Deployer and causes it to fail vm.assume(destPool != 447301751254033913445893214690834296930546521452); bytes memory unusedVar = abi.encode(makeAddr("unused")); - bytes[] memory sourceTokenData = new bytes[](1); - sourceTokenData[0] = abi.encode( - Internal.SourceTokenData({ - sourcePoolAddress: unusedVar, - destTokenAddress: abi.encode(destPool), - extraData: unusedVar - }) - ); + Internal.RampTokenAmount[] memory sourceTokenAmounts = new Internal.RampTokenAmount[](1); + sourceTokenAmounts[0] = Internal.RampTokenAmount({ + sourcePoolAddress: unusedVar, + destTokenAddress: abi.encode(destPool), + extraData: unusedVar, + amount: 1 + }); try s_offRamp.releaseOrMintTokens( - new Client.EVMTokenAmount[](1), abi.encode(OWNER), OWNER, SOURCE_CHAIN_SELECTOR_1, sourceTokenData, new bytes[](1) + sourceTokenAmounts, abi.encode(OWNER), OWNER, SOURCE_CHAIN_SELECTOR_1, new bytes[](1) ) {} catch (bytes memory reason) { // Any revert should be a TokenHandlingError, InvalidEVMAddress, InvalidDataLength or NoContract as those are caught by the offramp assertTrue( diff --git a/contracts/src/v0.8/ccip/test/offRamp/EVM2EVMMultiOffRampSetup.t.sol b/contracts/src/v0.8/ccip/test/offRamp/EVM2EVMMultiOffRampSetup.t.sol index 7355d6a072..fcaa6139e8 100644 --- a/contracts/src/v0.8/ccip/test/offRamp/EVM2EVMMultiOffRampSetup.t.sol +++ b/contracts/src/v0.8/ccip/test/offRamp/EVM2EVMMultiOffRampSetup.t.sol @@ -250,13 +250,12 @@ contract EVM2EVMMultiOffRampSetup is TokenSetup, PriceRegistrySetup, MultiOCR3Ba Client.EVMTokenAmount[] memory destTokenAmounts = new Client.EVMTokenAmount[](numberOfTokens); for (uint256 i = 0; i < numberOfTokens; ++i) { - Internal.SourceTokenData memory sourceTokenData = - abi.decode(original.sourceTokenData[i], (Internal.SourceTokenData)); + Internal.RampTokenAmount memory tokenAmount = original.tokenAmounts[i]; - address destPoolAddress = abi.decode(sourceTokenData.destTokenAddress, (address)); + address destPoolAddress = abi.decode(tokenAmount.destTokenAddress, (address)); TokenPool pool = TokenPool(destPoolAddress); destTokenAmounts[i].token = address(pool.getToken()); - destTokenAmounts[i].amount = original.tokenAmounts[i].amount; + destTokenAmounts[i].amount = tokenAmount.amount; } return Client.Any2EVMMessage({ @@ -297,6 +296,19 @@ contract EVM2EVMMultiOffRampSetup is TokenSetup, PriceRegistrySetup, MultiOCR3Ba bool allowOutOfOrderExecution ) internal view returns (Internal.Any2EVMRampMessage memory) { bytes memory data = abi.encode(0); + + Internal.RampTokenAmount[] memory rampTokenAmounts = new Internal.RampTokenAmount[](tokenAmounts.length); + + // Correctly set the TokenDataPayload for each token. Tokens have to be set up in the TokenSetup. + for (uint256 i = 0; i < tokenAmounts.length; ++i) { + rampTokenAmounts[i] = Internal.RampTokenAmount({ + sourcePoolAddress: abi.encode(s_sourcePoolByToken[tokenAmounts[i].token]), + destTokenAddress: abi.encode(s_destTokenBySourceToken[tokenAmounts[i].token]), + extraData: "", + amount: tokenAmounts[i].amount + }); + } + Internal.Any2EVMRampMessage memory message = Internal.Any2EVMRampMessage({ header: Internal.RampMessageHeader({ messageId: "", @@ -308,22 +320,10 @@ contract EVM2EVMMultiOffRampSetup is TokenSetup, PriceRegistrySetup, MultiOCR3Ba sender: abi.encode(OWNER), data: data, receiver: address(s_receiver), - tokenAmounts: tokenAmounts, - sourceTokenData: new bytes[](tokenAmounts.length), + tokenAmounts: rampTokenAmounts, gasLimit: GAS_LIMIT }); - // Correctly set the TokenDataPayload for each token. Tokens have to be set up in the TokenSetup. - for (uint256 i = 0; i < tokenAmounts.length; ++i) { - message.sourceTokenData[i] = abi.encode( - Internal.SourceTokenData({ - sourcePoolAddress: abi.encode(s_sourcePoolByToken[tokenAmounts[i].token]), - destTokenAddress: abi.encode(s_destTokenBySourceToken[tokenAmounts[i].token]), - extraData: "" - }) - ); - } - message.header.messageId = Internal._hash(message, onRamp); return message; @@ -417,17 +417,16 @@ contract EVM2EVMMultiOffRampSetup is TokenSetup, PriceRegistrySetup, MultiOCR3Ba function _getDefaultSourceTokenData(Client.EVMTokenAmount[] memory srcTokenAmounts) internal view - returns (bytes[] memory) + returns (Internal.RampTokenAmount[] memory) { - bytes[] memory sourceTokenData = new bytes[](srcTokenAmounts.length); + Internal.RampTokenAmount[] memory sourceTokenData = new Internal.RampTokenAmount[](srcTokenAmounts.length); for (uint256 i = 0; i < srcTokenAmounts.length; ++i) { - sourceTokenData[i] = abi.encode( - Internal.SourceTokenData({ - sourcePoolAddress: abi.encode(s_sourcePoolByToken[srcTokenAmounts[i].token]), - destTokenAddress: abi.encode(s_destTokenBySourceToken[srcTokenAmounts[i].token]), - extraData: "" - }) - ); + sourceTokenData[i] = Internal.RampTokenAmount({ + sourcePoolAddress: abi.encode(s_sourcePoolByToken[srcTokenAmounts[i].token]), + destTokenAddress: abi.encode(s_destTokenBySourceToken[srcTokenAmounts[i].token]), + extraData: "", + amount: srcTokenAmounts[i].amount + }); } return sourceTokenData; } diff --git a/contracts/src/v0.8/ccip/test/onRamp/EVM2EVMMultiOnRamp.t.sol b/contracts/src/v0.8/ccip/test/onRamp/EVM2EVMMultiOnRamp.t.sol index 2b16c87e2e..253012570b 100644 --- a/contracts/src/v0.8/ccip/test/onRamp/EVM2EVMMultiOnRamp.t.sol +++ b/contracts/src/v0.8/ccip/test/onRamp/EVM2EVMMultiOnRamp.t.sol @@ -65,7 +65,7 @@ contract EVM2EVMMultiOnRamp_constructor is EVM2EVMMultiOnRampSetup { sequenceNumber: 0, metadataHash: keccak256( abi.encode( - Internal.EVM_2_EVM_MESSAGE_HASH, SOURCE_CHAIN_SELECTOR, destChainConfigArg.destChainSelector, address(s_onRamp) + Internal.EVM_2_ANY_MESSAGE_HASH, SOURCE_CHAIN_SELECTOR, destChainConfigArg.destChainSelector, address(s_onRamp) ) ) }); @@ -195,6 +195,8 @@ contract EVM2EVMMultiOnRamp_applyDestChainConfigUpdates is EVM2EVMMultiOnRampSet type(uint32).max ) ); + destChainConfigArgs.dynamicConfig.chainFamilySelector = Internal.CHAIN_FAMILY_SELECTOR_EVM; + bool isNewChain = true; if (destChainConfigArgs.destChainSelector == DEST_CHAIN_SELECTOR) { @@ -210,7 +212,7 @@ contract EVM2EVMMultiOnRamp_applyDestChainConfigUpdates is EVM2EVMMultiOnRampSet sequenceNumber: 0, metadataHash: keccak256( abi.encode( - Internal.EVM_2_EVM_MESSAGE_HASH, SOURCE_CHAIN_SELECTOR, destChainConfigArgs.destChainSelector, address(s_onRamp) + Internal.EVM_2_ANY_MESSAGE_HASH, SOURCE_CHAIN_SELECTOR, destChainConfigArgs.destChainSelector, address(s_onRamp) ) ) }); @@ -246,7 +248,7 @@ contract EVM2EVMMultiOnRamp_applyDestChainConfigUpdates is EVM2EVMMultiOnRampSet sequenceNumber: 0, metadataHash: keccak256( abi.encode( - Internal.EVM_2_EVM_MESSAGE_HASH, + Internal.EVM_2_ANY_MESSAGE_HASH, SOURCE_CHAIN_SELECTOR, destChainConfigArgs[0].destChainSelector, address(s_onRamp) @@ -260,7 +262,7 @@ contract EVM2EVMMultiOnRamp_applyDestChainConfigUpdates is EVM2EVMMultiOnRampSet sequenceNumber: 0, metadataHash: keccak256( abi.encode( - Internal.EVM_2_EVM_MESSAGE_HASH, + Internal.EVM_2_ANY_MESSAGE_HASH, SOURCE_CHAIN_SELECTOR, destChainConfigArgs[1].destChainSelector, address(s_onRamp) @@ -294,6 +296,8 @@ contract EVM2EVMMultiOnRamp_applyDestChainConfigUpdates is EVM2EVMMultiOnRampSet assertEq(vm.getRecordedLogs().length, 0); } + // Reverts + function test_InvalidDestChainConfigDestChainSelectorEqZero_Revert() public { EVM2EVMMultiOnRamp.DestChainConfigArgs[] memory destChainConfigArgs = _generateDestChainConfigArgs(); EVM2EVMMultiOnRamp.DestChainConfigArgs memory destChainConfigArg = destChainConfigArgs[0]; @@ -305,7 +309,7 @@ contract EVM2EVMMultiOnRamp_applyDestChainConfigUpdates is EVM2EVMMultiOnRampSet s_onRamp.applyDestChainConfigUpdates(destChainConfigArgs); } - function test_applyDestChainConfigUpdatesDefaultTxGasLimitEqZero() public { + function test_applyDestChainConfigUpdatesDefaultTxGasLimitEqZero_Revert() public { EVM2EVMMultiOnRamp.DestChainConfigArgs[] memory destChainConfigArgs = _generateDestChainConfigArgs(); EVM2EVMMultiOnRamp.DestChainConfigArgs memory destChainConfigArg = destChainConfigArgs[0]; @@ -343,6 +347,18 @@ contract EVM2EVMMultiOnRamp_applyDestChainConfigUpdates is EVM2EVMMultiOnRampSet s_onRamp.applyDestChainConfigUpdates(destChainConfigArgs); } + + function test_InvalidChainFamilySelector_Revert() public { + EVM2EVMMultiOnRamp.DestChainConfigArgs[] memory destChainConfigArgs = _generateDestChainConfigArgs(); + EVM2EVMMultiOnRamp.DestChainConfigArgs memory destChainConfigArg = destChainConfigArgs[0]; + + destChainConfigArg.dynamicConfig.chainFamilySelector = bytes4(uint32(1)); + + vm.expectRevert( + abi.encodeWithSelector(EVM2EVMMultiOnRamp.InvalidDestChainConfig.selector, destChainConfigArg.destChainSelector) + ); + s_onRamp.applyDestChainConfigUpdates(destChainConfigArgs); + } } contract EVM2EVMMultiOnRamp_forwardFromRouter is EVM2EVMMultiOnRampSetup { @@ -576,7 +592,7 @@ contract EVM2EVMMultiOnRamp_forwardFromRouter is EVM2EVMMultiOnRampSetup { // Make sure the tokens are in the contract deal(s_sourceFeeToken, address(s_onRamp), feeTokenAmount); - Internal.EVM2EVMMessage memory expectedEvent = _messageToEvent(message, 1, 1, feeTokenAmount, originalSender); + Internal.EVM2AnyRampMessage memory expectedEvent = _messageToEvent(message, 1, 1, feeTokenAmount, originalSender); vm.expectEmit(); emit EVM2EVMMultiOnRamp.FeePaid(s_sourceFeeToken, feeTokenAmount); @@ -585,7 +601,8 @@ contract EVM2EVMMultiOnRamp_forwardFromRouter is EVM2EVMMultiOnRampSetup { // Assert the message Id is correct assertEq( - expectedEvent.messageId, s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, feeTokenAmount, originalSender) + expectedEvent.header.messageId, + s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, feeTokenAmount, originalSender) ); } @@ -735,7 +752,7 @@ contract EVM2EVMMultiOnRamp_forwardFromRouter is EVM2EVMMultiOnRampSetup { s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, 1, OWNER); } - function test_InvalidAddress_Revert() public { + function test_InvalidEVMAddress_Revert() public { Client.EVM2AnyMessage memory message = _generateEmptyMessage(); message.receiver = abi.encode(type(uint208).max); @@ -854,6 +871,41 @@ contract EVM2EVMMultiOnRamp_forwardFromRouter is EVM2EVMMultiOnRampSetup { s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, 0, OWNER); } + function test_InvalidEVMAddressDestToken_Revert() public { + address sourceETH = s_sourceTokens[1]; + vm.stopPrank(); + vm.startPrank(OWNER); + + MaybeRevertingBurnMintTokenPool newPool = new MaybeRevertingBurnMintTokenPool( + BurnMintERC677(sourceETH), new address[](0), address(s_mockRMN), address(s_sourceRouter) + ); + BurnMintERC677(sourceETH).grantMintAndBurnRoles(address(newPool)); + deal(address(sourceETH), address(newPool), type(uint256).max); + + // Add TokenPool to OnRamp + s_tokenAdminRegistry.setPool(sourceETH, address(newPool)); + + bytes memory nonEvmAddress = abi.encode(type(uint208).max); + + // Allow chain in TokenPool + TokenPool.ChainUpdate[] memory chainUpdates = new TokenPool.ChainUpdate[](1); + chainUpdates[0] = TokenPool.ChainUpdate({ + remoteChainSelector: DEST_CHAIN_SELECTOR, + remotePoolAddress: abi.encode(s_destTokenPool), + remoteTokenAddress: nonEvmAddress, + allowed: true, + outboundRateLimiterConfig: getOutboundRateLimiterConfig(), + inboundRateLimiterConfig: getInboundRateLimiterConfig() + }); + newPool.applyChainUpdates(chainUpdates); + + Client.EVM2AnyMessage memory message = _generateSingleTokenMessage(address(sourceETH), 1000); + + vm.startPrank(address(s_sourceRouter)); + vm.expectRevert(abi.encodeWithSelector(Internal.InvalidEVMAddress.selector, nonEvmAddress)); + s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, 0, OWNER); + } + function test_forwardFromRouter_UnsupportedToken_Revert() public { Client.EVM2AnyMessage memory message = _generateEmptyMessage(); message.tokenAmounts = new Client.EVMTokenAmount[](1); @@ -968,7 +1020,7 @@ contract EVM2EVMMultiOnRamp_getDataAvailabilityCost is EVM2EVMMultiOnRamp_getFee s_onRamp.getDestChainConfig(DEST_CHAIN_SELECTOR).dynamicConfig; uint256 dataAvailabilityGas = destChainDynamicConfig.destDataAvailabilityOverheadGas - + destChainDynamicConfig.destGasPerDataAvailabilityByte * Internal.MESSAGE_FIXED_BYTES; + + destChainDynamicConfig.destGasPerDataAvailabilityByte * Internal.ANY_2_EVM_MESSAGE_FIXED_BYTES; uint256 expectedDataAvailabilityCostUSD = USD_PER_DATA_AVAILABILITY_GAS * dataAvailabilityGas * destChainDynamicConfig.destDataAvailabilityMultiplierBps * 1e14; @@ -989,7 +1041,7 @@ contract EVM2EVMMultiOnRamp_getDataAvailabilityCost is EVM2EVMMultiOnRamp_getFee uint256 dataAvailabilityCostUSD2 = s_onRamp.getDataAvailabilityCost(DEST_CHAIN_SELECTOR + 1, USD_PER_DATA_AVAILABILITY_GAS, 0, 0, 0); dataAvailabilityGas = destChainDynamicConfig.destDataAvailabilityOverheadGas - + destChainDynamicConfig.destGasPerDataAvailabilityByte * Internal.MESSAGE_FIXED_BYTES; + + destChainDynamicConfig.destGasPerDataAvailabilityByte * Internal.ANY_2_EVM_MESSAGE_FIXED_BYTES; expectedDataAvailabilityCostUSD = USD_PER_DATA_AVAILABILITY_GAS * dataAvailabilityGas * destChainDynamicConfig.destDataAvailabilityMultiplierBps * 1e14; @@ -1005,7 +1057,7 @@ contract EVM2EVMMultiOnRamp_getDataAvailabilityCost is EVM2EVMMultiOnRamp_getFee s_onRamp.getDestChainConfig(DEST_CHAIN_SELECTOR).dynamicConfig; uint256 dataAvailabilityLengthBytes = - Internal.MESSAGE_FIXED_BYTES + 100 + (5 * Internal.MESSAGE_FIXED_BYTES_PER_TOKEN) + 50; + Internal.ANY_2_EVM_MESSAGE_FIXED_BYTES + 100 + (5 * Internal.ANY_2_EVM_MESSAGE_FIXED_BYTES_PER_TOKEN) + 50; uint256 dataAvailabilityGas = destChainDynamicConfig.destDataAvailabilityOverheadGas + destChainDynamicConfig.destGasPerDataAvailabilityByte * dataAvailabilityLengthBytes; uint256 expectedDataAvailabilityCostUSD = USD_PER_DATA_AVAILABILITY_GAS * dataAvailabilityGas @@ -1055,6 +1107,7 @@ contract EVM2EVMMultiOnRamp_getDataAvailabilityCost is EVM2EVMMultiOnRamp_getFee destChainConfigArgs[0].dynamicConfig.destGasPerDataAvailabilityByte = destGasPerDataAvailabilityByte; destChainConfigArgs[0].dynamicConfig.destDataAvailabilityMultiplierBps = destDataAvailabilityMultiplierBps; destChainConfigArgs[0].dynamicConfig.defaultTxGasLimit = GAS_LIMIT; + destChainConfigArgs[0].dynamicConfig.chainFamilySelector = Internal.CHAIN_FAMILY_SELECTOR_EVM; s_onRamp.applyDestChainConfigUpdates(destChainConfigArgs); @@ -1066,8 +1119,8 @@ contract EVM2EVMMultiOnRamp_getDataAvailabilityCost is EVM2EVMMultiOnRamp_getFee tokenTransferBytesOverhead ); - uint256 dataAvailabilityLengthBytes = Internal.MESSAGE_FIXED_BYTES + messageDataLength - + (numberOfTokens * Internal.MESSAGE_FIXED_BYTES_PER_TOKEN) + tokenTransferBytesOverhead; + uint256 dataAvailabilityLengthBytes = Internal.ANY_2_EVM_MESSAGE_FIXED_BYTES + messageDataLength + + (numberOfTokens * Internal.ANY_2_EVM_MESSAGE_FIXED_BYTES_PER_TOKEN) + tokenTransferBytesOverhead; uint256 dataAvailabilityGas = destDataAvailabilityOverheadGas + destGasPerDataAvailabilityByte * dataAvailabilityLengthBytes; @@ -1716,7 +1769,7 @@ contract EVM2EVMMultiOnRamp_withdrawFeeTokens is EVM2EVMMultiOnRampSetup { } } -contract EVM2EVMOnRamp_applyPremiumMultiplierWeiPerEthUpdates is EVM2EVMMultiOnRampSetup { +contract EVM2EVMMultiOnRamp_applyPremiumMultiplierWeiPerEthUpdates is EVM2EVMMultiOnRampSetup { function test_Fuzz_applyPremiumMultiplierWeiPerEthUpdates_Success( EVM2EVMMultiOnRamp.PremiumMultiplierWeiPerEthArgs memory premiumMultiplierWeiPerEthArg ) public { @@ -1994,3 +2047,94 @@ contract EVM2EVMMultiOnRamp_getTokenPool is EVM2EVMMultiOnRampSetup { assertEq(address(0), nonExistentPool); } } + +contract EVM2EVMMultiOnRamp_validateDestFamilyAddress is EVM2EVMMultiOnRampSetup { + function test_ValidEVMAddress_Success() public view { + bytes memory encodedAddress = abi.encode(address(10000)); + s_onRamp.validateDestFamilyAddress(Internal.CHAIN_FAMILY_SELECTOR_EVM, encodedAddress); + } + + function test_ValidNonEVMAddress_Success() public view { + s_onRamp.validateDestFamilyAddress(bytes4(uint32(1)), abi.encode(type(uint208).max)); + } + + // Reverts + + function test_InvalidEVMAddress_Revert() public { + bytes memory invalidAddress = abi.encode(type(uint208).max); + vm.expectRevert(abi.encodeWithSelector(Internal.InvalidEVMAddress.selector, invalidAddress)); + s_onRamp.validateDestFamilyAddress(Internal.CHAIN_FAMILY_SELECTOR_EVM, invalidAddress); + } +} + +contract EVM2EVMMultiOnRamp_convertParsedExtraArgs is EVM2EVMMultiOnRampSetup { + EVM2EVMMultiOnRamp.DestChainDynamicConfig private s_destChainDynamicConfig; + + function setUp() public virtual override { + super.setUp(); + s_destChainDynamicConfig = _generateDestChainConfigArgs()[0].dynamicConfig; + } + + function test_EVMExtraArgsV1_Success() public view { + Client.EVMExtraArgsV1 memory inputArgs = Client.EVMExtraArgsV1({gasLimit: GAS_LIMIT}); + bytes memory inputExtraArgs = Client._argsToBytes(inputArgs); + Client.EVMExtraArgsV2 memory expectedOutputArgs = + Client.EVMExtraArgsV2({gasLimit: GAS_LIMIT, allowOutOfOrderExecution: false}); + + vm.assertEq( + s_onRamp.convertParsedExtraArgs(inputExtraArgs, s_destChainDynamicConfig), abi.encode(expectedOutputArgs) + ); + } + + function test_EVMExtraArgsV2_Success() public view { + Client.EVMExtraArgsV2 memory inputArgs = + Client.EVMExtraArgsV2({gasLimit: GAS_LIMIT, allowOutOfOrderExecution: true}); + bytes memory inputExtraArgs = Client._argsToBytes(inputArgs); + + vm.assertEq(s_onRamp.convertParsedExtraArgs(inputExtraArgs, s_destChainDynamicConfig), abi.encode(inputArgs)); + } + + function test_EVMExtraArgsDefault_Success() public view { + Client.EVMExtraArgsV2 memory expectedOutputArgs = + Client.EVMExtraArgsV2({gasLimit: s_destChainDynamicConfig.defaultTxGasLimit, allowOutOfOrderExecution: false}); + + vm.assertEq(s_onRamp.convertParsedExtraArgs("", s_destChainDynamicConfig), abi.encode(expectedOutputArgs)); + } + + function test_EmptyExtraArgs_Success() public { + s_destChainDynamicConfig.chainFamilySelector = bytes4(uint32(1)); + vm.assertEq(s_onRamp.convertParsedExtraArgs("", s_destChainDynamicConfig), ""); + } + + // Reverts + + function test_EVMExtraArgsInvalidExtraArgsTag_Revert() public { + Client.EVMExtraArgsV2 memory inputArgs = + Client.EVMExtraArgsV2({gasLimit: GAS_LIMIT, allowOutOfOrderExecution: true}); + bytes memory inputExtraArgs = Client._argsToBytes(inputArgs); + // Invalidate selector + inputExtraArgs[0] = bytes1(uint8(0)); + + vm.expectRevert(EVM2EVMMultiOnRamp.InvalidExtraArgsTag.selector); + s_onRamp.convertParsedExtraArgs(inputExtraArgs, s_destChainDynamicConfig); + } + + function test_EVMExtraArgsEnforceOutOfOrder_Revert() public { + Client.EVMExtraArgsV2 memory inputArgs = + Client.EVMExtraArgsV2({gasLimit: GAS_LIMIT, allowOutOfOrderExecution: false}); + bytes memory inputExtraArgs = Client._argsToBytes(inputArgs); + s_destChainDynamicConfig.enforceOutOfOrder = true; + + vm.expectRevert(EVM2EVMMultiOnRamp.ExtraArgOutOfOrderExecutionMustBeTrue.selector); + s_onRamp.convertParsedExtraArgs(inputExtraArgs, s_destChainDynamicConfig); + } + + function test_EVMExtraArgsGasLimitTooHigh_Revert() public { + Client.EVMExtraArgsV2 memory inputArgs = + Client.EVMExtraArgsV2({gasLimit: s_destChainDynamicConfig.maxPerMsgGasLimit + 1, allowOutOfOrderExecution: true}); + bytes memory inputExtraArgs = Client._argsToBytes(inputArgs); + + vm.expectRevert(EVM2EVMMultiOnRamp.MessageGasLimitTooHigh.selector); + s_onRamp.convertParsedExtraArgs(inputExtraArgs, s_destChainDynamicConfig); + } +} diff --git a/contracts/src/v0.8/ccip/test/onRamp/EVM2EVMMultiOnRampSetup.t.sol b/contracts/src/v0.8/ccip/test/onRamp/EVM2EVMMultiOnRampSetup.t.sol index d9e2921421..97136d048f 100644 --- a/contracts/src/v0.8/ccip/test/onRamp/EVM2EVMMultiOnRampSetup.t.sol +++ b/contracts/src/v0.8/ccip/test/onRamp/EVM2EVMMultiOnRampSetup.t.sol @@ -21,9 +21,6 @@ import {PriceRegistrySetup} from "../priceRegistry/PriceRegistry.t.sol"; import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; contract EVM2EVMMultiOnRampSetup is TokenSetup, PriceRegistrySetup { - // Duplicate event of the CCIPSendRequested in the IOnRamp - event CCIPSendRequested(Internal.EVM2EVMMessage message); - address internal constant CUSTOM_TOKEN = address(12345); uint224 internal constant CUSTOM_TOKEN_PRICE = 1e17; // $0.1 CUSTOM @@ -170,7 +167,7 @@ contract EVM2EVMMultiOnRampSetup is TokenSetup, PriceRegistrySetup { uint64 nonce, uint256 feeTokenAmount, address originalSender - ) public view returns (Internal.EVM2EVMMessage memory) { + ) public view returns (Internal.EVM2AnyRampMessage memory) { return _messageToEvent( message, SOURCE_CHAIN_SELECTOR, @@ -194,46 +191,46 @@ contract EVM2EVMMultiOnRampSetup is TokenSetup, PriceRegistrySetup { address originalSender, bytes32 metadataHash, TokenAdminRegistry tokenAdminRegistry - ) internal view returns (Internal.EVM2EVMMessage memory) { - Client.EVMExtraArgsV2 memory extraArgs = s_onRamp.extraArgsFromBytes(message.extraArgs, destChainSelector); - - Internal.EVM2EVMMessage memory messageEvent = Internal.EVM2EVMMessage({ - sequenceNumber: seqNum, - feeTokenAmount: feeTokenAmount, + ) internal view returns (Internal.EVM2AnyRampMessage memory) { + Client.EVMExtraArgsV2 memory extraArgs = s_onRamp.parseEVMExtraArgsFromBytes(message.extraArgs, destChainSelector); + + Internal.EVM2AnyRampMessage memory messageEvent = Internal.EVM2AnyRampMessage({ + header: Internal.RampMessageHeader({ + messageId: "", + sourceChainSelector: sourceChainSelector, + destChainSelector: destChainSelector, + sequenceNumber: seqNum, + nonce: extraArgs.allowOutOfOrderExecution ? 0 : nonce + }), sender: originalSender, - nonce: extraArgs.allowOutOfOrderExecution ? 0 : nonce, - gasLimit: extraArgs.gasLimit, - strict: false, - sourceChainSelector: sourceChainSelector, - receiver: abi.decode(message.receiver, (address)), data: message.data, - tokenAmounts: message.tokenAmounts, - sourceTokenData: new bytes[](message.tokenAmounts.length), + receiver: message.receiver, + extraArgs: abi.encode(extraArgs), feeToken: message.feeToken, - messageId: "" + feeTokenAmount: feeTokenAmount, + tokenAmounts: new Internal.RampTokenAmount[](message.tokenAmounts.length) }); for (uint256 i = 0; i < message.tokenAmounts.length; ++i) { - messageEvent.sourceTokenData[i] = _getSourceTokenData(message.tokenAmounts[i], tokenAdminRegistry); + messageEvent.tokenAmounts[i] = _getSourceTokenData(message.tokenAmounts[i], tokenAdminRegistry); } - messageEvent.messageId = Internal._hash(messageEvent, metadataHash); + messageEvent.header.messageId = Internal._hash(messageEvent, metadataHash); return messageEvent; } function _getSourceTokenData( Client.EVMTokenAmount memory tokenAmount, TokenAdminRegistry tokenAdminRegistry - ) internal view returns (bytes memory) { + ) internal view returns (Internal.RampTokenAmount memory) { address destToken = s_destTokenBySourceToken[tokenAmount.token]; - return abi.encode( - Internal.SourceTokenData({ - sourcePoolAddress: abi.encode(tokenAdminRegistry.getTokenConfig(tokenAmount.token).tokenPool), - destTokenAddress: abi.encode(destToken), - extraData: "" - }) - ); + return Internal.RampTokenAmount({ + sourcePoolAddress: abi.encode(tokenAdminRegistry.getTokenConfig(tokenAmount.token).tokenPool), + destTokenAddress: abi.encode(destToken), + extraData: "", + amount: tokenAmount.amount + }); } function _generateDynamicMultiOnRampConfig( @@ -277,7 +274,8 @@ contract EVM2EVMMultiOnRampSetup is TokenSetup, PriceRegistrySetup { defaultTxGasLimit: GAS_LIMIT, gasMultiplierWeiPerEth: 5e17, networkFeeUSDCents: 1_00, - enforceOutOfOrder: false + enforceOutOfOrder: false, + chainFamilySelector: Internal.CHAIN_FAMILY_SELECTOR_EVM }), prevOnRamp: address(0) }); @@ -327,7 +325,7 @@ contract EVM2EVMMultiOnRampSetup is TokenSetup, PriceRegistrySetup { return ( onRamp, - keccak256(abi.encode(Internal.EVM_2_EVM_MESSAGE_HASH, sourceChainSelector, DEST_CHAIN_SELECTOR, address(onRamp))) + keccak256(abi.encode(Internal.EVM_2_ANY_MESSAGE_HASH, sourceChainSelector, DEST_CHAIN_SELECTOR, address(onRamp))) ); } diff --git a/contracts/src/v0.8/keystone/CapabilitiesRegistry.sol b/contracts/src/v0.8/keystone/CapabilitiesRegistry.sol index 56df507867..6b150dc5d7 100644 --- a/contracts/src/v0.8/keystone/CapabilitiesRegistry.sol +++ b/contracts/src/v0.8/keystone/CapabilitiesRegistry.sol @@ -1010,10 +1010,7 @@ contract CapabilitiesRegistry is OwnerIsCreator, TypeAndVersionInterface { /// beforeCapabilityConfigSet if ( capability.configurationContract.code.length == 0 || - !IERC165(capability.configurationContract).supportsInterface( - ICapabilityConfiguration.getCapabilityConfiguration.selector ^ - ICapabilityConfiguration.beforeCapabilityConfigSet.selector - ) + !IERC165(capability.configurationContract).supportsInterface(type(ICapabilityConfiguration).interfaceId) ) revert InvalidCapabilityConfigurationContractInterface(capability.configurationContract); } s_capabilities[hashedCapabilityId] = capability; diff --git a/contracts/src/v0.8/vrf/dev/VRFV2PlusWrapper.sol b/contracts/src/v0.8/vrf/dev/VRFV2PlusWrapper.sol index 1d74128085..86578fb140 100644 --- a/contracts/src/v0.8/vrf/dev/VRFV2PlusWrapper.sol +++ b/contracts/src/v0.8/vrf/dev/VRFV2PlusWrapper.sol @@ -232,6 +232,7 @@ contract VRFV2PlusWrapper is ConfirmedOwner, TypeAndVersionInterface, VRFConsume * @param _fulfillmentFlatFeeLinkDiscountPPM is the flat fee discount in millionths of native that VRFCoordinatorV2Plus * charges for link payment. */ + /// @dev This function while having only 12 parameters is causing a Stack too deep error when running forge coverage. function setConfig( uint32 _wrapperGasOverhead, uint32 _coordinatorGasOverheadNative, diff --git a/contracts/src/v0.8/vrf/test/BatchVRFCoordinatorV2Plus.t.sol b/contracts/src/v0.8/vrf/test/BatchVRFCoordinatorV2Plus.t.sol new file mode 100644 index 0000000000..298af8c76e --- /dev/null +++ b/contracts/src/v0.8/vrf/test/BatchVRFCoordinatorV2Plus.t.sol @@ -0,0 +1,178 @@ +pragma solidity 0.8.19; + +import {console} from "forge-std/console.sol"; +import {VRF} from "../VRF.sol"; +import {VRFTypes} from "../VRFTypes.sol"; +import {BatchVRFCoordinatorV2Plus} from "../dev/BatchVRFCoordinatorV2Plus.sol"; +import {VRFV2PlusClient} from "../dev/libraries/VRFV2PlusClient.sol"; +import {VRFCoordinatorV2_5} from "../dev/VRFCoordinatorV2_5.sol"; +import "./BaseTest.t.sol"; +import {FixtureVRFCoordinatorV2_5} from "./FixtureVRFCoordinatorV2_5.t.sol"; + +contract BatchVRFCoordinatorV2PlusTest is FixtureVRFCoordinatorV2_5 { + BatchVRFCoordinatorV2Plus private s_batchCoordinator; + + event RandomWordsFulfilled( + uint256 indexed requestId, + uint256 outputSeed, + uint256 indexed subId, + uint96 payment, + bool nativePayment, + bool success, + bool onlyPremium + ); + + function setUp() public override { + FixtureVRFCoordinatorV2_5.setUp(); + + s_batchCoordinator = new BatchVRFCoordinatorV2Plus(address(s_coordinator)); + } + + function test_fulfillRandomWords() public { + _setUpConfig(); + _setUpProvingKey(); + _setUpSubscription(); + + uint32 requestBlock = 10; + vm.roll(requestBlock); + + vm.startPrank(SUBSCRIPTION_OWNER); + vm.deal(SUBSCRIPTION_OWNER, 10 ether); + s_coordinator.fundSubscriptionWithNative{value: 10 ether}(s_subId); + + // Request random words. + s_consumer.requestRandomWords(CALLBACK_GAS_LIMIT, MIN_CONFIRMATIONS, NUM_WORDS, VRF_KEY_HASH, true); + vm.stopPrank(); + + // Move on to the next block. + // Store the previous block's blockhash. + vm.roll(requestBlock + 1); + s_bhs.store(requestBlock); + + VRFTypes.Proof[] memory proofs = new VRFTypes.Proof[](2); + VRFTypes.RequestCommitmentV2Plus[] memory rcs = new VRFTypes.RequestCommitmentV2Plus[](2); + + // Proof generated via the generate-proof-v2-plus script command. Example usage: + // _printGenerateProofV2PlusCommand(address(s_consumer), 1, requestBlock, true); + /* + go run . generate-proof-v2-plus \ + -key-hash 0x9f2353bde94264dbc3d554a94cceba2d7d2b4fdce4304d3e09a1fea9fbeb1528 \ + -pre-seed 33855227690351884611579800220581891477580182035146587491531555927634180294480 \ + -block-hash 0x0a \ + -block-num 10 \ + -sender 0xdc90e8ce61c1af8a638b95264037c8e67ee5765c \ + -native-payment true + + */ + proofs[0] = VRFTypes.Proof({ + pk: [ + 72488970228380509287422715226575535698893157273063074627791787432852706183111, + 62070622898698443831883535403436258712770888294397026493185421712108624767191 + ], + gamma: [ + 80420391742429647505172101941811820476888293644816377569181566466584288434705, + 24046736031266889997051641830469514057863365715722268340801477580836256044582 + ], + c: 74775128390693502914275156263410881155583102046081919417827483535122161050585, + s: 69563235412360165148368009853509434870917653835330501139204071967997764190111, + seed: 33855227690351884611579800220581891477580182035146587491531555927634180294480, + uWitness: 0xfB0663eaf48785540dE0FD0F837FD9c09BF4B80A, + cGammaWitness: [ + 53711159452748734758194447734939737695995909567499536035707522847057731697403, + 113650002631484103366420937668971311744887820666944514581352028601506700116835 + ], + sHashWitness: [ + 89656531714223714144489731263049239277719465105516547297952288438117443488525, + 90859682705760125677895017864538514058733199985667976488434404721197234427011 + ], + zInv: 97275608653505690744303242942631893944856831559408852202478373762878300587548 + }); + rcs[0] = VRFTypes.RequestCommitmentV2Plus({ + blockNum: requestBlock, + subId: s_subId, + callbackGasLimit: CALLBACK_GAS_LIMIT, + numWords: 1, + sender: address(s_consumer), + extraArgs: VRFV2PlusClient._argsToBytes(VRFV2PlusClient.ExtraArgsV1({nativePayment: true})) + }); + + VRFCoordinatorV2_5.Output memory output = s_coordinator.getRandomnessFromProofExternal( + abi.decode(abi.encode(proofs[0]), (VRF.Proof)), + rcs[0] + ); + + requestBlock = 20; + vm.roll(requestBlock); + + vm.startPrank(SUBSCRIPTION_OWNER); + s_linkToken.setBalance(address(SUBSCRIPTION_OWNER), 10 ether); + s_linkToken.transferAndCall(address(s_coordinator), 10 ether, abi.encode(s_subId)); + + // Request random words. + s_consumer1.requestRandomWords(CALLBACK_GAS_LIMIT, MIN_CONFIRMATIONS, NUM_WORDS, VRF_KEY_HASH, false); + vm.stopPrank(); + + // Move on to the next block. + // Store the previous block's blockhash. + vm.roll(requestBlock + 1); + s_bhs.store(requestBlock); + + // Proof generated via the generate-proof-v2-plus script command. Example usage: + // _printGenerateProofV2PlusCommand(address(s_consumer1), 1, requestBlock, false); + /* + go run . generate-proof-v2-plus \ + -key-hash 0x9f2353bde94264dbc3d554a94cceba2d7d2b4fdce4304d3e09a1fea9fbeb1528 \ + -pre-seed 76568185840201037774581758921393822690942290841865097674309745036496166431060 \ + -block-hash 0x14 \ + -block-num 20 \ + -sender 0x2f1c0761d6e4b1e5f01968d6c746f695e5f3e25d \ + -native-payment false + */ + proofs[1] = VRFTypes.Proof({ + pk: [ + 72488970228380509287422715226575535698893157273063074627791787432852706183111, + 62070622898698443831883535403436258712770888294397026493185421712108624767191 + ], + gamma: [ + 21323932463597506192387578758854201988004673105893105492473194972397109828006, + 96834737826889397196571646974355352644437196500310392203712129010026003355112 + ], + c: 8775807990949224376582975115621037245862755412370175152581490650310350359728, + s: 6805708577951013810918872616271445638109899206333819877111740872779453350091, + seed: 76568185840201037774581758921393822690942290841865097674309745036496166431060, + uWitness: 0xE82fF24Fecfbe73d682f38308bE3E039Dfabdf5c, + cGammaWitness: [ + 92810770919624535241476539842820168209710445519252592382122118536598338376923, + 17271305664006119131434661141858450289379246199095231636439133258170648418554 + ], + sHashWitness: [ + 29540023305939374439696120003978246982707698669656874393367212257432197207536, + 93902323936532381028323379401739289810874348405259732508442252936582467730050 + ], + zInv: 88845170436601946907659333156418518556235340365885668267853966404617557948692 + }); + rcs[1] = VRFTypes.RequestCommitmentV2Plus({ + blockNum: requestBlock, + subId: s_subId, + callbackGasLimit: CALLBACK_GAS_LIMIT, + numWords: 1, + sender: address(s_consumer1), + extraArgs: VRFV2PlusClient._argsToBytes(VRFV2PlusClient.ExtraArgsV1({nativePayment: false})) + }); + + VRFCoordinatorV2_5.Output memory output1 = s_coordinator.getRandomnessFromProofExternal( + abi.decode(abi.encode(proofs[1]), (VRF.Proof)), + rcs[1] + ); + + // The payments are NOT pre-calculated and simply copied from the actual event. + // We can assert and ignore the payment field but the code will be considerably longer. + vm.expectEmit(true, true, false, true, address(s_coordinator)); + emit RandomWordsFulfilled(output.requestId, output.randomness, s_subId, 500000000000142215, true, true, false); + vm.expectEmit(true, true, false, true, address(s_coordinator)); + emit RandomWordsFulfilled(output1.requestId, output1.randomness, s_subId, 800000000000304103, false, true, false); + + // Fulfill the requests. + s_batchCoordinator.fulfillRandomWords(proofs, rcs); + } +} diff --git a/contracts/src/v0.8/vrf/test/FixtureVRFCoordinatorV2_5.t.sol b/contracts/src/v0.8/vrf/test/FixtureVRFCoordinatorV2_5.t.sol new file mode 100644 index 0000000000..c1c2c7eb27 --- /dev/null +++ b/contracts/src/v0.8/vrf/test/FixtureVRFCoordinatorV2_5.t.sol @@ -0,0 +1,134 @@ +pragma solidity ^0.8.19; + +import {console} from "forge-std/console.sol"; +import "@openzeppelin/contracts/utils/Strings.sol"; +import {VRF} from "../VRF.sol"; +import {VRFTypes} from "../VRFTypes.sol"; +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 "./BaseTest.t.sol"; + +contract FixtureVRFCoordinatorV2_5 is BaseTest, VRF { + address internal SUBSCRIPTION_OWNER = makeAddr("SUBSCRIPTION_OWNER"); + + uint64 internal constant GAS_LANE_MAX_GAS = 5000 gwei; + uint16 internal constant MIN_CONFIRMATIONS = 0; + uint32 internal constant CALLBACK_GAS_LIMIT = 1_000_000; + uint32 internal constant NUM_WORDS = 1; + + // VRF KeyV2 generated from a node; not sensitive information. + // The secret key used to generate this key is: 10. + bytes internal constant VRF_UNCOMPRESSED_PUBLIC_KEY = + hex"a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7893aba425419bc27a3b6c7e693a24c696f794c2ed877a1593cbee53b037368d7"; + bytes internal constant VRF_COMPRESSED_PUBLIC_KEY = + hex"a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c701"; + bytes32 internal constant VRF_KEY_HASH = hex"9f2353bde94264dbc3d554a94cceba2d7d2b4fdce4304d3e09a1fea9fbeb1528"; + + BlockhashStore internal s_bhs; + ExposedVRFCoordinatorV2_5 internal s_coordinator; + + // Use multiple consumers because VRFV2PlusConsumerExample cannot have multiple pending requests. + uint256 internal s_subId; + VRFV2PlusConsumerExample internal s_consumer; + VRFV2PlusConsumerExample internal s_consumer1; + + MockLinkToken internal s_linkToken; + MockV3Aggregator internal s_linkNativeFeed; + + function setUp() public virtual override { + BaseTest.setUp(); + vm.stopPrank(); + + vm.startPrank(OWNER); + s_bhs = new BlockhashStore(); + + // Deploy coordinator. + s_coordinator = new ExposedVRFCoordinatorV2_5(address(s_bhs)); + s_linkToken = new MockLinkToken(); + s_linkNativeFeed = new MockV3Aggregator(18, 500000000000000000); // .5 ETH (good for testing) + + // Configure the coordinator. + s_coordinator.setLINKAndLINKNativeFeed(address(s_linkToken), address(s_linkNativeFeed)); + vm.stopPrank(); + + // Deploy consumers. + vm.startPrank(SUBSCRIPTION_OWNER); + s_consumer = new VRFV2PlusConsumerExample(address(s_coordinator), address(s_linkToken)); + s_consumer1 = new VRFV2PlusConsumerExample(address(s_coordinator), address(s_linkToken)); + vm.stopPrank(); + } + + function _setUpConfig() internal { + vm.prank(OWNER); + s_coordinator.setConfig( + 0, // minRequestConfirmations + 2_500_000, // maxGasLimit + 1, // stalenessSeconds + 50_000, // gasAfterPaymentCalculation + 50000000000000000, // fallbackWeiPerUnitLink + 500_000, // fulfillmentFlatFeeNativePPM + 100_000, // fulfillmentFlatFeeLinkDiscountPPM + 15, // nativePremiumPercentage + 10 // linkPremiumPercentage + ); + } + + function _setUpProvingKey() internal { + uint256[2] memory uncompressedKeyParts = this._getProvingKeyParts(VRF_UNCOMPRESSED_PUBLIC_KEY); + vm.prank(OWNER); + s_coordinator.registerProvingKey(uncompressedKeyParts, GAS_LANE_MAX_GAS); + } + + function _setUpSubscription() internal { + vm.startPrank(SUBSCRIPTION_OWNER); + s_subId = s_coordinator.createSubscription(); + s_coordinator.addConsumer(s_subId, address(s_consumer)); + s_consumer.setSubId(s_subId); + s_coordinator.addConsumer(s_subId, address(s_consumer1)); + s_consumer1.setSubId(s_subId); + vm.stopPrank(); + } + + // note: Call this function via this.getProvingKeyParts to be able to pass memory as calldata and + // index over the byte array. + function _getProvingKeyParts(bytes calldata uncompressedKey) public pure returns (uint256[2] memory) { + uint256 keyPart1 = uint256(bytes32(uncompressedKey[0:32])); + uint256 keyPart2 = uint256(bytes32(uncompressedKey[32:64])); + return [keyPart1, keyPart2]; + } + + /** + * Prints the command to generate a proof for a VRF request. + * + * This function provides a convenient way to generate the proof off-chain to be copied into the tests. + * + * An example of the command looks like this: + * go run . generate-proof-v2-plus \ + * -key-hash 0x9f2353bde94264dbc3d554a94cceba2d7d2b4fdce4304d3e09a1fea9fbeb1528 \ + * -pre-seed 76568185840201037774581758921393822690942290841865097674309745036496166431060 \ + * -block-hash 0x14 \ + * -block-num 20 \ + * -sender 0x2f1c0761d6e4b1e5f01968d6c746f695e5f3e25d \ + * -native-payment false + */ + function _printGenerateProofV2PlusCommand( + address sender, + uint64 nonce, + uint256 requestBlock, + bool nativePayment + ) internal { + (, uint256 preSeed) = s_coordinator.computeRequestIdExternal(VRF_KEY_HASH, sender, s_subId, nonce); + + console.log("go run . generate-proof-v2-plus \\"); + console.log(string.concat(" -key-hash ", Strings.toHexString(uint256(VRF_KEY_HASH)), " \\")); + console.log(string.concat(" -pre-seed ", Strings.toString(preSeed), " \\")); + console.log(string.concat(" -block-hash ", Strings.toHexString(uint256(blockhash(requestBlock))), " \\")); + console.log(string.concat(" -block-num ", Strings.toString(requestBlock), " \\")); + console.log(string.concat(" -sender ", Strings.toHexString(sender), " \\")); + console.log(string.concat(" -native-payment ", nativePayment ? "true" : "false")); + } +} diff --git a/contracts/test/v0.8/automation/IKeeperRegistryMaster.test.ts b/contracts/test/v0.8/automation/IKeeperRegistryMaster.test.ts deleted file mode 100644 index f894aa87cd..0000000000 --- a/contracts/test/v0.8/automation/IKeeperRegistryMaster.test.ts +++ /dev/null @@ -1,116 +0,0 @@ -import fs from 'fs' -import { ethers } from 'hardhat' -import { assert } from 'chai' -import { KeeperRegistry2_1__factory as KeeperRegistryFactory } from '../../../typechain/factories/KeeperRegistry2_1__factory' -import { KeeperRegistryLogicA2_1__factory as KeeperRegistryLogicAFactory } from '../../../typechain/factories/KeeperRegistryLogicA2_1__factory' -import { KeeperRegistryLogicB2_1__factory as KeeperRegistryLogicBFactory } from '../../../typechain/factories/KeeperRegistryLogicB2_1__factory' -import { KeeperRegistryBase2_1__factory as KeeperRegistryBaseFactory } from '../../../typechain/factories/KeeperRegistryBase2_1__factory' -import { Chainable__factory as ChainableFactory } from '../../../typechain/factories/Chainable__factory' -import { IKeeperRegistryMaster__factory as IKeeperRegistryMasterFactory } from '../../../typechain/factories/IKeeperRegistryMaster__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 = [ - KeeperRegistryFactory.abi, - KeeperRegistryLogicAFactory.abi, - KeeperRegistryLogicBFactory.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('IKeeperRegistryMaster', () => { - it('is up to date', async () => { - const checksum = ethers.utils.id(compositeABIs.join('')) - const knownChecksum = fs - .readFileSync( - 'src/v0.8/automation/interfaces/v2_1/IKeeperRegistryMaster.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, - ...KeeperRegistryBaseFactory.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( - IKeeperRegistryMasterFactory.abi, - IAutomationRegistryConsumerFactory.abi, - ) - }) - - it('satisfies the MigratableKeeperRegistryInterface interface', async () => { - assertSatisfiesInterface( - IKeeperRegistryMasterFactory.abi, - MigratableKeeperRegistryInterfaceFactory.abi, - ) - }) - - it('satisfies the MigratableKeeperRegistryInterfaceV2 interface', async () => { - assertSatisfiesInterface( - IKeeperRegistryMasterFactory.abi, - MigratableKeeperRegistryInterfaceV2Factory.abi, - ) - }) - - it('satisfies the OCR2Abstract interface', async () => { - assertSatisfiesInterface( - IKeeperRegistryMasterFactory.abi, - OCR2AbstractFactory.abi, - ) - }) - - it('satisfies the IAutomationV2Common interface', async () => { - assertSatisfiesInterface( - IKeeperRegistryMasterFactory.abi, - IAutomationV21PlusCommonFactory.abi, - ) - }) - - it('satisfies the IAutomationV2Common events', async () => { - assertSatisfiesEvents( - IKeeperRegistryMasterFactory.abi, - IAutomationV21PlusCommonFactory.abi, - ) - }) -}) diff --git a/contracts/test/v0.8/automation/KeeperRegistrar.test.ts b/contracts/test/v0.8/automation/KeeperRegistrar.test.ts deleted file mode 100644 index 8aef681010..0000000000 --- a/contracts/test/v0.8/automation/KeeperRegistrar.test.ts +++ /dev/null @@ -1,812 +0,0 @@ -import { ethers } from 'hardhat' -import { assert, expect } from 'chai' -import { evmRevert, evmRevertCustomError } from '../../test-helpers/matchers' -import { getUsers, Personas } from '../../test-helpers/setup' -import { BigNumber, Signer } from 'ethers' -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 { KeeperRegistry1_2 as KeeperRegistry } from '../../../typechain/KeeperRegistry1_2' -import { KeeperRegistry1_2__factory as KeeperRegistryFactory } from '../../../typechain/factories/KeeperRegistry1_2__factory' -import { KeeperRegistrar } from '../../../typechain/KeeperRegistrar' -import { KeeperRegistrar__factory as KeeperRegistrarFactory } from '../../../typechain/factories/KeeperRegistrar__factory' - -import { MockV3Aggregator } from '../../../typechain/MockV3Aggregator' -import { LinkToken } from '../../../typechain/LinkToken' -import { UpkeepMock } from '../../../typechain/UpkeepMock' -import { toWei } from '../../test-helpers/helpers' - -let linkTokenFactory: LinkTokenFactory -let mockV3AggregatorFactory: MockV3AggregatorFactory -let keeperRegistryFactory: KeeperRegistryFactory -let keeperRegistrar: KeeperRegistrarFactory -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/tests/MockV3Aggregator.sol:MockV3Aggregator', - )) as unknown as MockV3AggregatorFactory - // @ts-ignore bug in autogen file - keeperRegistryFactory = await ethers.getContractFactory('KeeperRegistry1_2') - keeperRegistrar = await ethers.getContractFactory('KeeperRegistrar') - upkeepMockFactory = await ethers.getContractFactory('UpkeepMock') -}) - -const errorMsgs = { - onlyOwner: 'revert Only callable by owner', - onlyAdmin: 'OnlyAdminOrOwner', - hashPayload: 'HashMismatch', - requestNotFound: 'RequestNotFound', -} - -describe('KeeperRegistrar', () => { - const upkeepName = 'SampleUpkeep' - - const linkEth = BigNumber.from(300000000) - const gasWei = BigNumber.from(100) - const executeGas = BigNumber.from(100000) - const source = BigNumber.from(100) - const paymentPremiumPPB = BigNumber.from(250000000) - const flatFeeMicroLink = BigNumber.from(0) - const maxAllowedAutoApprove = 5 - - const blockCountPerTurn = BigNumber.from(3) - 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 maxPerformGas = BigNumber.from(5000000) - const minUpkeepSpend = BigNumber.from('1000000000000000000') - const amount = BigNumber.from('5000000000000000000') - const amount1 = BigNumber.from('6000000000000000000') - const transcoder = ethers.constants.AddressZero - - // 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 registry: KeeperRegistry - let mock: UpkeepMock - let registrar: KeeperRegistrar - - beforeEach(async () => { - owner = personas.Default - admin = personas.Neil - someAddress = personas.Ned - registrarOwner = personas.Nelly - stranger = personas.Nancy - requestSender = personas.Norbert - - const config = { - paymentPremiumPPB, - flatFeeMicroLink, - blockCountPerTurn, - checkGasLimit, - stalenessSeconds, - gasCeilingMultiplier, - minUpkeepSpend, - maxPerformGas, - fallbackGasPrice, - fallbackLinkPrice, - transcoder, - registrar: ethers.constants.AddressZero, - } - - linkToken = await linkTokenFactory.connect(owner).deploy() - gasPriceFeed = await mockV3AggregatorFactory - .connect(owner) - .deploy(0, gasWei) - linkEthFeed = await mockV3AggregatorFactory - .connect(owner) - .deploy(9, linkEth) - registry = await keeperRegistryFactory - .connect(owner) - .deploy( - linkToken.address, - linkEthFeed.address, - gasPriceFeed.address, - config, - ) - - mock = await upkeepMockFactory.deploy() - - registrar = await keeperRegistrar - .connect(registrarOwner) - .deploy( - linkToken.address, - autoApproveType_DISABLED, - BigNumber.from('0'), - registry.address, - minUpkeepSpend, - ) - - await linkToken - .connect(owner) - .transfer(await requestSender.getAddress(), toWei('1000')) - - config.registrar = registrar.address - await registry.setConfig(config) - }) - - describe('#typeAndVersion', () => { - it('uses the correct type and version', async () => { - const typeAndVersion = await registrar.typeAndVersion() - assert.equal(typeAndVersion, 'KeeperRegistrar 1.1.0') - }) - }) - - describe('#register', () => { - it('reverts if not called by the LINK token', async () => { - await evmRevertCustomError( - registrar - .connect(someAddress) - .register( - upkeepName, - emptyBytes, - mock.address, - executeGas, - await admin.getAddress(), - emptyBytes, - amount, - source, - await requestSender.getAddress(), - ), - registrar, - 'OnlyLink', - ) - }) - - it('reverts if the amount passed in data mismatches actual amount sent', async () => { - await registrar - .connect(registrarOwner) - .setRegistrationConfig( - autoApproveType_ENABLED_ALL, - maxAllowedAutoApprove, - registry.address, - minUpkeepSpend, - ) - - const abiEncodedBytes = registrar.interface.encodeFunctionData( - 'register', - [ - upkeepName, - emptyBytes, - mock.address, - executeGas, - await admin.getAddress(), - emptyBytes, - amount1, - source, - await requestSender.getAddress(), - ], - ) - - await evmRevertCustomError( - linkToken - .connect(requestSender) - .transferAndCall(registrar.address, amount, abiEncodedBytes), - registrar, - 'AmountMismatch', - ) - }) - - it('reverts if the sender passed in data mismatches actual sender', async () => { - const abiEncodedBytes = registrar.interface.encodeFunctionData( - 'register', - [ - upkeepName, - emptyBytes, - mock.address, - executeGas, - await admin.getAddress(), - emptyBytes, - amount, - source, - await admin.getAddress(), // Should have been requestSender.getAddress() - ], - ) - await evmRevertCustomError( - linkToken - .connect(requestSender) - .transferAndCall(registrar.address, amount, abiEncodedBytes), - registrar, - 'SenderMismatch', - ) - }) - - it('reverts if the admin address is 0x0000...', async () => { - const abiEncodedBytes = registrar.interface.encodeFunctionData( - 'register', - [ - upkeepName, - emptyBytes, - mock.address, - executeGas, - '0x0000000000000000000000000000000000000000', - emptyBytes, - amount, - source, - await requestSender.getAddress(), - ], - ) - - await evmRevertCustomError( - linkToken - .connect(requestSender) - .transferAndCall(registrar.address, amount, abiEncodedBytes), - registrar, - '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) - .setRegistrationConfig( - autoApproveType_ENABLED_ALL, - maxAllowedAutoApprove, - registry.address, - minUpkeepSpend, - ) - - //register with auto approve ON - const abiEncodedBytes = registrar.interface.encodeFunctionData( - 'register', - [ - upkeepName, - emptyBytes, - mock.address, - executeGas, - await admin.getAddress(), - emptyBytes, - amount, - source, - 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.executeGas, executeGas.toNumber()) - - 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) - .setRegistrationConfig( - autoApproveType_DISABLED, - maxAllowedAutoApprove, - registry.address, - minUpkeepSpend, - ) - - //register with auto approve OFF - const abiEncodedBytes = registrar.interface.encodeFunctionData( - 'register', - [ - upkeepName, - emptyBytes, - mock.address, - executeGas, - await admin.getAddress(), - emptyBytes, - amount, - source, - 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).setRegistrationConfig( - autoApproveType_ENABLED_ALL, - 1, // maxAllowedAutoApprove - registry.address, - minUpkeepSpend, - ) - - //register within threshold, new upkeep should be registered - let abiEncodedBytes = registrar.interface.encodeFunctionData('register', [ - upkeepName, - emptyBytes, - mock.address, - executeGas, - await admin.getAddress(), - emptyBytes, - amount, - source, - 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, - executeGas.toNumber() + 1, // make unique hash - await admin.getAddress(), - emptyBytes, - amount, - source, - await requestSender.getAddress(), - ]) - await linkToken - .connect(requestSender) - .transferAndCall(registrar.address, amount, abiEncodedBytes) - assert.equal((await registry.getState()).state.numUpkeeps.toNumber(), 1) // Still 1 - - // Now set new max limit to 2. One more upkeep should get auto approved - await registrar.connect(registrarOwner).setRegistrationConfig( - autoApproveType_ENABLED_ALL, - 2, // maxAllowedAutoApprove - registry.address, - minUpkeepSpend, - ) - abiEncodedBytes = registrar.interface.encodeFunctionData('register', [ - upkeepName, - emptyBytes, - mock.address, - executeGas.toNumber() + 2, // make unique hash - await admin.getAddress(), - emptyBytes, - amount, - source, - await requestSender.getAddress(), - ]) - await linkToken - .connect(requestSender) - .transferAndCall(registrar.address, amount, abiEncodedBytes) - assert.equal((await registry.getState()).state.numUpkeeps.toNumber(), 2) // 1 -> 2 - - // One more upkeep should not get registered - abiEncodedBytes = registrar.interface.encodeFunctionData('register', [ - upkeepName, - emptyBytes, - mock.address, - executeGas.toNumber() + 3, // make unique hash - await admin.getAddress(), - emptyBytes, - amount, - source, - await requestSender.getAddress(), - ]) - await linkToken - .connect(requestSender) - .transferAndCall(registrar.address, amount, abiEncodedBytes) - assert.equal((await registry.getState()).state.numUpkeeps.toNumber(), 2) // Still 2 - }) - - 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) - .setRegistrationConfig( - autoApproveType_ENABLED_SENDER_ALLOWLIST, - maxAllowedAutoApprove, - registry.address, - minUpkeepSpend, - ) - - // 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, - executeGas, - await admin.getAddress(), - emptyBytes, - amount, - source, - 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.executeGas, executeGas.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) - .setRegistrationConfig( - autoApproveType_ENABLED_SENDER_ALLOWLIST, - maxAllowedAutoApprove, - registry.address, - minUpkeepSpend, - ) - - // 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, - executeGas, - await admin.getAddress(), - emptyBytes, - amount, - source, - 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('#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('#approve', () => { - let hash: string - - beforeEach(async () => { - await registrar - .connect(registrarOwner) - .setRegistrationConfig( - autoApproveType_DISABLED, - maxAllowedAutoApprove, - registry.address, - minUpkeepSpend, - ) - - //register with auto approve OFF - const abiEncodedBytes = registrar.interface.encodeFunctionData( - 'register', - [ - upkeepName, - emptyBytes, - mock.address, - executeGas, - await admin.getAddress(), - emptyBytes, - amount, - source, - 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, - executeGas, - await admin.getAddress(), - 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, - executeGas, - await admin.getAddress(), - emptyBytes, - '0x000000000000000000000000322813fd9a801c5507c9de605d63cea4f2ce6c44', - ) - await evmRevertCustomError(tx, registrar, errorMsgs.requestNotFound) - }) - - it('reverts if any member of the payload changes', async () => { - let tx = registrar - .connect(registrarOwner) - .approve( - upkeepName, - ethers.Wallet.createRandom().address, - executeGas, - await admin.getAddress(), - emptyBytes, - hash, - ) - await evmRevertCustomError(tx, registrar, errorMsgs.hashPayload) - tx = registrar - .connect(registrarOwner) - .approve( - upkeepName, - mock.address, - 10000, - await admin.getAddress(), - emptyBytes, - hash, - ) - await evmRevertCustomError(tx, registrar, errorMsgs.hashPayload) - tx = registrar - .connect(registrarOwner) - .approve( - upkeepName, - mock.address, - executeGas, - ethers.Wallet.createRandom().address, - emptyBytes, - hash, - ) - await evmRevertCustomError(tx, registrar, errorMsgs.hashPayload) - tx = registrar - .connect(registrarOwner) - .approve( - upkeepName, - mock.address, - executeGas, - await admin.getAddress(), - '0x1234', - hash, - ) - await evmRevertCustomError(tx, registrar, errorMsgs.hashPayload) - }) - - it('approves an existing registration request', async () => { - const tx = await registrar - .connect(registrarOwner) - .approve( - upkeepName, - mock.address, - executeGas, - await admin.getAddress(), - emptyBytes, - 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, - executeGas, - await admin.getAddress(), - emptyBytes, - hash, - ) - const tx = registrar - .connect(registrarOwner) - .approve( - upkeepName, - mock.address, - executeGas, - await admin.getAddress(), - emptyBytes, - hash, - ) - await evmRevertCustomError(tx, registrar, errorMsgs.requestNotFound) - }) - }) - - describe('#cancel', () => { - let hash: string - - beforeEach(async () => { - await registrar - .connect(registrarOwner) - .setRegistrationConfig( - autoApproveType_DISABLED, - maxAllowedAutoApprove, - registry.address, - minUpkeepSpend, - ) - - //register with auto approve OFF - const abiEncodedBytes = registrar.interface.encodeFunctionData( - 'register', - [ - upkeepName, - emptyBytes, - mock.address, - executeGas, - await admin.getAddress(), - emptyBytes, - amount, - source, - 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 evmRevertCustomError(tx, registrar, errorMsgs.onlyAdmin) - }) - - it('reverts if the hash does not exist', async () => { - const tx = registrar - .connect(registrarOwner) - .cancel( - '0x000000000000000000000000322813fd9a801c5507c9de605d63cea4f2ce6c44', - ) - await evmRevertCustomError(tx, registrar, errorMsgs.requestNotFound) - }) - - it('refunds the total request balance to the admin address', 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 evmRevertCustomError(tx, registrar, errorMsgs.requestNotFound) - tx = registrar - .connect(registrarOwner) - .approve( - upkeepName, - mock.address, - executeGas, - await admin.getAddress(), - emptyBytes, - hash, - ) - await evmRevertCustomError(tx, registrar, errorMsgs.requestNotFound) - }) - }) -}) diff --git a/contracts/test/v0.8/automation/KeeperRegistry2_1.test.ts b/contracts/test/v0.8/automation/KeeperRegistry2_1.test.ts deleted file mode 100644 index 05bdabbea5..0000000000 --- a/contracts/test/v0.8/automation/KeeperRegistry2_1.test.ts +++ /dev/null @@ -1,5655 +0,0 @@ -import { ethers } from 'hardhat' -import { assert } from 'chai' -import { KeeperRegistry2_1__factory as KeeperRegistryFactory } from '../../../typechain/factories/KeeperRegistry2_1__factory' -import { KeeperRegistryLogicA2_1__factory as KeeperRegistryLogicAFactory } from '../../../typechain/factories/KeeperRegistryLogicA2_1__factory' -import { KeeperRegistryLogicB2_1__factory as KeeperRegistryLogicBFactory } from '../../../typechain/factories/KeeperRegistryLogicB2_1__factory' -import { AutomationForwarderLogic__factory as AutomationForwarderLogicFactory } from '../../../typechain/factories/AutomationForwarderLogic__factory' - -// const describeMaybe = process.env.SKIP_SLOW ? describe.skip : describe -// const itMaybe = process.env.SKIP_SLOW ? it.skip : it - -////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////////////// - -/*********************************** REGISTRY v2.1 IS FROZEN ************************************/ - -// We are leaving the original tests enabled, however as 2.1 is still actively being deployed - -describe('KeeperRegistry2_1 - Frozen [ @skip-coverage ]', () => { - it('has not changed', () => { - assert.equal( - ethers.utils.id(KeeperRegistryFactory.bytecode), - '0x05aaa1024d7400e9c4824dde093b96edf5888fa6e6be2c2fc4dca7ae47cc9de9', - 'KeeperRegistry bytecode has changed', - ) - assert.equal( - ethers.utils.id(KeeperRegistryLogicAFactory.bytecode), - '0xdcc8805e88c550b2a25b972bee9f4e4c3649f01e26f8dda6b25d7a9c5da8ab2f', - 'KeeperRegistryLogicA bytecode has changed', - ) - assert.equal( - ethers.utils.id(KeeperRegistryLogicBFactory.bytecode), - '0x891c26ba35b9b13afc9400fac5471d15842828ab717cbdc70ee263210c542563', - 'KeeperRegistryLogicB bytecode has changed', - ) - assert.equal( - ethers.utils.id(AutomationForwarderLogicFactory.bytecode), - '0x6b89065111e9236407329fae3d68b33c311b7d3b6c2ae3dd15c1691a28b1aca7', - 'AutomationForwarderLogic bytecode has changed', - ) - }) -}) - -////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////////////// - -// copied from AutomationRegistryInterface2_1.sol -// enum UpkeepFailureReason { -// NONE, -// UPKEEP_CANCELLED, -// UPKEEP_PAUSED, -// TARGET_CHECK_REVERTED, -// UPKEEP_NOT_NEEDED, -// PERFORM_DATA_EXCEEDS_LIMIT, -// INSUFFICIENT_BALANCE, -// CHECK_CALLBACK_REVERTED, -// REVERT_DATA_EXCEEDS_LIMIT, -// REGISTRY_PAUSED, -// } -// -// // copied from AutomationRegistryInterface2_1.sol -// enum Mode { -// DEFAULT, -// ARBITRUM, -// OPTIMISM, -// } -// -// // copied from KeeperRegistryBase2_1.sol -// enum Trigger { -// CONDITION, -// LOG, -// } -// -// // un-exported types that must be extracted from the utils contract -// type Report = Parameters[0] -// type OnChainConfig = Parameters[0] -// type LogTrigger = Parameters[0] -// type ConditionalTrigger = Parameters[0] -// type Log = Parameters[0] -// -// // ----------------------------------------------------------------------------------------------- -// -// // These values should match the constants declared in registry -// let registryConditionalOverhead: BigNumber -// let registryLogOverhead: BigNumber -// let registryPerSignerGasOverhead: BigNumber -// let registryPerPerformByteGasOverhead: BigNumber -// let cancellationDelay: number -// -// // This is the margin for gas that we test for. Gas charged should always be greater -// // than total gas used in tx but should not increase beyond this margin -// const gasCalculationMargin = BigNumber.from(8000) -// -// const linkEth = BigNumber.from(5000000000000000) // 1 Link = 0.005 Eth -// const gasWei = BigNumber.from(1000000000) // 1 gwei -// // ----------------------------------------------------------------------------------------------- -// // test-wide configs for upkeeps -// const linkDivisibility = BigNumber.from('1000000000000000000') -// const performGas = BigNumber.from('1000000') -// const paymentPremiumBase = BigNumber.from('1000000000') -// const paymentPremiumPPB = BigNumber.from('250000000') -// const flatFeeMicroLink = BigNumber.from(0) -// -// const randomBytes = '0x1234abcd' -// const emptyBytes = '0x' -// const emptyBytes32 = -// '0x0000000000000000000000000000000000000000000000000000000000000000' -// -// const transmitGasOverhead = 1_000_000 -// const checkGasOverhead = 400_000 -// -// const stalenessSeconds = BigNumber.from(43820) -// const gasCeilingMultiplier = BigNumber.from(2) -// const checkGasLimit = BigNumber.from(10000000) -// const fallbackGasPrice = gasWei.mul(BigNumber.from('2')) -// const fallbackLinkPrice = linkEth.div(BigNumber.from('2')) -// const maxCheckDataSize = BigNumber.from(1000) -// const maxPerformDataSize = BigNumber.from(1000) -// const maxRevertDataSize = BigNumber.from(1000) -// const maxPerformGas = BigNumber.from(5000000) -// const minUpkeepSpend = BigNumber.from(0) -// const f = 1 -// const offchainVersion = 1 -// const offchainBytes = '0x' -// const zeroAddress = ethers.constants.AddressZero -// const epochAndRound5_1 = -// '0x0000000000000000000000000000000000000000000000000000000000000501' -// -// let logTriggerConfig: string -// -// // ----------------------------------------------------------------------------------------------- -// -// // Smart contract factories -// let linkTokenFactory: LinkTokenFactory -// let mockV3AggregatorFactory: MockV3AggregatorFactory -// let upkeepMockFactory: UpkeepMockFactory -// let upkeepAutoFunderFactory: UpkeepAutoFunderFactory -// let mockArbGasInfoFactory: MockArbGasInfoFactory -// let mockOVMGasPriceOracleFactory: MockOVMGasPriceOracleFactory -// let streamsLookupUpkeepFactory: StreamsLookupUpkeepFactory -// let personas: Personas -// -// // contracts -// let linkToken: LinkToken -// let linkEthFeed: MockV3Aggregator -// let gasPriceFeed: MockV3Aggregator -// let registry: IKeeperRegistry // default registry, used for most tests -// let arbRegistry: IKeeperRegistry // arbitrum registry -// let opRegistry: IKeeperRegistry // optimism registry -// let mgRegistry: IKeeperRegistry // "migrate registry" used in migration tests -// let blankRegistry: IKeeperRegistry // used to test initial configurations -// let mock: UpkeepMock -// let autoFunderUpkeep: UpkeepAutoFunder -// let ltUpkeep: MockContract -// let transcoder: UpkeepTranscoder -// let mockArbGasInfo: MockArbGasInfo -// let mockOVMGasPriceOracle: MockOVMGasPriceOracle -// let streamsLookupUpkeep: StreamsLookupUpkeep -// let automationUtils: AutomationUtils -// -// function now() { -// return Math.floor(Date.now() / 1000) -// } -// -// async function getUpkeepID(tx: ContractTransaction): Promise { -// const receipt = await tx.wait() -// for (const event of receipt.events || []) { -// if ( -// event.args && -// event.eventSignature == 'UpkeepRegistered(uint256,uint32,address)' -// ) { -// return event.args[0] -// } -// } -// throw new Error('could not find upkeep ID in tx event logs') -// } -// -// const getTriggerType = (upkeepId: BigNumber): Trigger => { -// const hexBytes = ethers.utils.defaultAbiCoder.encode(['uint256'], [upkeepId]) -// const bytes = ethers.utils.arrayify(hexBytes) -// for (let idx = 4; idx < 15; idx++) { -// if (bytes[idx] != 0) { -// return Trigger.CONDITION -// } -// } -// return bytes[15] as Trigger -// } -// -// const encodeConfig = (onchainConfig: OnChainConfig) => { -// return ( -// '0x' + -// automationUtils.interface -// .encodeFunctionData('_onChainConfig', [onchainConfig]) -// .slice(10) -// ) -// } -// -// const encodeBlockTrigger = (conditionalTrigger: ConditionalTrigger) => { -// return ( -// '0x' + -// automationUtils.interface -// .encodeFunctionData('_conditionalTrigger', [conditionalTrigger]) -// .slice(10) -// ) -// } -// -// const encodeLogTrigger = (logTrigger: LogTrigger) => { -// return ( -// '0x' + -// automationUtils.interface -// .encodeFunctionData('_logTrigger', [logTrigger]) -// .slice(10) -// ) -// } -// -// const encodeLog = (log: Log) => { -// return ( -// '0x' + automationUtils.interface.encodeFunctionData('_log', [log]).slice(10) -// ) -// } -// -// const encodeReport = (report: Report) => { -// return ( -// '0x' + -// automationUtils.interface.encodeFunctionData('_report', [report]).slice(10) -// ) -// } -// -// type UpkeepData = { -// Id: BigNumberish -// performGas: BigNumberish -// performData: BytesLike -// trigger: BytesLike -// } -// -// const makeReport = (upkeeps: UpkeepData[]) => { -// const upkeepIds = upkeeps.map((u) => u.Id) -// const performGases = upkeeps.map((u) => u.performGas) -// const triggers = upkeeps.map((u) => u.trigger) -// const performDatas = upkeeps.map((u) => u.performData) -// return encodeReport({ -// fastGasWei: gasWei, -// linkNative: linkEth, -// upkeepIds, -// gasLimits: performGases, -// triggers, -// performDatas, -// }) -// } -// -// const makeLatestBlockReport = async (upkeepsIDs: BigNumberish[]) => { -// const latestBlock = await ethers.provider.getBlock('latest') -// const upkeeps: UpkeepData[] = [] -// for (let i = 0; i < upkeepsIDs.length; i++) { -// upkeeps.push({ -// Id: upkeepsIDs[i], -// performGas, -// trigger: encodeBlockTrigger({ -// blockNum: latestBlock.number, -// blockHash: latestBlock.hash, -// }), -// performData: '0x', -// }) -// } -// return makeReport(upkeeps) -// } -// -// const signReport = ( -// reportContext: string[], -// report: any, -// signers: Wallet[], -// ) => { -// const reportDigest = ethers.utils.keccak256(report) -// const packedArgs = ethers.utils.solidityPack( -// ['bytes32', 'bytes32[3]'], -// [reportDigest, reportContext], -// ) -// const packedDigest = ethers.utils.keccak256(packedArgs) -// -// const signatures = [] -// for (const signer of signers) { -// signatures.push(signer._signingKey().signDigest(packedDigest)) -// } -// const vs = signatures.map((i) => '0' + (i.v - 27).toString(16)).join('') -// return { -// vs: '0x' + vs.padEnd(64, '0'), -// rs: signatures.map((i) => i.r), -// ss: signatures.map((i) => i.s), -// } -// } -// -// const parseUpkeepPerformedLogs = (receipt: ContractReceipt) => { -// const parsedLogs = [] -// for (const rawLog of receipt.logs) { -// try { -// const log = registry.interface.parseLog(rawLog) -// if ( -// log.name == -// registry.interface.events[ -// 'UpkeepPerformed(uint256,bool,uint96,uint256,uint256,bytes)' -// ].name -// ) { -// parsedLogs.push(log as unknown as UpkeepPerformedEvent) -// } -// } catch { -// continue -// } -// } -// return parsedLogs -// } -// -// const parseReorgedUpkeepReportLogs = (receipt: ContractReceipt) => { -// const parsedLogs = [] -// for (const rawLog of receipt.logs) { -// try { -// const log = registry.interface.parseLog(rawLog) -// if ( -// log.name == -// registry.interface.events['ReorgedUpkeepReport(uint256,bytes)'].name -// ) { -// parsedLogs.push(log as unknown as ReorgedUpkeepReportEvent) -// } -// } catch { -// continue -// } -// } -// return parsedLogs -// } -// -// const parseStaleUpkeepReportLogs = (receipt: ContractReceipt) => { -// const parsedLogs = [] -// for (const rawLog of receipt.logs) { -// try { -// const log = registry.interface.parseLog(rawLog) -// if ( -// log.name == -// registry.interface.events['StaleUpkeepReport(uint256,bytes)'].name -// ) { -// parsedLogs.push(log as unknown as StaleUpkeepReportEvent) -// } -// } catch { -// continue -// } -// } -// return parsedLogs -// } -// -// const parseInsufficientFundsUpkeepReportLogs = (receipt: ContractReceipt) => { -// const parsedLogs = [] -// for (const rawLog of receipt.logs) { -// try { -// const log = registry.interface.parseLog(rawLog) -// if ( -// log.name == -// registry.interface.events[ -// 'InsufficientFundsUpkeepReport(uint256,bytes)' -// ].name -// ) { -// parsedLogs.push(log as unknown as InsufficientFundsUpkeepReportEvent) -// } -// } catch { -// continue -// } -// } -// return parsedLogs -// } -// -// const parseCancelledUpkeepReportLogs = (receipt: ContractReceipt) => { -// const parsedLogs = [] -// for (const rawLog of receipt.logs) { -// try { -// const log = registry.interface.parseLog(rawLog) -// if ( -// log.name == -// registry.interface.events['CancelledUpkeepReport(uint256,bytes)'].name -// ) { -// parsedLogs.push(log as unknown as CancelledUpkeepReportEvent) -// } -// } catch { -// continue -// } -// } -// return parsedLogs -// } -// -// describe('KeeperRegistry2_1', () => { -// let owner: Signer -// let keeper1: Signer -// let keeper2: Signer -// let keeper3: Signer -// let keeper4: Signer -// let keeper5: Signer -// let nonkeeper: Signer -// let signer1: Wallet -// let signer2: Wallet -// let signer3: Wallet -// let signer4: Wallet -// let signer5: Wallet -// let admin: Signer -// let payee1: Signer -// let payee2: Signer -// let payee3: Signer -// let payee4: Signer -// let payee5: Signer -// -// let upkeepId: BigNumber // conditional upkeep -// let afUpkeepId: BigNumber // auto funding upkeep -// let logUpkeepId: BigNumber // log trigger upkeepID -// let streamsLookupUpkeepId: BigNumber // streams lookup upkeep -// const numUpkeeps = 4 // see above -// let keeperAddresses: string[] -// let payees: string[] -// let signers: Wallet[] -// let signerAddresses: string[] -// let config: any -// let baseConfig: Parameters -// let upkeepManager: string -// -// before(async () => { -// personas = (await getUsers()).personas -// -// const utilsFactory = await ethers.getContractFactory('AutomationUtils2_1') -// automationUtils = await utilsFactory.deploy() -// -// linkTokenFactory = await ethers.getContractFactory( -// 'src/v0.8/shared/test/helpers/LinkTokenTestHelper.sol:LinkTokenTestHelper', -// ) -// // need full path because there are two contracts with name MockV3Aggregator -// mockV3AggregatorFactory = (await ethers.getContractFactory( -// 'src/v0.8/tests/MockV3Aggregator.sol:MockV3Aggregator', -// )) as unknown as MockV3AggregatorFactory -// upkeepMockFactory = await ethers.getContractFactory('UpkeepMock') -// upkeepAutoFunderFactory = -// await ethers.getContractFactory('UpkeepAutoFunder') -// mockArbGasInfoFactory = await ethers.getContractFactory('MockArbGasInfo') -// mockOVMGasPriceOracleFactory = await ethers.getContractFactory( -// 'MockOVMGasPriceOracle', -// ) -// streamsLookupUpkeepFactory = await ethers.getContractFactory( -// 'StreamsLookupUpkeep', -// ) -// -// owner = personas.Default -// keeper1 = personas.Carol -// keeper2 = personas.Eddy -// keeper3 = personas.Nancy -// keeper4 = personas.Norbert -// keeper5 = personas.Nick -// nonkeeper = personas.Ned -// admin = personas.Neil -// payee1 = personas.Nelly -// payee2 = personas.Norbert -// payee3 = personas.Nick -// payee4 = personas.Eddy -// payee5 = personas.Carol -// upkeepManager = await personas.Norbert.getAddress() -// // signers -// signer1 = new ethers.Wallet( -// '0x7777777000000000000000000000000000000000000000000000000000000001', -// ) -// signer2 = new ethers.Wallet( -// '0x7777777000000000000000000000000000000000000000000000000000000002', -// ) -// signer3 = new ethers.Wallet( -// '0x7777777000000000000000000000000000000000000000000000000000000003', -// ) -// signer4 = new ethers.Wallet( -// '0x7777777000000000000000000000000000000000000000000000000000000004', -// ) -// signer5 = new ethers.Wallet( -// '0x7777777000000000000000000000000000000000000000000000000000000005', -// ) -// -// keeperAddresses = [ -// await keeper1.getAddress(), -// await keeper2.getAddress(), -// await keeper3.getAddress(), -// await keeper4.getAddress(), -// await keeper5.getAddress(), -// ] -// payees = [ -// await payee1.getAddress(), -// await payee2.getAddress(), -// await payee3.getAddress(), -// await payee4.getAddress(), -// await payee5.getAddress(), -// ] -// signers = [signer1, signer2, signer3, signer4, signer5] -// -// // We append 26 random addresses to keepers, payees and signers to get a system of 31 oracles -// // This allows f value of 1 - 10 -// for (let i = 0; i < 26; i++) { -// keeperAddresses.push(randomAddress()) -// payees.push(randomAddress()) -// signers.push(ethers.Wallet.createRandom()) -// } -// signerAddresses = [] -// for (const signer of signers) { -// signerAddresses.push(await signer.getAddress()) -// } -// -// logTriggerConfig = -// '0x' + -// automationUtils.interface -// .encodeFunctionData('_logTriggerConfig', [ -// { -// contractAddress: randomAddress(), -// filterSelector: 0, -// topic0: ethers.utils.randomBytes(32), -// topic1: ethers.utils.randomBytes(32), -// topic2: ethers.utils.randomBytes(32), -// topic3: ethers.utils.randomBytes(32), -// }, -// ]) -// .slice(10) -// }) -// -// const linkForGas = ( -// upkeepGasSpent: BigNumber, -// gasOverhead: BigNumber, -// gasMultiplier: BigNumber, -// premiumPPB: BigNumber, -// flatFee: BigNumber, -// l1CostWei?: BigNumber, -// numUpkeepsBatch?: BigNumber, -// ) => { -// l1CostWei = l1CostWei === undefined ? BigNumber.from(0) : l1CostWei -// numUpkeepsBatch = -// numUpkeepsBatch === undefined ? BigNumber.from(1) : numUpkeepsBatch -// -// const gasSpent = gasOverhead.add(BigNumber.from(upkeepGasSpent)) -// const base = gasWei -// .mul(gasMultiplier) -// .mul(gasSpent) -// .mul(linkDivisibility) -// .div(linkEth) -// const l1Fee = l1CostWei -// .mul(gasMultiplier) -// .div(numUpkeepsBatch) -// .mul(linkDivisibility) -// .div(linkEth) -// const gasPayment = base.add(l1Fee) -// -// const premium = gasWei -// .mul(gasMultiplier) -// .mul(upkeepGasSpent) -// .add(l1CostWei.mul(gasMultiplier).div(numUpkeepsBatch)) -// .mul(linkDivisibility) -// .div(linkEth) -// .mul(premiumPPB) -// .div(paymentPremiumBase) -// .add(BigNumber.from(flatFee).mul('1000000000000')) -// -// return { -// total: gasPayment.add(premium), -// gasPaymemnt: gasPayment, -// premium, -// } -// } -// -// const verifyMaxPayment = async ( -// registry: IKeeperRegistry, -// l1CostWei?: BigNumber, -// ) => { -// type TestCase = { -// name: string -// multiplier: number -// gas: number -// premium: number -// flatFee: number -// } -// -// const tests: TestCase[] = [ -// { -// name: 'no fees', -// multiplier: 1, -// gas: 100000, -// premium: 0, -// flatFee: 0, -// }, -// { -// name: 'basic fees', -// multiplier: 1, -// gas: 100000, -// premium: 250000000, -// flatFee: 1000000, -// }, -// { -// name: 'max fees', -// multiplier: 3, -// gas: 10000000, -// premium: 250000000, -// flatFee: 1000000, -// }, -// ] -// -// const fPlusOne = BigNumber.from(f + 1) -// const totalConditionalOverhead = registryConditionalOverhead -// .add(registryPerSignerGasOverhead.mul(fPlusOne)) -// .add(registryPerPerformByteGasOverhead.mul(maxPerformDataSize)) -// const totalLogOverhead = registryLogOverhead -// .add(registryPerSignerGasOverhead.mul(fPlusOne)) -// .add(registryPerPerformByteGasOverhead.mul(maxPerformDataSize)) -// -// for (const test of tests) { -// await registry.connect(owner).setConfig( -// signerAddresses, -// keeperAddresses, -// f, -// encodeConfig({ -// paymentPremiumPPB: test.premium, -// flatFeeMicroLink: test.flatFee, -// checkGasLimit, -// stalenessSeconds, -// gasCeilingMultiplier: test.multiplier, -// minUpkeepSpend, -// maxCheckDataSize, -// maxPerformDataSize, -// maxRevertDataSize, -// maxPerformGas, -// fallbackGasPrice, -// fallbackLinkPrice, -// transcoder: transcoder.address, -// registrars: [], -// upkeepPrivilegeManager: upkeepManager, -// }), -// offchainVersion, -// offchainBytes, -// ) -// -// const conditionalPrice = await registry.getMaxPaymentForGas( -// Trigger.CONDITION, -// test.gas, -// ) -// expect(conditionalPrice).to.equal( -// linkForGas( -// BigNumber.from(test.gas), -// totalConditionalOverhead, -// BigNumber.from(test.multiplier), -// BigNumber.from(test.premium), -// BigNumber.from(test.flatFee), -// l1CostWei, -// ).total, -// ) -// -// const logPrice = await registry.getMaxPaymentForGas(Trigger.LOG, test.gas) -// expect(logPrice).to.equal( -// linkForGas( -// BigNumber.from(test.gas), -// totalLogOverhead, -// BigNumber.from(test.multiplier), -// BigNumber.from(test.premium), -// BigNumber.from(test.flatFee), -// l1CostWei, -// ).total, -// ) -// } -// } -// -// const verifyConsistentAccounting = async ( -// maxAllowedSpareChange: BigNumber, -// ) => { -// const expectedLinkBalance = (await registry.getState()).state -// .expectedLinkBalance -// const linkTokenBalance = await linkToken.balanceOf(registry.address) -// const upkeepIdBalance = (await registry.getUpkeep(upkeepId)).balance -// let totalKeeperBalance = BigNumber.from(0) -// for (let i = 0; i < keeperAddresses.length; i++) { -// totalKeeperBalance = totalKeeperBalance.add( -// (await registry.getTransmitterInfo(keeperAddresses[i])).balance, -// ) -// } -// const ownerBalance = (await registry.getState()).state.ownerLinkBalance -// assert.isTrue(expectedLinkBalance.eq(linkTokenBalance)) -// assert.isTrue( -// upkeepIdBalance -// .add(totalKeeperBalance) -// .add(ownerBalance) -// .lte(expectedLinkBalance), -// ) -// assert.isTrue( -// expectedLinkBalance -// .sub(upkeepIdBalance) -// .sub(totalKeeperBalance) -// .sub(ownerBalance) -// .lte(maxAllowedSpareChange), -// ) -// } -// -// interface GetTransmitTXOptions { -// numSigners?: number -// startingSignerIndex?: number -// gasLimit?: BigNumberish -// gasPrice?: BigNumberish -// performGas?: BigNumberish -// performData?: string -// checkBlockNum?: number -// checkBlockHash?: string -// logBlockHash?: BytesLike -// txHash?: BytesLike -// logIndex?: number -// timestamp?: number -// } -// -// const getTransmitTx = async ( -// registry: IKeeperRegistry, -// transmitter: Signer, -// upkeepIds: BigNumber[], -// overrides: GetTransmitTXOptions = {}, -// ) => { -// const latestBlock = await ethers.provider.getBlock('latest') -// const configDigest = (await registry.getState()).state.latestConfigDigest -// const config = { -// numSigners: f + 1, -// startingSignerIndex: 0, -// performData: '0x', -// performGas, -// checkBlockNum: latestBlock.number, -// checkBlockHash: latestBlock.hash, -// logIndex: 0, -// txHash: undefined, // assigned uniquely below -// logBlockHash: undefined, // assigned uniquely below -// timestamp: now(), -// gasLimit: undefined, -// gasPrice: undefined, -// } -// Object.assign(config, overrides) -// const upkeeps: UpkeepData[] = [] -// for (let i = 0; i < upkeepIds.length; i++) { -// let trigger: string -// switch (getTriggerType(upkeepIds[i])) { -// case Trigger.CONDITION: -// trigger = encodeBlockTrigger({ -// blockNum: config.checkBlockNum, -// blockHash: config.checkBlockHash, -// }) -// break -// case Trigger.LOG: -// trigger = encodeLogTrigger({ -// logBlockHash: config.logBlockHash || ethers.utils.randomBytes(32), -// txHash: config.txHash || ethers.utils.randomBytes(32), -// logIndex: config.logIndex, -// blockNum: config.checkBlockNum, -// blockHash: config.checkBlockHash, -// }) -// break -// } -// upkeeps.push({ -// Id: upkeepIds[i], -// performGas: config.performGas, -// trigger, -// performData: config.performData, -// }) -// } -// -// const report = makeReport(upkeeps) -// const reportContext = [configDigest, epochAndRound5_1, emptyBytes32] -// const sigs = signReport( -// reportContext, -// report, -// signers.slice( -// config.startingSignerIndex, -// config.startingSignerIndex + config.numSigners, -// ), -// ) -// -// type txOverride = { -// gasLimit?: BigNumberish | Promise -// gasPrice?: BigNumberish | Promise -// } -// const txOverrides: txOverride = {} -// if (config.gasLimit) { -// txOverrides.gasLimit = config.gasLimit -// } -// if (config.gasPrice) { -// txOverrides.gasPrice = config.gasPrice -// } -// -// return registry -// .connect(transmitter) -// .transmit( -// [configDigest, epochAndRound5_1, emptyBytes32], -// report, -// sigs.rs, -// sigs.ss, -// sigs.vs, -// txOverrides, -// ) -// } -// -// const getTransmitTxWithReport = async ( -// registry: IKeeperRegistry, -// transmitter: Signer, -// report: BytesLike, -// ) => { -// const configDigest = (await registry.getState()).state.latestConfigDigest -// const reportContext = [configDigest, epochAndRound5_1, emptyBytes32] -// const sigs = signReport(reportContext, report, signers.slice(0, f + 1)) -// -// return registry -// .connect(transmitter) -// .transmit( -// [configDigest, epochAndRound5_1, emptyBytes32], -// report, -// sigs.rs, -// sigs.ss, -// sigs.vs, -// ) -// } -// -// const setup = async () => { -// linkToken = await linkTokenFactory.connect(owner).deploy() -// gasPriceFeed = await mockV3AggregatorFactory -// .connect(owner) -// .deploy(0, gasWei) -// linkEthFeed = await mockV3AggregatorFactory -// .connect(owner) -// .deploy(9, linkEth) -// const upkeepTranscoderFactory = await ethers.getContractFactory( -// 'UpkeepTranscoder4_0', -// ) -// transcoder = await upkeepTranscoderFactory.connect(owner).deploy() -// mockArbGasInfo = await mockArbGasInfoFactory.connect(owner).deploy() -// mockOVMGasPriceOracle = await mockOVMGasPriceOracleFactory -// .connect(owner) -// .deploy() -// streamsLookupUpkeep = await streamsLookupUpkeepFactory -// .connect(owner) -// .deploy( -// BigNumber.from('10000'), -// BigNumber.from('100'), -// false /* useArbBlock */, -// true /* staging */, -// false /* verify mercury response */, -// ) -// -// const arbOracleCode = await ethers.provider.send('eth_getCode', [ -// mockArbGasInfo.address, -// ]) -// await ethers.provider.send('hardhat_setCode', [ -// '0x000000000000000000000000000000000000006C', -// arbOracleCode, -// ]) -// -// const optOracleCode = await ethers.provider.send('eth_getCode', [ -// mockOVMGasPriceOracle.address, -// ]) -// await ethers.provider.send('hardhat_setCode', [ -// '0x420000000000000000000000000000000000000F', -// optOracleCode, -// ]) -// -// const mockArbSys = await new MockArbSysFactory(owner).deploy() -// const arbSysCode = await ethers.provider.send('eth_getCode', [ -// mockArbSys.address, -// ]) -// await ethers.provider.send('hardhat_setCode', [ -// '0x0000000000000000000000000000000000000064', -// arbSysCode, -// ]) -// -// config = { -// paymentPremiumPPB, -// flatFeeMicroLink, -// checkGasLimit, -// stalenessSeconds, -// gasCeilingMultiplier, -// minUpkeepSpend, -// maxCheckDataSize, -// maxPerformDataSize, -// maxRevertDataSize, -// maxPerformGas, -// fallbackGasPrice, -// fallbackLinkPrice, -// transcoder: transcoder.address, -// registrars: [], -// upkeepPrivilegeManager: upkeepManager, -// } -// -// baseConfig = [ -// signerAddresses, -// keeperAddresses, -// f, -// encodeConfig(config), -// offchainVersion, -// offchainBytes, -// ] -// -// registry = await deployRegistry21( -// owner, -// Mode.DEFAULT, -// linkToken.address, -// linkEthFeed.address, -// gasPriceFeed.address, -// ) -// -// arbRegistry = await deployRegistry21( -// owner, -// Mode.ARBITRUM, -// linkToken.address, -// linkEthFeed.address, -// gasPriceFeed.address, -// ) -// -// opRegistry = await deployRegistry21( -// owner, -// Mode.OPTIMISM, -// linkToken.address, -// linkEthFeed.address, -// gasPriceFeed.address, -// ) -// -// mgRegistry = await deployRegistry21( -// owner, -// Mode.DEFAULT, -// linkToken.address, -// linkEthFeed.address, -// gasPriceFeed.address, -// ) -// -// blankRegistry = await deployRegistry21( -// owner, -// Mode.DEFAULT, -// linkToken.address, -// linkEthFeed.address, -// gasPriceFeed.address, -// ) -// -// registryConditionalOverhead = await registry.getConditionalGasOverhead() -// registryLogOverhead = await registry.getLogGasOverhead() -// registryPerSignerGasOverhead = await registry.getPerSignerGasOverhead() -// registryPerPerformByteGasOverhead = -// await registry.getPerPerformByteGasOverhead() -// cancellationDelay = (await registry.getCancellationDelay()).toNumber() -// -// for (const reg of [registry, arbRegistry, opRegistry, mgRegistry]) { -// await reg.connect(owner).setConfig(...baseConfig) -// await reg.connect(owner).setPayees(payees) -// await linkToken.connect(admin).approve(reg.address, toWei('1000')) -// await linkToken.connect(owner).approve(reg.address, toWei('1000')) -// } -// -// mock = await upkeepMockFactory.deploy() -// await linkToken -// .connect(owner) -// .transfer(await admin.getAddress(), toWei('1000')) -// let tx = await registry -// .connect(owner) -// [ -// 'registerUpkeep(address,uint32,address,bytes,bytes)' -// ](mock.address, performGas, await admin.getAddress(), randomBytes, '0x') -// upkeepId = await getUpkeepID(tx) -// -// autoFunderUpkeep = await upkeepAutoFunderFactory -// .connect(owner) -// .deploy(linkToken.address, registry.address) -// tx = await registry -// .connect(owner) -// [ -// 'registerUpkeep(address,uint32,address,bytes,bytes)' -// ](autoFunderUpkeep.address, performGas, autoFunderUpkeep.address, randomBytes, '0x') -// afUpkeepId = await getUpkeepID(tx) -// -// ltUpkeep = await deployMockContract(owner, ILogAutomationactory.abi) -// tx = await registry -// .connect(owner) -// [ -// 'registerUpkeep(address,uint32,address,uint8,bytes,bytes,bytes)' -// ](ltUpkeep.address, performGas, await admin.getAddress(), Trigger.LOG, '0x', logTriggerConfig, emptyBytes) -// logUpkeepId = await getUpkeepID(tx) -// -// await autoFunderUpkeep.setUpkeepId(afUpkeepId) -// // Give enough funds for upkeep as well as to the upkeep contract -// await linkToken -// .connect(owner) -// .transfer(autoFunderUpkeep.address, toWei('1000')) -// -// tx = await registry -// .connect(owner) -// [ -// 'registerUpkeep(address,uint32,address,bytes,bytes)' -// ](streamsLookupUpkeep.address, performGas, await admin.getAddress(), randomBytes, '0x') -// streamsLookupUpkeepId = await getUpkeepID(tx) -// } -// -// const getMultipleUpkeepsDeployedAndFunded = async ( -// numPassingConditionalUpkeeps: number, -// numPassingLogUpkeeps: number, -// numFailingUpkeeps: number, -// ) => { -// const passingConditionalUpkeepIds = [] -// const passingLogUpkeepIds = [] -// const failingUpkeepIds = [] -// for (let i = 0; i < numPassingConditionalUpkeeps; i++) { -// const mock = await upkeepMockFactory.deploy() -// await mock.setCanPerform(true) -// await mock.setPerformGasToBurn(BigNumber.from('0')) -// const tx = await registry -// .connect(owner) -// [ -// 'registerUpkeep(address,uint32,address,bytes,bytes)' -// ](mock.address, performGas, await admin.getAddress(), randomBytes, '0x') -// const condUpkeepId = await getUpkeepID(tx) -// passingConditionalUpkeepIds.push(condUpkeepId) -// -// // Add funds to passing upkeeps -// await registry.connect(admin).addFunds(condUpkeepId, toWei('100')) -// } -// for (let i = 0; i < numPassingLogUpkeeps; i++) { -// const mock = await upkeepMockFactory.deploy() -// await mock.setCanPerform(true) -// await mock.setPerformGasToBurn(BigNumber.from('0')) -// const tx = await registry -// .connect(owner) -// [ -// 'registerUpkeep(address,uint32,address,uint8,bytes,bytes,bytes)' -// ](mock.address, performGas, await admin.getAddress(), Trigger.LOG, '0x', logTriggerConfig, emptyBytes) -// const logUpkeepId = await getUpkeepID(tx) -// passingLogUpkeepIds.push(logUpkeepId) -// -// // Add funds to passing upkeeps -// await registry.connect(admin).addFunds(logUpkeepId, toWei('100')) -// } -// for (let i = 0; i < numFailingUpkeeps; i++) { -// const mock = await upkeepMockFactory.deploy() -// await mock.setCanPerform(true) -// await mock.setPerformGasToBurn(BigNumber.from('0')) -// const tx = await registry -// .connect(owner) -// [ -// 'registerUpkeep(address,uint32,address,bytes,bytes)' -// ](mock.address, performGas, await admin.getAddress(), randomBytes, '0x') -// const failingUpkeepId = await getUpkeepID(tx) -// failingUpkeepIds.push(failingUpkeepId) -// } -// return { -// passingConditionalUpkeepIds, -// passingLogUpkeepIds, -// failingUpkeepIds, -// } -// } -// -// beforeEach(async () => { -// await loadFixture(setup) -// }) -// -// describe('#transmit', () => { -// const fArray = [1, 5, 10] -// -// it('reverts when registry is paused', async () => { -// await registry.connect(owner).pause() -// await evmRevert( -// getTransmitTx(registry, keeper1, [upkeepId]), -// 'RegistryPaused()', -// ) -// }) -// -// it('reverts when called by non active transmitter', async () => { -// await evmRevert( -// getTransmitTx(registry, payee1, [upkeepId]), -// 'OnlyActiveTransmitters()', -// ) -// }) -// -// it('reverts when report data lengths mismatches', async () => { -// const upkeepIds = [] -// const gasLimits: BigNumber[] = [] -// const triggers: string[] = [] -// const performDatas = [] -// -// upkeepIds.push(upkeepId) -// gasLimits.push(performGas) -// triggers.push('0x') -// performDatas.push('0x') -// // Push an extra perform data -// performDatas.push('0x') -// -// const report = encodeReport({ -// fastGasWei: 0, -// linkNative: 0, -// upkeepIds, -// gasLimits, -// triggers, -// performDatas, -// }) -// -// await evmRevert( -// getTransmitTxWithReport(registry, keeper1, report), -// 'InvalidReport()', -// ) -// }) -// -// it('returns early when invalid upkeepIds are included in report', async () => { -// const tx = await getTransmitTx(registry, keeper1, [ -// upkeepId.add(BigNumber.from('1')), -// ]) -// -// const receipt = await tx.wait() -// const cancelledUpkeepReportLogs = parseCancelledUpkeepReportLogs(receipt) -// // exactly 1 CancelledUpkeepReport log should be emitted -// assert.equal(cancelledUpkeepReportLogs.length, 1) -// }) -// -// it('returns early when upkeep has insufficient funds', async () => { -// const tx = await getTransmitTx(registry, keeper1, [upkeepId]) -// const receipt = await tx.wait() -// const insufficientFundsUpkeepReportLogs = -// parseInsufficientFundsUpkeepReportLogs(receipt) -// // exactly 1 InsufficientFundsUpkeepReportLogs log should be emitted -// assert.equal(insufficientFundsUpkeepReportLogs.length, 1) -// }) -// -// it('permits retrying log triggers after funds are added', async () => { -// const txHash = ethers.utils.randomBytes(32) -// let tx = await getTransmitTx(registry, keeper1, [logUpkeepId], { -// txHash, -// logIndex: 0, -// }) -// let receipt = await tx.wait() -// const insufficientFundsLogs = -// parseInsufficientFundsUpkeepReportLogs(receipt) -// assert.equal(insufficientFundsLogs.length, 1) -// registry.connect(admin).addFunds(logUpkeepId, toWei('100')) -// tx = await getTransmitTx(registry, keeper1, [logUpkeepId], { -// txHash, -// logIndex: 0, -// }) -// receipt = await tx.wait() -// const performedLogs = parseUpkeepPerformedLogs(receipt) -// assert.equal(performedLogs.length, 1) -// }) -// -// context('When the upkeep is funded', async () => { -// beforeEach(async () => { -// // Fund the upkeep -// await Promise.all([ -// registry.connect(admin).addFunds(upkeepId, toWei('100')), -// registry.connect(admin).addFunds(logUpkeepId, toWei('100')), -// ]) -// }) -// -// it('handles duplicate upkeepIDs', async () => { -// const tests: [string, BigNumber, number, number][] = [ -// // [name, upkeep, num stale, num performed] -// ['conditional', upkeepId, 1, 1], // checkBlocks must be sequential -// ['log-trigger', logUpkeepId, 0, 2], // logs are deduped based on the "trigger ID" -// ] -// for (const [type, id, nStale, nPerformed] of tests) { -// const tx = await getTransmitTx(registry, keeper1, [id, id]) -// const receipt = await tx.wait() -// const staleUpkeepReport = parseStaleUpkeepReportLogs(receipt) -// const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) -// assert.equal( -// staleUpkeepReport.length, -// nStale, -// `wrong log count for ${type} upkeep`, -// ) -// assert.equal( -// upkeepPerformedLogs.length, -// nPerformed, -// `wrong log count for ${type} upkeep`, -// ) -// } -// }) -// -// it('handles duplicate log triggers', async () => { -// const logBlockHash = ethers.utils.randomBytes(32) -// const txHash = ethers.utils.randomBytes(32) -// const logIndex = 0 -// const expectedDedupKey = ethers.utils.solidityKeccak256( -// ['uint256', 'bytes32', 'bytes32', 'uint32'], -// [logUpkeepId, logBlockHash, txHash, logIndex], -// ) -// assert.isFalse(await registry.hasDedupKey(expectedDedupKey)) -// const tx = await getTransmitTx( -// registry, -// keeper1, -// [logUpkeepId, logUpkeepId], -// { logBlockHash, txHash, logIndex }, // will result in the same dedup key -// ) -// const receipt = await tx.wait() -// const staleUpkeepReport = parseStaleUpkeepReportLogs(receipt) -// const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) -// assert.equal(staleUpkeepReport.length, 1) -// assert.equal(upkeepPerformedLogs.length, 1) -// assert.isTrue(await registry.hasDedupKey(expectedDedupKey)) -// await expect(tx) -// .to.emit(registry, 'DedupKeyAdded') -// .withArgs(expectedDedupKey) -// }) -// -// it('returns early when check block number is less than last perform (block)', async () => { -// // First perform an upkeep to put last perform block number on upkeep state -// const tx = await getTransmitTx(registry, keeper1, [upkeepId]) -// await tx.wait() -// const lastPerformed = (await registry.getUpkeep(upkeepId)) -// .lastPerformedBlockNumber -// const lastPerformBlock = await ethers.provider.getBlock(lastPerformed) -// assert.equal(lastPerformed.toString(), tx.blockNumber?.toString()) -// // Try to transmit a report which has checkBlockNumber = lastPerformed-1, should result in stale report -// const transmitTx = await getTransmitTx(registry, keeper1, [upkeepId], { -// checkBlockNum: lastPerformBlock.number - 1, -// checkBlockHash: lastPerformBlock.parentHash, -// }) -// const receipt = await transmitTx.wait() -// const staleUpkeepReportLogs = parseStaleUpkeepReportLogs(receipt) -// // exactly 1 StaleUpkeepReportLogs log should be emitted -// assert.equal(staleUpkeepReportLogs.length, 1) -// }) -// -// it('handles case when check block hash does not match', async () => { -// const tests: [string, BigNumber][] = [ -// ['conditional', upkeepId], -// ['log-trigger', logUpkeepId], -// ] -// for (const [type, id] of tests) { -// const latestBlock = await ethers.provider.getBlock('latest') -// // Try to transmit a report which has incorrect checkBlockHash -// const tx = await getTransmitTx(registry, keeper1, [id], { -// checkBlockNum: latestBlock.number - 1, -// checkBlockHash: latestBlock.hash, // should be latestBlock.parentHash -// }) -// -// const receipt = await tx.wait() -// const reorgedUpkeepReportLogs = parseReorgedUpkeepReportLogs(receipt) -// // exactly 1 ReorgedUpkeepReportLogs log should be emitted -// assert.equal( -// reorgedUpkeepReportLogs.length, -// 1, -// `wrong log count for ${type} upkeep`, -// ) -// } -// }) -// -// it('handles case when check block number is older than 256 blocks', async () => { -// for (let i = 0; i < 256; i++) { -// await ethers.provider.send('evm_mine', []) -// } -// const tests: [string, BigNumber][] = [ -// ['conditional', upkeepId], -// ['log-trigger', logUpkeepId], -// ] -// for (const [type, id] of tests) { -// const latestBlock = await ethers.provider.getBlock('latest') -// const old = await ethers.provider.getBlock(latestBlock.number - 256) -// // Try to transmit a report which has incorrect checkBlockHash -// const tx = await getTransmitTx(registry, keeper1, [id], { -// checkBlockNum: old.number, -// checkBlockHash: old.hash, -// }) -// -// const receipt = await tx.wait() -// const reorgedUpkeepReportLogs = parseReorgedUpkeepReportLogs(receipt) -// // exactly 1 ReorgedUpkeepReportLogs log should be emitted -// assert.equal( -// reorgedUpkeepReportLogs.length, -// 1, -// `wrong log count for ${type} upkeep`, -// ) -// } -// }) -// -// it('allows bypassing reorg protection with empty blockhash', async () => { -// const tests: [string, BigNumber][] = [ -// ['conditional', upkeepId], -// ['log-trigger', logUpkeepId], -// ] -// for (const [type, id] of tests) { -// const latestBlock = await ethers.provider.getBlock('latest') -// const tx = await getTransmitTx(registry, keeper1, [id], { -// checkBlockNum: latestBlock.number, -// checkBlockHash: emptyBytes32, -// }) -// const receipt = await tx.wait() -// const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) -// assert.equal( -// upkeepPerformedLogs.length, -// 1, -// `wrong log count for ${type} upkeep`, -// ) -// } -// }) -// -// it('allows very old trigger block numbers when bypassing reorg protection with empty blockhash', async () => { -// // mine enough blocks so that blockhash(1) is unavailable -// for (let i = 0; i <= 256; i++) { -// await ethers.provider.send('evm_mine', []) -// } -// const tests: [string, BigNumber][] = [ -// ['conditional', upkeepId], -// ['log-trigger', logUpkeepId], -// ] -// for (const [type, id] of tests) { -// const tx = await getTransmitTx(registry, keeper1, [id], { -// checkBlockNum: 1, -// checkBlockHash: emptyBytes32, -// }) -// const receipt = await tx.wait() -// const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) -// assert.equal( -// upkeepPerformedLogs.length, -// 1, -// `wrong log count for ${type} upkeep`, -// ) -// } -// }) -// -// it('returns early when future block number is provided as trigger, irrespective of blockhash being present', async () => { -// const tests: [string, BigNumber][] = [ -// ['conditional', upkeepId], -// ['log-trigger', logUpkeepId], -// ] -// for (const [type, id] of tests) { -// const latestBlock = await ethers.provider.getBlock('latest') -// -// // Should fail when blockhash is empty -// let tx = await getTransmitTx(registry, keeper1, [id], { -// checkBlockNum: latestBlock.number + 100, -// checkBlockHash: emptyBytes32, -// }) -// let receipt = await tx.wait() -// let reorgedUpkeepReportLogs = parseReorgedUpkeepReportLogs(receipt) -// // exactly 1 ReorgedUpkeepReportLogs log should be emitted -// assert.equal( -// reorgedUpkeepReportLogs.length, -// 1, -// `wrong log count for ${type} upkeep`, -// ) -// -// // Should also fail when blockhash is not empty -// tx = await getTransmitTx(registry, keeper1, [id], { -// checkBlockNum: latestBlock.number + 100, -// checkBlockHash: latestBlock.hash, -// }) -// receipt = await tx.wait() -// reorgedUpkeepReportLogs = parseReorgedUpkeepReportLogs(receipt) -// // exactly 1 ReorgedUpkeepReportLogs log should be emitted -// assert.equal( -// reorgedUpkeepReportLogs.length, -// 1, -// `wrong log count for ${type} upkeep`, -// ) -// } -// }) -// -// it('returns early when upkeep is cancelled and cancellation delay has gone', async () => { -// const latestBlockReport = await makeLatestBlockReport([upkeepId]) -// await registry.connect(admin).cancelUpkeep(upkeepId) -// -// for (let i = 0; i < cancellationDelay; i++) { -// await ethers.provider.send('evm_mine', []) -// } -// -// const tx = await getTransmitTxWithReport( -// registry, -// keeper1, -// latestBlockReport, -// ) -// -// const receipt = await tx.wait() -// const cancelledUpkeepReportLogs = -// parseCancelledUpkeepReportLogs(receipt) -// // exactly 1 CancelledUpkeepReport log should be emitted -// assert.equal(cancelledUpkeepReportLogs.length, 1) -// }) -// -// it('does not revert if the target cannot execute', async () => { -// await mock.setCanPerform(false) -// const tx = await getTransmitTx(registry, keeper1, [upkeepId]) -// -// const receipt = await tx.wait() -// const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) -// // exactly 1 Upkeep Performed should be emitted -// assert.equal(upkeepPerformedLogs.length, 1) -// const upkeepPerformedLog = upkeepPerformedLogs[0] -// -// const success = upkeepPerformedLog.args.success -// assert.equal(success, false) -// }) -// -// it('does not revert if the target runs out of gas', async () => { -// await mock.setCanPerform(false) -// -// const tx = await getTransmitTx(registry, keeper1, [upkeepId], { -// performGas: 10, // too little gas -// }) -// -// const receipt = await tx.wait() -// const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) -// // exactly 1 Upkeep Performed should be emitted -// assert.equal(upkeepPerformedLogs.length, 1) -// const upkeepPerformedLog = upkeepPerformedLogs[0] -// -// const success = upkeepPerformedLog.args.success -// assert.equal(success, false) -// }) -// -// it('reverts if not enough gas supplied', async () => { -// await evmRevert( -// getTransmitTx(registry, keeper1, [upkeepId], { -// gasLimit: performGas, -// }), -// ) -// }) -// -// it('executes the data passed to the registry', async () => { -// await mock.setCanPerform(true) -// -// const tx = await getTransmitTx(registry, keeper1, [upkeepId], { -// performData: randomBytes, -// }) -// const receipt = await tx.wait() -// -// const upkeepPerformedWithABI = [ -// 'event UpkeepPerformedWith(bytes upkeepData)', -// ] -// const iface = new ethers.utils.Interface(upkeepPerformedWithABI) -// const parsedLogs = [] -// for (let i = 0; i < receipt.logs.length; i++) { -// const log = receipt.logs[i] -// try { -// parsedLogs.push(iface.parseLog(log)) -// } catch (e) { -// // ignore log -// } -// } -// assert.equal(parsedLogs.length, 1) -// assert.equal(parsedLogs[0].args.upkeepData, randomBytes) -// }) -// -// it('uses actual execution price for payment and premium calculation', async () => { -// // Actual multiplier is 2, but we set gasPrice to be 1x gasWei -// const gasPrice = gasWei.mul(BigNumber.from('1')) -// await mock.setCanPerform(true) -// const registryPremiumBefore = (await registry.getState()).state -// .totalPremium -// const tx = await getTransmitTx(registry, keeper1, [upkeepId], { -// gasPrice, -// }) -// const receipt = await tx.wait() -// const registryPremiumAfter = (await registry.getState()).state -// .totalPremium -// const premium = registryPremiumAfter.sub(registryPremiumBefore) -// -// const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) -// // exactly 1 Upkeep Performed should be emitted -// assert.equal(upkeepPerformedLogs.length, 1) -// const upkeepPerformedLog = upkeepPerformedLogs[0] -// -// const gasUsed = upkeepPerformedLog.args.gasUsed -// const gasOverhead = upkeepPerformedLog.args.gasOverhead -// const totalPayment = upkeepPerformedLog.args.totalPayment -// -// assert.equal( -// linkForGas( -// gasUsed, -// gasOverhead, -// BigNumber.from('1'), // Not the config multiplier, but the actual gas used -// paymentPremiumPPB, -// flatFeeMicroLink, -// ).total.toString(), -// totalPayment.toString(), -// ) -// -// assert.equal( -// linkForGas( -// gasUsed, -// gasOverhead, -// BigNumber.from('1'), // Not the config multiplier, but the actual gas used -// paymentPremiumPPB, -// flatFeeMicroLink, -// ).premium.toString(), -// premium.toString(), -// ) -// }) -// -// it('only pays at a rate up to the gas ceiling [ @skip-coverage ]', async () => { -// // Actual multiplier is 2, but we set gasPrice to be 10x -// const gasPrice = gasWei.mul(BigNumber.from('10')) -// await mock.setCanPerform(true) -// -// const tx = await getTransmitTx(registry, keeper1, [upkeepId], { -// gasPrice, -// }) -// const receipt = await tx.wait() -// const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) -// // exactly 1 Upkeep Performed should be emitted -// assert.equal(upkeepPerformedLogs.length, 1) -// const upkeepPerformedLog = upkeepPerformedLogs[0] -// -// const gasUsed = upkeepPerformedLog.args.gasUsed -// const gasOverhead = upkeepPerformedLog.args.gasOverhead -// const totalPayment = upkeepPerformedLog.args.totalPayment -// -// assert.equal( -// linkForGas( -// gasUsed, -// gasOverhead, -// gasCeilingMultiplier, // Should be same with exisitng multiplier -// paymentPremiumPPB, -// flatFeeMicroLink, -// ).total.toString(), -// totalPayment.toString(), -// ) -// }) -// -// it('correctly accounts for l payment', async () => { -// await mock.setCanPerform(true) -// // Same as MockArbGasInfo.sol -// const l1CostWeiArb = BigNumber.from(1000000) -// -// let tx = await arbRegistry -// .connect(owner) -// [ -// 'registerUpkeep(address,uint32,address,bytes,bytes)' -// ](mock.address, performGas, await admin.getAddress(), randomBytes, '0x') -// const testUpkeepId = await getUpkeepID(tx) -// await arbRegistry.connect(owner).addFunds(testUpkeepId, toWei('100')) -// -// // Do the thing -// tx = await getTransmitTx( -// arbRegistry, -// keeper1, -// [testUpkeepId], -// -// { gasPrice: gasWei.mul('5') }, // High gas price so that it gets capped -// ) -// const receipt = await tx.wait() -// const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) -// // exactly 1 Upkeep Performed should be emitted -// assert.equal(upkeepPerformedLogs.length, 1) -// const upkeepPerformedLog = upkeepPerformedLogs[0] -// -// const gasUsed = upkeepPerformedLog.args.gasUsed -// const gasOverhead = upkeepPerformedLog.args.gasOverhead -// const totalPayment = upkeepPerformedLog.args.totalPayment -// -// assert.equal( -// linkForGas( -// gasUsed, -// gasOverhead, -// gasCeilingMultiplier, -// paymentPremiumPPB, -// flatFeeMicroLink, -// l1CostWeiArb.div(gasCeilingMultiplier), // Dividing by gasCeilingMultiplier as it gets multiplied later -// ).total.toString(), -// totalPayment.toString(), -// ) -// }) -// -// itMaybe('can self fund', async () => { -// const maxPayment = await registry.getMaxPaymentForGas( -// Trigger.CONDITION, -// performGas, -// ) -// -// // First set auto funding amount to 0 and verify that balance is deducted upon performUpkeep -// let initialBalance = toWei('100') -// await registry.connect(owner).addFunds(afUpkeepId, initialBalance) -// await autoFunderUpkeep.setAutoFundLink(0) -// await autoFunderUpkeep.setIsEligible(true) -// await getTransmitTx(registry, keeper1, [afUpkeepId]) -// -// let postUpkeepBalance = (await registry.getUpkeep(afUpkeepId)).balance -// assert.isTrue(postUpkeepBalance.lt(initialBalance)) // Balance should be deducted -// assert.isTrue(postUpkeepBalance.gte(initialBalance.sub(maxPayment))) // Balance should not be deducted more than maxPayment -// -// // Now set auto funding amount to 100 wei and verify that the balance increases -// initialBalance = postUpkeepBalance -// const autoTopupAmount = toWei('100') -// await autoFunderUpkeep.setAutoFundLink(autoTopupAmount) -// await autoFunderUpkeep.setIsEligible(true) -// await getTransmitTx(registry, keeper1, [afUpkeepId]) -// -// postUpkeepBalance = (await registry.getUpkeep(afUpkeepId)).balance -// // Balance should increase by autoTopupAmount and decrease by max maxPayment -// assert.isTrue( -// postUpkeepBalance.gte( -// initialBalance.add(autoTopupAmount).sub(maxPayment), -// ), -// ) -// }) -// -// it('can self cancel', async () => { -// await registry.connect(owner).addFunds(afUpkeepId, toWei('100')) -// -// await autoFunderUpkeep.setIsEligible(true) -// await autoFunderUpkeep.setShouldCancel(true) -// -// let registration = await registry.getUpkeep(afUpkeepId) -// const oldExpiration = registration.maxValidBlocknumber -// -// // Do the thing -// await getTransmitTx(registry, keeper1, [afUpkeepId]) -// -// // Verify upkeep gets cancelled -// registration = await registry.getUpkeep(afUpkeepId) -// const newExpiration = registration.maxValidBlocknumber -// assert.isTrue(newExpiration.lt(oldExpiration)) -// }) -// -// it('reverts when configDigest mismatches', async () => { -// const report = await makeLatestBlockReport([upkeepId]) -// const reportContext = [emptyBytes32, epochAndRound5_1, emptyBytes32] // wrong config digest -// const sigs = signReport(reportContext, report, signers.slice(0, f + 1)) -// await evmRevert( -// registry -// .connect(keeper1) -// .transmit( -// [reportContext[0], reportContext[1], reportContext[2]], -// report, -// sigs.rs, -// sigs.ss, -// sigs.vs, -// ), -// 'ConfigDigestMismatch()', -// ) -// }) -// -// it('reverts with incorrect number of signatures', async () => { -// const configDigest = (await registry.getState()).state -// .latestConfigDigest -// const report = await makeLatestBlockReport([upkeepId]) -// const reportContext = [configDigest, epochAndRound5_1, emptyBytes32] // wrong config digest -// const sigs = signReport(reportContext, report, signers.slice(0, f + 2)) -// await evmRevert( -// registry -// .connect(keeper1) -// .transmit( -// [reportContext[0], reportContext[1], reportContext[2]], -// report, -// sigs.rs, -// sigs.ss, -// sigs.vs, -// ), -// 'IncorrectNumberOfSignatures()', -// ) -// }) -// -// it('reverts with invalid signature for inactive signers', async () => { -// const configDigest = (await registry.getState()).state -// .latestConfigDigest -// const report = await makeLatestBlockReport([upkeepId]) -// const reportContext = [configDigest, epochAndRound5_1, emptyBytes32] // wrong config digest -// const sigs = signReport(reportContext, report, [ -// new ethers.Wallet(ethers.Wallet.createRandom()), -// new ethers.Wallet(ethers.Wallet.createRandom()), -// ]) -// await evmRevert( -// registry -// .connect(keeper1) -// .transmit( -// [reportContext[0], reportContext[1], reportContext[2]], -// report, -// sigs.rs, -// sigs.ss, -// sigs.vs, -// ), -// 'OnlyActiveSigners()', -// ) -// }) -// -// it('reverts with invalid signature for duplicated signers', async () => { -// const configDigest = (await registry.getState()).state -// .latestConfigDigest -// const report = await makeLatestBlockReport([upkeepId]) -// const reportContext = [configDigest, epochAndRound5_1, emptyBytes32] // wrong config digest -// const sigs = signReport(reportContext, report, [signer1, signer1]) -// await evmRevert( -// registry -// .connect(keeper1) -// .transmit( -// [reportContext[0], reportContext[1], reportContext[2]], -// report, -// sigs.rs, -// sigs.ss, -// sigs.vs, -// ), -// 'DuplicateSigners()', -// ) -// }) -// -// itMaybe( -// 'has a large enough gas overhead to cover upkeep that use all its gas [ @skip-coverage ]', -// async () => { -// await registry.connect(owner).setConfigTypeSafe( -// signerAddresses, -// keeperAddresses, -// 10, // maximise f to maximise overhead -// config, -// offchainVersion, -// offchainBytes, -// ) -// const tx = await registry -// .connect(owner) -// ['registerUpkeep(address,uint32,address,bytes,bytes)']( -// mock.address, -// maxPerformGas, // max allowed gas -// await admin.getAddress(), -// randomBytes, -// '0x', -// ) -// const testUpkeepId = await getUpkeepID(tx) -// await registry.connect(admin).addFunds(testUpkeepId, toWei('100')) -// -// let performData = '0x' -// for (let i = 0; i < maxPerformDataSize.toNumber(); i++) { -// performData += '11' -// } // max allowed performData -// -// await mock.setCanPerform(true) -// await mock.setPerformGasToBurn(maxPerformGas) -// -// await getTransmitTx(registry, keeper1, [testUpkeepId], { -// gasLimit: maxPerformGas.add(transmitGasOverhead), -// numSigners: 11, -// performData, -// }) // Should not revert -// }, -// ) -// -// itMaybe( -// 'performs upkeep, deducts payment, updates lastPerformed and emits events', -// async () => { -// await mock.setCanPerform(true) -// -// for (const i in fArray) { -// const newF = fArray[i] -// await registry -// .connect(owner) -// .setConfigTypeSafe( -// signerAddresses, -// keeperAddresses, -// newF, -// config, -// offchainVersion, -// offchainBytes, -// ) -// const checkBlock = await ethers.provider.getBlock('latest') -// -// const keeperBefore = await registry.getTransmitterInfo( -// await keeper1.getAddress(), -// ) -// const registrationBefore = await registry.getUpkeep(upkeepId) -// const registryPremiumBefore = (await registry.getState()).state -// .totalPremium -// const keeperLinkBefore = await linkToken.balanceOf( -// await keeper1.getAddress(), -// ) -// const registryLinkBefore = await linkToken.balanceOf( -// registry.address, -// ) -// -// // Do the thing -// const tx = await getTransmitTx(registry, keeper1, [upkeepId], { -// checkBlockNum: checkBlock.number, -// checkBlockHash: checkBlock.hash, -// numSigners: newF + 1, -// }) -// -// const receipt = await tx.wait() -// -// const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) -// // exactly 1 Upkeep Performed should be emitted -// assert.equal(upkeepPerformedLogs.length, 1) -// const upkeepPerformedLog = upkeepPerformedLogs[0] -// -// const id = upkeepPerformedLog.args.id -// const success = upkeepPerformedLog.args.success -// const trigger = upkeepPerformedLog.args.trigger -// const gasUsed = upkeepPerformedLog.args.gasUsed -// const gasOverhead = upkeepPerformedLog.args.gasOverhead -// const totalPayment = upkeepPerformedLog.args.totalPayment -// assert.equal(id.toString(), upkeepId.toString()) -// assert.equal(success, true) -// assert.equal( -// trigger, -// encodeBlockTrigger({ -// blockNum: checkBlock.number, -// blockHash: checkBlock.hash, -// }), -// ) -// assert.isTrue(gasUsed.gt(BigNumber.from('0'))) -// assert.isTrue(gasOverhead.gt(BigNumber.from('0'))) -// assert.isTrue(totalPayment.gt(BigNumber.from('0'))) -// -// const keeperAfter = await registry.getTransmitterInfo( -// await keeper1.getAddress(), -// ) -// const registrationAfter = await registry.getUpkeep(upkeepId) -// const keeperLinkAfter = await linkToken.balanceOf( -// await keeper1.getAddress(), -// ) -// const registryLinkAfter = await linkToken.balanceOf( -// registry.address, -// ) -// const registryPremiumAfter = (await registry.getState()).state -// .totalPremium -// const premium = registryPremiumAfter.sub(registryPremiumBefore) -// // Keeper payment is gasPayment + premium / num keepers -// const keeperPayment = totalPayment -// .sub(premium) -// .add(premium.div(BigNumber.from(keeperAddresses.length))) -// -// assert.equal( -// keeperAfter.balance.sub(keeperPayment).toString(), -// keeperBefore.balance.toString(), -// ) -// assert.equal( -// registrationBefore.balance.sub(totalPayment).toString(), -// registrationAfter.balance.toString(), -// ) -// assert.isTrue(keeperLinkAfter.eq(keeperLinkBefore)) -// assert.isTrue(registryLinkBefore.eq(registryLinkAfter)) -// -// // Amount spent should be updated correctly -// assert.equal( -// registrationAfter.amountSpent.sub(totalPayment).toString(), -// registrationBefore.amountSpent.toString(), -// ) -// assert.isTrue( -// registrationAfter.amountSpent -// .sub(registrationBefore.amountSpent) -// .eq(registrationBefore.balance.sub(registrationAfter.balance)), -// ) -// // Last perform block number should be updated -// assert.equal( -// registrationAfter.lastPerformedBlockNumber.toString(), -// tx.blockNumber?.toString(), -// ) -// -// // Latest epoch should be 5 -// assert.equal((await registry.getState()).state.latestEpoch, 5) -// } -// }, -// ) -// -// describeMaybe( -// 'Gas benchmarking conditional upkeeps [ @skip-coverage ]', -// function () { -// const fs = [1, 10] -// fs.forEach(function (newF) { -// it( -// 'When f=' + -// newF + -// ' calculates gas overhead appropriately within a margin for different scenarios', -// async () => { -// // Perform the upkeep once to remove non-zero storage slots and have predictable gas measurement -// let tx = await getTransmitTx(registry, keeper1, [upkeepId]) -// await tx.wait() -// -// // Different test scenarios -// let longBytes = '0x' -// for (let i = 0; i < maxPerformDataSize.toNumber(); i++) { -// longBytes += '11' -// } -// const upkeepSuccessArray = [true, false] -// const performGasArray = [5000, performGas] -// const performDataArray = ['0x', longBytes] -// -// for (const i in upkeepSuccessArray) { -// for (const j in performGasArray) { -// for (const k in performDataArray) { -// const upkeepSuccess = upkeepSuccessArray[i] -// const performGas = performGasArray[j] -// const performData = performDataArray[k] -// -// await mock.setCanPerform(upkeepSuccess) -// await mock.setPerformGasToBurn(performGas) -// await registry -// .connect(owner) -// .setConfigTypeSafe( -// signerAddresses, -// keeperAddresses, -// newF, -// config, -// offchainVersion, -// offchainBytes, -// ) -// tx = await getTransmitTx(registry, keeper1, [upkeepId], { -// numSigners: newF + 1, -// performData, -// }) -// const receipt = await tx.wait() -// const upkeepPerformedLogs = -// parseUpkeepPerformedLogs(receipt) -// // exactly 1 Upkeep Performed should be emitted -// assert.equal(upkeepPerformedLogs.length, 1) -// const upkeepPerformedLog = upkeepPerformedLogs[0] -// -// const upkeepGasUsed = upkeepPerformedLog.args.gasUsed -// const chargedGasOverhead = -// upkeepPerformedLog.args.gasOverhead -// const actualGasOverhead = -// receipt.gasUsed.sub(upkeepGasUsed) -// -// assert.isTrue(upkeepGasUsed.gt(BigNumber.from('0'))) -// assert.isTrue(chargedGasOverhead.gt(BigNumber.from('0'))) -// -// console.log( -// 'Gas Benchmarking conditional upkeeps:', -// 'upkeepSuccess=', -// upkeepSuccess, -// 'performGas=', -// performGas.toString(), -// 'performData length=', -// performData.length / 2 - 1, -// 'sig verification ( f =', -// newF, -// '): calculated overhead: ', -// chargedGasOverhead.toString(), -// ' actual overhead: ', -// actualGasOverhead.toString(), -// ' margin over gasUsed: ', -// chargedGasOverhead.sub(actualGasOverhead).toString(), -// ) -// -// // Overhead should not get capped -// const gasOverheadCap = registryConditionalOverhead -// .add( -// registryPerSignerGasOverhead.mul( -// BigNumber.from(newF + 1), -// ), -// ) -// .add( -// BigNumber.from( -// registryPerPerformByteGasOverhead.toNumber() * -// performData.length, -// ), -// ) -// const gasCapMinusOverhead = -// gasOverheadCap.sub(chargedGasOverhead) -// assert.isTrue( -// gasCapMinusOverhead.gt(BigNumber.from(0)), -// 'Gas overhead got capped. Verify gas overhead variables in test match those in the registry. To not have the overheads capped increase REGISTRY_GAS_OVERHEAD by atleast ' + -// gasCapMinusOverhead.toString(), -// ) -// // total gas charged should be greater than tx gas but within gasCalculationMargin -// assert.isTrue( -// chargedGasOverhead.gt(actualGasOverhead), -// 'Gas overhead calculated is too low, increase account gas variables (ACCOUNTING_FIXED_GAS_OVERHEAD/ACCOUNTING_PER_SIGNER_GAS_OVERHEAD) by atleast ' + -// actualGasOverhead.sub(chargedGasOverhead).toString(), -// ) -// -// assert.isTrue( -// chargedGasOverhead -// .sub(actualGasOverhead) -// .lt(gasCalculationMargin), -// ), -// 'Gas overhead calculated is too high, decrease account gas variables (ACCOUNTING_FIXED_GAS_OVERHEAD/ACCOUNTING_PER_SIGNER_GAS_OVERHEAD) by atleast ' + -// chargedGasOverhead -// .sub(chargedGasOverhead) -// .sub(gasCalculationMargin) -// .toString() -// } -// } -// } -// }, -// ) -// }) -// }, -// ) -// -// describeMaybe( -// 'Gas benchmarking log upkeeps [ @skip-coverage ]', -// function () { -// const fs = [1, 10] -// fs.forEach(function (newF) { -// it( -// 'When f=' + -// newF + -// ' calculates gas overhead appropriately within a margin', -// async () => { -// // Perform the upkeep once to remove non-zero storage slots and have predictable gas measurement -// let tx = await getTransmitTx(registry, keeper1, [logUpkeepId]) -// await tx.wait() -// const performData = '0x' -// await mock.setCanPerform(true) -// await mock.setPerformGasToBurn(performGas) -// await registry.setConfigTypeSafe( -// signerAddresses, -// keeperAddresses, -// newF, -// config, -// offchainVersion, -// offchainBytes, -// ) -// tx = await getTransmitTx(registry, keeper1, [logUpkeepId], { -// numSigners: newF + 1, -// performData, -// }) -// const receipt = await tx.wait() -// const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) -// // exactly 1 Upkeep Performed should be emitted -// assert.equal(upkeepPerformedLogs.length, 1) -// const upkeepPerformedLog = upkeepPerformedLogs[0] -// -// const upkeepGasUsed = upkeepPerformedLog.args.gasUsed -// const chargedGasOverhead = upkeepPerformedLog.args.gasOverhead -// const actualGasOverhead = receipt.gasUsed.sub(upkeepGasUsed) -// -// assert.isTrue(upkeepGasUsed.gt(BigNumber.from('0'))) -// assert.isTrue(chargedGasOverhead.gt(BigNumber.from('0'))) -// -// console.log( -// 'Gas Benchmarking log upkeeps:', -// 'upkeepSuccess=', -// true, -// 'performGas=', -// performGas.toString(), -// 'performData length=', -// performData.length / 2 - 1, -// 'sig verification ( f =', -// newF, -// '): calculated overhead: ', -// chargedGasOverhead.toString(), -// ' actual overhead: ', -// actualGasOverhead.toString(), -// ' margin over gasUsed: ', -// chargedGasOverhead.sub(actualGasOverhead).toString(), -// ) -// -// // Overhead should not get capped -// const gasOverheadCap = registryLogOverhead -// .add( -// registryPerSignerGasOverhead.mul(BigNumber.from(newF + 1)), -// ) -// .add( -// BigNumber.from( -// registryPerPerformByteGasOverhead.toNumber() * -// performData.length, -// ), -// ) -// const gasCapMinusOverhead = -// gasOverheadCap.sub(chargedGasOverhead) -// assert.isTrue( -// gasCapMinusOverhead.gt(BigNumber.from(0)), -// 'Gas overhead got capped. Verify gas overhead variables in test match those in the registry. To not have the overheads capped increase REGISTRY_GAS_OVERHEAD by atleast ' + -// gasCapMinusOverhead.toString(), -// ) -// // total gas charged should be greater than tx gas but within gasCalculationMargin -// assert.isTrue( -// chargedGasOverhead.gt(actualGasOverhead), -// 'Gas overhead calculated is too low, increase account gas variables (ACCOUNTING_FIXED_GAS_OVERHEAD/ACCOUNTING_PER_SIGNER_GAS_OVERHEAD) by atleast ' + -// actualGasOverhead.sub(chargedGasOverhead).toString(), -// ) -// -// assert.isTrue( -// chargedGasOverhead -// .sub(actualGasOverhead) -// .lt(gasCalculationMargin), -// ), -// 'Gas overhead calculated is too high, decrease account gas variables (ACCOUNTING_FIXED_GAS_OVERHEAD/ACCOUNTING_PER_SIGNER_GAS_OVERHEAD) by atleast ' + -// chargedGasOverhead -// .sub(chargedGasOverhead) -// .sub(gasCalculationMargin) -// .toString() -// }, -// ) -// }) -// }, -// ) -// }) -// }) -// -// describeMaybe( -// '#transmit with upkeep batches [ @skip-coverage ]', -// function () { -// const numPassingConditionalUpkeepsArray = [0, 1, 5] -// const numPassingLogUpkeepsArray = [0, 1, 5] -// const numFailingUpkeepsArray = [0, 3] -// -// for (let idx = 0; idx < numPassingConditionalUpkeepsArray.length; idx++) { -// for (let jdx = 0; jdx < numPassingLogUpkeepsArray.length; jdx++) { -// for (let kdx = 0; kdx < numFailingUpkeepsArray.length; kdx++) { -// const numPassingConditionalUpkeeps = -// numPassingConditionalUpkeepsArray[idx] -// const numPassingLogUpkeeps = numPassingLogUpkeepsArray[jdx] -// const numFailingUpkeeps = numFailingUpkeepsArray[kdx] -// if ( -// numPassingConditionalUpkeeps == 0 && -// numPassingLogUpkeeps == 0 -// ) { -// continue -// } -// it( -// '[Conditional:' + -// numPassingConditionalUpkeeps + -// ',Log:' + -// numPassingLogUpkeeps + -// ',Failures:' + -// numFailingUpkeeps + -// '] performs successful upkeeps and does not charge failing upkeeps', -// async () => { -// const allUpkeeps = await getMultipleUpkeepsDeployedAndFunded( -// numPassingConditionalUpkeeps, -// numPassingLogUpkeeps, -// numFailingUpkeeps, -// ) -// const passingConditionalUpkeepIds = -// allUpkeeps.passingConditionalUpkeepIds -// const passingLogUpkeepIds = allUpkeeps.passingLogUpkeepIds -// const failingUpkeepIds = allUpkeeps.failingUpkeepIds -// -// const keeperBefore = await registry.getTransmitterInfo( -// await keeper1.getAddress(), -// ) -// const keeperLinkBefore = await linkToken.balanceOf( -// await keeper1.getAddress(), -// ) -// const registryLinkBefore = await linkToken.balanceOf( -// registry.address, -// ) -// const registryPremiumBefore = (await registry.getState()).state -// .totalPremium -// const registrationConditionalPassingBefore = await Promise.all( -// passingConditionalUpkeepIds.map(async (id) => { -// const reg = await registry.getUpkeep(BigNumber.from(id)) -// assert.equal(reg.lastPerformedBlockNumber.toString(), '0') -// return reg -// }), -// ) -// const registrationLogPassingBefore = await Promise.all( -// passingLogUpkeepIds.map(async (id) => { -// const reg = await registry.getUpkeep(BigNumber.from(id)) -// assert.equal(reg.lastPerformedBlockNumber.toString(), '0') -// return reg -// }), -// ) -// const registrationFailingBefore = await Promise.all( -// failingUpkeepIds.map(async (id) => { -// const reg = await registry.getUpkeep(BigNumber.from(id)) -// assert.equal(reg.lastPerformedBlockNumber.toString(), '0') -// return reg -// }), -// ) -// -// const tx = await getTransmitTx( -// registry, -// keeper1, -// passingConditionalUpkeepIds.concat( -// passingLogUpkeepIds.concat(failingUpkeepIds), -// ), -// ) -// -// const receipt = await tx.wait() -// const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) -// // exactly numPassingUpkeeps Upkeep Performed should be emitted -// assert.equal( -// upkeepPerformedLogs.length, -// numPassingConditionalUpkeeps + numPassingLogUpkeeps, -// ) -// const insufficientFundsLogs = -// parseInsufficientFundsUpkeepReportLogs(receipt) -// // exactly numFailingUpkeeps Upkeep Performed should be emitted -// assert.equal(insufficientFundsLogs.length, numFailingUpkeeps) -// -// const keeperAfter = await registry.getTransmitterInfo( -// await keeper1.getAddress(), -// ) -// const keeperLinkAfter = await linkToken.balanceOf( -// await keeper1.getAddress(), -// ) -// const registryLinkAfter = await linkToken.balanceOf( -// registry.address, -// ) -// const registrationConditionalPassingAfter = await Promise.all( -// passingConditionalUpkeepIds.map(async (id) => { -// return await registry.getUpkeep(BigNumber.from(id)) -// }), -// ) -// const registrationLogPassingAfter = await Promise.all( -// passingLogUpkeepIds.map(async (id) => { -// return await registry.getUpkeep(BigNumber.from(id)) -// }), -// ) -// const registrationFailingAfter = await Promise.all( -// failingUpkeepIds.map(async (id) => { -// return await registry.getUpkeep(BigNumber.from(id)) -// }), -// ) -// const registryPremiumAfter = (await registry.getState()).state -// .totalPremium -// const premium = registryPremiumAfter.sub(registryPremiumBefore) -// -// let netPayment = BigNumber.from('0') -// for (let i = 0; i < numPassingConditionalUpkeeps; i++) { -// const id = upkeepPerformedLogs[i].args.id -// const gasUsed = upkeepPerformedLogs[i].args.gasUsed -// const gasOverhead = upkeepPerformedLogs[i].args.gasOverhead -// const totalPayment = upkeepPerformedLogs[i].args.totalPayment -// -// expect(id).to.equal(passingConditionalUpkeepIds[i]) -// assert.isTrue(gasUsed.gt(BigNumber.from('0'))) -// assert.isTrue(gasOverhead.gt(BigNumber.from('0'))) -// assert.isTrue(totalPayment.gt(BigNumber.from('0'))) -// -// // Balance should be deducted -// assert.equal( -// registrationConditionalPassingBefore[i].balance -// .sub(totalPayment) -// .toString(), -// registrationConditionalPassingAfter[i].balance.toString(), -// ) -// -// // Amount spent should be updated correctly -// assert.equal( -// registrationConditionalPassingAfter[i].amountSpent -// .sub(totalPayment) -// .toString(), -// registrationConditionalPassingBefore[ -// i -// ].amountSpent.toString(), -// ) -// -// // Last perform block number should be updated -// assert.equal( -// registrationConditionalPassingAfter[ -// i -// ].lastPerformedBlockNumber.toString(), -// tx.blockNumber?.toString(), -// ) -// -// netPayment = netPayment.add(totalPayment) -// } -// -// for (let i = 0; i < numPassingLogUpkeeps; i++) { -// const id = -// upkeepPerformedLogs[numPassingConditionalUpkeeps + i].args -// .id -// const gasUsed = -// upkeepPerformedLogs[numPassingConditionalUpkeeps + i].args -// .gasUsed -// const gasOverhead = -// upkeepPerformedLogs[numPassingConditionalUpkeeps + i].args -// .gasOverhead -// const totalPayment = -// upkeepPerformedLogs[numPassingConditionalUpkeeps + i].args -// .totalPayment -// -// expect(id).to.equal(passingLogUpkeepIds[i]) -// assert.isTrue(gasUsed.gt(BigNumber.from('0'))) -// assert.isTrue(gasOverhead.gt(BigNumber.from('0'))) -// assert.isTrue(totalPayment.gt(BigNumber.from('0'))) -// -// // Balance should be deducted -// assert.equal( -// registrationLogPassingBefore[i].balance -// .sub(totalPayment) -// .toString(), -// registrationLogPassingAfter[i].balance.toString(), -// ) -// -// // Amount spent should be updated correctly -// assert.equal( -// registrationLogPassingAfter[i].amountSpent -// .sub(totalPayment) -// .toString(), -// registrationLogPassingBefore[i].amountSpent.toString(), -// ) -// -// // Last perform block number should not be updated for log triggers -// assert.equal( -// registrationLogPassingAfter[ -// i -// ].lastPerformedBlockNumber.toString(), -// '0', -// ) -// -// netPayment = netPayment.add(totalPayment) -// } -// -// for (let i = 0; i < numFailingUpkeeps; i++) { -// // InsufficientFunds log should be emitted -// const id = insufficientFundsLogs[i].args.id -// expect(id).to.equal(failingUpkeepIds[i]) -// -// // Balance and amount spent should be same -// assert.equal( -// registrationFailingBefore[i].balance.toString(), -// registrationFailingAfter[i].balance.toString(), -// ) -// assert.equal( -// registrationFailingBefore[i].amountSpent.toString(), -// registrationFailingAfter[i].amountSpent.toString(), -// ) -// -// // Last perform block number should not be updated -// assert.equal( -// registrationFailingAfter[ -// i -// ].lastPerformedBlockNumber.toString(), -// '0', -// ) -// } -// -// // Keeper payment is gasPayment + premium / num keepers -// const keeperPayment = netPayment -// .sub(premium) -// .add(premium.div(BigNumber.from(keeperAddresses.length))) -// -// // Keeper should be paid net payment for all passed upkeeps -// assert.equal( -// keeperAfter.balance.sub(keeperPayment).toString(), -// keeperBefore.balance.toString(), -// ) -// -// assert.isTrue(keeperLinkAfter.eq(keeperLinkBefore)) -// assert.isTrue(registryLinkBefore.eq(registryLinkAfter)) -// }, -// ) -// -// it( -// '[Conditional:' + -// numPassingConditionalUpkeeps + -// ',Log' + -// numPassingLogUpkeeps + -// ',Failures:' + -// numFailingUpkeeps + -// '] splits gas overhead appropriately among performed upkeeps [ @skip-coverage ]', -// async () => { -// const allUpkeeps = await getMultipleUpkeepsDeployedAndFunded( -// numPassingConditionalUpkeeps, -// numPassingLogUpkeeps, -// numFailingUpkeeps, -// ) -// const passingConditionalUpkeepIds = -// allUpkeeps.passingConditionalUpkeepIds -// const passingLogUpkeepIds = allUpkeeps.passingLogUpkeepIds -// const failingUpkeepIds = allUpkeeps.failingUpkeepIds -// -// // Perform the upkeeps once to remove non-zero storage slots and have predictable gas measurement -// let tx = await getTransmitTx( -// registry, -// keeper1, -// passingConditionalUpkeepIds.concat( -// passingLogUpkeepIds.concat(failingUpkeepIds), -// ), -// ) -// -// await tx.wait() -// -// // Do the actual thing -// -// tx = await getTransmitTx( -// registry, -// keeper1, -// passingConditionalUpkeepIds.concat( -// passingLogUpkeepIds.concat(failingUpkeepIds), -// ), -// ) -// -// const receipt = await tx.wait() -// const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) -// // exactly numPassingUpkeeps Upkeep Performed should be emitted -// assert.equal( -// upkeepPerformedLogs.length, -// numPassingConditionalUpkeeps + numPassingLogUpkeeps, -// ) -// -// const gasConditionalOverheadCap = -// registryConditionalOverhead.add( -// registryPerSignerGasOverhead.mul(BigNumber.from(f + 1)), -// ) -// const gasLogOverheadCap = registryLogOverhead.add( -// registryPerSignerGasOverhead.mul(BigNumber.from(f + 1)), -// ) -// -// const overheadCanGetCapped = -// numFailingUpkeeps > 0 && -// numPassingConditionalUpkeeps <= 1 && -// numPassingLogUpkeeps <= 1 -// // Can happen if there are failing upkeeps and only 1 successful upkeep of each type -// let netGasUsedPlusOverhead = BigNumber.from('0') -// -// for (let i = 0; i < numPassingConditionalUpkeeps; i++) { -// const gasUsed = upkeepPerformedLogs[i].args.gasUsed -// const gasOverhead = upkeepPerformedLogs[i].args.gasOverhead -// -// assert.isTrue(gasUsed.gt(BigNumber.from('0'))) -// assert.isTrue(gasOverhead.gt(BigNumber.from('0'))) -// -// // Overhead should not exceed capped -// assert.isTrue(gasOverhead.lte(gasConditionalOverheadCap)) -// -// // Overhead should be same for every upkeep since they have equal performData, hence same caps -// assert.isTrue( -// gasOverhead.eq(upkeepPerformedLogs[0].args.gasOverhead), -// ) -// -// netGasUsedPlusOverhead = netGasUsedPlusOverhead -// .add(gasUsed) -// .add(gasOverhead) -// } -// for (let i = 0; i < numPassingLogUpkeeps; i++) { -// const gasUsed = -// upkeepPerformedLogs[numPassingConditionalUpkeeps + i].args -// .gasUsed -// const gasOverhead = -// upkeepPerformedLogs[numPassingConditionalUpkeeps + i].args -// .gasOverhead -// -// assert.isTrue(gasUsed.gt(BigNumber.from('0'))) -// assert.isTrue(gasOverhead.gt(BigNumber.from('0'))) -// -// // Overhead should not exceed capped -// assert.isTrue(gasOverhead.lte(gasLogOverheadCap)) -// -// // Overhead should be same for every upkeep since they have equal performData, hence same caps -// assert.isTrue( -// gasOverhead.eq( -// upkeepPerformedLogs[numPassingConditionalUpkeeps].args -// .gasOverhead, -// ), -// ) -// -// netGasUsedPlusOverhead = netGasUsedPlusOverhead -// .add(gasUsed) -// .add(gasOverhead) -// } -// -// const overheadsGotCapped = -// (numPassingConditionalUpkeeps > 0 && -// upkeepPerformedLogs[0].args.gasOverhead.eq( -// gasConditionalOverheadCap, -// )) || -// (numPassingLogUpkeeps > 0 && -// upkeepPerformedLogs[ -// numPassingConditionalUpkeeps -// ].args.gasOverhead.eq(gasLogOverheadCap)) -// // Should only get capped in certain scenarios -// if (overheadsGotCapped) { -// assert.isTrue( -// overheadCanGetCapped, -// 'Gas overhead got capped. Verify gas overhead variables in test match those in the registry. To not have the overheads capped increase REGISTRY_GAS_OVERHEAD', -// ) -// } -// -// console.log( -// 'Gas Benchmarking - batching (passedConditionalUpkeeps: ', -// numPassingConditionalUpkeeps, -// 'passedLogUpkeeps:', -// numPassingLogUpkeeps, -// 'failedUpkeeps:', -// numFailingUpkeeps, -// '): ', -// 'overheadsGotCapped', -// overheadsGotCapped, -// numPassingConditionalUpkeeps > 0 -// ? 'calculated conditional overhead' -// : '', -// numPassingConditionalUpkeeps > 0 -// ? upkeepPerformedLogs[0].args.gasOverhead.toString() -// : '', -// numPassingLogUpkeeps > 0 ? 'calculated log overhead' : '', -// numPassingLogUpkeeps > 0 -// ? upkeepPerformedLogs[ -// numPassingConditionalUpkeeps -// ].args.gasOverhead.toString() -// : '', -// ' margin over gasUsed', -// netGasUsedPlusOverhead.sub(receipt.gasUsed).toString(), -// ) -// -// // If overheads dont get capped then total gas charged should be greater than tx gas -// // We don't check whether the net is within gasMargin as the margin changes with numFailedUpkeeps -// // Which is ok, as long as individual gas overhead is capped -// if (!overheadsGotCapped) { -// assert.isTrue( -// netGasUsedPlusOverhead.gt(receipt.gasUsed), -// 'Gas overhead is too low, increase ACCOUNTING_PER_UPKEEP_GAS_OVERHEAD', -// ) -// } -// }, -// ) -// } -// } -// } -// -// it('has enough perform gas overhead for large batches [ @skip-coverage ]', async () => { -// const numUpkeeps = 20 -// const upkeepIds: BigNumber[] = [] -// let totalPerformGas = BigNumber.from('0') -// for (let i = 0; i < numUpkeeps; i++) { -// const mock = await upkeepMockFactory.deploy() -// const tx = await registry -// .connect(owner) -// [ -// 'registerUpkeep(address,uint32,address,bytes,bytes)' -// ](mock.address, performGas, await admin.getAddress(), randomBytes, '0x') -// const testUpkeepId = await getUpkeepID(tx) -// upkeepIds.push(testUpkeepId) -// -// // Add funds to passing upkeeps -// await registry.connect(owner).addFunds(testUpkeepId, toWei('10')) -// -// await mock.setCanPerform(true) -// await mock.setPerformGasToBurn(performGas) -// -// totalPerformGas = totalPerformGas.add(performGas) -// } -// -// // Should revert with no overhead added -// await evmRevert( -// getTransmitTx(registry, keeper1, upkeepIds, { -// gasLimit: totalPerformGas, -// }), -// ) -// // Should not revert with overhead added -// await getTransmitTx(registry, keeper1, upkeepIds, { -// gasLimit: totalPerformGas.add(transmitGasOverhead), -// }) -// }) -// -// it('splits l2 payment among performed upkeeps', async () => { -// const numUpkeeps = 7 -// const upkeepIds: BigNumber[] = [] -// // Same as MockArbGasInfo.sol -// const l1CostWeiArb = BigNumber.from(1000000) -// -// for (let i = 0; i < numUpkeeps; i++) { -// const mock = await upkeepMockFactory.deploy() -// const tx = await arbRegistry -// .connect(owner) -// [ -// 'registerUpkeep(address,uint32,address,bytes,bytes)' -// ](mock.address, performGas, await admin.getAddress(), randomBytes, '0x') -// const testUpkeepId = await getUpkeepID(tx) -// upkeepIds.push(testUpkeepId) -// -// // Add funds to passing upkeeps -// await arbRegistry.connect(owner).addFunds(testUpkeepId, toWei('100')) -// } -// -// // Do the thing -// const tx = await getTransmitTx( -// arbRegistry, -// keeper1, -// upkeepIds, -// -// { gasPrice: gasWei.mul('5') }, // High gas price so that it gets capped -// ) -// -// const receipt = await tx.wait() -// const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) -// // exactly numPassingUpkeeps Upkeep Performed should be emitted -// assert.equal(upkeepPerformedLogs.length, numUpkeeps) -// -// // Verify the payment calculation in upkeepPerformed[0] -// const upkeepPerformedLog = upkeepPerformedLogs[0] -// -// const gasUsed = upkeepPerformedLog.args.gasUsed -// const gasOverhead = upkeepPerformedLog.args.gasOverhead -// const totalPayment = upkeepPerformedLog.args.totalPayment -// -// assert.equal( -// linkForGas( -// gasUsed, -// gasOverhead, -// gasCeilingMultiplier, -// paymentPremiumPPB, -// flatFeeMicroLink, -// l1CostWeiArb.div(gasCeilingMultiplier), // Dividing by gasCeilingMultiplier as it gets multiplied later -// BigNumber.from(numUpkeeps), -// ).total.toString(), -// totalPayment.toString(), -// ) -// }) -// }, -// ) -// -// describe('#recoverFunds', () => { -// const sent = toWei('7') -// -// beforeEach(async () => { -// await linkToken.connect(admin).approve(registry.address, toWei('100')) -// await linkToken -// .connect(owner) -// .transfer(await keeper1.getAddress(), toWei('1000')) -// -// // add funds to upkeep 1 and perform and withdraw some payment -// const tx = await registry -// .connect(owner) -// [ -// 'registerUpkeep(address,uint32,address,bytes,bytes)' -// ](mock.address, performGas, await admin.getAddress(), emptyBytes, emptyBytes) -// -// const id1 = await getUpkeepID(tx) -// await registry.connect(admin).addFunds(id1, toWei('5')) -// -// await getTransmitTx(registry, keeper1, [id1]) -// await getTransmitTx(registry, keeper2, [id1]) -// await getTransmitTx(registry, keeper3, [id1]) -// -// await registry -// .connect(payee1) -// .withdrawPayment( -// await keeper1.getAddress(), -// await nonkeeper.getAddress(), -// ) -// -// // transfer funds directly to the registry -// await linkToken.connect(keeper1).transfer(registry.address, sent) -// -// // add funds to upkeep 2 and perform and withdraw some payment -// const tx2 = await registry -// .connect(owner) -// [ -// 'registerUpkeep(address,uint32,address,bytes,bytes)' -// ](mock.address, performGas, await admin.getAddress(), emptyBytes, emptyBytes) -// const id2 = await getUpkeepID(tx2) -// await registry.connect(admin).addFunds(id2, toWei('5')) -// -// await getTransmitTx(registry, keeper1, [id2]) -// await getTransmitTx(registry, keeper2, [id2]) -// await getTransmitTx(registry, keeper3, [id2]) -// -// await registry -// .connect(payee2) -// .withdrawPayment( -// await keeper2.getAddress(), -// await nonkeeper.getAddress(), -// ) -// -// // transfer funds using onTokenTransfer -// const data = ethers.utils.defaultAbiCoder.encode(['uint256'], [id2]) -// await linkToken -// .connect(owner) -// .transferAndCall(registry.address, toWei('1'), data) -// -// // withdraw some funds -// await registry.connect(owner).cancelUpkeep(id1) -// await registry -// .connect(admin) -// .withdrawFunds(id1, await nonkeeper.getAddress()) -// }) -// -// it('reverts if not called by owner', async () => { -// await evmRevert( -// registry.connect(keeper1).recoverFunds(), -// 'Only callable by owner', -// ) -// }) -// -// it('allows any funds that have been accidentally transfered to be moved', async () => { -// const balanceBefore = await linkToken.balanceOf(registry.address) -// const ownerBefore = await linkToken.balanceOf(await owner.getAddress()) -// -// await registry.connect(owner).recoverFunds() -// -// const balanceAfter = await linkToken.balanceOf(registry.address) -// const ownerAfter = await linkToken.balanceOf(await owner.getAddress()) -// -// assert.isTrue(balanceBefore.eq(balanceAfter.add(sent))) -// assert.isTrue(ownerAfter.eq(ownerBefore.add(sent))) -// }) -// }) -// -// describe('#getMinBalanceForUpkeep / #checkUpkeep / #transmit', () => { -// it('calculates the minimum balance appropriately', async () => { -// await mock.setCanCheck(true) -// -// const oneWei = BigNumber.from(1) -// const minBalance = await registry.getMinBalanceForUpkeep(upkeepId) -// const tooLow = minBalance.sub(oneWei) -// -// await registry.connect(admin).addFunds(upkeepId, tooLow) -// let checkUpkeepResult = await registry -// .connect(zeroAddress) -// .callStatic['checkUpkeep(uint256)'](upkeepId) -// -// assert.equal(checkUpkeepResult.upkeepNeeded, false) -// assert.equal( -// checkUpkeepResult.upkeepFailureReason, -// UpkeepFailureReason.INSUFFICIENT_BALANCE, -// ) -// -// await registry.connect(admin).addFunds(upkeepId, oneWei) -// checkUpkeepResult = await registry -// .connect(zeroAddress) -// .callStatic['checkUpkeep(uint256)'](upkeepId) -// assert.equal(checkUpkeepResult.upkeepNeeded, true) -// }) -// -// it('uses maxPerformData size in checkUpkeep but actual performDataSize in transmit', async () => { -// const tx1 = await registry -// .connect(owner) -// [ -// 'registerUpkeep(address,uint32,address,bytes,bytes)' -// ](mock.address, performGas, await admin.getAddress(), randomBytes, '0x') -// const upkeepID1 = await getUpkeepID(tx1) -// const tx2 = await registry -// .connect(owner) -// [ -// 'registerUpkeep(address,uint32,address,bytes,bytes)' -// ](mock.address, performGas, await admin.getAddress(), randomBytes, '0x') -// const upkeepID2 = await getUpkeepID(tx2) -// await mock.setCanCheck(true) -// await mock.setCanPerform(true) -// -// // upkeep 1 is underfunded, 2 is fully funded -// const minBalance1 = ( -// await registry.getMinBalanceForUpkeep(upkeepID1) -// ).sub(1) -// const minBalance2 = await registry.getMinBalanceForUpkeep(upkeepID2) -// await registry.connect(owner).addFunds(upkeepID1, minBalance1) -// await registry.connect(owner).addFunds(upkeepID2, minBalance2) -// -// // upkeep 1 check should return false, 2 should return true -// let checkUpkeepResult = await registry -// .connect(zeroAddress) -// .callStatic['checkUpkeep(uint256)'](upkeepID1) -// assert.equal(checkUpkeepResult.upkeepNeeded, false) -// assert.equal( -// checkUpkeepResult.upkeepFailureReason, -// UpkeepFailureReason.INSUFFICIENT_BALANCE, -// ) -// -// checkUpkeepResult = await registry -// .connect(zeroAddress) -// .callStatic['checkUpkeep(uint256)'](upkeepID2) -// assert.equal(checkUpkeepResult.upkeepNeeded, true) -// -// // upkeep 1 perform should return with insufficient balance using max performData size -// let maxPerformData = '0x' -// for (let i = 0; i < maxPerformDataSize.toNumber(); i++) { -// maxPerformData += '11' -// } -// -// const tx = await getTransmitTx(registry, keeper1, [upkeepID1], { -// gasPrice: gasWei.mul(gasCeilingMultiplier), -// performData: maxPerformData, -// }) -// -// const receipt = await tx.wait() -// const insufficientFundsUpkeepReportLogs = -// parseInsufficientFundsUpkeepReportLogs(receipt) -// // exactly 1 InsufficientFundsUpkeepReportLogs log should be emitted -// assert.equal(insufficientFundsUpkeepReportLogs.length, 1) -// -// // upkeep 1 perform should succeed with empty performData -// await getTransmitTx(registry, keeper1, [upkeepID1], { -// gasPrice: gasWei.mul(gasCeilingMultiplier), -// }), -// // upkeep 2 perform should succeed with max performData size -// await getTransmitTx(registry, keeper1, [upkeepID2], { -// gasPrice: gasWei.mul(gasCeilingMultiplier), -// performData: maxPerformData, -// }) -// }) -// }) -// -// describe('#withdrawFunds', () => { -// let upkeepId2: BigNumber -// -// beforeEach(async () => { -// const tx = await registry -// .connect(owner) -// [ -// 'registerUpkeep(address,uint32,address,bytes,bytes)' -// ](mock.address, performGas, await admin.getAddress(), randomBytes, '0x') -// upkeepId2 = await getUpkeepID(tx) -// -// await registry.connect(admin).addFunds(upkeepId, toWei('100')) -// await registry.connect(admin).addFunds(upkeepId2, toWei('100')) -// -// // Do a perform so that upkeep is charged some amount -// await getTransmitTx(registry, keeper1, [upkeepId]) -// await getTransmitTx(registry, keeper1, [upkeepId2]) -// }) -// -// it('reverts if called on a non existing ID', async () => { -// await evmRevert( -// registry -// .connect(admin) -// .withdrawFunds(upkeepId.add(1), await payee1.getAddress()), -// 'OnlyCallableByAdmin()', -// ) -// }) -// -// it('reverts if called by anyone but the admin', async () => { -// await evmRevert( -// registry -// .connect(owner) -// .withdrawFunds(upkeepId, await payee1.getAddress()), -// 'OnlyCallableByAdmin()', -// ) -// }) -// -// it('reverts if called on an uncanceled upkeep', async () => { -// await evmRevert( -// registry -// .connect(admin) -// .withdrawFunds(upkeepId, await payee1.getAddress()), -// 'UpkeepNotCanceled()', -// ) -// }) -// -// it('reverts if called with the 0 address', async () => { -// await evmRevert( -// registry.connect(admin).withdrawFunds(upkeepId, zeroAddress), -// 'InvalidRecipient()', -// ) -// }) -// -// describe('after the registration is paused, then cancelled', () => { -// it('allows the admin to withdraw', async () => { -// const balance = await registry.getBalance(upkeepId) -// const payee = await payee1.getAddress() -// await registry.connect(admin).pauseUpkeep(upkeepId) -// await registry.connect(owner).cancelUpkeep(upkeepId) -// await expect(() => -// registry.connect(admin).withdrawFunds(upkeepId, payee), -// ).to.changeTokenBalance(linkToken, payee1, balance) -// }) -// }) -// -// describe('after the registration is cancelled', () => { -// beforeEach(async () => { -// await registry.connect(owner).cancelUpkeep(upkeepId) -// await registry.connect(owner).cancelUpkeep(upkeepId2) -// }) -// -// it('can be called successively on two upkeeps', async () => { -// await registry -// .connect(admin) -// .withdrawFunds(upkeepId, await payee1.getAddress()) -// await registry -// .connect(admin) -// .withdrawFunds(upkeepId2, await payee1.getAddress()) -// }) -// -// it('moves the funds out and updates the balance and emits an event', async () => { -// const payee1Before = await linkToken.balanceOf( -// await payee1.getAddress(), -// ) -// const registryBefore = await linkToken.balanceOf(registry.address) -// -// let registration = await registry.getUpkeep(upkeepId) -// const previousBalance = registration.balance -// -// const tx = await registry -// .connect(admin) -// .withdrawFunds(upkeepId, await payee1.getAddress()) -// await expect(tx) -// .to.emit(registry, 'FundsWithdrawn') -// .withArgs(upkeepId, previousBalance, await payee1.getAddress()) -// -// const payee1After = await linkToken.balanceOf(await payee1.getAddress()) -// const registryAfter = await linkToken.balanceOf(registry.address) -// -// assert.isTrue(payee1Before.add(previousBalance).eq(payee1After)) -// assert.isTrue(registryBefore.sub(previousBalance).eq(registryAfter)) -// -// registration = await registry.getUpkeep(upkeepId) -// assert.equal(0, registration.balance.toNumber()) -// }) -// }) -// }) -// -// describe('#simulatePerformUpkeep', () => { -// it('reverts if called by non zero address', async () => { -// await evmRevert( -// registry -// .connect(await owner.getAddress()) -// .callStatic.simulatePerformUpkeep(upkeepId, '0x'), -// 'OnlySimulatedBackend()', -// ) -// }) -// -// it('reverts when registry is paused', async () => { -// await registry.connect(owner).pause() -// await evmRevert( -// registry -// .connect(zeroAddress) -// .callStatic.simulatePerformUpkeep(upkeepId, '0x'), -// 'RegistryPaused()', -// ) -// }) -// -// it('returns false and gasUsed when perform fails', async () => { -// await mock.setCanPerform(false) -// -// const simulatePerformResult = await registry -// .connect(zeroAddress) -// .callStatic.simulatePerformUpkeep(upkeepId, '0x') -// -// assert.equal(simulatePerformResult.success, false) -// assert.isTrue(simulatePerformResult.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used -// }) -// -// it('returns true, gasUsed, and performGas when perform succeeds', async () => { -// await mock.setCanPerform(true) -// -// const simulatePerformResult = await registry -// .connect(zeroAddress) -// .callStatic.simulatePerformUpkeep(upkeepId, '0x') -// -// assert.equal(simulatePerformResult.success, true) -// assert.isTrue(simulatePerformResult.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used -// }) -// -// it('returns correct amount of gasUsed when perform succeeds', async () => { -// await mock.setCanPerform(true) -// await mock.setPerformGasToBurn(performGas) -// -// const simulatePerformResult = await registry -// .connect(zeroAddress) -// .callStatic.simulatePerformUpkeep(upkeepId, '0x') -// -// assert.equal(simulatePerformResult.success, true) -// // Full execute gas should be used, with some performGasBuffer(1000) -// assert.isTrue( -// simulatePerformResult.gasUsed.gt( -// performGas.sub(BigNumber.from('1000')), -// ), -// ) -// }) -// }) -// -// describe('#checkUpkeep', () => { -// it('reverts if called by non zero address', async () => { -// await evmRevert( -// registry -// .connect(await owner.getAddress()) -// .callStatic['checkUpkeep(uint256)'](upkeepId), -// 'OnlySimulatedBackend()', -// ) -// }) -// -// it('returns false and error code if the upkeep is cancelled by admin', async () => { -// await registry.connect(admin).cancelUpkeep(upkeepId) -// -// const checkUpkeepResult = await registry -// .connect(zeroAddress) -// .callStatic['checkUpkeep(uint256)'](upkeepId) -// -// assert.equal(checkUpkeepResult.upkeepNeeded, false) -// assert.equal(checkUpkeepResult.performData, '0x') -// assert.equal( -// checkUpkeepResult.upkeepFailureReason, -// UpkeepFailureReason.UPKEEP_CANCELLED, -// ) -// expect(checkUpkeepResult.gasUsed).to.equal(0) -// expect(checkUpkeepResult.gasLimit).to.equal(performGas) -// }) -// -// it('returns false and error code if the upkeep is cancelled by owner', async () => { -// await registry.connect(owner).cancelUpkeep(upkeepId) -// -// const checkUpkeepResult = await registry -// .connect(zeroAddress) -// .callStatic['checkUpkeep(uint256)'](upkeepId) -// -// assert.equal(checkUpkeepResult.upkeepNeeded, false) -// assert.equal(checkUpkeepResult.performData, '0x') -// assert.equal( -// checkUpkeepResult.upkeepFailureReason, -// UpkeepFailureReason.UPKEEP_CANCELLED, -// ) -// expect(checkUpkeepResult.gasUsed).to.equal(0) -// expect(checkUpkeepResult.gasLimit).to.equal(performGas) -// }) -// -// it('returns false and error code if the registry is paused', async () => { -// await registry.connect(owner).pause() -// -// const checkUpkeepResult = await registry -// .connect(zeroAddress) -// .callStatic['checkUpkeep(uint256)'](upkeepId) -// -// assert.equal(checkUpkeepResult.upkeepNeeded, false) -// assert.equal(checkUpkeepResult.performData, '0x') -// assert.equal( -// checkUpkeepResult.upkeepFailureReason, -// UpkeepFailureReason.REGISTRY_PAUSED, -// ) -// expect(checkUpkeepResult.gasUsed).to.equal(0) -// expect(checkUpkeepResult.gasLimit).to.equal(performGas) -// }) -// -// it('returns false and error code if the upkeep is paused', async () => { -// await registry.connect(admin).pauseUpkeep(upkeepId) -// -// const checkUpkeepResult = await registry -// .connect(zeroAddress) -// .callStatic['checkUpkeep(uint256)'](upkeepId) -// -// assert.equal(checkUpkeepResult.upkeepNeeded, false) -// assert.equal(checkUpkeepResult.performData, '0x') -// assert.equal( -// checkUpkeepResult.upkeepFailureReason, -// UpkeepFailureReason.UPKEEP_PAUSED, -// ) -// expect(checkUpkeepResult.gasUsed).to.equal(0) -// expect(checkUpkeepResult.gasLimit).to.equal(performGas) -// }) -// -// it('returns false and error code if user is out of funds', async () => { -// const checkUpkeepResult = await registry -// .connect(zeroAddress) -// .callStatic['checkUpkeep(uint256)'](upkeepId) -// -// assert.equal(checkUpkeepResult.upkeepNeeded, false) -// assert.equal(checkUpkeepResult.performData, '0x') -// assert.equal( -// checkUpkeepResult.upkeepFailureReason, -// UpkeepFailureReason.INSUFFICIENT_BALANCE, -// ) -// expect(checkUpkeepResult.gasUsed).to.equal(0) -// expect(checkUpkeepResult.gasLimit).to.equal(performGas) -// }) -// -// context('when the registration is funded', () => { -// beforeEach(async () => { -// await linkToken.connect(admin).approve(registry.address, toWei('200')) -// await registry.connect(admin).addFunds(upkeepId, toWei('100')) -// await registry.connect(admin).addFunds(logUpkeepId, toWei('100')) -// }) -// -// it('returns false, error code, and revert data if the target check reverts', async () => { -// await mock.setShouldRevertCheck(true) -// await mock.setCheckRevertReason( -// 'custom revert error, clever way to insert offchain data', -// ) -// const checkUpkeepResult = await registry -// .connect(zeroAddress) -// .callStatic['checkUpkeep(uint256)'](upkeepId) -// assert.equal(checkUpkeepResult.upkeepNeeded, false) -// -// const revertReasonBytes = `0x${checkUpkeepResult.performData.slice(10)}` // remove sighash -// assert.equal( -// ethers.utils.defaultAbiCoder.decode(['string'], revertReasonBytes)[0], -// 'custom revert error, clever way to insert offchain data', -// ) -// assert.equal( -// checkUpkeepResult.upkeepFailureReason, -// UpkeepFailureReason.TARGET_CHECK_REVERTED, -// ) -// assert.isTrue(checkUpkeepResult.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used -// expect(checkUpkeepResult.gasLimit).to.equal(performGas) -// // Feed data should be returned here -// assert.isTrue(checkUpkeepResult.fastGasWei.gt(BigNumber.from('0'))) -// assert.isTrue(checkUpkeepResult.linkNative.gt(BigNumber.from('0'))) -// }) -// -// it('returns false, error code, and no revert data if the target check revert data exceeds maxRevertDataSize', async () => { -// await mock.setShouldRevertCheck(true) -// let longRevertReason = '' -// for (let i = 0; i <= maxRevertDataSize.toNumber(); i++) { -// longRevertReason += 'x' -// } -// await mock.setCheckRevertReason(longRevertReason) -// const checkUpkeepResult = await registry -// .connect(zeroAddress) -// .callStatic['checkUpkeep(uint256)'](upkeepId) -// assert.equal(checkUpkeepResult.upkeepNeeded, false) -// -// assert.equal(checkUpkeepResult.performData, '0x') -// assert.equal( -// checkUpkeepResult.upkeepFailureReason, -// UpkeepFailureReason.REVERT_DATA_EXCEEDS_LIMIT, -// ) -// assert.isTrue(checkUpkeepResult.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used -// expect(checkUpkeepResult.gasLimit).to.equal(performGas) -// }) -// -// it('returns false and error code if the upkeep is not needed', async () => { -// await mock.setCanCheck(false) -// const checkUpkeepResult = await registry -// .connect(zeroAddress) -// .callStatic['checkUpkeep(uint256)'](upkeepId) -// -// assert.equal(checkUpkeepResult.upkeepNeeded, false) -// assert.equal(checkUpkeepResult.performData, '0x') -// assert.equal( -// checkUpkeepResult.upkeepFailureReason, -// UpkeepFailureReason.UPKEEP_NOT_NEEDED, -// ) -// assert.isTrue(checkUpkeepResult.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used -// expect(checkUpkeepResult.gasLimit).to.equal(performGas) -// }) -// -// it('returns false and error code if the performData exceeds limit', async () => { -// let longBytes = '0x' -// for (let i = 0; i < 5000; i++) { -// longBytes += '1' -// } -// await mock.setCanCheck(true) -// await mock.setPerformData(longBytes) -// -// const checkUpkeepResult = await registry -// .connect(zeroAddress) -// .callStatic['checkUpkeep(uint256)'](upkeepId) -// -// assert.equal(checkUpkeepResult.upkeepNeeded, false) -// assert.equal(checkUpkeepResult.performData, '0x') -// assert.equal( -// checkUpkeepResult.upkeepFailureReason, -// UpkeepFailureReason.PERFORM_DATA_EXCEEDS_LIMIT, -// ) -// assert.isTrue(checkUpkeepResult.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used -// expect(checkUpkeepResult.gasLimit).to.equal(performGas) -// }) -// -// it('returns true with gas used if the target can execute', async () => { -// await mock.setCanCheck(true) -// await mock.setPerformData(randomBytes) -// -// const latestBlock = await ethers.provider.getBlock('latest') -// -// const checkUpkeepResult = await registry -// .connect(zeroAddress) -// .callStatic['checkUpkeep(uint256)'](upkeepId, { -// blockTag: latestBlock.number, -// }) -// -// assert.equal(checkUpkeepResult.upkeepNeeded, true) -// assert.equal(checkUpkeepResult.performData, randomBytes) -// assert.equal( -// checkUpkeepResult.upkeepFailureReason, -// UpkeepFailureReason.NONE, -// ) -// assert.isTrue(checkUpkeepResult.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used -// expect(checkUpkeepResult.gasLimit).to.equal(performGas) -// assert.isTrue(checkUpkeepResult.fastGasWei.eq(gasWei)) -// assert.isTrue(checkUpkeepResult.linkNative.eq(linkEth)) -// }) -// -// it('calls checkLog for log-trigger upkeeps', async () => { -// const log: Log = { -// index: 0, -// timestamp: 0, -// txHash: ethers.utils.randomBytes(32), -// blockNumber: 100, -// blockHash: ethers.utils.randomBytes(32), -// source: randomAddress(), -// topics: [ethers.utils.randomBytes(32), ethers.utils.randomBytes(32)], -// data: ethers.utils.randomBytes(1000), -// } -// -// await ltUpkeep.mock.checkLog.withArgs(log, '0x').returns(true, '0x1234') -// -// const checkData = encodeLog(log) -// -// const checkUpkeepResult = await registry -// .connect(zeroAddress) -// .callStatic['checkUpkeep(uint256,bytes)'](logUpkeepId, checkData) -// -// expect(checkUpkeepResult.upkeepNeeded).to.be.true -// expect(checkUpkeepResult.performData).to.equal('0x1234') -// }) -// -// itMaybe( -// 'has a large enough gas overhead to cover upkeeps that use all their gas [ @skip-coverage ]', -// async () => { -// await mock.setCanCheck(true) -// await mock.setCheckGasToBurn(checkGasLimit) -// const gas = checkGasLimit.add(checkGasOverhead) -// const checkUpkeepResult = await registry -// .connect(zeroAddress) -// .callStatic['checkUpkeep(uint256)'](upkeepId, { -// gasLimit: gas, -// }) -// -// assert.equal(checkUpkeepResult.upkeepNeeded, true) -// }, -// ) -// }) -// }) -// -// describe('#addFunds', () => { -// const amount = toWei('1') -// -// it('reverts if the registration does not exist', async () => { -// await evmRevert( -// registry.connect(keeper1).addFunds(upkeepId.add(1), amount), -// 'UpkeepCancelled()', -// ) -// }) -// -// it('adds to the balance of the registration', async () => { -// await registry.connect(admin).addFunds(upkeepId, amount) -// const registration = await registry.getUpkeep(upkeepId) -// assert.isTrue(amount.eq(registration.balance)) -// }) -// -// it('lets anyone add funds to an upkeep not just admin', async () => { -// await linkToken.connect(owner).transfer(await payee1.getAddress(), amount) -// await linkToken.connect(payee1).approve(registry.address, amount) -// -// await registry.connect(payee1).addFunds(upkeepId, amount) -// const registration = await registry.getUpkeep(upkeepId) -// assert.isTrue(amount.eq(registration.balance)) -// }) -// -// it('emits a log', async () => { -// const tx = await registry.connect(admin).addFunds(upkeepId, amount) -// await expect(tx) -// .to.emit(registry, 'FundsAdded') -// .withArgs(upkeepId, await admin.getAddress(), amount) -// }) -// -// it('reverts if the upkeep is canceled', async () => { -// await registry.connect(admin).cancelUpkeep(upkeepId) -// await evmRevert( -// registry.connect(keeper1).addFunds(upkeepId, amount), -// 'UpkeepCancelled()', -// ) -// }) -// }) -// -// describe('#getActiveUpkeepIDs', () => { -// it('reverts if startIndex is out of bounds ', async () => { -// await evmRevert( -// registry.getActiveUpkeepIDs(numUpkeeps, 0), -// 'IndexOutOfRange()', -// ) -// await evmRevert( -// registry.getActiveUpkeepIDs(numUpkeeps + 1, 0), -// 'IndexOutOfRange()', -// ) -// }) -// -// it('returns upkeep IDs bounded by maxCount', async () => { -// let upkeepIds = await registry.getActiveUpkeepIDs(0, 1) -// assert(upkeepIds.length == 1) -// assert(upkeepIds[0].eq(upkeepId)) -// upkeepIds = await registry.getActiveUpkeepIDs(1, 3) -// assert(upkeepIds.length == 3) -// expect(upkeepIds).to.deep.equal([ -// afUpkeepId, -// logUpkeepId, -// streamsLookupUpkeepId, -// ]) -// }) -// -// it('returns as many ids as possible if maxCount > num available', async () => { -// const upkeepIds = await registry.getActiveUpkeepIDs(1, numUpkeeps + 100) -// assert(upkeepIds.length == numUpkeeps - 1) -// }) -// -// it('returns all upkeep IDs if maxCount is 0', async () => { -// let upkeepIds = await registry.getActiveUpkeepIDs(0, 0) -// assert(upkeepIds.length == numUpkeeps) -// upkeepIds = await registry.getActiveUpkeepIDs(2, 0) -// assert(upkeepIds.length == numUpkeeps - 2) -// }) -// }) -// -// describe('#getMaxPaymentForGas', () => { -// const arbL1PriceinWei = BigNumber.from(1000) // Same as MockArbGasInfo.sol -// const l1CostWeiArb = arbL1PriceinWei.mul(16).mul(maxPerformDataSize) -// const l1CostWeiOpt = BigNumber.from(2000000) // Same as MockOVMGasPriceOracle.sol -// itMaybe('calculates the max fee appropriately', async () => { -// await verifyMaxPayment(registry) -// }) -// -// itMaybe('calculates the max fee appropriately for Arbitrum', async () => { -// await verifyMaxPayment(arbRegistry, l1CostWeiArb) -// }) -// -// itMaybe('calculates the max fee appropriately for Optimism', async () => { -// await verifyMaxPayment(opRegistry, l1CostWeiOpt) -// }) -// -// it('uses the fallback gas price if the feed has issues', async () => { -// const expectedFallbackMaxPayment = linkForGas( -// performGas, -// registryConditionalOverhead -// .add(registryPerSignerGasOverhead.mul(f + 1)) -// .add(maxPerformDataSize.mul(registryPerPerformByteGasOverhead)), -// gasCeilingMultiplier.mul('2'), // fallbackGasPrice is 2x gas price -// paymentPremiumPPB, -// flatFeeMicroLink, -// ).total -// -// // Stale feed -// let roundId = 99 -// const answer = 100 -// let updatedAt = 946684800 // New Years 2000 🥳 -// let startedAt = 946684799 -// await gasPriceFeed -// .connect(owner) -// .updateRoundData(roundId, answer, updatedAt, startedAt) -// -// assert.equal( -// expectedFallbackMaxPayment.toString(), -// ( -// await registry.getMaxPaymentForGas(Trigger.CONDITION, performGas) -// ).toString(), -// ) -// -// // Negative feed price -// roundId = 100 -// updatedAt = now() -// startedAt = 946684799 -// await gasPriceFeed -// .connect(owner) -// .updateRoundData(roundId, -100, updatedAt, startedAt) -// -// assert.equal( -// expectedFallbackMaxPayment.toString(), -// ( -// await registry.getMaxPaymentForGas(Trigger.CONDITION, performGas) -// ).toString(), -// ) -// -// // Zero feed price -// roundId = 101 -// updatedAt = now() -// startedAt = 946684799 -// await gasPriceFeed -// .connect(owner) -// .updateRoundData(roundId, 0, updatedAt, startedAt) -// -// assert.equal( -// expectedFallbackMaxPayment.toString(), -// ( -// await registry.getMaxPaymentForGas(Trigger.CONDITION, performGas) -// ).toString(), -// ) -// }) -// -// it('uses the fallback link price if the feed has issues', async () => { -// const expectedFallbackMaxPayment = linkForGas( -// performGas, -// registryConditionalOverhead -// .add(registryPerSignerGasOverhead.mul(f + 1)) -// .add(maxPerformDataSize.mul(registryPerPerformByteGasOverhead)), -// gasCeilingMultiplier.mul('2'), // fallbackLinkPrice is 1/2 link price, so multiply by 2 -// paymentPremiumPPB, -// flatFeeMicroLink, -// ).total -// -// // Stale feed -// let roundId = 99 -// const answer = 100 -// let updatedAt = 946684800 // New Years 2000 🥳 -// let startedAt = 946684799 -// await linkEthFeed -// .connect(owner) -// .updateRoundData(roundId, answer, updatedAt, startedAt) -// -// assert.equal( -// expectedFallbackMaxPayment.toString(), -// ( -// await registry.getMaxPaymentForGas(Trigger.CONDITION, performGas) -// ).toString(), -// ) -// -// // Negative feed price -// roundId = 100 -// updatedAt = now() -// startedAt = 946684799 -// await linkEthFeed -// .connect(owner) -// .updateRoundData(roundId, -100, updatedAt, startedAt) -// -// assert.equal( -// expectedFallbackMaxPayment.toString(), -// ( -// await registry.getMaxPaymentForGas(Trigger.CONDITION, performGas) -// ).toString(), -// ) -// -// // Zero feed price -// roundId = 101 -// updatedAt = now() -// startedAt = 946684799 -// await linkEthFeed -// .connect(owner) -// .updateRoundData(roundId, 0, updatedAt, startedAt) -// -// assert.equal( -// expectedFallbackMaxPayment.toString(), -// ( -// await registry.getMaxPaymentForGas(Trigger.CONDITION, performGas) -// ).toString(), -// ) -// }) -// }) -// -// describe('#typeAndVersion', () => { -// it('uses the correct type and version', async () => { -// const typeAndVersion = await registry.typeAndVersion() -// assert.equal(typeAndVersion, 'KeeperRegistry 2.1.0') -// }) -// }) -// -// describe('#onTokenTransfer', () => { -// const amount = toWei('1') -// -// it('reverts if not called by the LINK token', async () => { -// const data = ethers.utils.defaultAbiCoder.encode(['uint256'], [upkeepId]) -// -// await evmRevert( -// registry -// .connect(keeper1) -// .onTokenTransfer(await keeper1.getAddress(), amount, data), -// 'OnlyCallableByLINKToken()', -// ) -// }) -// -// it('reverts if not called with more or less than 32 bytes', async () => { -// const longData = ethers.utils.defaultAbiCoder.encode( -// ['uint256', 'uint256'], -// ['33', '34'], -// ) -// const shortData = '0x12345678' -// -// await evmRevert( -// linkToken -// .connect(owner) -// .transferAndCall(registry.address, amount, longData), -// ) -// await evmRevert( -// linkToken -// .connect(owner) -// .transferAndCall(registry.address, amount, shortData), -// ) -// }) -// -// it('reverts if the upkeep is canceled', async () => { -// await registry.connect(admin).cancelUpkeep(upkeepId) -// await evmRevert( -// registry.connect(keeper1).addFunds(upkeepId, amount), -// 'UpkeepCancelled()', -// ) -// }) -// -// it('updates the funds of the job id passed', async () => { -// const data = ethers.utils.defaultAbiCoder.encode(['uint256'], [upkeepId]) -// -// const before = (await registry.getUpkeep(upkeepId)).balance -// await linkToken -// .connect(owner) -// .transferAndCall(registry.address, amount, data) -// const after = (await registry.getUpkeep(upkeepId)).balance -// -// assert.isTrue(before.add(amount).eq(after)) -// }) -// }) -// -// describeMaybe('#setConfig - onchain', () => { -// const payment = BigNumber.from(1) -// const flatFee = BigNumber.from(2) -// const maxGas = BigNumber.from(6) -// const staleness = BigNumber.from(4) -// const ceiling = BigNumber.from(5) -// const newMinUpkeepSpend = BigNumber.from(9) -// const newMaxCheckDataSize = BigNumber.from(10000) -// const newMaxPerformDataSize = BigNumber.from(10000) -// const newMaxRevertDataSize = BigNumber.from(10000) -// const newMaxPerformGas = BigNumber.from(10000000) -// const fbGasEth = BigNumber.from(7) -// const fbLinkEth = BigNumber.from(8) -// const newTranscoder = randomAddress() -// const newRegistrars = [randomAddress(), randomAddress()] -// const upkeepManager = randomAddress() -// -// const newConfig: OnChainConfig = { -// paymentPremiumPPB: payment, -// flatFeeMicroLink: flatFee, -// checkGasLimit: maxGas, -// stalenessSeconds: staleness, -// gasCeilingMultiplier: ceiling, -// minUpkeepSpend: newMinUpkeepSpend, -// maxCheckDataSize: newMaxCheckDataSize, -// maxPerformDataSize: newMaxPerformDataSize, -// maxRevertDataSize: newMaxRevertDataSize, -// maxPerformGas: newMaxPerformGas, -// fallbackGasPrice: fbGasEth, -// fallbackLinkPrice: fbLinkEth, -// transcoder: newTranscoder, -// registrars: newRegistrars, -// upkeepPrivilegeManager: upkeepManager, -// } -// -// it('reverts when called by anyone but the proposed owner', async () => { -// await evmRevert( -// registry -// .connect(payee1) -// .setConfigTypeSafe( -// signerAddresses, -// keeperAddresses, -// f, -// newConfig, -// offchainVersion, -// offchainBytes, -// ), -// 'Only callable by owner', -// ) -// }) -// -// it('reverts if signers or transmitters are the zero address', async () => { -// await evmRevert( -// registry -// .connect(owner) -// .setConfigTypeSafe( -// [randomAddress(), randomAddress(), randomAddress(), zeroAddress], -// [ -// randomAddress(), -// randomAddress(), -// randomAddress(), -// randomAddress(), -// ], -// f, -// newConfig, -// offchainVersion, -// offchainBytes, -// ), -// 'InvalidSigner()', -// ) -// -// await evmRevert( -// registry -// .connect(owner) -// .setConfigTypeSafe( -// [ -// randomAddress(), -// randomAddress(), -// randomAddress(), -// randomAddress(), -// ], -// [randomAddress(), randomAddress(), randomAddress(), zeroAddress], -// f, -// newConfig, -// offchainVersion, -// offchainBytes, -// ), -// 'InvalidTransmitter()', -// ) -// }) -// -// it('updates the onchainConfig and configDigest', async () => { -// const old = await registry.getState() -// const oldConfig = old.config -// const oldState = old.state -// assert.isTrue(paymentPremiumPPB.eq(oldConfig.paymentPremiumPPB)) -// assert.isTrue(flatFeeMicroLink.eq(oldConfig.flatFeeMicroLink)) -// assert.isTrue(stalenessSeconds.eq(oldConfig.stalenessSeconds)) -// assert.isTrue(gasCeilingMultiplier.eq(oldConfig.gasCeilingMultiplier)) -// -// await registry -// .connect(owner) -// .setConfigTypeSafe( -// signerAddresses, -// keeperAddresses, -// f, -// newConfig, -// offchainVersion, -// offchainBytes, -// ) -// -// const updated = await registry.getState() -// const updatedConfig = updated.config -// const updatedState = updated.state -// assert.equal(updatedConfig.paymentPremiumPPB, payment.toNumber()) -// assert.equal(updatedConfig.flatFeeMicroLink, flatFee.toNumber()) -// assert.equal(updatedConfig.stalenessSeconds, staleness.toNumber()) -// assert.equal(updatedConfig.gasCeilingMultiplier, ceiling.toNumber()) -// assert.equal( -// updatedConfig.minUpkeepSpend.toString(), -// newMinUpkeepSpend.toString(), -// ) -// assert.equal( -// updatedConfig.maxCheckDataSize, -// newMaxCheckDataSize.toNumber(), -// ) -// assert.equal( -// updatedConfig.maxPerformDataSize, -// newMaxPerformDataSize.toNumber(), -// ) -// assert.equal( -// updatedConfig.maxRevertDataSize, -// newMaxRevertDataSize.toNumber(), -// ) -// assert.equal(updatedConfig.maxPerformGas, newMaxPerformGas.toNumber()) -// assert.equal(updatedConfig.checkGasLimit, maxGas.toNumber()) -// assert.equal( -// updatedConfig.fallbackGasPrice.toNumber(), -// fbGasEth.toNumber(), -// ) -// assert.equal( -// updatedConfig.fallbackLinkPrice.toNumber(), -// fbLinkEth.toNumber(), -// ) -// assert.equal(updatedState.latestEpoch, 0) -// -// assert(oldState.configCount + 1 == updatedState.configCount) -// assert( -// oldState.latestConfigBlockNumber != -// updatedState.latestConfigBlockNumber, -// ) -// assert(oldState.latestConfigDigest != updatedState.latestConfigDigest) -// -// assert.equal(updatedConfig.transcoder, newTranscoder) -// assert.deepEqual(updatedConfig.registrars, newRegistrars) -// assert.equal(updatedConfig.upkeepPrivilegeManager, upkeepManager) -// }) -// -// it('maintains paused state when config is changed', async () => { -// await registry.pause() -// const old = await registry.getState() -// assert.isTrue(old.state.paused) -// -// await registry -// .connect(owner) -// .setConfigTypeSafe( -// signerAddresses, -// keeperAddresses, -// f, -// newConfig, -// offchainVersion, -// offchainBytes, -// ) -// -// const updated = await registry.getState() -// assert.isTrue(updated.state.paused) -// }) -// -// it('emits an event', async () => { -// const tx = await registry -// .connect(owner) -// .setConfigTypeSafe( -// signerAddresses, -// keeperAddresses, -// f, -// newConfig, -// offchainVersion, -// offchainBytes, -// ) -// await expect(tx).to.emit(registry, 'ConfigSet') -// }) -// }) -// -// describe('#setConfig - offchain', () => { -// let newKeepers: string[] -// -// beforeEach(async () => { -// newKeepers = [ -// await personas.Eddy.getAddress(), -// await personas.Nick.getAddress(), -// await personas.Neil.getAddress(), -// await personas.Carol.getAddress(), -// ] -// }) -// -// it('reverts when called by anyone but the owner', async () => { -// await evmRevert( -// registry -// .connect(payee1) -// .setConfigTypeSafe( -// newKeepers, -// newKeepers, -// f, -// config, -// offchainVersion, -// offchainBytes, -// ), -// 'Only callable by owner', -// ) -// }) -// -// it('reverts if too many keeperAddresses set', async () => { -// for (let i = 0; i < 40; i++) { -// newKeepers.push(randomAddress()) -// } -// await evmRevert( -// registry -// .connect(owner) -// .setConfigTypeSafe( -// newKeepers, -// newKeepers, -// f, -// config, -// offchainVersion, -// offchainBytes, -// ), -// 'TooManyOracles()', -// ) -// }) -// -// it('reverts if f=0', async () => { -// await evmRevert( -// registry -// .connect(owner) -// .setConfigTypeSafe( -// newKeepers, -// newKeepers, -// 0, -// config, -// offchainVersion, -// offchainBytes, -// ), -// 'IncorrectNumberOfFaultyOracles()', -// ) -// }) -// -// it('reverts if signers != transmitters length', async () => { -// const signers = [randomAddress()] -// await evmRevert( -// registry -// .connect(owner) -// .setConfigTypeSafe( -// signers, -// newKeepers, -// f, -// config, -// offchainVersion, -// offchainBytes, -// ), -// 'IncorrectNumberOfSigners()', -// ) -// }) -// -// it('reverts if signers <= 3f', async () => { -// newKeepers.pop() -// await evmRevert( -// registry -// .connect(owner) -// .setConfigTypeSafe( -// newKeepers, -// newKeepers, -// f, -// config, -// offchainVersion, -// offchainBytes, -// ), -// 'IncorrectNumberOfSigners()', -// ) -// }) -// -// it('reverts on repeated signers', async () => { -// const newSigners = [ -// await personas.Eddy.getAddress(), -// await personas.Eddy.getAddress(), -// await personas.Eddy.getAddress(), -// await personas.Eddy.getAddress(), -// ] -// await evmRevert( -// registry -// .connect(owner) -// .setConfigTypeSafe( -// newSigners, -// newKeepers, -// f, -// config, -// offchainVersion, -// offchainBytes, -// ), -// 'RepeatedSigner()', -// ) -// }) -// -// it('reverts on repeated transmitters', async () => { -// const newTransmitters = [ -// await personas.Eddy.getAddress(), -// await personas.Eddy.getAddress(), -// await personas.Eddy.getAddress(), -// await personas.Eddy.getAddress(), -// ] -// await evmRevert( -// registry -// .connect(owner) -// .setConfigTypeSafe( -// newKeepers, -// newTransmitters, -// f, -// config, -// offchainVersion, -// offchainBytes, -// ), -// 'RepeatedTransmitter()', -// ) -// }) -// -// itMaybe('stores new config and emits event', async () => { -// // Perform an upkeep so that totalPremium is updated -// await registry.connect(admin).addFunds(upkeepId, toWei('100')) -// let tx = await getTransmitTx(registry, keeper1, [upkeepId]) -// await tx.wait() -// -// const newOffChainVersion = BigNumber.from('2') -// const newOffChainConfig = '0x1122' -// -// const old = await registry.getState() -// const oldState = old.state -// assert(oldState.totalPremium.gt(BigNumber.from('0'))) -// -// const newSigners = newKeepers -// tx = await registry -// .connect(owner) -// .setConfigTypeSafe( -// newSigners, -// newKeepers, -// f, -// config, -// newOffChainVersion, -// newOffChainConfig, -// ) -// -// const updated = await registry.getState() -// const updatedState = updated.state -// assert(oldState.totalPremium.eq(updatedState.totalPremium)) -// -// // Old signer addresses which are not in new signers should be non active -// for (let i = 0; i < signerAddresses.length; i++) { -// const signer = signerAddresses[i] -// if (!newSigners.includes(signer)) { -// assert((await registry.getSignerInfo(signer)).active == false) -// assert((await registry.getSignerInfo(signer)).index == 0) -// } -// } -// // New signer addresses should be active -// for (let i = 0; i < newSigners.length; i++) { -// const signer = newSigners[i] -// assert((await registry.getSignerInfo(signer)).active == true) -// assert((await registry.getSignerInfo(signer)).index == i) -// } -// // Old transmitter addresses which are not in new transmitter should be non active, update lastCollected but retain other info -// for (let i = 0; i < keeperAddresses.length; i++) { -// const transmitter = keeperAddresses[i] -// if (!newKeepers.includes(transmitter)) { -// assert( -// (await registry.getTransmitterInfo(transmitter)).active == false, -// ) -// assert((await registry.getTransmitterInfo(transmitter)).index == i) -// assert( -// (await registry.getTransmitterInfo(transmitter)).lastCollected.eq( -// oldState.totalPremium.sub( -// oldState.totalPremium.mod(keeperAddresses.length), -// ), -// ), -// ) -// } -// } -// // New transmitter addresses should be active -// for (let i = 0; i < newKeepers.length; i++) { -// const transmitter = newKeepers[i] -// assert((await registry.getTransmitterInfo(transmitter)).active == true) -// assert((await registry.getTransmitterInfo(transmitter)).index == i) -// assert( -// (await registry.getTransmitterInfo(transmitter)).lastCollected.eq( -// oldState.totalPremium, -// ), -// ) -// } -// -// // config digest should be updated -// assert(oldState.configCount + 1 == updatedState.configCount) -// assert( -// oldState.latestConfigBlockNumber != -// updatedState.latestConfigBlockNumber, -// ) -// assert(oldState.latestConfigDigest != updatedState.latestConfigDigest) -// -// //New config should be updated -// assert.deepEqual(updated.signers, newKeepers) -// assert.deepEqual(updated.transmitters, newKeepers) -// -// // Event should have been emitted -// await expect(tx).to.emit(registry, 'ConfigSet') -// }) -// }) -// -// describe('#setPeerRegistryMigrationPermission() / #getPeerRegistryMigrationPermission()', () => { -// const peer = randomAddress() -// it('allows the owner to set the peer registries', async () => { -// let permission = await registry.getPeerRegistryMigrationPermission(peer) -// expect(permission).to.equal(0) -// await registry.setPeerRegistryMigrationPermission(peer, 1) -// permission = await registry.getPeerRegistryMigrationPermission(peer) -// expect(permission).to.equal(1) -// await registry.setPeerRegistryMigrationPermission(peer, 2) -// permission = await registry.getPeerRegistryMigrationPermission(peer) -// expect(permission).to.equal(2) -// await registry.setPeerRegistryMigrationPermission(peer, 0) -// permission = await registry.getPeerRegistryMigrationPermission(peer) -// expect(permission).to.equal(0) -// }) -// it('reverts if passed an unsupported permission', async () => { -// await expect( -// registry.connect(admin).setPeerRegistryMigrationPermission(peer, 10), -// ).to.be.reverted -// }) -// it('reverts if not called by the owner', async () => { -// await expect( -// registry.connect(admin).setPeerRegistryMigrationPermission(peer, 1), -// ).to.be.revertedWith('Only callable by owner') -// }) -// }) -// -// describe('#registerUpkeep', () => { -// it('reverts when registry is paused', async () => { -// await registry.connect(owner).pause() -// await evmRevert( -// registry -// .connect(owner) -// [ -// 'registerUpkeep(address,uint32,address,bytes,bytes)' -// ](mock.address, performGas, await admin.getAddress(), emptyBytes, '0x'), -// 'RegistryPaused()', -// ) -// }) -// -// it('reverts if the target is not a contract', async () => { -// await evmRevert( -// registry -// .connect(owner) -// [ -// 'registerUpkeep(address,uint32,address,bytes,bytes)' -// ](zeroAddress, performGas, await admin.getAddress(), emptyBytes, '0x'), -// 'NotAContract()', -// ) -// }) -// -// it('reverts if called by a non-owner', async () => { -// await evmRevert( -// registry -// .connect(keeper1) -// [ -// 'registerUpkeep(address,uint32,address,bytes,bytes)' -// ](mock.address, performGas, await admin.getAddress(), emptyBytes, '0x'), -// 'OnlyCallableByOwnerOrRegistrar()', -// ) -// }) -// -// it('reverts if execute gas is too low', async () => { -// await evmRevert( -// registry -// .connect(owner) -// [ -// 'registerUpkeep(address,uint32,address,bytes,bytes)' -// ](mock.address, 2299, await admin.getAddress(), emptyBytes, '0x'), -// 'GasLimitOutsideRange()', -// ) -// }) -// -// it('reverts if execute gas is too high', async () => { -// await evmRevert( -// registry -// .connect(owner) -// [ -// 'registerUpkeep(address,uint32,address,bytes,bytes)' -// ](mock.address, 5000001, await admin.getAddress(), emptyBytes, '0x'), -// 'GasLimitOutsideRange()', -// ) -// }) -// -// it('reverts if checkData is too long', async () => { -// let longBytes = '0x' -// for (let i = 0; i < 10000; i++) { -// longBytes += '1' -// } -// await evmRevert( -// registry -// .connect(owner) -// [ -// 'registerUpkeep(address,uint32,address,bytes,bytes)' -// ](mock.address, performGas, await admin.getAddress(), longBytes, '0x'), -// 'CheckDataExceedsLimit()', -// ) -// }) -// -// it('creates a record of the registration', async () => { -// const performGases = [100000, 500000] -// const checkDatas = [emptyBytes, '0x12'] -// -// for (let jdx = 0; jdx < performGases.length; jdx++) { -// const performGas = performGases[jdx] -// for (let kdx = 0; kdx < checkDatas.length; kdx++) { -// const checkData = checkDatas[kdx] -// const tx = await registry -// .connect(owner) -// [ -// 'registerUpkeep(address,uint32,address,bytes,bytes)' -// ](mock.address, performGas, await admin.getAddress(), checkData, '0x') -// -// //confirm the upkeep details and verify emitted events -// const testUpkeepId = await getUpkeepID(tx) -// await expect(tx) -// .to.emit(registry, 'UpkeepRegistered') -// .withArgs(testUpkeepId, performGas, await admin.getAddress()) -// -// await expect(tx) -// .to.emit(registry, 'UpkeepCheckDataSet') -// .withArgs(testUpkeepId, checkData) -// await expect(tx) -// .to.emit(registry, 'UpkeepTriggerConfigSet') -// .withArgs(testUpkeepId, '0x') -// -// const registration = await registry.getUpkeep(testUpkeepId) -// -// assert.equal(mock.address, registration.target) -// assert.notEqual( -// ethers.constants.AddressZero, -// await registry.getForwarder(testUpkeepId), -// ) -// assert.equal( -// performGas.toString(), -// registration.performGas.toString(), -// ) -// assert.equal(await admin.getAddress(), registration.admin) -// assert.equal(0, registration.balance.toNumber()) -// assert.equal(0, registration.amountSpent.toNumber()) -// assert.equal(0, registration.lastPerformedBlockNumber) -// assert.equal(checkData, registration.checkData) -// assert.equal(registration.paused, false) -// assert.equal(registration.offchainConfig, '0x') -// assert(registration.maxValidBlocknumber.eq('0xffffffff')) -// } -// } -// }) -// }) -// -// describe('#pauseUpkeep', () => { -// it('reverts if the registration does not exist', async () => { -// await evmRevert( -// registry.connect(keeper1).pauseUpkeep(upkeepId.add(1)), -// 'OnlyCallableByAdmin()', -// ) -// }) -// -// it('reverts if the upkeep is already canceled', async () => { -// await registry.connect(admin).cancelUpkeep(upkeepId) -// -// await evmRevert( -// registry.connect(admin).pauseUpkeep(upkeepId), -// 'UpkeepCancelled()', -// ) -// }) -// -// it('reverts if the upkeep is already paused', async () => { -// await registry.connect(admin).pauseUpkeep(upkeepId) -// -// await evmRevert( -// registry.connect(admin).pauseUpkeep(upkeepId), -// 'OnlyUnpausedUpkeep()', -// ) -// }) -// -// it('reverts if the caller is not the upkeep admin', async () => { -// await evmRevert( -// registry.connect(keeper1).pauseUpkeep(upkeepId), -// 'OnlyCallableByAdmin()', -// ) -// }) -// -// it('pauses the upkeep and emits an event', async () => { -// const tx = await registry.connect(admin).pauseUpkeep(upkeepId) -// await expect(tx).to.emit(registry, 'UpkeepPaused').withArgs(upkeepId) -// -// const registration = await registry.getUpkeep(upkeepId) -// assert.equal(registration.paused, true) -// }) -// }) -// -// describe('#unpauseUpkeep', () => { -// it('reverts if the registration does not exist', async () => { -// await evmRevert( -// registry.connect(keeper1).unpauseUpkeep(upkeepId.add(1)), -// 'OnlyCallableByAdmin()', -// ) -// }) -// -// it('reverts if the upkeep is already canceled', async () => { -// await registry.connect(owner).cancelUpkeep(upkeepId) -// -// await evmRevert( -// registry.connect(admin).unpauseUpkeep(upkeepId), -// 'UpkeepCancelled()', -// ) -// }) -// -// it('marks the contract as paused', async () => { -// assert.isFalse((await registry.getState()).state.paused) -// -// await registry.connect(owner).pause() -// -// assert.isTrue((await registry.getState()).state.paused) -// }) -// -// it('reverts if the upkeep is not paused', async () => { -// await evmRevert( -// registry.connect(admin).unpauseUpkeep(upkeepId), -// 'OnlyPausedUpkeep()', -// ) -// }) -// -// it('reverts if the caller is not the upkeep admin', async () => { -// await registry.connect(admin).pauseUpkeep(upkeepId) -// -// const registration = await registry.getUpkeep(upkeepId) -// -// assert.equal(registration.paused, true) -// -// await evmRevert( -// registry.connect(keeper1).unpauseUpkeep(upkeepId), -// 'OnlyCallableByAdmin()', -// ) -// }) -// -// it('unpauses the upkeep and emits an event', async () => { -// const originalCount = (await registry.getActiveUpkeepIDs(0, 0)).length -// -// await registry.connect(admin).pauseUpkeep(upkeepId) -// -// const tx = await registry.connect(admin).unpauseUpkeep(upkeepId) -// -// await expect(tx).to.emit(registry, 'UpkeepUnpaused').withArgs(upkeepId) -// -// const registration = await registry.getUpkeep(upkeepId) -// assert.equal(registration.paused, false) -// -// const upkeepIds = await registry.getActiveUpkeepIDs(0, 0) -// assert.equal(upkeepIds.length, originalCount) -// }) -// }) -// -// describe('#setUpkeepCheckData', () => { -// it('reverts if the registration does not exist', async () => { -// await evmRevert( -// registry -// .connect(keeper1) -// .setUpkeepCheckData(upkeepId.add(1), randomBytes), -// 'OnlyCallableByAdmin()', -// ) -// }) -// -// it('reverts if the caller is not upkeep admin', async () => { -// await evmRevert( -// registry.connect(keeper1).setUpkeepCheckData(upkeepId, randomBytes), -// 'OnlyCallableByAdmin()', -// ) -// }) -// -// it('reverts if the upkeep is cancelled', async () => { -// await registry.connect(admin).cancelUpkeep(upkeepId) -// -// await evmRevert( -// registry.connect(admin).setUpkeepCheckData(upkeepId, randomBytes), -// 'UpkeepCancelled()', -// ) -// }) -// -// it('is allowed to update on paused upkeep', async () => { -// await registry.connect(admin).pauseUpkeep(upkeepId) -// await registry.connect(admin).setUpkeepCheckData(upkeepId, randomBytes) -// -// const registration = await registry.getUpkeep(upkeepId) -// assert.equal(randomBytes, registration.checkData) -// }) -// -// it('reverts if new data exceeds limit', async () => { -// let longBytes = '0x' -// for (let i = 0; i < 10000; i++) { -// longBytes += '1' -// } -// -// await evmRevert( -// registry.connect(admin).setUpkeepCheckData(upkeepId, longBytes), -// 'CheckDataExceedsLimit()', -// ) -// }) -// -// it('updates the upkeep check data and emits an event', async () => { -// const tx = await registry -// .connect(admin) -// .setUpkeepCheckData(upkeepId, randomBytes) -// await expect(tx) -// .to.emit(registry, 'UpkeepCheckDataSet') -// .withArgs(upkeepId, randomBytes) -// -// const registration = await registry.getUpkeep(upkeepId) -// assert.equal(randomBytes, registration.checkData) -// }) -// }) -// -// describe('#setUpkeepGasLimit', () => { -// const newGasLimit = BigNumber.from('300000') -// -// it('reverts if the registration does not exist', async () => { -// await evmRevert( -// registry.connect(admin).setUpkeepGasLimit(upkeepId.add(1), newGasLimit), -// 'OnlyCallableByAdmin()', -// ) -// }) -// -// it('reverts if the upkeep is canceled', async () => { -// await registry.connect(admin).cancelUpkeep(upkeepId) -// await evmRevert( -// registry.connect(admin).setUpkeepGasLimit(upkeepId, newGasLimit), -// 'UpkeepCancelled()', -// ) -// }) -// -// it('reverts if called by anyone but the admin', async () => { -// await evmRevert( -// registry.connect(owner).setUpkeepGasLimit(upkeepId, newGasLimit), -// 'OnlyCallableByAdmin()', -// ) -// }) -// -// it('reverts if new gas limit is out of bounds', async () => { -// await evmRevert( -// registry -// .connect(admin) -// .setUpkeepGasLimit(upkeepId, BigNumber.from('100')), -// 'GasLimitOutsideRange()', -// ) -// await evmRevert( -// registry -// .connect(admin) -// .setUpkeepGasLimit(upkeepId, BigNumber.from('6000000')), -// 'GasLimitOutsideRange()', -// ) -// }) -// -// it('updates the gas limit successfully', async () => { -// const initialGasLimit = (await registry.getUpkeep(upkeepId)).performGas -// assert.equal(initialGasLimit, performGas.toNumber()) -// await registry.connect(admin).setUpkeepGasLimit(upkeepId, newGasLimit) -// const updatedGasLimit = (await registry.getUpkeep(upkeepId)).performGas -// assert.equal(updatedGasLimit, newGasLimit.toNumber()) -// }) -// -// it('emits a log', async () => { -// const tx = await registry -// .connect(admin) -// .setUpkeepGasLimit(upkeepId, newGasLimit) -// await expect(tx) -// .to.emit(registry, 'UpkeepGasLimitSet') -// .withArgs(upkeepId, newGasLimit) -// }) -// }) -// -// describe('#setUpkeepOffchainConfig', () => { -// const newConfig = '0xc0ffeec0ffee' -// -// it('reverts if the registration does not exist', async () => { -// await evmRevert( -// registry -// .connect(admin) -// .setUpkeepOffchainConfig(upkeepId.add(1), newConfig), -// 'OnlyCallableByAdmin()', -// ) -// }) -// -// it('reverts if the upkeep is canceled', async () => { -// await registry.connect(admin).cancelUpkeep(upkeepId) -// await evmRevert( -// registry.connect(admin).setUpkeepOffchainConfig(upkeepId, newConfig), -// 'UpkeepCancelled()', -// ) -// }) -// -// it('reverts if called by anyone but the admin', async () => { -// await evmRevert( -// registry.connect(owner).setUpkeepOffchainConfig(upkeepId, newConfig), -// 'OnlyCallableByAdmin()', -// ) -// }) -// -// it('updates the config successfully', async () => { -// const initialConfig = (await registry.getUpkeep(upkeepId)).offchainConfig -// assert.equal(initialConfig, '0x') -// await registry.connect(admin).setUpkeepOffchainConfig(upkeepId, newConfig) -// const updatedConfig = (await registry.getUpkeep(upkeepId)).offchainConfig -// assert.equal(newConfig, updatedConfig) -// }) -// -// it('emits a log', async () => { -// const tx = await registry -// .connect(admin) -// .setUpkeepOffchainConfig(upkeepId, newConfig) -// await expect(tx) -// .to.emit(registry, 'UpkeepOffchainConfigSet') -// .withArgs(upkeepId, newConfig) -// }) -// }) -// -// describe('#setUpkeepTriggerConfig', () => { -// const newConfig = '0xdeadbeef' -// -// it('reverts if the registration does not exist', async () => { -// await evmRevert( -// registry -// .connect(admin) -// .setUpkeepTriggerConfig(upkeepId.add(1), newConfig), -// 'OnlyCallableByAdmin()', -// ) -// }) -// -// it('reverts if the upkeep is canceled', async () => { -// await registry.connect(admin).cancelUpkeep(upkeepId) -// await evmRevert( -// registry.connect(admin).setUpkeepTriggerConfig(upkeepId, newConfig), -// 'UpkeepCancelled()', -// ) -// }) -// -// it('reverts if called by anyone but the admin', async () => { -// await evmRevert( -// registry.connect(owner).setUpkeepTriggerConfig(upkeepId, newConfig), -// 'OnlyCallableByAdmin()', -// ) -// }) -// -// it('emits a log', async () => { -// const tx = await registry -// .connect(admin) -// .setUpkeepTriggerConfig(upkeepId, newConfig) -// await expect(tx) -// .to.emit(registry, 'UpkeepTriggerConfigSet') -// .withArgs(upkeepId, newConfig) -// }) -// }) -// -// describe('#transferUpkeepAdmin', () => { -// it('reverts when called by anyone but the current upkeep admin', async () => { -// await evmRevert( -// registry -// .connect(payee1) -// .transferUpkeepAdmin(upkeepId, await payee2.getAddress()), -// 'OnlyCallableByAdmin()', -// ) -// }) -// -// it('reverts when transferring to self', async () => { -// await evmRevert( -// registry -// .connect(admin) -// .transferUpkeepAdmin(upkeepId, await admin.getAddress()), -// 'ValueNotChanged()', -// ) -// }) -// -// it('reverts when the upkeep is cancelled', async () => { -// await registry.connect(admin).cancelUpkeep(upkeepId) -// -// await evmRevert( -// registry -// .connect(admin) -// .transferUpkeepAdmin(upkeepId, await keeper1.getAddress()), -// 'UpkeepCancelled()', -// ) -// }) -// -// it('allows cancelling transfer by reverting to zero address', async () => { -// await registry -// .connect(admin) -// .transferUpkeepAdmin(upkeepId, await payee1.getAddress()) -// const tx = await registry -// .connect(admin) -// .transferUpkeepAdmin(upkeepId, ethers.constants.AddressZero) -// -// await expect(tx) -// .to.emit(registry, 'UpkeepAdminTransferRequested') -// .withArgs( -// upkeepId, -// await admin.getAddress(), -// ethers.constants.AddressZero, -// ) -// }) -// -// it('does not change the upkeep admin', async () => { -// await registry -// .connect(admin) -// .transferUpkeepAdmin(upkeepId, await payee1.getAddress()) -// -// const upkeep = await registry.getUpkeep(upkeepId) -// assert.equal(await admin.getAddress(), upkeep.admin) -// }) -// -// it('emits an event announcing the new upkeep admin', async () => { -// const tx = await registry -// .connect(admin) -// .transferUpkeepAdmin(upkeepId, await payee1.getAddress()) -// -// await expect(tx) -// .to.emit(registry, 'UpkeepAdminTransferRequested') -// .withArgs(upkeepId, await admin.getAddress(), await payee1.getAddress()) -// }) -// -// it('does not emit an event when called with the same proposed upkeep admin', async () => { -// await registry -// .connect(admin) -// .transferUpkeepAdmin(upkeepId, await payee1.getAddress()) -// -// const tx = await registry -// .connect(admin) -// .transferUpkeepAdmin(upkeepId, await payee1.getAddress()) -// const receipt = await tx.wait() -// assert.equal(0, receipt.logs.length) -// }) -// }) -// -// describe('#acceptUpkeepAdmin', () => { -// beforeEach(async () => { -// // Start admin transfer to payee1 -// await registry -// .connect(admin) -// .transferUpkeepAdmin(upkeepId, await payee1.getAddress()) -// }) -// -// it('reverts when not called by the proposed upkeep admin', async () => { -// await evmRevert( -// registry.connect(payee2).acceptUpkeepAdmin(upkeepId), -// 'OnlyCallableByProposedAdmin()', -// ) -// }) -// -// it('reverts when the upkeep is cancelled', async () => { -// await registry.connect(admin).cancelUpkeep(upkeepId) -// -// await evmRevert( -// registry.connect(payee1).acceptUpkeepAdmin(upkeepId), -// 'UpkeepCancelled()', -// ) -// }) -// -// it('does change the admin', async () => { -// await registry.connect(payee1).acceptUpkeepAdmin(upkeepId) -// -// const upkeep = await registry.getUpkeep(upkeepId) -// assert.equal(await payee1.getAddress(), upkeep.admin) -// }) -// -// it('emits an event announcing the new upkeep admin', async () => { -// const tx = await registry.connect(payee1).acceptUpkeepAdmin(upkeepId) -// await expect(tx) -// .to.emit(registry, 'UpkeepAdminTransferred') -// .withArgs(upkeepId, await admin.getAddress(), await payee1.getAddress()) -// }) -// }) -// -// describe('#withdrawOwnerFunds', () => { -// it('can only be called by owner', async () => { -// await evmRevert( -// registry.connect(keeper1).withdrawOwnerFunds(), -// 'Only callable by owner', -// ) -// }) -// -// itMaybe('withdraws the collected fees to owner', async () => { -// await registry.connect(admin).addFunds(upkeepId, toWei('100')) -// // Very high min spend, whole balance as cancellation fees -// const minUpkeepSpend = toWei('1000') -// await registry.connect(owner).setConfigTypeSafe( -// signerAddresses, -// keeperAddresses, -// f, -// { -// paymentPremiumPPB, -// flatFeeMicroLink, -// checkGasLimit, -// stalenessSeconds, -// gasCeilingMultiplier, -// minUpkeepSpend, -// maxCheckDataSize, -// maxPerformDataSize, -// maxRevertDataSize, -// maxPerformGas, -// fallbackGasPrice, -// fallbackLinkPrice, -// transcoder: transcoder.address, -// registrars: [], -// upkeepPrivilegeManager: upkeepManager, -// }, -// offchainVersion, -// offchainBytes, -// ) -// const upkeepBalance = (await registry.getUpkeep(upkeepId)).balance -// const ownerBefore = await linkToken.balanceOf(await owner.getAddress()) -// -// await registry.connect(owner).cancelUpkeep(upkeepId) -// -// // Transfered to owner balance on registry -// let ownerRegistryBalance = (await registry.getState()).state -// .ownerLinkBalance -// assert.isTrue(ownerRegistryBalance.eq(upkeepBalance)) -// -// // Now withdraw -// await registry.connect(owner).withdrawOwnerFunds() -// -// ownerRegistryBalance = (await registry.getState()).state.ownerLinkBalance -// const ownerAfter = await linkToken.balanceOf(await owner.getAddress()) -// -// // Owner registry balance should be changed to 0 -// assert.isTrue(ownerRegistryBalance.eq(BigNumber.from('0'))) -// -// // Owner should be credited with the balance -// assert.isTrue(ownerBefore.add(upkeepBalance).eq(ownerAfter)) -// }) -// }) -// -// describe('#transferPayeeship', () => { -// it('reverts when called by anyone but the current payee', async () => { -// await evmRevert( -// registry -// .connect(payee2) -// .transferPayeeship( -// await keeper1.getAddress(), -// await payee2.getAddress(), -// ), -// 'OnlyCallableByPayee()', -// ) -// }) -// -// it('reverts when transferring to self', async () => { -// await evmRevert( -// registry -// .connect(payee1) -// .transferPayeeship( -// await keeper1.getAddress(), -// await payee1.getAddress(), -// ), -// 'ValueNotChanged()', -// ) -// }) -// -// it('does not change the payee', async () => { -// await registry -// .connect(payee1) -// .transferPayeeship( -// await keeper1.getAddress(), -// await payee2.getAddress(), -// ) -// -// const info = await registry.getTransmitterInfo(await keeper1.getAddress()) -// assert.equal(await payee1.getAddress(), info.payee) -// }) -// -// it('emits an event announcing the new payee', async () => { -// const tx = await registry -// .connect(payee1) -// .transferPayeeship( -// await keeper1.getAddress(), -// await payee2.getAddress(), -// ) -// await expect(tx) -// .to.emit(registry, 'PayeeshipTransferRequested') -// .withArgs( -// await keeper1.getAddress(), -// await payee1.getAddress(), -// await payee2.getAddress(), -// ) -// }) -// -// it('does not emit an event when called with the same proposal', async () => { -// await registry -// .connect(payee1) -// .transferPayeeship( -// await keeper1.getAddress(), -// await payee2.getAddress(), -// ) -// -// const tx = await registry -// .connect(payee1) -// .transferPayeeship( -// await keeper1.getAddress(), -// await payee2.getAddress(), -// ) -// const receipt = await tx.wait() -// assert.equal(0, receipt.logs.length) -// }) -// }) -// -// describe('#acceptPayeeship', () => { -// beforeEach(async () => { -// await registry -// .connect(payee1) -// .transferPayeeship( -// await keeper1.getAddress(), -// await payee2.getAddress(), -// ) -// }) -// -// it('reverts when called by anyone but the proposed payee', async () => { -// await evmRevert( -// registry.connect(payee1).acceptPayeeship(await keeper1.getAddress()), -// 'OnlyCallableByProposedPayee()', -// ) -// }) -// -// it('emits an event announcing the new payee', async () => { -// const tx = await registry -// .connect(payee2) -// .acceptPayeeship(await keeper1.getAddress()) -// await expect(tx) -// .to.emit(registry, 'PayeeshipTransferred') -// .withArgs( -// await keeper1.getAddress(), -// await payee1.getAddress(), -// await payee2.getAddress(), -// ) -// }) -// -// it('does change the payee', async () => { -// await registry.connect(payee2).acceptPayeeship(await keeper1.getAddress()) -// -// const info = await registry.getTransmitterInfo(await keeper1.getAddress()) -// assert.equal(await payee2.getAddress(), info.payee) -// }) -// }) -// -// describe('#pause', () => { -// it('reverts if called by a non-owner', async () => { -// await evmRevert( -// registry.connect(keeper1).pause(), -// 'Only callable by owner', -// ) -// }) -// -// it('marks the contract as paused', async () => { -// assert.isFalse((await registry.getState()).state.paused) -// -// await registry.connect(owner).pause() -// -// assert.isTrue((await registry.getState()).state.paused) -// }) -// -// it('Does not allow transmits when paused', async () => { -// await registry.connect(owner).pause() -// -// await evmRevert( -// getTransmitTx(registry, keeper1, [upkeepId]), -// 'RegistryPaused()', -// ) -// }) -// -// it('Does not allow creation of new upkeeps when paused', async () => { -// await registry.connect(owner).pause() -// -// await evmRevert( -// registry -// .connect(owner) -// [ -// 'registerUpkeep(address,uint32,address,bytes,bytes)' -// ](mock.address, performGas, await admin.getAddress(), emptyBytes, '0x'), -// 'RegistryPaused()', -// ) -// }) -// }) -// -// describe('#unpause', () => { -// beforeEach(async () => { -// await registry.connect(owner).pause() -// }) -// -// it('reverts if called by a non-owner', async () => { -// await evmRevert( -// registry.connect(keeper1).unpause(), -// 'Only callable by owner', -// ) -// }) -// -// it('marks the contract as not paused', async () => { -// assert.isTrue((await registry.getState()).state.paused) -// -// await registry.connect(owner).unpause() -// -// assert.isFalse((await registry.getState()).state.paused) -// }) -// }) -// -// describe('#migrateUpkeeps() / #receiveUpkeeps()', async () => { -// context('when permissions are set', () => { -// beforeEach(async () => { -// await linkToken.connect(owner).approve(registry.address, toWei('100')) -// await registry.connect(owner).addFunds(upkeepId, toWei('100')) -// await registry.setPeerRegistryMigrationPermission(mgRegistry.address, 1) -// await mgRegistry.setPeerRegistryMigrationPermission(registry.address, 2) -// }) -// -// it('migrates an upkeep', async () => { -// const offchainBytes = '0x987654abcd' -// await registry -// .connect(admin) -// .setUpkeepOffchainConfig(upkeepId, offchainBytes) -// const reg1Upkeep = await registry.getUpkeep(upkeepId) -// const forwarderAddress = await registry.getForwarder(upkeepId) -// expect(reg1Upkeep.balance).to.equal(toWei('100')) -// expect(reg1Upkeep.checkData).to.equal(randomBytes) -// expect(forwarderAddress).to.not.equal(ethers.constants.AddressZero) -// expect(reg1Upkeep.offchainConfig).to.equal(offchainBytes) -// expect((await registry.getState()).state.numUpkeeps).to.equal( -// numUpkeeps, -// ) -// const forwarder = await IAutomationForwarderFactory.connect( -// forwarderAddress, -// owner, -// ) -// expect(await forwarder.getRegistry()).to.equal(registry.address) -// // Set an upkeep admin transfer in progress too -// await registry -// .connect(admin) -// .transferUpkeepAdmin(upkeepId, await payee1.getAddress()) -// -// // migrate -// await registry -// .connect(admin) -// .migrateUpkeeps([upkeepId], mgRegistry.address) -// expect((await registry.getState()).state.numUpkeeps).to.equal( -// numUpkeeps - 1, -// ) -// expect((await mgRegistry.getState()).state.numUpkeeps).to.equal(1) -// expect((await registry.getUpkeep(upkeepId)).balance).to.equal(0) -// expect((await registry.getUpkeep(upkeepId)).checkData).to.equal('0x') -// expect((await mgRegistry.getUpkeep(upkeepId)).balance).to.equal( -// toWei('100'), -// ) -// expect( -// (await mgRegistry.getState()).state.expectedLinkBalance, -// ).to.equal(toWei('100')) -// expect((await mgRegistry.getUpkeep(upkeepId)).checkData).to.equal( -// randomBytes, -// ) -// expect((await mgRegistry.getUpkeep(upkeepId)).offchainConfig).to.equal( -// offchainBytes, -// ) -// expect(await mgRegistry.getForwarder(upkeepId)).to.equal( -// forwarderAddress, -// ) -// // test that registry is updated on forwarder -// expect(await forwarder.getRegistry()).to.equal(mgRegistry.address) -// // migration will delete the upkeep and nullify admin transfer -// await expect( -// registry.connect(payee1).acceptUpkeepAdmin(upkeepId), -// ).to.be.revertedWith('UpkeepCancelled()') -// await expect( -// mgRegistry.connect(payee1).acceptUpkeepAdmin(upkeepId), -// ).to.be.revertedWith('OnlyCallableByProposedAdmin()') -// }) -// -// it('migrates a paused upkeep', async () => { -// expect((await registry.getUpkeep(upkeepId)).balance).to.equal( -// toWei('100'), -// ) -// expect((await registry.getUpkeep(upkeepId)).checkData).to.equal( -// randomBytes, -// ) -// expect((await registry.getState()).state.numUpkeeps).to.equal( -// numUpkeeps, -// ) -// await registry.connect(admin).pauseUpkeep(upkeepId) -// // verify the upkeep is paused -// expect((await registry.getUpkeep(upkeepId)).paused).to.equal(true) -// // migrate -// await registry -// .connect(admin) -// .migrateUpkeeps([upkeepId], mgRegistry.address) -// expect((await registry.getState()).state.numUpkeeps).to.equal( -// numUpkeeps - 1, -// ) -// expect((await mgRegistry.getState()).state.numUpkeeps).to.equal(1) -// expect((await registry.getUpkeep(upkeepId)).balance).to.equal(0) -// expect((await mgRegistry.getUpkeep(upkeepId)).balance).to.equal( -// toWei('100'), -// ) -// expect((await registry.getUpkeep(upkeepId)).checkData).to.equal('0x') -// expect((await mgRegistry.getUpkeep(upkeepId)).checkData).to.equal( -// randomBytes, -// ) -// expect( -// (await mgRegistry.getState()).state.expectedLinkBalance, -// ).to.equal(toWei('100')) -// // verify the upkeep is still paused after migration -// expect((await mgRegistry.getUpkeep(upkeepId)).paused).to.equal(true) -// }) -// -// it('emits an event on both contracts', async () => { -// expect((await registry.getUpkeep(upkeepId)).balance).to.equal( -// toWei('100'), -// ) -// expect((await registry.getUpkeep(upkeepId)).checkData).to.equal( -// randomBytes, -// ) -// expect((await registry.getState()).state.numUpkeeps).to.equal( -// numUpkeeps, -// ) -// const tx = registry -// .connect(admin) -// .migrateUpkeeps([upkeepId], mgRegistry.address) -// await expect(tx) -// .to.emit(registry, 'UpkeepMigrated') -// .withArgs(upkeepId, toWei('100'), mgRegistry.address) -// await expect(tx) -// .to.emit(mgRegistry, 'UpkeepReceived') -// .withArgs(upkeepId, toWei('100'), registry.address) -// }) -// -// it('is only migratable by the admin', async () => { -// await expect( -// registry -// .connect(owner) -// .migrateUpkeeps([upkeepId], mgRegistry.address), -// ).to.be.revertedWith('OnlyCallableByAdmin()') -// await registry -// .connect(admin) -// .migrateUpkeeps([upkeepId], mgRegistry.address) -// }) -// }) -// -// context('when permissions are not set', () => { -// it('reverts', async () => { -// // no permissions -// await registry.setPeerRegistryMigrationPermission(mgRegistry.address, 0) -// await mgRegistry.setPeerRegistryMigrationPermission(registry.address, 0) -// await expect(registry.migrateUpkeeps([upkeepId], mgRegistry.address)).to -// .be.reverted -// // only outgoing permissions -// await registry.setPeerRegistryMigrationPermission(mgRegistry.address, 1) -// await mgRegistry.setPeerRegistryMigrationPermission(registry.address, 0) -// await expect(registry.migrateUpkeeps([upkeepId], mgRegistry.address)).to -// .be.reverted -// // only incoming permissions -// await registry.setPeerRegistryMigrationPermission(mgRegistry.address, 0) -// await mgRegistry.setPeerRegistryMigrationPermission(registry.address, 2) -// await expect(registry.migrateUpkeeps([upkeepId], mgRegistry.address)).to -// .be.reverted -// // permissions opposite direction -// await registry.setPeerRegistryMigrationPermission(mgRegistry.address, 2) -// await mgRegistry.setPeerRegistryMigrationPermission(registry.address, 1) -// await expect(registry.migrateUpkeeps([upkeepId], mgRegistry.address)).to -// .be.reverted -// }) -// }) -// }) -// -// describe('#setPayees', () => { -// const IGNORE_ADDRESS = '0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF' -// -// it('reverts when not called by the owner', async () => { -// await evmRevert( -// registry.connect(keeper1).setPayees(payees), -// 'Only callable by owner', -// ) -// }) -// -// it('reverts with different numbers of payees than transmitters', async () => { -// await evmRevert( -// registry.connect(owner).setPayees([...payees, randomAddress()]), -// 'ParameterLengthError()', -// ) -// }) -// -// it('reverts if the payee is the zero address', async () => { -// await blankRegistry.connect(owner).setConfig(...baseConfig) // used to test initial config -// -// await evmRevert( -// blankRegistry // used to test initial config -// .connect(owner) -// .setPayees([ethers.constants.AddressZero, ...payees.slice(1)]), -// 'InvalidPayee()', -// ) -// }) -// -// itMaybe( -// 'sets the payees when exisitng payees are zero address', -// async () => { -// //Initial payees should be zero address -// await blankRegistry.connect(owner).setConfig(...baseConfig) // used to test initial config -// -// for (let i = 0; i < keeperAddresses.length; i++) { -// const payee = ( -// await blankRegistry.getTransmitterInfo(keeperAddresses[i]) -// ).payee // used to test initial config -// assert.equal(payee, zeroAddress) -// } -// -// await blankRegistry.connect(owner).setPayees(payees) // used to test initial config -// -// for (let i = 0; i < keeperAddresses.length; i++) { -// const payee = ( -// await blankRegistry.getTransmitterInfo(keeperAddresses[i]) -// ).payee -// assert.equal(payee, payees[i]) -// } -// }, -// ) -// -// it('does not change the payee if IGNORE_ADDRESS is used as payee', async () => { -// const signers = Array.from({ length: 5 }, randomAddress) -// const keepers = Array.from({ length: 5 }, randomAddress) -// const payees = Array.from({ length: 5 }, randomAddress) -// const newTransmitter = randomAddress() -// const newPayee = randomAddress() -// const ignoreAddresses = new Array(payees.length).fill(IGNORE_ADDRESS) -// const newPayees = [...ignoreAddresses, newPayee] -// // arbitrum registry -// // configure registry with 5 keepers // optimism registry -// await blankRegistry // used to test initial configurations -// .connect(owner) -// .setConfigTypeSafe( -// signers, -// keepers, -// f, -// config, -// offchainVersion, -// offchainBytes, -// ) -// // arbitrum registry -// // set initial payees // optimism registry -// await blankRegistry.connect(owner).setPayees(payees) // used to test initial configurations -// // arbitrum registry -// // add another keeper // optimism registry -// await blankRegistry // used to test initial configurations -// .connect(owner) -// .setConfigTypeSafe( -// [...signers, randomAddress()], -// [...keepers, newTransmitter], -// f, -// config, -// offchainVersion, -// offchainBytes, -// ) -// // arbitrum registry -// // update payee list // optimism registry // arbitrum registry -// await blankRegistry.connect(owner).setPayees(newPayees) // used to test initial configurations // optimism registry -// const ignored = await blankRegistry.getTransmitterInfo(newTransmitter) // used to test initial configurations -// assert.equal(newPayee, ignored.payee) -// assert.equal(true, ignored.active) -// }) -// -// it('reverts if payee is non zero and owner tries to change payee', async () => { -// const newPayees = [randomAddress(), ...payees.slice(1)] -// -// await evmRevert( -// registry.connect(owner).setPayees(newPayees), -// 'InvalidPayee()', -// ) -// }) -// -// it('emits events for every payee added and removed', async () => { -// const tx = await registry.connect(owner).setPayees(payees) -// await expect(tx) -// .to.emit(registry, 'PayeesUpdated') -// .withArgs(keeperAddresses, payees) -// }) -// }) -// -// describe('#cancelUpkeep', () => { -// it('reverts if the ID is not valid', async () => { -// await evmRevert( -// registry.connect(owner).cancelUpkeep(upkeepId.add(1)), -// 'CannotCancel()', -// ) -// }) -// -// it('reverts if called by a non-owner/non-admin', async () => { -// await evmRevert( -// registry.connect(keeper1).cancelUpkeep(upkeepId), -// 'OnlyCallableByOwnerOrAdmin()', -// ) -// }) -// -// describe('when called by the owner', async () => { -// it('sets the registration to invalid immediately', async () => { -// const tx = await registry.connect(owner).cancelUpkeep(upkeepId) -// const receipt = await tx.wait() -// const registration = await registry.getUpkeep(upkeepId) -// assert.equal( -// registration.maxValidBlocknumber.toNumber(), -// receipt.blockNumber, -// ) -// }) -// -// it('emits an event', async () => { -// const tx = await registry.connect(owner).cancelUpkeep(upkeepId) -// const receipt = await tx.wait() -// await expect(tx) -// .to.emit(registry, 'UpkeepCanceled') -// .withArgs(upkeepId, BigNumber.from(receipt.blockNumber)) -// }) -// -// it('immediately prevents upkeep', async () => { -// await registry.connect(owner).cancelUpkeep(upkeepId) -// -// const tx = await getTransmitTx(registry, keeper1, [upkeepId]) -// const receipt = await tx.wait() -// const cancelledUpkeepReportLogs = -// parseCancelledUpkeepReportLogs(receipt) -// // exactly 1 CancelledUpkeepReport log should be emitted -// assert.equal(cancelledUpkeepReportLogs.length, 1) -// }) -// -// it('does not revert if reverts if called multiple times', async () => { -// await registry.connect(owner).cancelUpkeep(upkeepId) -// await evmRevert( -// registry.connect(owner).cancelUpkeep(upkeepId), -// 'CannotCancel()', -// ) -// }) -// -// describe('when called by the owner when the admin has just canceled', () => { -// let oldExpiration: BigNumber -// -// beforeEach(async () => { -// await registry.connect(admin).cancelUpkeep(upkeepId) -// const registration = await registry.getUpkeep(upkeepId) -// oldExpiration = registration.maxValidBlocknumber -// }) -// -// it('allows the owner to cancel it more quickly', async () => { -// await registry.connect(owner).cancelUpkeep(upkeepId) -// -// const registration = await registry.getUpkeep(upkeepId) -// const newExpiration = registration.maxValidBlocknumber -// assert.isTrue(newExpiration.lt(oldExpiration)) -// }) -// }) -// }) -// -// describe('when called by the admin', async () => { -// it('reverts if called again by the admin', async () => { -// await registry.connect(admin).cancelUpkeep(upkeepId) -// -// await evmRevert( -// registry.connect(admin).cancelUpkeep(upkeepId), -// 'CannotCancel()', -// ) -// }) -// -// it('reverts if called by the owner after the timeout', async () => { -// await registry.connect(admin).cancelUpkeep(upkeepId) -// -// for (let i = 0; i < cancellationDelay; i++) { -// await ethers.provider.send('evm_mine', []) -// } -// -// await evmRevert( -// registry.connect(owner).cancelUpkeep(upkeepId), -// 'CannotCancel()', -// ) -// }) -// -// it('sets the registration to invalid in 50 blocks', async () => { -// const tx = await registry.connect(admin).cancelUpkeep(upkeepId) -// const receipt = await tx.wait() -// const registration = await registry.getUpkeep(upkeepId) -// assert.equal( -// registration.maxValidBlocknumber.toNumber(), -// receipt.blockNumber + 50, -// ) -// }) -// -// it('emits an event', async () => { -// const tx = await registry.connect(admin).cancelUpkeep(upkeepId) -// const receipt = await tx.wait() -// await expect(tx) -// .to.emit(registry, 'UpkeepCanceled') -// .withArgs( -// upkeepId, -// BigNumber.from(receipt.blockNumber + cancellationDelay), -// ) -// }) -// -// it('immediately prevents upkeep', async () => { -// await linkToken.connect(owner).approve(registry.address, toWei('100')) -// await registry.connect(owner).addFunds(upkeepId, toWei('100')) -// await registry.connect(admin).cancelUpkeep(upkeepId) -// -// await getTransmitTx(registry, keeper1, [upkeepId]) -// -// for (let i = 0; i < cancellationDelay; i++) { -// await ethers.provider.send('evm_mine', []) -// } -// -// const tx = await getTransmitTx(registry, keeper1, [upkeepId]) -// -// const receipt = await tx.wait() -// const cancelledUpkeepReportLogs = -// parseCancelledUpkeepReportLogs(receipt) -// // exactly 1 CancelledUpkeepReport log should be emitted -// assert.equal(cancelledUpkeepReportLogs.length, 1) -// }) -// -// describeMaybe('when an upkeep has been performed', async () => { -// beforeEach(async () => { -// await linkToken.connect(owner).approve(registry.address, toWei('100')) -// await registry.connect(owner).addFunds(upkeepId, toWei('100')) -// await getTransmitTx(registry, keeper1, [upkeepId]) -// }) -// -// it('deducts a cancellation fee from the upkeep and gives to owner', async () => { -// const minUpkeepSpend = toWei('10') -// -// await registry.connect(owner).setConfigTypeSafe( -// signerAddresses, -// keeperAddresses, -// f, -// { -// paymentPremiumPPB, -// flatFeeMicroLink, -// checkGasLimit, -// stalenessSeconds, -// gasCeilingMultiplier, -// minUpkeepSpend, -// maxCheckDataSize, -// maxPerformDataSize, -// maxRevertDataSize, -// maxPerformGas, -// fallbackGasPrice, -// fallbackLinkPrice, -// transcoder: transcoder.address, -// registrars: [], -// upkeepPrivilegeManager: upkeepManager, -// }, -// offchainVersion, -// offchainBytes, -// ) -// -// const payee1Before = await linkToken.balanceOf( -// await payee1.getAddress(), -// ) -// const upkeepBefore = (await registry.getUpkeep(upkeepId)).balance -// const ownerBefore = (await registry.getState()).state.ownerLinkBalance -// -// const amountSpent = toWei('100').sub(upkeepBefore) -// const cancellationFee = minUpkeepSpend.sub(amountSpent) -// -// await registry.connect(admin).cancelUpkeep(upkeepId) -// -// const payee1After = await linkToken.balanceOf( -// await payee1.getAddress(), -// ) -// const upkeepAfter = (await registry.getUpkeep(upkeepId)).balance -// const ownerAfter = (await registry.getState()).state.ownerLinkBalance -// -// // post upkeep balance should be previous balance minus cancellation fee -// assert.isTrue(upkeepBefore.sub(cancellationFee).eq(upkeepAfter)) -// // payee balance should not change -// assert.isTrue(payee1Before.eq(payee1After)) -// // owner should receive the cancellation fee -// assert.isTrue(ownerAfter.sub(ownerBefore).eq(cancellationFee)) -// }) -// -// it('deducts up to balance as cancellation fee', async () => { -// // Very high min spend, should deduct whole balance as cancellation fees -// const minUpkeepSpend = toWei('1000') -// await registry.connect(owner).setConfigTypeSafe( -// signerAddresses, -// keeperAddresses, -// f, -// { -// paymentPremiumPPB, -// flatFeeMicroLink, -// checkGasLimit, -// stalenessSeconds, -// gasCeilingMultiplier, -// minUpkeepSpend, -// maxCheckDataSize, -// maxPerformDataSize, -// maxRevertDataSize, -// maxPerformGas, -// fallbackGasPrice, -// fallbackLinkPrice, -// transcoder: transcoder.address, -// registrars: [], -// upkeepPrivilegeManager: upkeepManager, -// }, -// offchainVersion, -// offchainBytes, -// ) -// const payee1Before = await linkToken.balanceOf( -// await payee1.getAddress(), -// ) -// const upkeepBefore = (await registry.getUpkeep(upkeepId)).balance -// const ownerBefore = (await registry.getState()).state.ownerLinkBalance -// -// await registry.connect(admin).cancelUpkeep(upkeepId) -// const payee1After = await linkToken.balanceOf( -// await payee1.getAddress(), -// ) -// const ownerAfter = (await registry.getState()).state.ownerLinkBalance -// const upkeepAfter = (await registry.getUpkeep(upkeepId)).balance -// -// // all upkeep balance is deducted for cancellation fee -// assert.equal(0, upkeepAfter.toNumber()) -// // payee balance should not change -// assert.isTrue(payee1After.eq(payee1Before)) -// // all upkeep balance is transferred to the owner -// assert.isTrue(ownerAfter.sub(ownerBefore).eq(upkeepBefore)) -// }) -// -// it('does not deduct cancellation fee if more than minUpkeepSpend is spent', async () => { -// // Very low min spend, already spent in one perform upkeep -// const minUpkeepSpend = BigNumber.from(420) -// await registry.connect(owner).setConfigTypeSafe( -// signerAddresses, -// keeperAddresses, -// f, -// { -// paymentPremiumPPB, -// flatFeeMicroLink, -// checkGasLimit, -// stalenessSeconds, -// gasCeilingMultiplier, -// minUpkeepSpend, -// maxCheckDataSize, -// maxPerformDataSize, -// maxRevertDataSize, -// maxPerformGas, -// fallbackGasPrice, -// fallbackLinkPrice, -// transcoder: transcoder.address, -// registrars: [], -// upkeepPrivilegeManager: upkeepManager, -// }, -// offchainVersion, -// offchainBytes, -// ) -// const payee1Before = await linkToken.balanceOf( -// await payee1.getAddress(), -// ) -// const upkeepBefore = (await registry.getUpkeep(upkeepId)).balance -// const ownerBefore = (await registry.getState()).state.ownerLinkBalance -// -// await registry.connect(admin).cancelUpkeep(upkeepId) -// const payee1After = await linkToken.balanceOf( -// await payee1.getAddress(), -// ) -// const ownerAfter = (await registry.getState()).state.ownerLinkBalance -// const upkeepAfter = (await registry.getUpkeep(upkeepId)).balance -// -// // upkeep does not pay cancellation fee after cancellation because minimum upkeep spent is met -// assert.isTrue(upkeepBefore.eq(upkeepAfter)) -// // owner balance does not change -// assert.isTrue(ownerAfter.eq(ownerBefore)) -// // payee balance does not change -// assert.isTrue(payee1Before.eq(payee1After)) -// }) -// }) -// }) -// }) -// -// describe('#withdrawPayment', () => { -// beforeEach(async () => { -// await linkToken.connect(owner).approve(registry.address, toWei('100')) -// await registry.connect(owner).addFunds(upkeepId, toWei('100')) -// await getTransmitTx(registry, keeper1, [upkeepId]) -// }) -// -// it('reverts if called by anyone but the payee', async () => { -// await evmRevert( -// registry -// .connect(payee2) -// .withdrawPayment( -// await keeper1.getAddress(), -// await nonkeeper.getAddress(), -// ), -// 'OnlyCallableByPayee()', -// ) -// }) -// -// it('reverts if called with the 0 address', async () => { -// await evmRevert( -// registry -// .connect(payee2) -// .withdrawPayment(await keeper1.getAddress(), zeroAddress), -// 'InvalidRecipient()', -// ) -// }) -// -// it('updates the balances', async () => { -// const to = await nonkeeper.getAddress() -// const keeperBefore = await registry.getTransmitterInfo( -// await keeper1.getAddress(), -// ) -// const registrationBefore = (await registry.getUpkeep(upkeepId)).balance -// const toLinkBefore = await linkToken.balanceOf(to) -// const registryLinkBefore = await linkToken.balanceOf(registry.address) -// const registryPremiumBefore = (await registry.getState()).state -// .totalPremium -// const ownerBefore = (await registry.getState()).state.ownerLinkBalance -// -// // Withdrawing for first time, last collected = 0 -// assert.equal(keeperBefore.lastCollected.toString(), '0') -// -// //// Do the thing -// await registry -// .connect(payee1) -// .withdrawPayment(await keeper1.getAddress(), to) -// -// const keeperAfter = await registry.getTransmitterInfo( -// await keeper1.getAddress(), -// ) -// const registrationAfter = (await registry.getUpkeep(upkeepId)).balance -// const toLinkAfter = await linkToken.balanceOf(to) -// const registryLinkAfter = await linkToken.balanceOf(registry.address) -// const registryPremiumAfter = (await registry.getState()).state -// .totalPremium -// const ownerAfter = (await registry.getState()).state.ownerLinkBalance -// -// // registry total premium should not change -// assert.isTrue(registryPremiumBefore.eq(registryPremiumAfter)) -// -// // Last collected should be updated to premium-change -// assert.isTrue( -// keeperAfter.lastCollected.eq( -// registryPremiumBefore.sub( -// registryPremiumBefore.mod(keeperAddresses.length), -// ), -// ), -// ) -// -// // owner balance should remain unchanged -// assert.isTrue(ownerAfter.eq(ownerBefore)) -// -// assert.isTrue(keeperAfter.balance.eq(BigNumber.from(0))) -// assert.isTrue(registrationBefore.eq(registrationAfter)) -// assert.isTrue(toLinkBefore.add(keeperBefore.balance).eq(toLinkAfter)) -// assert.isTrue( -// registryLinkBefore.sub(keeperBefore.balance).eq(registryLinkAfter), -// ) -// }) -// -// it('emits a log announcing the withdrawal', async () => { -// const balance = ( -// await registry.getTransmitterInfo(await keeper1.getAddress()) -// ).balance -// const tx = await registry -// .connect(payee1) -// .withdrawPayment( -// await keeper1.getAddress(), -// await nonkeeper.getAddress(), -// ) -// await expect(tx) -// .to.emit(registry, 'PaymentWithdrawn') -// .withArgs( -// await keeper1.getAddress(), -// balance, -// await nonkeeper.getAddress(), -// await payee1.getAddress(), -// ) -// }) -// }) -// -// describe('#checkCallback', () => { -// it('returns false with appropriate failure reason when target callback reverts', async () => { -// await streamsLookupUpkeep.setShouldRevertCallback(true) -// -// const values: any[] = ['0x1234', '0xabcd'] -// const res = await registry -// .connect(zeroAddress) -// .callStatic.checkCallback(streamsLookupUpkeepId, values, '0x') -// -// assert.isFalse(res.upkeepNeeded) -// assert.equal(res.performData, '0x') -// assert.equal( -// res.upkeepFailureReason, -// UpkeepFailureReason.CHECK_CALLBACK_REVERTED, -// ) -// assert.isTrue(res.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used -// }) -// -// it('returns false with appropriate failure reason when target callback returns big performData', async () => { -// let longBytes = '0x' -// for (let i = 0; i <= maxPerformDataSize.toNumber(); i++) { -// longBytes += '11' -// } -// const values: any[] = [longBytes, longBytes] -// const res = await registry -// .connect(zeroAddress) -// .callStatic.checkCallback(streamsLookupUpkeepId, values, '0x') -// -// assert.isFalse(res.upkeepNeeded) -// assert.equal(res.performData, '0x') -// assert.equal( -// res.upkeepFailureReason, -// UpkeepFailureReason.PERFORM_DATA_EXCEEDS_LIMIT, -// ) -// assert.isTrue(res.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used -// }) -// -// it('returns false with appropriate failure reason when target callback returns false', async () => { -// await streamsLookupUpkeep.setCallbackReturnBool(false) -// const values: any[] = ['0x1234', '0xabcd'] -// const res = await registry -// .connect(zeroAddress) -// .callStatic.checkCallback(streamsLookupUpkeepId, values, '0x') -// -// assert.isFalse(res.upkeepNeeded) -// assert.equal(res.performData, '0x') -// assert.equal( -// res.upkeepFailureReason, -// UpkeepFailureReason.UPKEEP_NOT_NEEDED, -// ) -// assert.isTrue(res.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used -// }) -// -// it('succeeds with upkeep needed', async () => { -// const values: any[] = ['0x1234', '0xabcd'] -// -// const res = await registry -// .connect(zeroAddress) -// .callStatic.checkCallback(streamsLookupUpkeepId, values, '0x') -// const expectedPerformData = ethers.utils.defaultAbiCoder.encode( -// ['bytes[]', 'bytes'], -// [values, '0x'], -// ) -// -// assert.isTrue(res.upkeepNeeded) -// assert.equal(res.performData, expectedPerformData) -// assert.equal(res.upkeepFailureReason, UpkeepFailureReason.NONE) -// assert.isTrue(res.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used -// }) -// }) -// -// describe('#setUpkeepPrivilegeConfig() / #getUpkeepPrivilegeConfig()', () => { -// it('reverts when non manager tries to set privilege config', async () => { -// await evmRevert( -// registry.connect(payee3).setUpkeepPrivilegeConfig(upkeepId, '0x1234'), -// 'OnlyCallableByUpkeepPrivilegeManager()', -// ) -// }) -// -// it('returns empty bytes for upkeep privilege config before setting', async () => { -// const cfg = await registry.getUpkeepPrivilegeConfig(upkeepId) -// assert.equal(cfg, '0x') -// }) -// -// it('allows upkeep manager to set privilege config', async () => { -// const tx = await registry -// .connect(personas.Norbert) -// .setUpkeepPrivilegeConfig(upkeepId, '0x1234') -// await expect(tx) -// .to.emit(registry, 'UpkeepPrivilegeConfigSet') -// .withArgs(upkeepId, '0x1234') -// -// const cfg = await registry.getUpkeepPrivilegeConfig(upkeepId) -// assert.equal(cfg, '0x1234') -// }) -// }) -// -// describe('#setAdminPrivilegeConfig() / #getAdminPrivilegeConfig()', () => { -// const admin = randomAddress() -// -// it('reverts when non manager tries to set privilege config', async () => { -// await evmRevert( -// registry.connect(payee3).setAdminPrivilegeConfig(admin, '0x1234'), -// 'OnlyCallableByUpkeepPrivilegeManager()', -// ) -// }) -// -// it('returns empty bytes for upkeep privilege config before setting', async () => { -// const cfg = await registry.getAdminPrivilegeConfig(admin) -// assert.equal(cfg, '0x') -// }) -// -// it('allows upkeep manager to set privilege config', async () => { -// const tx = await registry -// .connect(personas.Norbert) -// .setAdminPrivilegeConfig(admin, '0x1234') -// await expect(tx) -// .to.emit(registry, 'AdminPrivilegeConfigSet') -// .withArgs(admin, '0x1234') -// -// const cfg = await registry.getAdminPrivilegeConfig(admin) -// assert.equal(cfg, '0x1234') -// }) -// }) -// -// describe('transmitterPremiumSplit [ @skip-coverage ]', () => { -// beforeEach(async () => { -// await linkToken.connect(owner).approve(registry.address, toWei('100')) -// await registry.connect(owner).addFunds(upkeepId, toWei('100')) -// }) -// -// it('splits premium evenly across transmitters', async () => { -// // Do a transmit from keeper1 -// await getTransmitTx(registry, keeper1, [upkeepId]) -// -// const registryPremium = (await registry.getState()).state.totalPremium -// assert.isTrue(registryPremium.gt(BigNumber.from(0))) -// -// const premiumPerTransmitter = registryPremium.div( -// BigNumber.from(keeperAddresses.length), -// ) -// const k1Balance = ( -// await registry.getTransmitterInfo(await keeper1.getAddress()) -// ).balance -// // transmitter should be reimbursed for gas and get the premium -// assert.isTrue(k1Balance.gt(premiumPerTransmitter)) -// const k1GasReimbursement = k1Balance.sub(premiumPerTransmitter) -// -// const k2Balance = ( -// await registry.getTransmitterInfo(await keeper2.getAddress()) -// ).balance -// // non transmitter should get its share of premium -// assert.isTrue(k2Balance.eq(premiumPerTransmitter)) -// -// // Now do a transmit from keeper 2 -// await getTransmitTx(registry, keeper2, [upkeepId]) -// const registryPremiumNew = (await registry.getState()).state.totalPremium -// assert.isTrue(registryPremiumNew.gt(registryPremium)) -// const premiumPerTransmitterNew = registryPremiumNew.div( -// BigNumber.from(keeperAddresses.length), -// ) -// const additionalPremium = premiumPerTransmitterNew.sub( -// premiumPerTransmitter, -// ) -// -// const k1BalanceNew = ( -// await registry.getTransmitterInfo(await keeper1.getAddress()) -// ).balance -// // k1 should get the new premium -// assert.isTrue( -// k1BalanceNew.eq(k1GasReimbursement.add(premiumPerTransmitterNew)), -// ) -// -// const k2BalanceNew = ( -// await registry.getTransmitterInfo(await keeper2.getAddress()) -// ).balance -// // k2 should get gas reimbursement in addition to new premium -// assert.isTrue(k2BalanceNew.gt(k2Balance.add(additionalPremium))) -// }) -// -// it('updates last collected upon payment withdrawn', async () => { -// // Do a transmit from keeper1 -// await getTransmitTx(registry, keeper1, [upkeepId]) -// -// const registryPremium = (await registry.getState()).state.totalPremium -// const k1 = await registry.getTransmitterInfo(await keeper1.getAddress()) -// const k2 = await registry.getTransmitterInfo(await keeper2.getAddress()) -// -// // Withdrawing for first time, last collected = 0 -// assert.isTrue(k1.lastCollected.eq(BigNumber.from(0))) -// assert.isTrue(k2.lastCollected.eq(BigNumber.from(0))) -// -// //// Do the thing -// await registry -// .connect(payee1) -// .withdrawPayment( -// await keeper1.getAddress(), -// await nonkeeper.getAddress(), -// ) -// -// const k1New = await registry.getTransmitterInfo( -// await keeper1.getAddress(), -// ) -// const k2New = await registry.getTransmitterInfo( -// await keeper2.getAddress(), -// ) -// -// // transmitter info lastCollected should be updated for k1, not for k2 -// assert.isTrue( -// k1New.lastCollected.eq( -// registryPremium.sub(registryPremium.mod(keeperAddresses.length)), -// ), -// ) -// assert.isTrue(k2New.lastCollected.eq(BigNumber.from(0))) -// }) -// -// itMaybe( -// 'maintains consistent balance information across all parties', -// async () => { -// // throughout transmits, withdrawals, setConfigs total claim on balances should remain less than expected balance -// // some spare change can get lost but it should be less than maxAllowedSpareChange -// -// let maxAllowedSpareChange = BigNumber.from('0') -// await verifyConsistentAccounting(maxAllowedSpareChange) -// -// await getTransmitTx(registry, keeper1, [upkeepId]) -// maxAllowedSpareChange = maxAllowedSpareChange.add(BigNumber.from('31')) -// await verifyConsistentAccounting(maxAllowedSpareChange) -// -// await registry -// .connect(payee1) -// .withdrawPayment( -// await keeper1.getAddress(), -// await nonkeeper.getAddress(), -// ) -// await verifyConsistentAccounting(maxAllowedSpareChange) -// -// await registry -// .connect(payee2) -// .withdrawPayment( -// await keeper2.getAddress(), -// await nonkeeper.getAddress(), -// ) -// await verifyConsistentAccounting(maxAllowedSpareChange) -// -// await getTransmitTx(registry, keeper1, [upkeepId]) -// maxAllowedSpareChange = maxAllowedSpareChange.add(BigNumber.from('31')) -// await verifyConsistentAccounting(maxAllowedSpareChange) -// -// await registry.connect(owner).setConfigTypeSafe( -// signerAddresses.slice(2, 15), // only use 2-14th index keepers -// keeperAddresses.slice(2, 15), -// f, -// config, -// offchainVersion, -// offchainBytes, -// ) -// await verifyConsistentAccounting(maxAllowedSpareChange) -// -// await getTransmitTx(registry, keeper3, [upkeepId], { -// startingSignerIndex: 2, -// }) -// maxAllowedSpareChange = maxAllowedSpareChange.add(BigNumber.from('13')) -// await verifyConsistentAccounting(maxAllowedSpareChange) -// -// await registry -// .connect(payee1) -// .withdrawPayment( -// await keeper1.getAddress(), -// await nonkeeper.getAddress(), -// ) -// await verifyConsistentAccounting(maxAllowedSpareChange) -// -// await registry -// .connect(payee3) -// .withdrawPayment( -// await keeper3.getAddress(), -// await nonkeeper.getAddress(), -// ) -// await verifyConsistentAccounting(maxAllowedSpareChange) -// -// await registry.connect(owner).setConfigTypeSafe( -// signerAddresses.slice(0, 4), // only use 0-3rd index keepers -// keeperAddresses.slice(0, 4), -// f, -// config, -// offchainVersion, -// offchainBytes, -// ) -// await verifyConsistentAccounting(maxAllowedSpareChange) -// await getTransmitTx(registry, keeper1, [upkeepId]) -// maxAllowedSpareChange = maxAllowedSpareChange.add(BigNumber.from('4')) -// await getTransmitTx(registry, keeper3, [upkeepId]) -// maxAllowedSpareChange = maxAllowedSpareChange.add(BigNumber.from('4')) -// -// await verifyConsistentAccounting(maxAllowedSpareChange) -// await registry -// .connect(payee5) -// .withdrawPayment( -// await keeper5.getAddress(), -// await nonkeeper.getAddress(), -// ) -// await verifyConsistentAccounting(maxAllowedSpareChange) -// -// await registry -// .connect(payee1) -// .withdrawPayment( -// await keeper1.getAddress(), -// await nonkeeper.getAddress(), -// ) -// await verifyConsistentAccounting(maxAllowedSpareChange) -// }, -// ) -// }) -// }) diff --git a/contracts/test/v0.8/automation/KeeperRegistryCheckUpkeepGasUsageWrapper.test.ts b/contracts/test/v0.8/automation/KeeperRegistryCheckUpkeepGasUsageWrapper.test.ts deleted file mode 100644 index 9187487a8f..0000000000 --- a/contracts/test/v0.8/automation/KeeperRegistryCheckUpkeepGasUsageWrapper.test.ts +++ /dev/null @@ -1,110 +0,0 @@ -import { ethers } from 'hardhat' -import { BigNumber, Signer } from 'ethers' -import { assert } from 'chai' -import { KeeperRegistryCheckUpkeepGasUsageWrapper12 as GasWrapper } from '../../../typechain/KeeperRegistryCheckUpkeepGasUsageWrapper12' -import { KeeperRegistryCheckUpkeepGasUsageWrapper1_2__factory as GasWrapperFactory } from '../../../typechain/factories/KeeperRegistryCheckUpkeepGasUsageWrapper1_2__factory' -import { getUsers, Personas } from '../../test-helpers/setup' -import { - deployMockContract, - MockContract, -} from '@ethereum-waffle/mock-contract' -import { KeeperRegistry1_2__factory as KeeperRegistryFactory } from '../../../typechain/factories/KeeperRegistry1_2__factory' - -let personas: Personas -let owner: Signer -let caller: Signer -let nelly: Signer -let registryMockContract: MockContract -let gasWrapper: GasWrapper -let gasWrapperFactory: GasWrapperFactory - -const upkeepId = 123 - -describe('KeeperRegistryCheckUpkeepGasUsageWrapper1_2', () => { - before(async () => { - personas = (await getUsers()).personas - owner = personas.Default - caller = personas.Carol - nelly = personas.Nelly - - registryMockContract = await deployMockContract( - owner as any, - KeeperRegistryFactory.abi, - ) - // @ts-ignore bug in autogen file - gasWrapperFactory = await ethers.getContractFactory( - 'KeeperRegistryCheckUpkeepGasUsageWrapper1_2', - ) - gasWrapper = await gasWrapperFactory - .connect(owner) - .deploy(registryMockContract.address) - await gasWrapper.deployed() - }) - - describe('measureCheckGas()', () => { - it("returns gas used when registry's checkUpkeep executes successfully", async () => { - await registryMockContract.mock.checkUpkeep - .withArgs(upkeepId, await nelly.getAddress()) - .returns( - '0xabcd' /* performData */, - BigNumber.from(1000) /* maxLinkPayment */, - BigNumber.from(2000) /* gasLimit */, - BigNumber.from(3000) /* adjustedGasWei */, - BigNumber.from(4000) /* linkEth */, - ) - - const response = await gasWrapper - .connect(caller) - .callStatic.measureCheckGas( - BigNumber.from(upkeepId), - await nelly.getAddress(), - ) - - assert.isTrue(response[0], 'The checkUpkeepSuccess should be true') - assert.equal( - response[1], - '0xabcd', - 'The performData should be forwarded correctly', - ) - assert.isTrue( - response[2] > BigNumber.from(0), - 'The gasUsed value must be larger than 0', - ) - }) - - it("returns gas used when registry's checkUpkeep reverts", async () => { - await registryMockContract.mock.checkUpkeep - .withArgs(upkeepId, await nelly.getAddress()) - .revertsWithReason('Error') - - const response = await gasWrapper - .connect(caller) - .callStatic.measureCheckGas( - BigNumber.from(upkeepId), - await nelly.getAddress(), - ) - - assert.isFalse(response[0], 'The checkUpkeepSuccess should be false') - assert.equal( - response[1], - '0x', - 'The performData should be forwarded correctly', - ) - assert.isTrue( - response[2] > BigNumber.from(0), - 'The gasUsed value must be larger than 0', - ) - }) - }) - - describe('getKeeperRegistry()', () => { - it('returns the underlying keeper registry', async () => { - const registry = await gasWrapper.connect(caller).getKeeperRegistry() - assert.equal( - registry, - registryMockContract.address, - 'The underlying keeper registry is incorrect', - ) - }) - }) -}) diff --git a/contracts/test/v0.8/automation/UpkeepTranscoder.test.ts b/contracts/test/v0.8/automation/UpkeepTranscoder.test.ts deleted file mode 100644 index fc3a200956..0000000000 --- a/contracts/test/v0.8/automation/UpkeepTranscoder.test.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { ethers } from 'hardhat' -import { assert } from 'chai' -import { evmRevert, evmRevertCustomError } from '../../test-helpers/matchers' -import { UpkeepTranscoder__factory as UpkeepTranscoderFactory } from '../../../typechain/factories/UpkeepTranscoder__factory' -import { UpkeepTranscoder } from '../../../typechain' - -let upkeepTranscoderFactory: UpkeepTranscoderFactory -let transcoder: UpkeepTranscoder - -before(async () => { - upkeepTranscoderFactory = await ethers.getContractFactory('UpkeepTranscoder') -}) - -describe('UpkeepTranscoder', () => { - const formatV1 = 0 - const formatV2 = 1 - const formatV3 = 2 - - beforeEach(async () => { - transcoder = await upkeepTranscoderFactory.deploy() - }) - - describe('#typeAndVersion', () => { - it('uses the correct type and version', async () => { - const typeAndVersion = await transcoder.typeAndVersion() - assert.equal(typeAndVersion, 'UpkeepTranscoder 1.0.0') - }) - }) - - describe('#transcodeUpkeeps', () => { - const encodedData = '0xc0ffee' - - it('reverts if the from type is not an enum value', async () => { - await evmRevert(transcoder.transcodeUpkeeps(3, 1, encodedData)) - }) - - it('reverts if the from type != to type', async () => { - await evmRevertCustomError( - transcoder.transcodeUpkeeps(1, 2, encodedData), - transcoder, - 'InvalidTranscoding', - ) - }) - - context('when from and to versions equal', () => { - it('returns the data that was passed in', async () => { - let response = await transcoder.transcodeUpkeeps( - formatV1, - formatV1, - encodedData, - ) - assert.equal(encodedData, response) - - response = await transcoder.transcodeUpkeeps( - formatV2, - formatV2, - encodedData, - ) - assert.equal(encodedData, response) - - response = await transcoder.transcodeUpkeeps( - formatV3, - formatV3, - encodedData, - ) - assert.equal(encodedData, response) - }) - }) - }) -}) diff --git a/core/bridges/mocks/orm.go b/core/bridges/mocks/orm.go index cda12a9045..db8553fb21 100644 --- a/core/bridges/mocks/orm.go +++ b/core/bridges/mocks/orm.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/capabilities/launcher.go b/core/capabilities/launcher.go index e4f7f480f3..c6846ecd5a 100644 --- a/core/capabilities/launcher.go +++ b/core/capabilities/launcher.go @@ -97,7 +97,7 @@ func (w *launcher) LocalNode(ctx context.Context) (capabilities.Node, error) { return w.localNode, errors.New("unable to get local node: peerWrapper hasn't started yet") } - if w.localNode.WorkflowDON.ID == "" { + if w.localNode.WorkflowDON.ID == 0 { return w.localNode, errors.New("unable to get local node: waiting for initial call from syncer") } @@ -113,7 +113,7 @@ func (w *launcher) updateLocalNode(state registrysyncer.State) { for _, p := range d.NodeP2PIds { if p == pid { if d.AcceptsWorkflows { - if workflowDON.ID == "" { + if workflowDON.ID == 0 { workflowDON = *toDONInfo(d) w.lggr.Debug("Workflow DON identified: %+v", workflowDON) } else { @@ -353,7 +353,7 @@ func (w *launcher) addToRegistryAndSetDispatcher(ctx context.Context, capability err = w.dispatcher.SetReceiver( fullCapID, - fmt.Sprint(don.Id), + don.Id, capability, ) if err != nil { @@ -373,9 +373,9 @@ var ( ) func (w *launcher) exposeCapabilities(ctx context.Context, myPeerID p2ptypes.PeerID, don kcr.CapabilitiesRegistryDONInfo, state registrysyncer.State, remoteWorkflowDONs []kcr.CapabilitiesRegistryDONInfo) error { - idsToDONs := map[string]capabilities.DON{} + idsToDONs := map[uint32]capabilities.DON{} for _, d := range remoteWorkflowDONs { - idsToDONs[fmt.Sprint(d.Id)] = *toDONInfo(d) + idsToDONs[d.Id] = *toDONInfo(d) } for _, c := range don.CapabilityConfigurations { @@ -465,7 +465,7 @@ func (w *launcher) addReceiver(ctx context.Context, capability kcr.CapabilitiesR } w.lggr.Debugw("Enabling external access for capability", "id", fullCapID, "donID", don.Id) - err = w.dispatcher.SetReceiver(fullCapID, fmt.Sprint(don.Id), receiver) + err = w.dispatcher.SetReceiver(fullCapID, don.Id, receiver) if err != nil { return fmt.Errorf("failed to set receiver: %w", err) } @@ -502,9 +502,10 @@ func toDONInfo(don kcr.CapabilitiesRegistryDONInfo) *capabilities.DON { } return &capabilities.DON{ - ID: fmt.Sprint(don.Id), - Members: peerIDs, - F: don.F, + ID: don.Id, + ConfigVersion: don.ConfigCount, + Members: peerIDs, + F: don.F, } } diff --git a/core/capabilities/launcher_test.go b/core/capabilities/launcher_test.go index c29e5ebf38..a2d1c60169 100644 --- a/core/capabilities/launcher_test.go +++ b/core/capabilities/launcher_test.go @@ -3,7 +3,6 @@ package capabilities import ( "context" "crypto/rand" - "fmt" "testing" "github.com/stretchr/testify/assert" @@ -178,8 +177,8 @@ func TestLauncher_WiresUpExternalCapabilities(t *testing.T) { registry, ) - dispatcher.On("SetReceiver", fullTriggerCapID, fmt.Sprint(dID), mock.AnythingOfType("*remote.triggerPublisher")).Return(nil) - dispatcher.On("SetReceiver", fullTargetID, fmt.Sprint(dID), mock.AnythingOfType("*target.server")).Return(nil) + dispatcher.On("SetReceiver", fullTriggerCapID, dID, mock.AnythingOfType("*remote.triggerPublisher")).Return(nil) + dispatcher.On("SetReceiver", fullTargetID, dID, mock.AnythingOfType("*target.server")).Return(nil) err = launcher.Launch(ctx, state) require.NoError(t, err) @@ -428,8 +427,8 @@ func TestLauncher_WiresUpClientsForPublicWorkflowDON(t *testing.T) { registry, ) - dispatcher.On("SetReceiver", fullTriggerCapID, fmt.Sprint(capDonID), mock.AnythingOfType("*remote.triggerSubscriber")).Return(nil) - dispatcher.On("SetReceiver", fullTargetID, fmt.Sprint(capDonID), mock.AnythingOfType("*target.client")).Return(nil) + dispatcher.On("SetReceiver", fullTriggerCapID, capDonID, mock.AnythingOfType("*remote.triggerSubscriber")).Return(nil) + dispatcher.On("SetReceiver", fullTargetID, capDonID, mock.AnythingOfType("*target.client")).Return(nil) err = launcher.Launch(ctx, state) require.NoError(t, err) @@ -586,7 +585,7 @@ func TestLauncher_WiresUpClientsForPublicWorkflowDONButIgnoresPrivateCapabilitie registry, ) - dispatcher.On("SetReceiver", fullTriggerCapID, fmt.Sprint(triggerCapDonID), mock.AnythingOfType("*remote.triggerSubscriber")).Return(nil) + dispatcher.On("SetReceiver", fullTriggerCapID, triggerCapDonID, mock.AnythingOfType("*remote.triggerSubscriber")).Return(nil) err = launcher.Launch(ctx, state) require.NoError(t, err) @@ -634,7 +633,7 @@ func TestLauncher_LocalNode(t *testing.T) { IDsToDONs: map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo{ registrysyncer.DonID(dID): { Id: dID, - ConfigCount: uint32(0), + ConfigCount: uint32(2), F: uint8(1), IsPublic: true, AcceptsWorkflows: true, @@ -680,9 +679,10 @@ func TestLauncher_LocalNode(t *testing.T) { require.NoError(t, err) don := capabilities.DON{ - ID: fmt.Sprintf("%d", dID), - Members: toPeerIDs(workflowDonNodes), - F: 1, + ID: dID, + ConfigVersion: 2, + Members: toPeerIDs(workflowDonNodes), + F: 1, } expectedNode := capabilities.Node{ PeerID: &pid, diff --git a/core/capabilities/remote/dispatcher.go b/core/capabilities/remote/dispatcher.go index b91211b5bf..90fca50b22 100644 --- a/core/capabilities/remote/dispatcher.go +++ b/core/capabilities/remote/dispatcher.go @@ -35,7 +35,7 @@ type dispatcher struct { type key struct { capId string - donId string + donId uint32 } var _ services.Service = &dispatcher{} @@ -88,13 +88,13 @@ type receiver struct { ch chan *remotetypes.MessageBody } -func (d *dispatcher) SetReceiver(capabilityId string, donId string, rec remotetypes.Receiver) error { +func (d *dispatcher) SetReceiver(capabilityId string, donId uint32, rec remotetypes.Receiver) error { d.mu.Lock() defer d.mu.Unlock() k := key{capabilityId, donId} _, ok := d.receivers[k] if ok { - return fmt.Errorf("receiver already exists for capability %s and don %s", capabilityId, donId) + return fmt.Errorf("receiver already exists for capability %s and don %d", capabilityId, donId) } receiverCh := make(chan *remotetypes.MessageBody, receiverBufferSize) @@ -123,7 +123,7 @@ func (d *dispatcher) SetReceiver(capabilityId string, donId string, rec remotety return nil } -func (d *dispatcher) RemoveReceiver(capabilityId string, donId string) { +func (d *dispatcher) RemoveReceiver(capabilityId string, donId uint32) { d.mu.Lock() defer d.mu.Unlock() @@ -181,7 +181,7 @@ func (d *dispatcher) receive() { } receiverQueueUsage := float64(len(receiver.ch)) / receiverBufferSize - capReceiveChannelUsage.WithLabelValues(k.capId, k.donId).Set(receiverQueueUsage) + capReceiveChannelUsage.WithLabelValues(k.capId, fmt.Sprint(k.donId)).Set(receiverQueueUsage) select { case receiver.ch <- body: default: diff --git a/core/capabilities/remote/target/client_test.go b/core/capabilities/remote/target/client_test.go index 64b824d74d..6d26b51b8a 100644 --- a/core/capabilities/remote/target/client_test.go +++ b/core/capabilities/remote/target/client_test.go @@ -33,9 +33,9 @@ func Test_Client_DonTopologies(t *testing.T) { responseTest := func(t *testing.T, responseCh <-chan commoncap.CapabilityResponse, responseError error) { require.NoError(t, responseError) response := <-responseCh - responseValue, err := response.Value.Unwrap() + mp, err := response.Value.Unwrap() require.NoError(t, err) - assert.Equal(t, "aValue1", responseValue.(string)) + assert.Equal(t, "aValue1", mp.(map[string]any)["response"].(string)) } capability := &TestCapability{} @@ -64,9 +64,9 @@ func Test_Client_TransmissionSchedules(t *testing.T) { responseTest := func(t *testing.T, responseCh <-chan commoncap.CapabilityResponse, responseError error) { require.NoError(t, responseError) response := <-responseCh - responseValue, err := response.Value.Unwrap() + mp, err := response.Value.Unwrap() require.NoError(t, err) - assert.Equal(t, "aValue1", responseValue.(string)) + assert.Equal(t, "aValue1", mp.(map[string]any)["response"].(string)) } capability := &TestCapability{} @@ -130,7 +130,7 @@ func testClient(ctx context.Context, t *testing.T, numWorkflowPeers int, workflo } capDonInfo := commoncap.DON{ - ID: "capability-don", + ID: 1, Members: capabilityPeers, F: capabilityDonF, } @@ -149,7 +149,7 @@ func testClient(ctx context.Context, t *testing.T, numWorkflowPeers int, workflo workflowDonInfo := commoncap.DON{ Members: workflowPeers, - ID: "workflow-don", + ID: 2, } broker := newTestAsyncMessageBroker(t, 100) @@ -259,7 +259,7 @@ func (t *clientTestServer) Receive(_ context.Context, msg *remotetypes.MessageBo for receiver := range t.messageIDToSenders[messageID] { var responseMsg = &remotetypes.MessageBody{ CapabilityId: "cap_id@1.0.0", - CapabilityDonId: "capability-don", + CapabilityDonId: 1, CallerDonId: t.workflowDonInfo.ID, Method: remotetypes.MethodExecute, MessageId: []byte(messageID), diff --git a/core/capabilities/remote/target/endtoend_test.go b/core/capabilities/remote/target/endtoend_test.go index d5305afeb3..c01b8d99a4 100644 --- a/core/capabilities/remote/target/endtoend_test.go +++ b/core/capabilities/remote/target/endtoend_test.go @@ -74,9 +74,9 @@ func Test_RemoteTargetCapability_TransmissionSchedules(t *testing.T) { responseTest := func(t *testing.T, responseCh <-chan commoncap.CapabilityResponse, responseError error) { require.NoError(t, responseError) response := <-responseCh - responseValue, err := response.Value.Unwrap() + mp, err := response.Value.Unwrap() require.NoError(t, err) - assert.Equal(t, "aValue1", responseValue.(string)) + assert.Equal(t, "aValue1", mp.(map[string]any)["response"].(string)) } transmissionSchedule, err := values.NewMap(map[string]any{ @@ -106,9 +106,9 @@ func Test_RemoteTargetCapability_DonTopologies(t *testing.T) { responseTest := func(t *testing.T, responseCh <-chan commoncap.CapabilityResponse, responseError error) { require.NoError(t, responseError) response := <-responseCh - responseValue, err := response.Value.Unwrap() + mp, err := response.Value.Unwrap() require.NoError(t, err) - assert.Equal(t, "aValue1", responseValue.(string)) + assert.Equal(t, "aValue1", mp.(map[string]any)["response"].(string)) } transmissionSchedule, err := values.NewMap(map[string]any{ @@ -191,7 +191,7 @@ func testRemoteTarget(ctx context.Context, t *testing.T, underlying commoncap.Ta require.NoError(t, capabilityPeerID.UnmarshalText([]byte(NewPeerID()))) capDonInfo := commoncap.DON{ - ID: "capability-don", + ID: 2, Members: capabilityPeers, F: capabilityDonF, } @@ -212,13 +212,13 @@ func testRemoteTarget(ctx context.Context, t *testing.T, underlying commoncap.Ta workflowDonInfo := commoncap.DON{ Members: workflowPeers, - ID: "workflow-don", + ID: 1, F: workflowDonF, } broker := newTestAsyncMessageBroker(t, 1000) - workflowDONs := map[string]commoncap.DON{ + workflowDONs := map[uint32]commoncap.DON{ workflowDonInfo.ID: workflowDonInfo, } @@ -380,10 +380,10 @@ func (t *nodeDispatcher) Send(peerID p2ptypes.PeerID, msgBody *remotetypes.Messa return nil } -func (t *nodeDispatcher) SetReceiver(capabilityId string, donId string, receiver remotetypes.Receiver) error { +func (t *nodeDispatcher) SetReceiver(capabilityId string, donId uint32, receiver remotetypes.Receiver) error { return nil } -func (t *nodeDispatcher) RemoveReceiver(capabilityId string, donId string) {} +func (t *nodeDispatcher) RemoveReceiver(capabilityId string, donId uint32) {} type abstractTestCapability struct { } @@ -409,8 +409,12 @@ func (t TestCapability) Execute(ctx context.Context, request commoncap.Capabilit value := request.Inputs.Underlying["executeValue1"] + response, err := values.NewMap(map[string]any{"response": value}) + if err != nil { + return nil, err + } ch <- commoncap.CapabilityResponse{ - Value: value, + Value: response, } return ch, nil diff --git a/core/capabilities/remote/target/request/client_request_test.go b/core/capabilities/remote/target/request/client_request_test.go index a053623cd2..c20c24a550 100644 --- a/core/capabilities/remote/target/request/client_request_test.go +++ b/core/capabilities/remote/target/request/client_request_test.go @@ -30,7 +30,7 @@ func Test_ClientRequest_MessageValidation(t *testing.T) { } capDonInfo := commoncap.DON{ - ID: "capability-don", + ID: 1, Members: capabilityPeers, F: 1, } @@ -50,7 +50,7 @@ func Test_ClientRequest_MessageValidation(t *testing.T) { workflowDonInfo := commoncap.DON{ Members: workflowPeers, - ID: "workflow-don", + ID: 2, } executeInputs, err := values.NewMap( @@ -75,8 +75,10 @@ func Test_ClientRequest_MessageValidation(t *testing.T) { Config: transmissionSchedule, } + m, err := values.NewMap(map[string]any{"response": "response1"}) + require.NoError(t, err) capabilityResponse := commoncap.CapabilityResponse{ - Value: values.NewString("response1"), + Value: m, Err: nil, } @@ -106,8 +108,10 @@ func Test_ClientRequest_MessageValidation(t *testing.T) { require.NoError(t, err) + nm, err := values.NewMap(map[string]any{"response": "response2"}) + require.NoError(t, err) capabilityResponse2 := commoncap.CapabilityResponse{ - Value: values.NewString("response2"), + Value: nm, Err: nil, } @@ -297,8 +301,9 @@ func Test_ClientRequest_MessageValidation(t *testing.T) { require.NoError(t, err) response := <-request.ResponseChan() + resp := response.Value.Underlying["response"] - assert.Equal(t, response.Value, values.NewString("response1")) + assert.Equal(t, resp, values.NewString("response1")) }) } @@ -306,11 +311,11 @@ type clientRequestTestDispatcher struct { msgs chan *types.MessageBody } -func (t *clientRequestTestDispatcher) SetReceiver(capabilityId string, donId string, receiver types.Receiver) error { +func (t *clientRequestTestDispatcher) SetReceiver(capabilityId string, donId uint32, receiver types.Receiver) error { return nil } -func (t *clientRequestTestDispatcher) RemoveReceiver(capabilityId string, donId string) {} +func (t *clientRequestTestDispatcher) RemoveReceiver(capabilityId string, donId uint32) {} func (t *clientRequestTestDispatcher) Send(peerID p2ptypes.PeerID, msgBody *types.MessageBody) error { t.msgs <- msgBody diff --git a/core/capabilities/remote/target/request/server_request.go b/core/capabilities/remote/target/request/server_request.go index bb84fda4ac..25d88d2192 100644 --- a/core/capabilities/remote/target/request/server_request.go +++ b/core/capabilities/remote/target/request/server_request.go @@ -27,7 +27,7 @@ type ServerRequest struct { capabilityPeerId p2ptypes.PeerID capabilityID string - capabilityDonID string + capabilityDonID uint32 dispatcher types.Dispatcher @@ -47,7 +47,7 @@ type ServerRequest struct { lggr logger.Logger } -func NewServerRequest(capability capabilities.TargetCapability, capabilityID string, capabilityDonID string, capabilityPeerId p2ptypes.PeerID, +func NewServerRequest(capability capabilities.TargetCapability, capabilityID string, capabilityDonID uint32, capabilityPeerId p2ptypes.PeerID, callingDon commoncap.DON, requestMessageID string, dispatcher types.Dispatcher, requestTimeout time.Duration, lggr logger.Logger) *ServerRequest { return &ServerRequest{ diff --git a/core/capabilities/remote/target/request/server_request_test.go b/core/capabilities/remote/target/request/server_request_test.go index fe3fdd713b..053cba7064 100644 --- a/core/capabilities/remote/target/request/server_request_test.go +++ b/core/capabilities/remote/target/request/server_request_test.go @@ -33,7 +33,7 @@ func Test_ServerRequest_MessageValidation(t *testing.T) { callingDon := commoncap.DON{ Members: workflowPeers, - ID: "workflow-don", + ID: 1, F: 1, } @@ -58,7 +58,7 @@ func Test_ServerRequest_MessageValidation(t *testing.T) { require.NoError(t, err) t.Run("Send duplicate message", func(t *testing.T) { - req := request.NewServerRequest(capability, "capabilityID", "capabilityDonID", + req := request.NewServerRequest(capability, "capabilityID", 2, capabilityPeerID, callingDon, "requestMessageID", dispatcher, 10*time.Minute, lggr) err := sendValidRequest(req, workflowPeers, capabilityPeerID, rawRequest) @@ -68,7 +68,7 @@ func Test_ServerRequest_MessageValidation(t *testing.T) { }) t.Run("Send message with non calling don peer", func(t *testing.T) { - req := request.NewServerRequest(capability, "capabilityID", "capabilityDonID", + req := request.NewServerRequest(capability, "capabilityID", 2, capabilityPeerID, callingDon, "requestMessageID", dispatcher, 10*time.Minute, lggr) err := sendValidRequest(req, workflowPeers, capabilityPeerID, rawRequest) @@ -81,8 +81,8 @@ func Test_ServerRequest_MessageValidation(t *testing.T) { Receiver: capabilityPeerID[:], MessageId: []byte("workflowID" + "workflowExecutionID"), CapabilityId: "capabilityID", - CapabilityDonId: "capabilityDonID", - CallerDonId: "workflow-don", + CapabilityDonId: 2, + CallerDonId: 1, Method: types.MethodExecute, Payload: rawRequest, }) @@ -91,7 +91,7 @@ func Test_ServerRequest_MessageValidation(t *testing.T) { }) t.Run("Send message invalid payload", func(t *testing.T) { - req := request.NewServerRequest(capability, "capabilityID", "capabilityDonID", + req := request.NewServerRequest(capability, "capabilityID", 2, capabilityPeerID, callingDon, "requestMessageID", dispatcher, 10*time.Minute, lggr) err := sendValidRequest(req, workflowPeers, capabilityPeerID, rawRequest) @@ -103,8 +103,8 @@ func Test_ServerRequest_MessageValidation(t *testing.T) { Receiver: capabilityPeerID[:], MessageId: []byte("workflowID" + "workflowExecutionID"), CapabilityId: "capabilityID", - CapabilityDonId: "capabilityDonID", - CallerDonId: "workflow-don", + CapabilityDonId: 2, + CallerDonId: 1, Method: types.MethodExecute, Payload: append(rawRequest, []byte("asdf")...), }) @@ -116,7 +116,7 @@ func Test_ServerRequest_MessageValidation(t *testing.T) { t.Run("Send second valid request when capability errors", func(t *testing.T) { dispatcher := &testDispatcher{} - req := request.NewServerRequest(TestErrorCapability{}, "capabilityID", "capabilityDonID", + req := request.NewServerRequest(TestErrorCapability{}, "capabilityID", 2, capabilityPeerID, callingDon, "requestMessageID", dispatcher, 10*time.Minute, lggr) err := sendValidRequest(req, workflowPeers, capabilityPeerID, rawRequest) @@ -128,8 +128,8 @@ func Test_ServerRequest_MessageValidation(t *testing.T) { Receiver: capabilityPeerID[:], MessageId: []byte("workflowID" + "workflowExecutionID"), CapabilityId: "capabilityID", - CapabilityDonId: "capabilityDonID", - CallerDonId: "workflow-don", + CapabilityDonId: 2, + CallerDonId: 1, Method: types.MethodExecute, Payload: rawRequest, }) @@ -143,7 +143,7 @@ func Test_ServerRequest_MessageValidation(t *testing.T) { t.Run("Send second valid request", func(t *testing.T) { dispatcher := &testDispatcher{} - request := request.NewServerRequest(capability, "capabilityID", "capabilityDonID", + request := request.NewServerRequest(capability, "capabilityID", 2, capabilityPeerID, callingDon, "requestMessageID", dispatcher, 10*time.Minute, lggr) err := sendValidRequest(request, workflowPeers, capabilityPeerID, rawRequest) @@ -155,8 +155,8 @@ func Test_ServerRequest_MessageValidation(t *testing.T) { Receiver: capabilityPeerID[:], MessageId: []byte("workflowID" + "workflowExecutionID"), CapabilityId: "capabilityID", - CapabilityDonId: "capabilityDonID", - CallerDonId: "workflow-don", + CapabilityDonId: 2, + CallerDonId: 1, Method: types.MethodExecute, Payload: rawRequest, }) @@ -179,8 +179,8 @@ func sendValidRequest(request serverRequest, workflowPeers []p2ptypes.PeerID, ca Receiver: capabilityPeerID[:], MessageId: []byte("workflowID" + "workflowExecutionID"), CapabilityId: "capabilityID", - CapabilityDonId: "capabilityDonID", - CallerDonId: "workflow-don", + CapabilityDonId: 2, + CallerDonId: 1, Method: types.MethodExecute, Payload: rawRequest, }) @@ -190,11 +190,11 @@ type testDispatcher struct { msgs []*types.MessageBody } -func (t *testDispatcher) SetReceiver(capabilityId string, donId string, receiver types.Receiver) error { +func (t *testDispatcher) SetReceiver(capabilityId string, donId uint32, receiver types.Receiver) error { return nil } -func (t *testDispatcher) RemoveReceiver(capabilityId string, donId string) {} +func (t *testDispatcher) RemoveReceiver(capabilityId string, donId uint32) {} func (t *testDispatcher) Send(peerID p2ptypes.PeerID, msgBody *types.MessageBody) error { t.msgs = append(t.msgs, msgBody) @@ -225,8 +225,12 @@ func (t TestCapability) Execute(ctx context.Context, request commoncap.Capabilit value := request.Inputs.Underlying["executeValue1"] + response, err := values.NewMap(map[string]any{"response": value}) + if err != nil { + return nil, err + } ch <- commoncap.CapabilityResponse{ - Value: value, + Value: response, } return ch, nil diff --git a/core/capabilities/remote/target/server.go b/core/capabilities/remote/target/server.go index a918dd9170..1453cfc377 100644 --- a/core/capabilities/remote/target/server.go +++ b/core/capabilities/remote/target/server.go @@ -29,7 +29,7 @@ type server struct { underlying commoncap.TargetCapability capInfo commoncap.CapabilityInfo localDonInfo commoncap.DON - workflowDONs map[string]commoncap.DON + workflowDONs map[uint32]commoncap.DON dispatcher types.Dispatcher requestIDToRequest map[string]*request.ServerRequest @@ -44,7 +44,7 @@ var _ types.Receiver = &server{} var _ services.Service = &server{} func NewServer(peerID p2ptypes.PeerID, underlying commoncap.TargetCapability, capInfo commoncap.CapabilityInfo, localDonInfo commoncap.DON, - workflowDONs map[string]commoncap.DON, dispatcher types.Dispatcher, requestTimeout time.Duration, lggr logger.Logger) *server { + workflowDONs map[uint32]commoncap.DON, dispatcher types.Dispatcher, requestTimeout time.Duration, lggr logger.Logger) *server { return &server{ underlying: underlying, peerID: peerID, diff --git a/core/capabilities/remote/target/server_test.go b/core/capabilities/remote/target/server_test.go index 3c12ce813d..a5aa45efd0 100644 --- a/core/capabilities/remote/target/server_test.go +++ b/core/capabilities/remote/target/server_test.go @@ -112,7 +112,7 @@ func testRemoteTargetServer(ctx context.Context, t *testing.T, } capDonInfo := commoncap.DON{ - ID: "capability-don", + ID: 1, Members: capabilityPeers, F: capabilityDonF, } @@ -131,7 +131,7 @@ func testRemoteTargetServer(ctx context.Context, t *testing.T, workflowDonInfo := commoncap.DON{ Members: workflowPeers, - ID: "workflow-don", + ID: 2, F: workflowDonF, } @@ -141,7 +141,7 @@ func testRemoteTargetServer(ctx context.Context, t *testing.T, require.NoError(t, err) srvcs = append(srvcs, broker) - workflowDONs := map[string]commoncap.DON{ + workflowDONs := map[uint32]commoncap.DON{ workflowDonInfo.ID: workflowDonInfo, } @@ -219,8 +219,8 @@ func (r *serverTestClient) Execute(ctx context.Context, req commoncap.Capability for _, node := range r.capabilityDonInfo.Members { message := &remotetypes.MessageBody{ CapabilityId: "capability-id", - CapabilityDonId: "capability-don", - CallerDonId: "workflow-don", + CapabilityDonId: 1, + CallerDonId: 2, Method: remotetypes.MethodExecute, Payload: rawRequest, MessageId: []byte(messageID), diff --git a/core/capabilities/remote/trigger_publisher.go b/core/capabilities/remote/trigger_publisher.go index 9e7fc89352..d559dc36d4 100644 --- a/core/capabilities/remote/trigger_publisher.go +++ b/core/capabilities/remote/trigger_publisher.go @@ -25,7 +25,7 @@ type triggerPublisher struct { underlying commoncap.TriggerCapability capInfo commoncap.CapabilityInfo capDonInfo commoncap.DON - workflowDONs map[string]commoncap.DON + workflowDONs map[uint32]commoncap.DON dispatcher types.Dispatcher messageCache *messageCache[registrationKey, p2ptypes.PeerID] registrations map[registrationKey]*pubRegState @@ -36,7 +36,7 @@ type triggerPublisher struct { } type registrationKey struct { - callerDonId string + callerDonId uint32 workflowId string } @@ -48,7 +48,7 @@ type pubRegState struct { var _ types.Receiver = &triggerPublisher{} var _ services.Service = &triggerPublisher{} -func NewTriggerPublisher(config *types.RemoteTriggerConfig, underlying commoncap.TriggerCapability, capInfo commoncap.CapabilityInfo, capDonInfo commoncap.DON, workflowDONs map[string]commoncap.DON, dispatcher types.Dispatcher, lggr logger.Logger) *triggerPublisher { +func NewTriggerPublisher(config *types.RemoteTriggerConfig, underlying commoncap.TriggerCapability, capInfo commoncap.CapabilityInfo, capDonInfo commoncap.DON, workflowDONs map[uint32]commoncap.DON, dispatcher types.Dispatcher, lggr logger.Logger) *triggerPublisher { config.ApplyDefaults() return &triggerPublisher{ config: config, diff --git a/core/capabilities/remote/trigger_publisher_test.go b/core/capabilities/remote/trigger_publisher_test.go index db792fb506..ec38b962ce 100644 --- a/core/capabilities/remote/trigger_publisher_test.go +++ b/core/capabilities/remote/trigger_publisher_test.go @@ -29,12 +29,12 @@ func TestTriggerPublisher_Register(t *testing.T) { p2 := p2ptypes.PeerID{} require.NoError(t, p2.UnmarshalText([]byte(peerID2))) capDonInfo := commoncap.DON{ - ID: "capability-don", + ID: 1, Members: []p2ptypes.PeerID{p1}, F: 0, } workflowDonInfo := commoncap.DON{ - ID: "workflow-don", + ID: 2, Members: []p2ptypes.PeerID{p2}, F: 0, } @@ -46,7 +46,7 @@ func TestTriggerPublisher_Register(t *testing.T) { MinResponsesToAggregate: 1, MessageExpiryMs: 100_000, } - workflowDONs := map[string]commoncap.DON{ + workflowDONs := map[uint32]commoncap.DON{ workflowDonInfo.ID: workflowDonInfo, } underlying := &testTrigger{ diff --git a/core/capabilities/remote/trigger_subscriber_test.go b/core/capabilities/remote/trigger_subscriber_test.go index 3b819f9f3c..782b29d41f 100644 --- a/core/capabilities/remote/trigger_subscriber_test.go +++ b/core/capabilities/remote/trigger_subscriber_test.go @@ -18,11 +18,14 @@ import ( ) const ( - peerID1 = "12D3KooWF3dVeJ6YoT5HFnYhmwQWWMoEwVFzJQ5kKCMX3ZityxMC" - peerID2 = "12D3KooWQsmok6aD8PZqt3RnJhQRrNzKHLficq7zYFRp7kZ1hHP8" - workflowID1 = "workflowID1" - triggerEvent1 = "triggerEvent1" - triggerEvent2 = "triggerEvent2" + peerID1 = "12D3KooWF3dVeJ6YoT5HFnYhmwQWWMoEwVFzJQ5kKCMX3ZityxMC" + peerID2 = "12D3KooWQsmok6aD8PZqt3RnJhQRrNzKHLficq7zYFRp7kZ1hHP8" + workflowID1 = "workflowID1" +) + +var ( + triggerEvent1 = map[string]any{"event": "triggerEvent1"} + triggerEvent2 = map[string]any{"event": "triggerEvent2"} ) func TestTriggerSubscriber_RegisterAndReceive(t *testing.T) { @@ -38,12 +41,12 @@ func TestTriggerSubscriber_RegisterAndReceive(t *testing.T) { p2 := p2ptypes.PeerID{} require.NoError(t, p2.UnmarshalText([]byte(peerID2))) capDonInfo := commoncap.DON{ - ID: "capability-don", + ID: 1, Members: []p2ptypes.PeerID{p1}, F: 0, } workflowDonInfo := commoncap.DON{ - ID: "workflow-don", + ID: 2, Members: []p2ptypes.PeerID{p2}, F: 0, } @@ -77,7 +80,7 @@ func TestTriggerSubscriber_RegisterAndReceive(t *testing.T) { <-awaitRegistrationMessageCh // receive trigger event - triggerEventValue, err := values.Wrap(triggerEvent1) + triggerEventValue, err := values.NewMap(triggerEvent1) require.NoError(t, err) capResponse := commoncap.CapabilityResponse{ Value: triggerEventValue, diff --git a/core/capabilities/remote/types/messages.pb.go b/core/capabilities/remote/types/messages.pb.go index 0e51b39599..d1b86d9feb 100644 --- a/core/capabilities/remote/types/messages.pb.go +++ b/core/capabilities/remote/types/messages.pb.go @@ -138,24 +138,24 @@ type MessageBody struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Version uint32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"` - Sender []byte `protobuf:"bytes,2,opt,name=sender,proto3" json:"sender,omitempty"` - Receiver []byte `protobuf:"bytes,3,opt,name=receiver,proto3" json:"receiver,omitempty"` - Timestamp int64 `protobuf:"varint,4,opt,name=timestamp,proto3" json:"timestamp,omitempty"` - MessageId []byte `protobuf:"bytes,5,opt,name=message_id,json=messageId,proto3" json:"message_id,omitempty"` // scoped to sender - CapabilityId string `protobuf:"bytes,6,opt,name=capability_id,json=capabilityId,proto3" json:"capability_id,omitempty"` - CapabilityDonId string `protobuf:"bytes,7,opt,name=capability_don_id,json=capabilityDonId,proto3" json:"capability_don_id,omitempty"` - CallerDonId string `protobuf:"bytes,8,opt,name=caller_don_id,json=callerDonId,proto3" json:"caller_don_id,omitempty"` - Method string `protobuf:"bytes,9,opt,name=method,proto3" json:"method,omitempty"` - Error Error `protobuf:"varint,10,opt,name=error,proto3,enum=remote.Error" json:"error,omitempty"` - ErrorMsg string `protobuf:"bytes,11,opt,name=errorMsg,proto3" json:"errorMsg,omitempty"` + Version uint32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"` + Sender []byte `protobuf:"bytes,2,opt,name=sender,proto3" json:"sender,omitempty"` + Receiver []byte `protobuf:"bytes,3,opt,name=receiver,proto3" json:"receiver,omitempty"` + Timestamp int64 `protobuf:"varint,4,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + MessageId []byte `protobuf:"bytes,5,opt,name=message_id,json=messageId,proto3" json:"message_id,omitempty"` // scoped to sender + CapabilityId string `protobuf:"bytes,6,opt,name=capability_id,json=capabilityId,proto3" json:"capability_id,omitempty"` + Method string `protobuf:"bytes,9,opt,name=method,proto3" json:"method,omitempty"` + Error Error `protobuf:"varint,10,opt,name=error,proto3,enum=remote.Error" json:"error,omitempty"` + ErrorMsg string `protobuf:"bytes,11,opt,name=errorMsg,proto3" json:"errorMsg,omitempty"` // payload contains a CapabilityRequest or CapabilityResponse Payload []byte `protobuf:"bytes,12,opt,name=payload,proto3" json:"payload,omitempty"` // Types that are assignable to Metadata: // // *MessageBody_TriggerRegistrationMetadata // *MessageBody_TriggerEventMetadata - Metadata isMessageBody_Metadata `protobuf_oneof:"metadata"` + Metadata isMessageBody_Metadata `protobuf_oneof:"metadata"` + CapabilityDonId uint32 `protobuf:"varint,15,opt,name=capability_don_id,json=capabilityDonId,proto3" json:"capability_don_id,omitempty"` + CallerDonId uint32 `protobuf:"varint,16,opt,name=caller_don_id,json=callerDonId,proto3" json:"caller_don_id,omitempty"` } func (x *MessageBody) Reset() { @@ -232,20 +232,6 @@ func (x *MessageBody) GetCapabilityId() string { return "" } -func (x *MessageBody) GetCapabilityDonId() string { - if x != nil { - return x.CapabilityDonId - } - return "" -} - -func (x *MessageBody) GetCallerDonId() string { - if x != nil { - return x.CallerDonId - } - return "" -} - func (x *MessageBody) GetMethod() string { if x != nil { return x.Method @@ -295,6 +281,20 @@ func (x *MessageBody) GetTriggerEventMetadata() *TriggerEventMetadata { return nil } +func (x *MessageBody) GetCapabilityDonId() uint32 { + if x != nil { + return x.CapabilityDonId + } + return 0 +} + +func (x *MessageBody) GetCallerDonId() uint32 { + if x != nil { + return x.CallerDonId + } + return 0 +} + type isMessageBody_Metadata interface { isMessageBody_Metadata() } @@ -494,7 +494,7 @@ var file_core_capabilities_remote_types_messages_proto_rawDesc = []byte{ 0x67, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, - 0x62, 0x6f, 0x64, 0x79, 0x22, 0xcd, 0x04, 0x0a, 0x0b, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x62, 0x6f, 0x64, 0x79, 0x22, 0xd9, 0x04, 0x0a, 0x0b, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x6f, 0x64, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, @@ -506,12 +506,7 @@ var file_core_capabilities_remote_types_messages_proto_rawDesc = []byte{ 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, - 0x74, 0x79, 0x49, 0x64, 0x12, 0x2a, 0x0a, 0x11, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, - 0x74, 0x79, 0x5f, 0x64, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0f, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x44, 0x6f, 0x6e, 0x49, 0x64, - 0x12, 0x22, 0x0a, 0x0d, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x5f, 0x64, 0x6f, 0x6e, 0x5f, 0x69, - 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x44, - 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x09, + 0x74, 0x79, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x23, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0d, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, @@ -530,44 +525,50 @@ var file_core_capabilities_remote_types_messages_proto_rawDesc = []byte{ 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2e, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x48, 0x00, 0x52, 0x14, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x42, 0x0a, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, - 0x64, 0x61, 0x74, 0x61, 0x22, 0x52, 0x0a, 0x1b, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x52, - 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x61, 0x64, - 0x61, 0x74, 0x61, 0x12, 0x33, 0x0a, 0x16, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x72, 0x65, 0x63, 0x65, - 0x69, 0x76, 0x65, 0x64, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x13, 0x6c, 0x61, 0x73, 0x74, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, - 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x22, 0x63, 0x0a, 0x14, 0x54, 0x72, 0x69, 0x67, - 0x67, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x12, 0x28, 0x0a, 0x10, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x5f, 0x65, 0x76, 0x65, 0x6e, - 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x74, 0x72, 0x69, 0x67, - 0x67, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x77, 0x6f, - 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x49, 0x64, 0x73, 0x22, 0xe3, 0x01, - 0x0a, 0x13, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x34, 0x0a, 0x15, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x4d, 0x73, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x15, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x4d, 0x73, 0x12, 0x32, 0x0a, 0x14, 0x72, - 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x70, 0x69, 0x72, - 0x79, 0x4d, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x14, 0x72, 0x65, 0x67, 0x69, 0x73, - 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x70, 0x69, 0x72, 0x79, 0x4d, 0x73, 0x12, - 0x38, 0x0a, 0x17, 0x6d, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x54, - 0x6f, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x17, 0x6d, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x54, 0x6f, - 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x6d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x45, 0x78, 0x70, 0x69, 0x72, 0x79, 0x4d, 0x73, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x0f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x45, 0x78, 0x70, 0x69, 0x72, - 0x79, 0x4d, 0x73, 0x2a, 0x76, 0x0a, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x06, 0x0a, 0x02, - 0x4f, 0x4b, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x41, 0x54, 0x49, - 0x4f, 0x4e, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x01, 0x12, 0x18, 0x0a, 0x14, 0x43, - 0x41, 0x50, 0x41, 0x42, 0x49, 0x4c, 0x49, 0x54, 0x59, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x46, 0x4f, - 0x55, 0x4e, 0x44, 0x10, 0x02, 0x12, 0x13, 0x0a, 0x0f, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, - 0x5f, 0x52, 0x45, 0x51, 0x55, 0x45, 0x53, 0x54, 0x10, 0x03, 0x12, 0x0b, 0x0a, 0x07, 0x54, 0x49, - 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x04, 0x12, 0x12, 0x0a, 0x0e, 0x49, 0x4e, 0x54, 0x45, 0x52, - 0x4e, 0x41, 0x4c, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x05, 0x42, 0x20, 0x5a, 0x1e, 0x63, - 0x6f, 0x72, 0x65, 0x2f, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, - 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x2a, 0x0a, 0x11, 0x63, 0x61, 0x70, 0x61, + 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x5f, 0x64, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x0f, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x44, + 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x22, 0x0a, 0x0d, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x5f, 0x64, + 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x63, 0x61, 0x6c, + 0x6c, 0x65, 0x72, 0x44, 0x6f, 0x6e, 0x49, 0x64, 0x42, 0x0a, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, + 0x64, 0x61, 0x74, 0x61, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08, 0x4a, 0x04, 0x08, 0x08, 0x10, 0x09, + 0x22, 0x52, 0x0a, 0x1b, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, + 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, + 0x33, 0x0a, 0x16, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, + 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x13, 0x6c, 0x61, 0x73, 0x74, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x45, 0x76, 0x65, + 0x6e, 0x74, 0x49, 0x64, 0x22, 0x63, 0x0a, 0x14, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x28, 0x0a, 0x10, + 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, + 0x6f, 0x77, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x77, 0x6f, + 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x49, 0x64, 0x73, 0x22, 0xe3, 0x01, 0x0a, 0x13, 0x52, 0x65, + 0x6d, 0x6f, 0x74, 0x65, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x12, 0x34, 0x0a, 0x15, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x4d, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x15, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, + 0x66, 0x72, 0x65, 0x73, 0x68, 0x4d, 0x73, 0x12, 0x32, 0x0a, 0x14, 0x72, 0x65, 0x67, 0x69, 0x73, + 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x70, 0x69, 0x72, 0x79, 0x4d, 0x73, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x14, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x70, 0x69, 0x72, 0x79, 0x4d, 0x73, 0x12, 0x38, 0x0a, 0x17, 0x6d, + 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x54, 0x6f, 0x41, 0x67, 0x67, + 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x17, 0x6d, 0x69, + 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x54, 0x6f, 0x41, 0x67, 0x67, 0x72, + 0x65, 0x67, 0x61, 0x74, 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x45, 0x78, 0x70, 0x69, 0x72, 0x79, 0x4d, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x45, 0x78, 0x70, 0x69, 0x72, 0x79, 0x4d, 0x73, 0x2a, + 0x76, 0x0a, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x4b, 0x10, 0x00, + 0x12, 0x15, 0x0a, 0x11, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x46, + 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x01, 0x12, 0x18, 0x0a, 0x14, 0x43, 0x41, 0x50, 0x41, 0x42, + 0x49, 0x4c, 0x49, 0x54, 0x59, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x46, 0x4f, 0x55, 0x4e, 0x44, 0x10, + 0x02, 0x12, 0x13, 0x0a, 0x0f, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x52, 0x45, 0x51, + 0x55, 0x45, 0x53, 0x54, 0x10, 0x03, 0x12, 0x0b, 0x0a, 0x07, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, + 0x54, 0x10, 0x04, 0x12, 0x12, 0x0a, 0x0e, 0x49, 0x4e, 0x54, 0x45, 0x52, 0x4e, 0x41, 0x4c, 0x5f, + 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x05, 0x42, 0x20, 0x5a, 0x1e, 0x63, 0x6f, 0x72, 0x65, 0x2f, + 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x2f, 0x72, 0x65, 0x6d, + 0x6f, 0x74, 0x65, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( diff --git a/core/capabilities/remote/types/messages.proto b/core/capabilities/remote/types/messages.proto index a576e0e5fa..4855892f9f 100644 --- a/core/capabilities/remote/types/messages.proto +++ b/core/capabilities/remote/types/messages.proto @@ -19,14 +19,13 @@ message Message { } message MessageBody { + reserved 7, 8; uint32 version = 1; bytes sender = 2; bytes receiver = 3; int64 timestamp = 4; bytes message_id = 5; // scoped to sender string capability_id = 6; - string capability_don_id = 7; - string caller_don_id = 8; string method = 9; Error error = 10; string errorMsg = 11; @@ -38,6 +37,8 @@ message MessageBody { TriggerEventMetadata trigger_event_metadata = 14; } + uint32 capability_don_id = 15; + uint32 caller_don_id = 16; } message TriggerRegistrationMetadata { diff --git a/core/capabilities/remote/types/mocks/dispatcher.go b/core/capabilities/remote/types/mocks/dispatcher.go index 9ef7cee78d..35905dbd0a 100644 --- a/core/capabilities/remote/types/mocks/dispatcher.go +++ b/core/capabilities/remote/types/mocks/dispatcher.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks @@ -14,7 +14,7 @@ type Dispatcher struct { } // RemoveReceiver provides a mock function with given fields: capabilityId, donId -func (_m *Dispatcher) RemoveReceiver(capabilityId string, donId string) { +func (_m *Dispatcher) RemoveReceiver(capabilityId string, donId uint32) { _m.Called(capabilityId, donId) } @@ -37,7 +37,7 @@ func (_m *Dispatcher) Send(peerID ragep2ptypes.PeerID, msgBody *types.MessageBod } // SetReceiver provides a mock function with given fields: capabilityId, donId, receiver -func (_m *Dispatcher) SetReceiver(capabilityId string, donId string, receiver types.Receiver) error { +func (_m *Dispatcher) SetReceiver(capabilityId string, donId uint32, receiver types.Receiver) error { ret := _m.Called(capabilityId, donId, receiver) if len(ret) == 0 { @@ -45,7 +45,7 @@ func (_m *Dispatcher) SetReceiver(capabilityId string, donId string, receiver ty } var r0 error - if rf, ok := ret.Get(0).(func(string, string, types.Receiver) error); ok { + if rf, ok := ret.Get(0).(func(string, uint32, types.Receiver) error); ok { r0 = rf(capabilityId, donId, receiver) } else { r0 = ret.Error(0) diff --git a/core/capabilities/remote/types/mocks/receiver.go b/core/capabilities/remote/types/mocks/receiver.go index b232912353..701ab0f011 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/capabilities/remote/types/types.go b/core/capabilities/remote/types/types.go index 9c9cf67aa1..1f2527b622 100644 --- a/core/capabilities/remote/types/types.go +++ b/core/capabilities/remote/types/types.go @@ -20,8 +20,8 @@ const ( //go:generate mockery --quiet --name Dispatcher --output ./mocks/ --case=underscore type Dispatcher interface { - SetReceiver(capabilityId string, donId string, receiver Receiver) error - RemoveReceiver(capabilityId string, donId string) + SetReceiver(capabilityId string, donId uint32, receiver Receiver) error + RemoveReceiver(capabilityId string, donId uint32) Send(peerID p2ptypes.PeerID, msgBody *MessageBody) error } diff --git a/core/capabilities/remote/utils_test.go b/core/capabilities/remote/utils_test.go index b5f97af99e..8bebf71fb6 100644 --- a/core/capabilities/remote/utils_test.go +++ b/core/capabilities/remote/utils_test.go @@ -22,7 +22,7 @@ import ( const ( capId1 = "cap1" capId2 = "cap2" - donId1 = "donA" + donId1 = uint32(1) payload1 = "hello world" payload2 = "goodbye world" ) @@ -58,7 +58,7 @@ func newKeyPair(t *testing.T) (ed25519.PrivateKey, ragetypes.PeerID) { return privKey, peerID } -func encodeAndSign(t *testing.T, senderPrivKey ed25519.PrivateKey, senderId p2ptypes.PeerID, receiverId p2ptypes.PeerID, capabilityId string, donId string, payload []byte) p2ptypes.Message { +func encodeAndSign(t *testing.T, senderPrivKey ed25519.PrivateKey, senderId p2ptypes.PeerID, receiverId p2ptypes.PeerID, capabilityId string, donId uint32, payload []byte) p2ptypes.Message { body := remotetypes.MessageBody{ Sender: senderId[:], Receiver: receiverId[:], @@ -89,7 +89,7 @@ func TestToPeerID(t *testing.T) { } func TestDefaultModeAggregator_Aggregate(t *testing.T) { - val, err := values.Wrap(triggerEvent1) + val, err := values.NewMap(triggerEvent1) require.NoError(t, err) capResponse1 := commoncap.CapabilityResponse{ Value: val, @@ -98,7 +98,7 @@ func TestDefaultModeAggregator_Aggregate(t *testing.T) { marshaled1, err := pb.MarshalCapabilityResponse(capResponse1) require.NoError(t, err) - val2, err := values.Wrap(triggerEvent2) + val2, err := values.NewMap(triggerEvent2) require.NoError(t, err) capResponse2 := commoncap.CapabilityResponse{ Value: val2, diff --git a/core/capabilities/streams/codec.go b/core/capabilities/streams/codec.go index 21784cdcf4..d2bc451a39 100644 --- a/core/capabilities/streams/codec.go +++ b/core/capabilities/streams/codec.go @@ -18,36 +18,12 @@ type codec struct { var _ datastreams.ReportCodec = &codec{} -func (c *codec) UnwrapValid(wrapped values.Value, allowedSigners [][]byte, minRequiredSignatures int) ([]datastreams.FeedReport, error) { - signersMap := make(map[common.Address]struct{}) - for _, signer := range allowedSigners { - signersMap[common.BytesToAddress(signer)] = struct{}{} - } - dest := []datastreams.FeedReport{} - err := wrapped.UnwrapTo(&dest) +func (c *codec) Unwrap(wrapped values.Value) ([]datastreams.FeedReport, error) { + dest, err := datastreams.UnwrapFeedReportList(wrapped) if err != nil { return nil, fmt.Errorf("failed to unwrap: %v", err) } for i, report := range dest { - // signatures (report and context are signed together) - sigData := append(crypto.Keccak256(report.FullReport), report.ReportContext...) - fullHash := crypto.Keccak256(sigData) - validated := map[common.Address]struct{}{} - for _, sig := range report.Signatures { - signerPubkey, err2 := crypto.SigToPub(fullHash, sig) - if err2 != nil { - return nil, fmt.Errorf("malformed signer: %v", err2) - } - signerAddr := crypto.PubkeyToAddress(*signerPubkey) - if _, ok := signersMap[signerAddr]; !ok { - c.lggr.Debugw("invalid signer", "signerAddr", signerAddr) - continue - } - validated[signerAddr] = struct{}{} - } - if len(validated) < minRequiredSignatures { - return nil, fmt.Errorf("not enough valid signatures %d, needed %d", len(validated), minRequiredSignatures) - } // decoding fields id, err2 := datastreams.NewFeedID(report.FeedID) if err2 != nil { @@ -68,6 +44,36 @@ func (c *codec) Wrap(reports []datastreams.FeedReport) (values.Value, error) { return values.Wrap(reports) } +func (c *codec) Validate(report datastreams.FeedReport, allowedSigners [][]byte, minRequiredSignatures int) error { + signersMap := make(map[common.Address]struct{}) + for _, signer := range allowedSigners { + signersMap[common.BytesToAddress(signer)] = struct{}{} + } + // signatures (report and context are signed together) + sigData := append(crypto.Keccak256(report.FullReport), report.ReportContext...) + fullHash := crypto.Keccak256(sigData) + validated := map[common.Address]struct{}{} + for _, sig := range report.Signatures { + signerPubkey, err2 := crypto.SigToPub(fullHash, sig) + if err2 != nil { + return fmt.Errorf("malformed signer: %v", err2) + } + signerAddr := crypto.PubkeyToAddress(*signerPubkey) + if _, ok := signersMap[signerAddr]; !ok { + c.lggr.Debugw("invalid signer", "signerAddr", signerAddr) + continue + } + validated[signerAddr] = struct{}{} + if len(validated) >= minRequiredSignatures { + break // early exit + } + } + if len(validated) < minRequiredSignatures { + return fmt.Errorf("not enough valid signatures %d, needed %d", len(validated), minRequiredSignatures) + } + return nil +} + func NewCodec(lggr logger.Logger) *codec { return &codec{lggr: lggr} } diff --git a/core/capabilities/streams/codec_test.go b/core/capabilities/streams/codec_test.go index 22100b0af5..e3ada731e4 100644 --- a/core/capabilities/streams/codec_test.go +++ b/core/capabilities/streams/codec_test.go @@ -66,21 +66,25 @@ func TestCodec_WrapUnwrap(t *testing.T) { require.NoError(t, err) // wrong type - _, err = codec.UnwrapValid(values.NewBool(true), nil, 0) + _, err = codec.Unwrap(values.NewBool(true)) require.Error(t, err) - // wrong signatures - _, err = codec.UnwrapValid(wrapped, nil, 1) - require.Error(t, err) - - // success - reports, err := codec.UnwrapValid(wrapped, allowedSigners, 2) + // correct reports byt wrong signatures + unwrapped, err := codec.Unwrap(wrapped) require.NoError(t, err) - require.Equal(t, 2, len(reports)) - require.Equal(t, price1.Bytes(), reports[0].BenchmarkPrice) - require.Equal(t, price2.Bytes(), reports[1].BenchmarkPrice) - require.Equal(t, timestamp1, reports[0].ObservationTimestamp) - require.Equal(t, timestamp2, reports[1].ObservationTimestamp) + require.Equal(t, 2, len(unwrapped)) + require.Equal(t, price1.Bytes(), unwrapped[0].BenchmarkPrice) + require.Equal(t, price2.Bytes(), unwrapped[1].BenchmarkPrice) + require.Equal(t, timestamp1, unwrapped[0].ObservationTimestamp) + require.Equal(t, timestamp2, unwrapped[1].ObservationTimestamp) + for _, report := range unwrapped { + require.Error(t, codec.Validate(report, nil, 1)) + } + + // valid signatures + for _, report := range unwrapped { + require.NoError(t, codec.Validate(report, allowedSigners, 2)) + } } func newFeedID(t *testing.T) ([32]byte, string) { diff --git a/core/capabilities/streams/consensus_agg_test.go b/core/capabilities/streams/consensus_agg_test.go new file mode 100644 index 0000000000..506ad26f86 --- /dev/null +++ b/core/capabilities/streams/consensus_agg_test.go @@ -0,0 +1,123 @@ +package streams_test + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" + + "github.com/smartcontractkit/libocr/commontypes" + + "github.com/smartcontractkit/chainlink-common/pkg/capabilities" + "github.com/smartcontractkit/chainlink-common/pkg/capabilities/consensus/ocr3/datafeeds" + "github.com/smartcontractkit/chainlink-common/pkg/capabilities/datastreams" + "github.com/smartcontractkit/chainlink-common/pkg/values" + "github.com/smartcontractkit/chainlink/v2/core/capabilities/streams" + "github.com/smartcontractkit/chainlink/v2/core/logger" +) + +// Integration/load test that combines Data Feeds Consensus Aggregator and Streams Codec. +// For more meaningful measurements, increase the values of parameters P and T. +func TestStreamsConsensusAggregator(t *testing.T) { + Nt := 10 // trigger DON nodes + Ft := 3 // trigger DON faulty nodes + Nw := 10 // workflow DON nodes + Fw := 3 // workflow DON faulty nodes + P := 40 // feeds + T := 2 // test iterations + + triggerNodes := newNodes(t, Nt) + feeds := newFeedsWithSignedReports(t, triggerNodes, Nt, P, 1) + allowedSigners := make([][]byte, Nt) + for i := 0; i < Nt; i++ { + allowedSigners[i] = triggerNodes[i].bundle.PublicKey() // bad name - see comment on evmKeyring.PublicKey + } + + config := newAggConfig(t, feeds) + lggr := logger.TestLogger(t) + codec := streams.NewCodec(lggr) + agg, err := datafeeds.NewDataFeedsAggregator(*config, codec, lggr) + require.NoError(t, err) + + // init round - empty previous Outcome, empty observations + outcome, err := agg.Aggregate(nil, map[commontypes.OracleID][]values.Value{}, Fw) + require.NoError(t, err) + require.False(t, outcome.ShouldReport) + + // validate metadata + newState := &datafeeds.DataFeedsOutcomeMetadata{} + err = proto.Unmarshal(outcome.Metadata, newState) + require.NoError(t, err) + require.Equal(t, P, len(newState.FeedInfo)) + + // run test aggregations + startTs := time.Now().UnixMilli() + processingTime := int64(0) + for c := 0; c < T; c++ { + obs := newObservations(t, Nw, feeds, Ft+1, allowedSigners) + processingStart := time.Now().UnixMilli() + outcome, err = agg.Aggregate(outcome, obs, Fw) + processingTime += time.Now().UnixMilli() - processingStart + require.NoError(t, err) + } + totalTime := time.Now().UnixMilli() - startTs + lggr.Infow("elapsed", "totalMs", totalTime, "processingMs", processingTime) +} + +func newAggConfig(t *testing.T, feeds []feed) *values.Map { + feedMap := map[string]any{} + for _, feed := range feeds { + feedMap[feed.feedIDStr] = map[string]any{ + "deviation": "0.1", + "heartbeat": 1, + "remappedID": feed.feedIDStr, + } + } + unwrappedConfig := map[string]any{ + "feeds": feedMap, + "allowedPartialStaleness": "0.2", + } + config, err := values.NewMap(unwrappedConfig) + require.NoError(t, err) + return config +} + +func newObservations(t *testing.T, nNodes int, feeds []feed, minRequiredSignatures int, allowedSigners [][]byte) map[commontypes.OracleID][]values.Value { + observations := map[commontypes.OracleID][]values.Value{} + for i := 0; i < nNodes; i++ { + reportList := []datastreams.FeedReport{} + for j := 0; j < len(feeds); j++ { + reportIdx := 0 + signedStreamsReport := datastreams.FeedReport{ + FeedID: feeds[j].feedIDStr, + FullReport: feeds[j].reports[reportIdx].rawReport, + ReportContext: feeds[j].reports[reportIdx].reportCtx, + Signatures: feeds[j].reports[reportIdx].signatures, + } + reportList = append(reportList, signedStreamsReport) + } + + payloadVal, err := values.Wrap(reportList) + require.NoError(t, err) + + meta := datastreams.SignersMetadata{ + Signers: allowedSigners, + MinRequiredSignatures: minRequiredSignatures, + } + metaVal, err := values.Wrap(meta) + require.NoError(t, err) + + triggerEvent := capabilities.TriggerEvent{ + TriggerType: triggerID, + ID: "unused", + Timestamp: "1234", + Metadata: metaVal, + Payload: payloadVal, + } + wrappedEvent, err := values.Wrap(triggerEvent) + require.NoError(t, err) + observations[commontypes.OracleID(i)] = []values.Value{wrappedEvent} + } + return observations +} diff --git a/core/capabilities/streams/trigger_test.go b/core/capabilities/streams/trigger_test.go index b8106e9212..6d8c9a27c1 100644 --- a/core/capabilities/streams/trigger_test.go +++ b/core/capabilities/streams/trigger_test.go @@ -191,10 +191,13 @@ func newTriggerEvent(t *testing.T, reportList []datastreams.FeedReport, triggerE Payload: val, } - eventVal, err := values.Wrap(triggerEvent) + eventVal, err := values.WrapMap(triggerEvent) require.NoError(t, err) - marshaled, err := pb.MarshalCapabilityResponse(capabilities.CapabilityResponse{Value: eventVal}) + marshaled, err := pb.MarshalCapabilityResponse( + capabilities.CapabilityResponse{ + Value: eventVal, + }) require.NoError(t, err) msg := &remotetypes.MessageBody{ Sender: sender[:], diff --git a/core/capabilities/targets/mocks/chain_reader.go b/core/capabilities/targets/mocks/chain_reader.go index 14748c16bb..6ad40c7159 100644 --- a/core/capabilities/targets/mocks/chain_reader.go +++ b/core/capabilities/targets/mocks/chain_reader.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/capabilities/targets/mocks/chain_writer.go b/core/capabilities/targets/mocks/chain_writer.go index 8fe9a0585f..ef3613305b 100644 --- a/core/capabilities/targets/mocks/chain_writer.go +++ b/core/capabilities/targets/mocks/chain_writer.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/capabilities/transmission/local_target_capability.go b/core/capabilities/transmission/local_target_capability.go index c040e6ed43..1240d3a0e7 100644 --- a/core/capabilities/transmission/local_target_capability.go +++ b/core/capabilities/transmission/local_target_capability.go @@ -14,19 +14,21 @@ import ( type LocalTargetCapability struct { lggr logger.Logger capabilities.TargetCapability - localNode capabilities.Node + localNode capabilities.Node + capabilityID string } -func NewLocalTargetCapability(lggr logger.Logger, localDON capabilities.Node, underlying capabilities.TargetCapability) *LocalTargetCapability { +func NewLocalTargetCapability(lggr logger.Logger, capabilityID string, localDON capabilities.Node, underlying capabilities.TargetCapability) *LocalTargetCapability { return &LocalTargetCapability{ TargetCapability: underlying, + capabilityID: capabilityID, lggr: lggr, localNode: localDON, } } func (l *LocalTargetCapability) Execute(ctx context.Context, req capabilities.CapabilityRequest) (<-chan capabilities.CapabilityResponse, error) { - if l.localNode.PeerID == nil || l.localNode.WorkflowDON.ID == "" { + if l.localNode.PeerID == nil || l.localNode.WorkflowDON.ID == 0 { l.lggr.Debugf("empty DON info, executing immediately") return l.TargetCapability.Execute(ctx, req) } @@ -38,7 +40,7 @@ func (l *LocalTargetCapability) Execute(ctx context.Context, req capabilities.Ca peerIDToTransmissionDelay, err := GetPeerIDToTransmissionDelay(l.localNode.WorkflowDON.Members, req) if err != nil { - return nil, fmt.Errorf("failed to get peer ID to transmission delay map: %w", err) + return nil, fmt.Errorf("capability id: %s failed to get peer ID to transmission delay map: %w", l.capabilityID, err) } delay, existsForPeerID := peerIDToTransmissionDelay[*l.localNode.PeerID] diff --git a/core/capabilities/transmission/local_target_capability_test.go b/core/capabilities/transmission/local_target_capability_test.go index ded4b5c30f..93bf708cce 100644 --- a/core/capabilities/transmission/local_target_capability_test.go +++ b/core/capabilities/transmission/local_target_capability_test.go @@ -134,12 +134,12 @@ func TestScheduledExecutionStrategy_LocalDON(t *testing.T) { } localDON := capabilities.Node{ WorkflowDON: capabilities.DON{ - ID: "1", + ID: 1, Members: ids, }, PeerID: &ids[tc.position], } - localTargetCapability := NewLocalTargetCapability(log, localDON, mt) + localTargetCapability := NewLocalTargetCapability(log, "capabilityID", localDON, mt) _, err = localTargetCapability.Execute(tests.Context(t), req) diff --git a/core/chains/evm/client/chain_client.go b/core/chains/evm/client/chain_client.go index 8d1dcb6cc8..f18c900b03 100644 --- a/core/chains/evm/client/chain_client.go +++ b/core/chains/evm/client/chain_client.go @@ -60,6 +60,9 @@ type Client interface { HeadByNumber(ctx context.Context, n *big.Int) (*evmtypes.Head, error) HeadByHash(ctx context.Context, n common.Hash) (*evmtypes.Head, error) SubscribeNewHead(ctx context.Context, ch chan<- *evmtypes.Head) (ethereum.Subscription, error) + // LatestFinalizedBlock - returns the latest finalized block as it's returned from an RPC. + // CAUTION: Using this method might cause local finality violations. It's highly recommended + // to use HeadTracker to get latest finalized block. LatestFinalizedBlock(ctx context.Context) (head *evmtypes.Head, err error) SendTransactionReturnCode(ctx context.Context, tx *types.Transaction, fromAddress common.Address) (commonclient.SendTxReturnCode, error) @@ -132,6 +135,7 @@ func NewChainClient( chainID *big.Int, chainType chaintype.ChainType, clientErrors evmconfig.ClientErrors, + deathDeclarationDelay time.Duration, ) Client { multiNode := commonclient.NewMultiNode( lggr, @@ -146,6 +150,7 @@ func NewChainClient( return ClassifySendError(err, clientErrors, logger.Sugared(logger.Nop()), tx, common.Address{}, chainType.IsL2()) }, 0, // use the default value provided by the implementation + deathDeclarationDelay, ) return &chainClient{ multiNode: multiNode, @@ -306,12 +311,7 @@ func (c *chainClient) SubscribeFilterLogs(ctx context.Context, q ethereum.Filter } func (c *chainClient) SubscribeNewHead(ctx context.Context, ch chan<- *evmtypes.Head) (ethereum.Subscription, error) { - csf := newChainIDSubForwarder(c.ConfiguredChainID(), ch) - err := csf.start(c.multiNode.Subscribe(ctx, csf.srcCh, "newHeads")) - if err != nil { - return nil, err - } - return csf, nil + return c.multiNode.SubscribeNewHead(ctx, ch) } func (c *chainClient) SuggestGasPrice(ctx context.Context) (p *big.Int, err error) { diff --git a/core/chains/evm/client/chain_client_test.go b/core/chains/evm/client/chain_client_test.go index f18ec53967..33955c1645 100644 --- a/core/chains/evm/client/chain_client_test.go +++ b/core/chains/evm/client/chain_client_test.go @@ -751,7 +751,7 @@ func newMockRpc(t *testing.T) *mocks.RPCClient { mockRpc.On("Close").Return(nil).Once() mockRpc.On("ChainID", mock.Anything).Return(testutils.FixtureChainID, nil).Once() // node does not always manage to fully setup aliveLoop, so we have to make calls optional to avoid flakes - mockRpc.On("Subscribe", mock.Anything, mock.Anything, mock.Anything).Return(client.NewMockSubscription(), nil).Maybe() + mockRpc.On("SubscribeNewHead", mock.Anything, mock.Anything).Return(client.NewMockSubscription(), nil).Maybe() mockRpc.On("SetAliveLoopSub", mock.Anything).Return().Maybe() return mockRpc } @@ -777,6 +777,7 @@ func TestChainClient_BatchCallContext(t *testing.T) { } mockRpc := newMockRpc(t) + mockRpc.On("GetInterceptedChainInfo").Return(commonclient.ChainInfo{}, commonclient.ChainInfo{}).Maybe() mockRpc.On("BatchCallContext", mock.Anything, b).Run(func(args mock.Arguments) { reqs := args.Get(1).([]rpc.BatchElem) for i := 0; i < len(reqs); i++ { diff --git a/core/chains/evm/client/chain_id_sub_test.go b/core/chains/evm/client/chain_id_sub_test.go deleted file mode 100644 index f959376acc..0000000000 --- a/core/chains/evm/client/chain_id_sub_test.go +++ /dev/null @@ -1,105 +0,0 @@ -package client - -import ( - "errors" - "math/big" - "testing" - - "github.com/stretchr/testify/assert" - - evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" -) - -func TestChainIDSubForwarder(t *testing.T) { - t.Parallel() - - chainID := big.NewInt(123) - - t.Run("unsubscribe forwarder", func(t *testing.T) { - t.Parallel() - - ch := make(chan *evmtypes.Head) - forwarder := newChainIDSubForwarder(chainID, ch) - sub := NewMockSubscription() - err := forwarder.start(sub, nil) - assert.NoError(t, err) - forwarder.Unsubscribe() - - assert.True(t, sub.unsubscribed) - _, ok := <-sub.Err() - assert.False(t, ok) - _, ok = <-forwarder.Err() - assert.False(t, ok) - }) - - t.Run("unsubscribe forwarder with error", func(t *testing.T) { - t.Parallel() - - ch := make(chan *evmtypes.Head) - forwarder := newChainIDSubForwarder(chainID, ch) - sub := NewMockSubscription() - err := forwarder.start(sub, nil) - assert.NoError(t, err) - sub.Errors <- errors.New("boo") - forwarder.Unsubscribe() - - assert.True(t, sub.unsubscribed) - _, ok := <-sub.Err() - assert.False(t, ok) - _, ok = <-forwarder.Err() - assert.False(t, ok) - }) - - t.Run("unsubscribe forwarder with message", func(t *testing.T) { - t.Parallel() - - ch := make(chan *evmtypes.Head) - forwarder := newChainIDSubForwarder(chainID, ch) - sub := NewMockSubscription() - err := forwarder.start(sub, nil) - assert.NoError(t, err) - forwarder.srcCh <- &evmtypes.Head{} - forwarder.Unsubscribe() - - assert.True(t, sub.unsubscribed) - _, ok := <-sub.Err() - assert.False(t, ok) - _, ok = <-forwarder.Err() - assert.False(t, ok) - }) - - t.Run("non nil error parameter", func(t *testing.T) { - t.Parallel() - - ch := make(chan *evmtypes.Head) - forwarder := newChainIDSubForwarder(chainID, ch) - sub := NewMockSubscription() - errIn := errors.New("foo") - errOut := forwarder.start(sub, errIn) - assert.Equal(t, errIn, errOut) - }) - - t.Run("forwarding", func(t *testing.T) { - t.Parallel() - - ch := make(chan *evmtypes.Head) - forwarder := newChainIDSubForwarder(chainID, ch) - sub := NewMockSubscription() - err := forwarder.start(sub, nil) - assert.NoError(t, err) - - head := &evmtypes.Head{ - ID: 1, - } - forwarder.srcCh <- head - receivedHead := <-ch - assert.Equal(t, head, receivedHead) - assert.Equal(t, ubig.New(chainID), receivedHead.EVMChainID) - - expectedErr := errors.New("error") - sub.Errors <- expectedErr - receivedErr := <-forwarder.Err() - assert.Equal(t, expectedErr, receivedErr) - }) -} diff --git a/core/chains/evm/client/config_builder.go b/core/chains/evm/client/config_builder.go index ae41d40dd3..19e0f14fd6 100644 --- a/core/chains/evm/client/config_builder.go +++ b/core/chains/evm/client/config_builder.go @@ -38,6 +38,9 @@ func NewClientConfigs( noNewHeadsThreshold time.Duration, finalityDepth *uint32, finalityTagEnabled *bool, + finalizedBlockOffset *uint32, + enforceRepeatableRead *bool, + deathDeclarationDelay time.Duration, ) (commonclient.ChainConfig, evmconfig.NodePool, []*toml.Node, error) { nodes, err := parseNodeConfigs(nodeCfgs) @@ -45,21 +48,24 @@ func NewClientConfigs( return nil, nil, nil, err } nodePool := toml.NodePool{ - SelectionMode: selectionMode, - LeaseDuration: commonconfig.MustNewDuration(leaseDuration), - PollFailureThreshold: pollFailureThreshold, - PollInterval: commonconfig.MustNewDuration(pollInterval), - SyncThreshold: syncThreshold, - NodeIsSyncingEnabled: nodeIsSyncingEnabled, + SelectionMode: selectionMode, + LeaseDuration: commonconfig.MustNewDuration(leaseDuration), + PollFailureThreshold: pollFailureThreshold, + PollInterval: commonconfig.MustNewDuration(pollInterval), + SyncThreshold: syncThreshold, + NodeIsSyncingEnabled: nodeIsSyncingEnabled, + EnforceRepeatableRead: enforceRepeatableRead, + DeathDeclarationDelay: commonconfig.MustNewDuration(deathDeclarationDelay), } nodePoolCfg := &evmconfig.NodePoolConfig{C: nodePool} chainConfig := &evmconfig.EVMConfig{ C: &toml.EVMConfig{ Chain: toml.Chain{ - ChainType: chaintype.NewChainTypeConfig(chainType), - FinalityDepth: finalityDepth, - FinalityTagEnabled: finalityTagEnabled, - NoNewHeadsThreshold: commonconfig.MustNewDuration(noNewHeadsThreshold), + ChainType: chaintype.NewChainTypeConfig(chainType), + FinalityDepth: finalityDepth, + FinalityTagEnabled: finalityTagEnabled, + NoNewHeadsThreshold: commonconfig.MustNewDuration(noNewHeadsThreshold), + FinalizedBlockOffset: finalizedBlockOffset, }, }, } diff --git a/core/chains/evm/client/config_builder_test.go b/core/chains/evm/client/config_builder_test.go index 0e24161b27..7c08bf18c1 100644 --- a/core/chains/evm/client/config_builder_test.go +++ b/core/chains/evm/client/config_builder_test.go @@ -23,6 +23,9 @@ func TestClientConfigBuilder(t *testing.T) { syncThreshold := ptr(uint32(5)) nodeIsSyncingEnabled := ptr(false) chainTypeStr := "" + finalizedBlockOffset := ptr[uint32](16) + enforceRepeatableRead := ptr(true) + deathDeclarationDelay := time.Second * 3 nodeConfigs := []client.NodeConfig{ { Name: ptr("foo"), @@ -34,7 +37,8 @@ func TestClientConfigBuilder(t *testing.T) { finalityTagEnabled := ptr(true) noNewHeadsThreshold := time.Second chainCfg, nodePool, nodes, err := client.NewClientConfigs(selectionMode, leaseDuration, chainTypeStr, nodeConfigs, - pollFailureThreshold, pollInterval, syncThreshold, nodeIsSyncingEnabled, noNewHeadsThreshold, finalityDepth, finalityTagEnabled) + pollFailureThreshold, pollInterval, syncThreshold, nodeIsSyncingEnabled, noNewHeadsThreshold, finalityDepth, + finalityTagEnabled, finalizedBlockOffset, enforceRepeatableRead, deathDeclarationDelay) require.NoError(t, err) // Validate node pool configs @@ -44,6 +48,8 @@ func TestClientConfigBuilder(t *testing.T) { require.Equal(t, pollInterval, nodePool.PollInterval()) require.Equal(t, *syncThreshold, nodePool.SyncThreshold()) require.Equal(t, *nodeIsSyncingEnabled, nodePool.NodeIsSyncingEnabled()) + require.Equal(t, *enforceRepeatableRead, nodePool.EnforceRepeatableRead()) + require.Equal(t, deathDeclarationDelay, nodePool.DeathDeclarationDelay()) // Validate node configs require.Equal(t, *nodeConfigs[0].Name, *nodes[0].Name) @@ -54,6 +60,7 @@ func TestClientConfigBuilder(t *testing.T) { require.Equal(t, noNewHeadsThreshold, chainCfg.NodeNoNewHeadsThreshold()) require.Equal(t, *finalityDepth, chainCfg.FinalityDepth()) require.Equal(t, *finalityTagEnabled, chainCfg.FinalityTagEnabled()) + require.Equal(t, *finalizedBlockOffset, chainCfg.FinalizedBlockOffset()) // let combiler tell us, when we do not have sufficient data to create evm client _ = client.NewEvmClient(nodePool, chainCfg, nil, logger.Test(t), big.NewInt(10), nodes, chaintype.ChainType(chainTypeStr)) diff --git a/core/chains/evm/client/evm_client.go b/core/chains/evm/client/evm_client.go index 4d30944059..fd7fa5868a 100644 --- a/core/chains/evm/client/evm_client.go +++ b/core/chains/evm/client/evm_client.go @@ -35,5 +35,5 @@ func NewEvmClient(cfg evmconfig.NodePool, chainCfg commonclient.ChainConfig, cli } return NewChainClient(lggr, cfg.SelectionMode(), cfg.LeaseDuration(), chainCfg.NodeNoNewHeadsThreshold(), - primaries, sendonlys, chainID, chainType, clientErrors) + primaries, sendonlys, chainID, chainType, clientErrors, cfg.DeathDeclarationDelay()) } diff --git a/core/chains/evm/client/evm_client_test.go b/core/chains/evm/client/evm_client_test.go index 29113d4c3c..9ad25f9602 100644 --- a/core/chains/evm/client/evm_client_test.go +++ b/core/chains/evm/client/evm_client_test.go @@ -24,6 +24,9 @@ func TestNewEvmClient(t *testing.T) { syncThreshold := ptr(uint32(5)) nodeIsSyncingEnabled := ptr(false) chainTypeStr := "" + finalizedBlockOffset := ptr[uint32](16) + enforceRepeatableRead := ptr(true) + deathDeclarationDelay := time.Second * 3 nodeConfigs := []client.NodeConfig{ { Name: ptr("foo"), @@ -34,7 +37,8 @@ func TestNewEvmClient(t *testing.T) { finalityDepth := ptr(uint32(10)) finalityTagEnabled := ptr(true) chainCfg, nodePool, nodes, err := client.NewClientConfigs(selectionMode, leaseDuration, chainTypeStr, nodeConfigs, - pollFailureThreshold, pollInterval, syncThreshold, nodeIsSyncingEnabled, noNewHeadsThreshold, finalityDepth, finalityTagEnabled) + pollFailureThreshold, pollInterval, syncThreshold, nodeIsSyncingEnabled, noNewHeadsThreshold, finalityDepth, + finalityTagEnabled, finalizedBlockOffset, enforceRepeatableRead, deathDeclarationDelay) require.NoError(t, err) client := client.NewEvmClient(nodePool, chainCfg, nil, logger.Test(t), testutils.FixtureChainID, nodes, chaintype.ChainType(chainTypeStr)) diff --git a/core/chains/evm/client/helpers_test.go b/core/chains/evm/client/helpers_test.go index 391d580c1f..e1017a5564 100644 --- a/core/chains/evm/client/helpers_test.go +++ b/core/chains/evm/client/helpers_test.go @@ -87,6 +87,8 @@ type TestNodePoolConfig struct { NodeIsSyncingEnabledVal bool NodeFinalizedBlockPollInterval time.Duration NodeErrors config.ClientErrors + EnforceRepeatableReadVal bool + NodeDeathDeclarationDelay time.Duration } func (tc TestNodePoolConfig) PollFailureThreshold() uint32 { return tc.NodePollFailureThreshold } @@ -109,6 +111,14 @@ func (tc TestNodePoolConfig) Errors() config.ClientErrors { return tc.NodeErrors } +func (tc TestNodePoolConfig) EnforceRepeatableRead() bool { + return tc.EnforceRepeatableReadVal +} + +func (tc TestNodePoolConfig) DeathDeclarationDelay() time.Duration { + return tc.NodeDeathDeclarationDelay +} + func NewChainClientWithTestNode( t *testing.T, nodeCfg commonclient.NodeConfig, @@ -150,7 +160,7 @@ func NewChainClientWithTestNode( var chainType chaintype.ChainType clientErrors := NewTestClientErrors() - c := NewChainClient(lggr, nodeCfg.SelectionMode(), leaseDuration, noNewHeadsThreshold, primaries, sendonlys, chainID, chainType, &clientErrors) + c := NewChainClient(lggr, nodeCfg.SelectionMode(), leaseDuration, noNewHeadsThreshold, primaries, sendonlys, chainID, chainType, &clientErrors, 0) t.Cleanup(c.Close) return c, nil } @@ -165,7 +175,7 @@ func NewChainClientWithEmptyNode( lggr := logger.Test(t) var chainType chaintype.ChainType - c := NewChainClient(lggr, selectionMode, leaseDuration, noNewHeadsThreshold, nil, nil, chainID, chainType, nil) + c := NewChainClient(lggr, selectionMode, leaseDuration, noNewHeadsThreshold, nil, nil, chainID, chainType, nil, 0) t.Cleanup(c.Close) return c } @@ -191,7 +201,7 @@ func NewChainClientWithMockedRpc( cfg, clientMocks.ChainConfig{NoNewHeadsThresholdVal: noNewHeadsThreshold}, lggr, *parsed, nil, "eth-primary-node-0", 1, chainID, 1, rpc, "EVM") primaries := []commonclient.Node[*big.Int, *evmtypes.Head, RPCClient]{n} clientErrors := NewTestClientErrors() - c := NewChainClient(lggr, selectionMode, leaseDuration, noNewHeadsThreshold, primaries, nil, chainID, chainType, &clientErrors) + c := NewChainClient(lggr, selectionMode, leaseDuration, noNewHeadsThreshold, primaries, nil, chainID, chainType, &clientErrors, 0) t.Cleanup(c.Close) return c } diff --git a/core/chains/evm/client/mocks/client.go b/core/chains/evm/client/mocks/client.go index 58d5152662..8a5b29cf4c 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/chains/evm/client/mocks/rpc_client.go b/core/chains/evm/client/mocks/rpc_client.go index 980a215ccf..e6cb07af2f 100644 --- a/core/chains/evm/client/mocks/rpc_client.go +++ b/core/chains/evm/client/mocks/rpc_client.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks @@ -9,6 +9,8 @@ import ( common "github.com/ethereum/go-ethereum/common" + commonclient "github.com/smartcontractkit/chainlink/v2/common/client" + commontypes "github.com/smartcontractkit/chainlink/v2/common/types" context "context" @@ -442,6 +444,34 @@ func (_m *RPCClient) FilterEvents(ctx context.Context, query ethereum.FilterQuer return r0, r1 } +// GetInterceptedChainInfo provides a mock function with given fields: +func (_m *RPCClient) GetInterceptedChainInfo() (commonclient.ChainInfo, commonclient.ChainInfo) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for GetInterceptedChainInfo") + } + + var r0 commonclient.ChainInfo + var r1 commonclient.ChainInfo + if rf, ok := ret.Get(0).(func() (commonclient.ChainInfo, commonclient.ChainInfo)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() commonclient.ChainInfo); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(commonclient.ChainInfo) + } + + if rf, ok := ret.Get(1).(func() commonclient.ChainInfo); ok { + r1 = rf() + } else { + r1 = ret.Get(1).(commonclient.ChainInfo) + } + + return r0, r1 +} + // HeaderByHash provides a mock function with given fields: ctx, h func (_m *RPCClient) HeaderByHash(ctx context.Context, h common.Hash) (*coretypes.Header, error) { ret := _m.Called(ctx, h) @@ -805,32 +835,29 @@ func (_m *RPCClient) SimulateTransaction(ctx context.Context, tx *coretypes.Tran return r0 } -// Subscribe provides a mock function with given fields: ctx, channel, args -func (_m *RPCClient) Subscribe(ctx context.Context, channel chan<- *types.Head, args ...interface{}) (commontypes.Subscription, error) { - var _ca []interface{} - _ca = append(_ca, ctx, channel) - _ca = append(_ca, args...) - ret := _m.Called(_ca...) +// SubscribeFilterLogs provides a mock function with given fields: ctx, q, ch +func (_m *RPCClient) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- coretypes.Log) (ethereum.Subscription, error) { + ret := _m.Called(ctx, q, ch) if len(ret) == 0 { - panic("no return value specified for Subscribe") + panic("no return value specified for SubscribeFilterLogs") } - var r0 commontypes.Subscription + var r0 ethereum.Subscription var r1 error - if rf, ok := ret.Get(0).(func(context.Context, chan<- *types.Head, ...interface{}) (commontypes.Subscription, error)); ok { - return rf(ctx, channel, args...) + if rf, ok := ret.Get(0).(func(context.Context, ethereum.FilterQuery, chan<- coretypes.Log) (ethereum.Subscription, error)); ok { + return rf(ctx, q, ch) } - if rf, ok := ret.Get(0).(func(context.Context, chan<- *types.Head, ...interface{}) commontypes.Subscription); ok { - r0 = rf(ctx, channel, args...) + if rf, ok := ret.Get(0).(func(context.Context, ethereum.FilterQuery, chan<- coretypes.Log) ethereum.Subscription); ok { + r0 = rf(ctx, q, ch) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(commontypes.Subscription) + r0 = ret.Get(0).(ethereum.Subscription) } } - if rf, ok := ret.Get(1).(func(context.Context, chan<- *types.Head, ...interface{}) error); ok { - r1 = rf(ctx, channel, args...) + if rf, ok := ret.Get(1).(func(context.Context, ethereum.FilterQuery, chan<- coretypes.Log) error); ok { + r1 = rf(ctx, q, ch) } else { r1 = ret.Error(1) } @@ -838,29 +865,29 @@ func (_m *RPCClient) Subscribe(ctx context.Context, channel chan<- *types.Head, return r0, r1 } -// SubscribeFilterLogs provides a mock function with given fields: ctx, q, ch -func (_m *RPCClient) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- coretypes.Log) (ethereum.Subscription, error) { - ret := _m.Called(ctx, q, ch) +// SubscribeNewHead provides a mock function with given fields: ctx, channel +func (_m *RPCClient) SubscribeNewHead(ctx context.Context, channel chan<- *types.Head) (commontypes.Subscription, error) { + ret := _m.Called(ctx, channel) if len(ret) == 0 { - panic("no return value specified for SubscribeFilterLogs") + panic("no return value specified for SubscribeNewHead") } - var r0 ethereum.Subscription + var r0 commontypes.Subscription var r1 error - if rf, ok := ret.Get(0).(func(context.Context, ethereum.FilterQuery, chan<- coretypes.Log) (ethereum.Subscription, error)); ok { - return rf(ctx, q, ch) + if rf, ok := ret.Get(0).(func(context.Context, chan<- *types.Head) (commontypes.Subscription, error)); ok { + return rf(ctx, channel) } - if rf, ok := ret.Get(0).(func(context.Context, ethereum.FilterQuery, chan<- coretypes.Log) ethereum.Subscription); ok { - r0 = rf(ctx, q, ch) + if rf, ok := ret.Get(0).(func(context.Context, chan<- *types.Head) commontypes.Subscription); ok { + r0 = rf(ctx, channel) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(ethereum.Subscription) + r0 = ret.Get(0).(commontypes.Subscription) } } - if rf, ok := ret.Get(1).(func(context.Context, ethereum.FilterQuery, chan<- coretypes.Log) error); ok { - r1 = rf(ctx, q, ch) + if rf, ok := ret.Get(1).(func(context.Context, chan<- *types.Head) error); ok { + r1 = rf(ctx, channel) } else { r1 = ret.Error(1) } diff --git a/core/chains/evm/client/rpc_client.go b/core/chains/evm/client/rpc_client.go index 5b64900a0c..6499b18f79 100644 --- a/core/chains/evm/client/rpc_client.go +++ b/core/chains/evm/client/rpc_client.go @@ -101,6 +101,7 @@ type RPCClient interface { SuggestGasPrice(ctx context.Context) (p *big.Int, err error) SuggestGasTipCap(ctx context.Context) (t *big.Int, err error) TransactionReceiptGeth(ctx context.Context, txHash common.Hash) (r *types.Receipt, err error) + GetInterceptedChainInfo() (latest, highestUserObservations commonclient.ChainInfo) } type rawclient struct { @@ -132,6 +133,11 @@ type rpcClient struct { // this rpcClient. Closing and replacing should be serialized through // stateMu since it can happen on state transitions as well as rpcClient Close. chStopInFlight chan struct{} + + // intercepted values seen by callers of the rpcClient excluding health check calls. Need to ensure MultiNode provides repeatable read guarantee + highestUserObservations commonclient.ChainInfo + // most recent chain info observed during current lifecycle (reseted on DisconnectAll) + latestChainInfo commonclient.ChainInfo } // NewRPCCLient returns a new *rpcClient as commonclient.RPC @@ -289,21 +295,32 @@ func (r *rpcClient) getRPCDomain() string { } // registerSub adds the sub to the rpcClient list -func (r *rpcClient) registerSub(sub ethereum.Subscription) { +func (r *rpcClient) registerSub(sub ethereum.Subscription, stopInFLightCh chan struct{}) error { r.stateMu.Lock() defer r.stateMu.Unlock() + // ensure that the `sub` belongs to current life cycle of the `rpcClient` and it should not be killed due to + // previous `DisconnectAll` call. + select { + case <-stopInFLightCh: + sub.Unsubscribe() + return fmt.Errorf("failed to register subscription - all in-flight requests were canceled") + default: + } + // TODO: BCI-3358 - delete sub when caller unsubscribes. r.subs = append(r.subs, sub) + return nil } -// disconnectAll disconnects all clients connected to the rpcClient -// WARNING: NOT THREAD-SAFE -// This must be called from within the r.stateMu lock +// DisconnectAll disconnects all clients connected to the rpcClient func (r *rpcClient) DisconnectAll() { + r.stateMu.Lock() + defer r.stateMu.Unlock() if r.ws.rpc != nil { r.ws.rpc.Close() } r.cancelInflightRequests() r.unsubscribeAll() + r.latestChainInfo = commonclient.ChainInfo{} } // unsubscribeAll unsubscribes all subscriptions @@ -388,24 +405,35 @@ func (r *rpcClient) BatchCallContext(ctx context.Context, b []rpc.BatchElem) err return err } -func (r *rpcClient) Subscribe(ctx context.Context, channel chan<- *evmtypes.Head, args ...interface{}) (commontypes.Subscription, error) { - ctx, cancel, ws, _ := r.makeLiveQueryCtxAndSafeGetClients(ctx) +func (r *rpcClient) SubscribeNewHead(ctx context.Context, channel chan<- *evmtypes.Head) (_ commontypes.Subscription, err error) { + ctx, cancel, chStopInFlight, ws, _ := r.acquireQueryCtx(ctx) defer cancel() + args := []interface{}{"newHeads"} lggr := r.newRqLggr().With("args", args) lggr.Debug("RPC call: evmclient.Client#EthSubscribe") start := time.Now() - var sub commontypes.Subscription - sub, err := ws.rpc.EthSubscribe(ctx, channel, args...) - if err == nil { - sub = newSubscriptionErrorWrapper(sub, r.rpcClientErrorPrefix()) - r.registerSub(sub) + defer func() { + duration := time.Since(start) + r.logResult(lggr, err, duration, r.getRPCDomain(), "EthSubscribe") + err = r.wrapWS(err) + }() + subForwarder := newSubForwarder(channel, func(head *evmtypes.Head) *evmtypes.Head { + head.EVMChainID = ubig.New(r.chainID) + r.onNewHead(ctx, chStopInFlight, head) + return head + }, r.wrapRPCClientError) + err = subForwarder.start(ws.rpc.EthSubscribe(ctx, subForwarder.srcCh, args...)) + if err != nil { + return } - duration := time.Since(start) - r.logResult(lggr, err, duration, r.getRPCDomain(), "EthSubscribe") + err = r.registerSub(subForwarder, chStopInFlight) + if err != nil { + return + } - return sub, r.wrapWS(err) + return subForwarder, nil } // GethClient wrappers @@ -513,7 +541,7 @@ func (r *rpcClient) HeaderByHash(ctx context.Context, hash common.Hash) (header return } -func (r *rpcClient) LatestFinalizedBlock(ctx context.Context) (head *evmtypes.Head, err error) { +func (r *rpcClient) LatestFinalizedBlock(ctx context.Context) (*evmtypes.Head, error) { return r.blockByNumber(ctx, rpc.FinalizedBlockNumber.String()) } @@ -523,7 +551,25 @@ func (r *rpcClient) BlockByNumber(ctx context.Context, number *big.Int) (head *e } func (r *rpcClient) blockByNumber(ctx context.Context, number string) (head *evmtypes.Head, err error) { - err = r.CallContext(ctx, &head, "eth_getBlockByNumber", number, false) + ctx, cancel, chStopInFlight, ws, http := r.acquireQueryCtx(ctx) + defer cancel() + const method = "eth_getBlockByNumber" + args := []interface{}{number, false} + lggr := r.newRqLggr().With( + "method", method, + "args", args, + ) + + lggr.Debug("RPC call: evmclient.Client#CallContext") + start := time.Now() + if http != nil { + err = r.wrapHTTP(http.rpc.CallContext(ctx, &head, method, args...)) + } else { + err = r.wrapWS(ws.rpc.CallContext(ctx, &head, method, args...)) + } + duration := time.Since(start) + + r.logResult(lggr, err, duration, r.getRPCDomain(), "CallContext") if err != nil { return nil, err } @@ -532,6 +578,14 @@ func (r *rpcClient) blockByNumber(ctx context.Context, number string) (head *evm return } head.EVMChainID = ubig.New(r.chainID) + + switch number { + case rpc.FinalizedBlockNumber.String(): + r.onNewFinalizedHead(ctx, chStopInFlight, head) + case rpc.LatestBlockNumber.String(): + r.onNewHead(ctx, chStopInFlight, head) + } + return } @@ -958,24 +1012,30 @@ func (r *rpcClient) ClientVersion(ctx context.Context) (version string, err erro return } -func (r *rpcClient) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.Log) (sub ethereum.Subscription, err error) { - ctx, cancel, ws, _ := r.makeLiveQueryCtxAndSafeGetClients(ctx) +func (r *rpcClient) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.Log) (_ ethereum.Subscription, err error) { + ctx, cancel, chStopInFlight, ws, _ := r.acquireQueryCtx(ctx) defer cancel() lggr := r.newRqLggr().With("q", q) lggr.Debug("RPC call: evmclient.Client#SubscribeFilterLogs") start := time.Now() - sub, err = ws.geth.SubscribeFilterLogs(ctx, q, ch) - if err == nil { - sub = newSubscriptionErrorWrapper(sub, r.rpcClientErrorPrefix()) - r.registerSub(sub) + defer func() { + duration := time.Since(start) + r.logResult(lggr, err, duration, r.getRPCDomain(), "SubscribeFilterLogs") + err = r.wrapWS(err) + }() + sub := newSubForwarder(ch, nil, r.wrapRPCClientError) + err = sub.start(ws.geth.SubscribeFilterLogs(ctx, q, sub.srcCh)) + if err != nil { + return } - err = r.wrapWS(err) - duration := time.Since(start) - r.logResult(lggr, err, duration, r.getRPCDomain(), "SubscribeFilterLogs") + err = r.registerSub(sub, chStopInFlight) + if err != nil { + return + } - return + return sub, nil } func (r *rpcClient) SuggestGasTipCap(ctx context.Context) (tipCap *big.Int, err error) { @@ -1060,17 +1120,23 @@ func (r *rpcClient) wrapHTTP(err error) error { // makeLiveQueryCtxAndSafeGetClients wraps makeQueryCtx func (r *rpcClient) makeLiveQueryCtxAndSafeGetClients(parentCtx context.Context) (ctx context.Context, cancel context.CancelFunc, ws rawclient, http *rawclient) { + ctx, cancel, _, ws, http = r.acquireQueryCtx(parentCtx) + return +} + +func (r *rpcClient) acquireQueryCtx(parentCtx context.Context) (ctx context.Context, cancel context.CancelFunc, + chStopInFlight chan struct{}, ws rawclient, http *rawclient) { // Need to wrap in mutex because state transition can cancel and replace the // context r.stateMu.RLock() - cancelCh := r.chStopInFlight + chStopInFlight = r.chStopInFlight ws = r.ws if r.http != nil { cp := *r.http http = &cp } r.stateMu.RUnlock() - ctx, cancel = makeQueryCtx(parentCtx, cancelCh) + ctx, cancel = makeQueryCtx(parentCtx, chStopInFlight) return } @@ -1134,6 +1200,49 @@ func Name(r *rpcClient) string { return r.name } +func (r *rpcClient) onNewHead(ctx context.Context, requestCh <-chan struct{}, head *evmtypes.Head) { + if head == nil { + return + } + + r.stateMu.Lock() + defer r.stateMu.Unlock() + if !commonclient.CtxIsHeathCheckRequest(ctx) { + r.highestUserObservations.BlockNumber = max(r.highestUserObservations.BlockNumber, head.Number) + r.highestUserObservations.TotalDifficulty = commonclient.MaxTotalDifficulty(r.highestUserObservations.TotalDifficulty, head.TotalDifficulty) + } + select { + case <-requestCh: // no need to update latestChainInfo, as rpcClient already started new life cycle + return + default: + r.latestChainInfo.BlockNumber = head.Number + r.latestChainInfo.TotalDifficulty = head.TotalDifficulty + } +} + +func (r *rpcClient) onNewFinalizedHead(ctx context.Context, requestCh <-chan struct{}, head *evmtypes.Head) { + if head == nil { + return + } + r.stateMu.Lock() + defer r.stateMu.Unlock() + if !commonclient.CtxIsHeathCheckRequest(ctx) { + r.highestUserObservations.FinalizedBlockNumber = max(r.highestUserObservations.FinalizedBlockNumber, head.Number) + } + select { + case <-requestCh: // no need to update latestChainInfo, as rpcClient already started new life cycle + return + default: + r.latestChainInfo.FinalizedBlockNumber = head.Number + } +} + +func (r *rpcClient) GetInterceptedChainInfo() (latest, highestUserObservations commonclient.ChainInfo) { + r.stateMu.RLock() + defer r.stateMu.RUnlock() + return r.latestChainInfo, r.highestUserObservations +} + func ToBlockNumArg(number *big.Int) string { if number == nil { return "latest" diff --git a/core/chains/evm/client/rpc_client_test.go b/core/chains/evm/client/rpc_client_test.go new file mode 100644 index 0000000000..682c435245 --- /dev/null +++ b/core/chains/evm/client/rpc_client_test.go @@ -0,0 +1,300 @@ +package client_test + +import ( + "context" + "encoding/json" + "fmt" + "math/big" + "net/url" + "testing" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/core/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/tidwall/gjson" + "go.uber.org/zap" + + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + + "github.com/smartcontractkit/chainlink-common/pkg/logger" + + 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" +) + +func makeNewHeadWSMessage(head *evmtypes.Head) string { + asJSON, err := json.Marshal(head) + 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)) +} + +func TestRPCClient_SubscribeNewHead(t *testing.T) { + t.Parallel() + ctx, cancel := context.WithTimeout(tests.Context(t), tests.WaitTimeout(t)) + defer cancel() + + chainId := big.NewInt(123456) + lggr := logger.Test(t) + + serverCallBack := func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { + if method == "eth_unsubscribe" { + resp.Result = "true" + return + } + assert.Equal(t, "eth_subscribe", method) + if assert.True(t, params.IsArray()) && assert.Equal(t, "newHeads", params.Array()[0].String()) { + resp.Result = `"0x00"` + } + return + } + t.Run("Updates chain info on new blocks", func(t *testing.T) { + server := testutils.NewWSServer(t, chainId, serverCallBack) + wsURL := server.WSURL() + + rpc := client.NewRPCClient(lggr, *wsURL, nil, "rpc", 1, chainId, commonclient.Primary) + defer rpc.Close() + require.NoError(t, rpc.Dial(ctx)) + // set to default values + latest, highestUserObservations := rpc.GetInterceptedChainInfo() + assert.Equal(t, int64(0), latest.BlockNumber) + assert.Equal(t, int64(0), latest.FinalizedBlockNumber) + assert.Nil(t, latest.TotalDifficulty) + assert.Equal(t, int64(0), highestUserObservations.BlockNumber) + assert.Equal(t, int64(0), highestUserObservations.FinalizedBlockNumber) + assert.Nil(t, highestUserObservations.TotalDifficulty) + + ch := make(chan *evmtypes.Head) + sub, err := rpc.SubscribeNewHead(tests.Context(t), ch) + require.NoError(t, err) + defer sub.Unsubscribe() + go server.MustWriteBinaryMessageSync(t, makeNewHeadWSMessage(&evmtypes.Head{Number: 256, TotalDifficulty: big.NewInt(1000)})) + // received 256 head + <-ch + go server.MustWriteBinaryMessageSync(t, makeNewHeadWSMessage(&evmtypes.Head{Number: 128, TotalDifficulty: big.NewInt(500)})) + // received 128 head + <-ch + + latest, highestUserObservations = rpc.GetInterceptedChainInfo() + assert.Equal(t, int64(128), latest.BlockNumber) + assert.Equal(t, int64(0), latest.FinalizedBlockNumber) + assert.Equal(t, big.NewInt(500), latest.TotalDifficulty) + + assertHighestUserObservations := func(highestUserObservations commonclient.ChainInfo) { + assert.Equal(t, int64(256), highestUserObservations.BlockNumber) + assert.Equal(t, int64(0), highestUserObservations.FinalizedBlockNumber) + assert.Equal(t, big.NewInt(1000), highestUserObservations.TotalDifficulty) + } + + assertHighestUserObservations(highestUserObservations) + + // DisconnectAll resets latest + rpc.DisconnectAll() + + latest, highestUserObservations = rpc.GetInterceptedChainInfo() + assert.Equal(t, int64(0), latest.BlockNumber) + assert.Equal(t, int64(0), latest.FinalizedBlockNumber) + assert.Nil(t, latest.TotalDifficulty) + + assertHighestUserObservations(highestUserObservations) + }) + t.Run("App layer observations are not affected by new block if health check flag is present", func(t *testing.T) { + server := testutils.NewWSServer(t, chainId, serverCallBack) + wsURL := server.WSURL() + + rpc := client.NewRPCClient(lggr, *wsURL, nil, "rpc", 1, chainId, commonclient.Primary) + defer rpc.Close() + require.NoError(t, rpc.Dial(ctx)) + ch := make(chan *evmtypes.Head) + sub, err := rpc.SubscribeNewHead(commonclient.CtxAddHealthCheckFlag(tests.Context(t)), ch) + require.NoError(t, err) + defer sub.Unsubscribe() + go server.MustWriteBinaryMessageSync(t, makeNewHeadWSMessage(&evmtypes.Head{Number: 256, TotalDifficulty: big.NewInt(1000)})) + // received 256 head + <-ch + + latest, highestUserObservations := rpc.GetInterceptedChainInfo() + assert.Equal(t, int64(256), latest.BlockNumber) + assert.Equal(t, int64(0), latest.FinalizedBlockNumber) + assert.Equal(t, big.NewInt(1000), latest.TotalDifficulty) + + assert.Equal(t, int64(0), highestUserObservations.BlockNumber) + assert.Equal(t, int64(0), highestUserObservations.FinalizedBlockNumber) + assert.Equal(t, (*big.Int)(nil), highestUserObservations.TotalDifficulty) + }) + t.Run("Block's chain ID matched configured", func(t *testing.T) { + server := testutils.NewWSServer(t, chainId, serverCallBack) + wsURL := server.WSURL() + rpc := client.NewRPCClient(lggr, *wsURL, nil, "rpc", 1, chainId, commonclient.Primary) + defer rpc.Close() + require.NoError(t, rpc.Dial(ctx)) + ch := make(chan *evmtypes.Head) + sub, err := rpc.SubscribeNewHead(tests.Context(t), ch) + require.NoError(t, err) + defer sub.Unsubscribe() + go server.MustWriteBinaryMessageSync(t, makeNewHeadWSMessage(&evmtypes.Head{Number: 256})) + head := <-ch + require.Equal(t, chainId, head.ChainID()) + }) + t.Run("Failed SubscribeNewHead returns and logs proper error", func(t *testing.T) { + server := testutils.NewWSServer(t, chainId, func(reqMethod string, reqParams gjson.Result) (resp testutils.JSONRPCResponse) { + return resp + }) + wsURL := server.WSURL() + observedLggr, observed := logger.TestObserved(t, zap.DebugLevel) + rpc := client.NewRPCClient(observedLggr, *wsURL, nil, "rpc", 1, chainId, commonclient.Primary) + require.NoError(t, rpc.Dial(ctx)) + server.Close() + _, err := rpc.SubscribeNewHead(ctx, make(chan *evmtypes.Head)) + require.ErrorContains(t, err, "RPCClient returned error (rpc)") + tests.AssertLogEventually(t, observed, "evmclient.Client#EthSubscribe RPC call failure") + }) + t.Run("Subscription error is properly wrapper", func(t *testing.T) { + server := testutils.NewWSServer(t, chainId, serverCallBack) + wsURL := server.WSURL() + rpc := client.NewRPCClient(lggr, *wsURL, nil, "rpc", 1, chainId, commonclient.Primary) + defer rpc.Close() + require.NoError(t, rpc.Dial(ctx)) + sub, err := rpc.SubscribeNewHead(ctx, make(chan *evmtypes.Head)) + require.NoError(t, err) + go server.MustWriteBinaryMessageSync(t, "invalid msg") + select { + case err = <-sub.Err(): + require.ErrorContains(t, err, "RPCClient returned error (rpc): invalid character") + case <-ctx.Done(): + t.Errorf("Expected subscription to return an error, but test timeout instead") + } + }) +} + +func TestRPCClient_SubscribeFilterLogs(t *testing.T) { + t.Parallel() + + chainId := big.NewInt(123456) + lggr := logger.Test(t) + ctx, cancel := context.WithTimeout(tests.Context(t), tests.WaitTimeout(t)) + defer cancel() + t.Run("Failed SubscribeFilterLogs logs and returns proper error", func(t *testing.T) { + server := testutils.NewWSServer(t, chainId, func(reqMethod string, reqParams gjson.Result) (resp testutils.JSONRPCResponse) { + return resp + }) + wsURL := server.WSURL() + observedLggr, observed := logger.TestObserved(t, zap.DebugLevel) + rpc := client.NewRPCClient(observedLggr, *wsURL, nil, "rpc", 1, chainId, commonclient.Primary) + require.NoError(t, rpc.Dial(ctx)) + server.Close() + _, err := rpc.SubscribeFilterLogs(ctx, ethereum.FilterQuery{}, make(chan types.Log)) + require.ErrorContains(t, err, "RPCClient returned error (rpc)") + tests.AssertLogEventually(t, observed, "evmclient.Client#SubscribeFilterLogs RPC call failure") + }) + t.Run("Subscription error is properly wrapper", func(t *testing.T) { + server := testutils.NewWSServer(t, chainId, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { + assert.Equal(t, "eth_subscribe", method) + if assert.True(t, params.IsArray()) && assert.Equal(t, "logs", params.Array()[0].String()) { + resp.Result = `"0x00"` + resp.Notify = "{}" + } + return resp + }) + wsURL := server.WSURL() + rpc := client.NewRPCClient(lggr, *wsURL, nil, "rpc", 1, chainId, commonclient.Primary) + defer rpc.Close() + require.NoError(t, rpc.Dial(ctx)) + sub, err := rpc.SubscribeFilterLogs(ctx, ethereum.FilterQuery{}, make(chan types.Log)) + require.NoError(t, err) + go server.MustWriteBinaryMessageSync(t, "invalid msg") + errorCtx, cancel := context.WithTimeout(ctx, tests.DefaultWaitTimeout) + defer cancel() + select { + case err = <-sub.Err(): + require.ErrorContains(t, err, "RPCClient returned error (rpc): invalid character") + case <-errorCtx.Done(): + t.Errorf("Expected subscription to return an error, but test timeout instead") + } + }) +} + +func TestRPCClient_LatestFinalizedBlock(t *testing.T) { + t.Parallel() + ctx, cancel := context.WithTimeout(tests.Context(t), tests.WaitTimeout(t)) + defer cancel() + + chainId := big.NewInt(123456) + lggr := logger.Test(t) + + type rpcServer struct { + Head *evmtypes.Head + URL *url.URL + } + createRPCServer := func() *rpcServer { + server := &rpcServer{} + server.URL = testutils.NewWSServer(t, chainId, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { + assert.Equal(t, "eth_getBlockByNumber", method) + if assert.True(t, params.IsArray()) && assert.Equal(t, "finalized", params.Array()[0].String()) { + head := server.Head + jsonHead, err := json.Marshal(head) + if err != nil { + panic(fmt.Errorf("failed to marshal head: %w", err)) + } + resp.Result = string(jsonHead) + } + + return + }).WSURL() + + return server + } + + server := createRPCServer() + rpc := client.NewRPCClient(lggr, *server.URL, nil, "rpc", 1, chainId, commonclient.Primary) + require.NoError(t, rpc.Dial(ctx)) + defer rpc.Close() + server.Head = &evmtypes.Head{Number: 128} + // updates chain info + _, err := rpc.LatestFinalizedBlock(ctx) + require.NoError(t, err) + latest, highestUserObservations := rpc.GetInterceptedChainInfo() + + assert.Equal(t, int64(0), highestUserObservations.BlockNumber) + assert.Equal(t, int64(128), highestUserObservations.FinalizedBlockNumber) + + assert.Equal(t, int64(0), latest.BlockNumber) + assert.Equal(t, int64(128), latest.FinalizedBlockNumber) + + // lower block number does not update highestUserObservations + server.Head = &evmtypes.Head{Number: 127} + _, err = rpc.LatestFinalizedBlock(ctx) + require.NoError(t, err) + latest, highestUserObservations = rpc.GetInterceptedChainInfo() + + assert.Equal(t, int64(0), highestUserObservations.BlockNumber) + assert.Equal(t, int64(128), highestUserObservations.FinalizedBlockNumber) + + assert.Equal(t, int64(0), latest.BlockNumber) + assert.Equal(t, int64(127), latest.FinalizedBlockNumber) + + // health check flg prevents change in highestUserObservations + server.Head = &evmtypes.Head{Number: 256} + _, err = rpc.LatestFinalizedBlock(commonclient.CtxAddHealthCheckFlag(ctx)) + require.NoError(t, err) + latest, highestUserObservations = rpc.GetInterceptedChainInfo() + + assert.Equal(t, int64(0), highestUserObservations.BlockNumber) + assert.Equal(t, int64(128), highestUserObservations.FinalizedBlockNumber) + + assert.Equal(t, int64(0), latest.BlockNumber) + assert.Equal(t, int64(256), latest.FinalizedBlockNumber) + + // DisconnectAll resets latest ChainInfo + rpc.DisconnectAll() + latest, highestUserObservations = rpc.GetInterceptedChainInfo() + assert.Equal(t, int64(0), highestUserObservations.BlockNumber) + assert.Equal(t, int64(128), highestUserObservations.FinalizedBlockNumber) + + assert.Equal(t, int64(0), latest.BlockNumber) + assert.Equal(t, int64(0), latest.FinalizedBlockNumber) +} diff --git a/core/chains/evm/client/sub_error_wrapper.go b/core/chains/evm/client/sub_error_wrapper.go deleted file mode 100644 index 689991ce70..0000000000 --- a/core/chains/evm/client/sub_error_wrapper.go +++ /dev/null @@ -1,77 +0,0 @@ -package client - -import ( - "fmt" - - commontypes "github.com/smartcontractkit/chainlink/v2/common/types" -) - -// subErrorWrapper - adds specified prefix to a subscription error -type subErrorWrapper struct { - sub commontypes.Subscription - errorPrefix string - - done chan struct{} - unSub chan struct{} - errorCh chan error -} - -func newSubscriptionErrorWrapper(sub commontypes.Subscription, errorPrefix string) *subErrorWrapper { - s := &subErrorWrapper{ - sub: sub, - errorPrefix: errorPrefix, - done: make(chan struct{}), - unSub: make(chan struct{}), - errorCh: make(chan error), - } - - go func() { - for { - select { - // sub.Err channel is closed by sub.Unsubscribe - case err, ok := <-sub.Err(): - if !ok { - // might only happen if someone terminated wrapped subscription - // in any case - do our best to release resources - // we can't call Unsubscribe on root sub as this might cause panic - close(s.errorCh) - close(s.done) - return - } - - select { - case s.errorCh <- fmt.Errorf("%s: %w", s.errorPrefix, err): - case <-s.unSub: - s.close() - return - } - case <-s.unSub: - s.close() - return - } - } - }() - - return s -} - -func (s *subErrorWrapper) close() { - s.sub.Unsubscribe() - close(s.errorCh) - close(s.done) -} - -func (s *subErrorWrapper) Unsubscribe() { - select { - // already unsubscribed - case <-s.done: - // signal unsubscribe - case s.unSub <- struct{}{}: - // wait for unsubscribe to complete - <-s.done - } -} - -func (s *subErrorWrapper) Err() <-chan error { - return s.errorCh -} diff --git a/core/chains/evm/client/sub_error_wrapper_test.go b/core/chains/evm/client/sub_error_wrapper_test.go deleted file mode 100644 index 5dd8106957..0000000000 --- a/core/chains/evm/client/sub_error_wrapper_test.go +++ /dev/null @@ -1,74 +0,0 @@ -package client - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" -) - -func TestSubscriptionErrorWrapper(t *testing.T) { - t.Parallel() - t.Run("Unsubscribe wrapper releases resources", func(t *testing.T) { - t.Parallel() - - mockedSub := NewMockSubscription() - const prefix = "RPC returned error" - wrapper := newSubscriptionErrorWrapper(mockedSub, prefix) - wrapper.Unsubscribe() - - // mock's resources were relased - assert.True(t, mockedSub.unsubscribed) - _, ok := <-mockedSub.Err() - assert.False(t, ok) - // wrapper's channels are closed - _, ok = <-wrapper.Err() - assert.False(t, ok) - // 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(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() - const prefix = "RPC returned error" - wrapper := newSubscriptionErrorWrapper(sub, prefix) - sub.Errors <- fmt.Errorf("root error") - - err, ok := <-wrapper.Err() - assert.True(t, ok) - assert.Equal(t, "RPC returned error: root error", err.Error()) - - wrapper.Unsubscribe() - _, ok = <-wrapper.Err() - assert.False(t, ok) - }) - t.Run("Unsubscribe on root does not cause panic", func(t *testing.T) { - t.Parallel() - mockedSub := NewMockSubscription() - wrapper := newSubscriptionErrorWrapper(mockedSub, "") - - mockedSub.Unsubscribe() - // mock's resources were released - assert.True(t, mockedSub.unsubscribed) - _, ok := <-mockedSub.Err() - assert.False(t, ok) - // wrapper's channels are eventually closed - tests.AssertEventually(t, func() bool { - _, ok = <-wrapper.Err() - return !ok - }) - }) -} diff --git a/core/chains/evm/client/chain_id_sub.go b/core/chains/evm/client/sub_forwarder.go similarity index 51% rename from core/chains/evm/client/chain_id_sub.go rename to core/chains/evm/client/sub_forwarder.go index c3162b300c..93e9b106b4 100644 --- a/core/chains/evm/client/chain_id_sub.go +++ b/core/chains/evm/client/sub_forwarder.go @@ -1,42 +1,40 @@ package client import ( - "math/big" - "github.com/ethereum/go-ethereum" - - evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" ) -var _ ethereum.Subscription = &chainIDSubForwarder{} +var _ ethereum.Subscription = &subForwarder[any]{} -// chainIDSubForwarder wraps a head subscription in order to intercept and augment each head with chainID before forwarding. -type chainIDSubForwarder struct { - chainID *big.Int - destCh chan<- *evmtypes.Head +// subForwarder wraps a subscription in order to intercept and augment each result before forwarding. +type subForwarder[T any] struct { + destCh chan<- T - srcCh chan *evmtypes.Head + srcCh chan T srcSub ethereum.Subscription + interceptResult func(T) T + interceptError func(error) error + done chan struct{} err chan error unSub chan struct{} } -func newChainIDSubForwarder(chainID *big.Int, ch chan<- *evmtypes.Head) *chainIDSubForwarder { - return &chainIDSubForwarder{ - chainID: chainID, - destCh: ch, - srcCh: make(chan *evmtypes.Head), - done: make(chan struct{}), - err: make(chan error), - unSub: make(chan struct{}, 1), +func newSubForwarder[T any](destCh chan<- T, interceptResult func(T) T, 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), + unSub: make(chan struct{}, 1), } } // start spawns the forwarding loop for sub. -func (c *chainIDSubForwarder) start(sub ethereum.Subscription, err error) error { +func (c *subForwarder[T]) start(sub ethereum.Subscription, err error) error { if err != nil { close(c.srcCh) return err @@ -48,7 +46,7 @@ func (c *chainIDSubForwarder) start(sub ethereum.Subscription, err error) error // forwardLoop receives from src, adds the chainID, and then sends to dest. // It also handles Unsubscribing, which may interrupt either forwarding operation. -func (c *chainIDSubForwarder) forwardLoop() { +func (c *subForwarder[T]) forwardLoop() { // the error channel must be closed when unsubscribing defer close(c.err) defer close(c.done) @@ -56,6 +54,9 @@ func (c *chainIDSubForwarder) forwardLoop() { for { select { case err := <-c.srcSub.Err(): + if c.interceptError != nil { + err = c.interceptError(err) + } select { case c.err <- err: case <-c.unSub: @@ -64,7 +65,9 @@ func (c *chainIDSubForwarder) forwardLoop() { return case h := <-c.srcCh: - h.EVMChainID = ubig.New(c.chainID) + if c.interceptResult != nil { + h = c.interceptResult(h) + } select { case c.destCh <- h: case <-c.unSub: @@ -79,7 +82,7 @@ func (c *chainIDSubForwarder) forwardLoop() { } } -func (c *chainIDSubForwarder) Unsubscribe() { +func (c *subForwarder[T]) Unsubscribe() { // tell forwardLoop to unsubscribe select { case c.unSub <- struct{}{}: @@ -90,6 +93,6 @@ func (c *chainIDSubForwarder) Unsubscribe() { <-c.done } -func (c *chainIDSubForwarder) Err() <-chan error { +func (c *subForwarder[T]) Err() <-chan error { return c.err } diff --git a/core/chains/evm/client/sub_forwarder_test.go b/core/chains/evm/client/sub_forwarder_test.go new file mode 100644 index 0000000000..1bc0122603 --- /dev/null +++ b/core/chains/evm/client/sub_forwarder_test.go @@ -0,0 +1,190 @@ +package client + +import ( + "errors" + "fmt" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + + commontypes "github.com/smartcontractkit/chainlink/v2/common/types" + evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" +) + +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 { + head.EVMChainID = ubig.New(chainID) + return head + }, nil) + } + + chainID := big.NewInt(123) + + t.Run("unsubscribe forwarder", func(t *testing.T) { + t.Parallel() + + ch := make(chan *evmtypes.Head) + forwarder := newChainIDSubForwarder(chainID, ch) + sub := NewMockSubscription() + err := forwarder.start(sub, nil) + assert.NoError(t, err) + forwarder.Unsubscribe() + + assert.True(t, sub.unsubscribed) + _, ok := <-sub.Err() + assert.False(t, ok) + _, ok = <-forwarder.Err() + assert.False(t, ok) + }) + + t.Run("unsubscribe forwarder with error", func(t *testing.T) { + t.Parallel() + + ch := make(chan *evmtypes.Head) + forwarder := newChainIDSubForwarder(chainID, ch) + sub := NewMockSubscription() + err := forwarder.start(sub, nil) + assert.NoError(t, err) + sub.Errors <- errors.New("boo") + forwarder.Unsubscribe() + + assert.True(t, sub.unsubscribed) + _, ok := <-sub.Err() + assert.False(t, ok) + _, ok = <-forwarder.Err() + assert.False(t, ok) + }) + + t.Run("unsubscribe forwarder with message", func(t *testing.T) { + t.Parallel() + + ch := make(chan *evmtypes.Head) + forwarder := newChainIDSubForwarder(chainID, ch) + sub := NewMockSubscription() + err := forwarder.start(sub, nil) + assert.NoError(t, err) + forwarder.srcCh <- &evmtypes.Head{} + forwarder.Unsubscribe() + + assert.True(t, sub.unsubscribed) + _, ok := <-sub.Err() + assert.False(t, ok) + _, ok = <-forwarder.Err() + assert.False(t, ok) + }) + + t.Run("non nil error parameter", func(t *testing.T) { + t.Parallel() + + ch := make(chan *evmtypes.Head) + forwarder := newChainIDSubForwarder(chainID, ch) + sub := NewMockSubscription() + errIn := errors.New("foo") + errOut := forwarder.start(sub, errIn) + assert.Equal(t, errIn, errOut) + }) + + t.Run("forwarding", func(t *testing.T) { + t.Parallel() + + ch := make(chan *evmtypes.Head) + forwarder := newChainIDSubForwarder(chainID, ch) + sub := NewMockSubscription() + err := forwarder.start(sub, nil) + assert.NoError(t, err) + + head := &evmtypes.Head{ + ID: 1, + } + forwarder.srcCh <- head + receivedHead := <-ch + assert.Equal(t, head, receivedHead) + assert.Equal(t, ubig.New(chainID), receivedHead.EVMChainID) + + expectedErr := errors.New("error") + sub.Errors <- expectedErr + receivedErr := <-forwarder.Err() + assert.Equal(t, expectedErr, receivedErr) + }) +} + +func TestSubscriptionErrorWrapper(t *testing.T) { + t.Parallel() + newSubscriptionErrorWrapper := func(t *testing.T, sub commontypes.Subscription, errorPrefix string) ethereum.Subscription { + ch := make(chan *evmtypes.Head) + result := newSubForwarder(ch, nil, func(err error) error { + return fmt.Errorf("%s: %w", errorPrefix, err) + }) + require.NoError(t, result.start(sub, nil)) + return result + } + t.Run("Unsubscribe wrapper releases resources", func(t *testing.T) { + t.Parallel() + + mockedSub := NewMockSubscription() + const prefix = "RPC returned error" + wrapper := newSubscriptionErrorWrapper(t, mockedSub, prefix) + wrapper.Unsubscribe() + + // mock's resources were released + assert.True(t, mockedSub.unsubscribed) + _, ok := <-mockedSub.Err() + assert.False(t, ok) + // wrapper's channels are closed + _, ok = <-wrapper.Err() + assert.False(t, ok) + // 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() + const prefix = "RPC returned error" + wrapper := newSubscriptionErrorWrapper(t, sub, prefix) + sub.Errors <- fmt.Errorf("root error") + + err, ok := <-wrapper.Err() + assert.True(t, ok) + assert.Equal(t, "RPC returned error: root error", err.Error()) + + wrapper.Unsubscribe() + _, ok = <-wrapper.Err() + assert.False(t, ok) + }) + t.Run("Unsubscribe on root does not cause panic", func(t *testing.T) { + t.Parallel() + mockedSub := NewMockSubscription() + wrapper := newSubscriptionErrorWrapper(t, mockedSub, "") + + mockedSub.Unsubscribe() + // mock's resources were released + assert.True(t, mockedSub.unsubscribed) + _, ok := <-mockedSub.Err() + assert.False(t, ok) + // wrapper's channels are eventually closed + tests.AssertEventually(t, func() bool { + _, ok = <-wrapper.Err() + return !ok + }) + }) +} diff --git a/core/chains/evm/config/chain_scoped.go b/core/chains/evm/config/chain_scoped.go index 8064e2de20..db598e3e82 100644 --- a/core/chains/evm/config/chain_scoped.go +++ b/core/chains/evm/config/chain_scoped.go @@ -179,3 +179,7 @@ func (e *EVMConfig) OperatorFactoryAddress() string { func (e *EVMConfig) LogPrunePageSize() uint32 { return *e.C.LogPrunePageSize } + +func (e *EVMConfig) FinalizedBlockOffset() uint32 { + return *e.C.FinalizedBlockOffset +} diff --git a/core/chains/evm/config/chain_scoped_node_pool.go b/core/chains/evm/config/chain_scoped_node_pool.go index 5026936682..a497436648 100644 --- a/core/chains/evm/config/chain_scoped_node_pool.go +++ b/core/chains/evm/config/chain_scoped_node_pool.go @@ -38,6 +38,12 @@ func (n *NodePoolConfig) FinalizedBlockPollInterval() time.Duration { return n.C.FinalizedBlockPollInterval.Duration() } -func (n *NodePoolConfig) Errors() ClientErrors { - return &clientErrorsConfig{c: n.C.Errors} +func (n *NodePoolConfig) Errors() ClientErrors { return &clientErrorsConfig{c: n.C.Errors} } + +func (n *NodePoolConfig) EnforceRepeatableRead() bool { + return *n.C.EnforceRepeatableRead +} + +func (n *NodePoolConfig) DeathDeclarationDelay() time.Duration { + return n.C.DeathDeclarationDelay.Duration() } diff --git a/core/chains/evm/config/config.go b/core/chains/evm/config/config.go index b44c112e20..ffb2a496ba 100644 --- a/core/chains/evm/config/config.go +++ b/core/chains/evm/config/config.go @@ -45,6 +45,7 @@ type EVM interface { OperatorFactoryAddress() string RPCDefaultBatchSize() uint32 NodeNoNewHeadsThreshold() time.Duration + FinalizedBlockOffset() uint32 IsEnabled() bool TOMLString() (string, error) @@ -170,6 +171,8 @@ type NodePool interface { NodeIsSyncingEnabled() bool FinalizedBlockPollInterval() time.Duration Errors() ClientErrors + EnforceRepeatableRead() bool + DeathDeclarationDelay() time.Duration } // TODO BCF-2509 does the chainscopedconfig really need the entire app config? diff --git a/core/chains/evm/config/config_test.go b/core/chains/evm/config/config_test.go index 26ac2db085..ba362bda98 100644 --- a/core/chains/evm/config/config_test.go +++ b/core/chains/evm/config/config_test.go @@ -326,6 +326,8 @@ func TestNodePoolConfig(t *testing.T) { require.Equal(t, time.Duration(10000000000), cfg.EVM().NodePool().PollInterval()) require.Equal(t, uint32(5), cfg.EVM().NodePool().PollFailureThreshold()) require.Equal(t, false, cfg.EVM().NodePool().NodeIsSyncingEnabled()) + require.Equal(t, false, cfg.EVM().NodePool().EnforceRepeatableRead()) + require.Equal(t, time.Duration(10000000000), cfg.EVM().NodePool().DeathDeclarationDelay()) } func TestClientErrorsConfig(t *testing.T) { diff --git a/core/chains/evm/config/mocks/chain_scoped_config.go b/core/chains/evm/config/mocks/chain_scoped_config.go index 7f5f9666ae..730c6c61c4 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/chains/evm/config/mocks/gas_estimator.go b/core/chains/evm/config/mocks/gas_estimator.go index 549f16fe55..9ad7420977 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/chains/evm/config/toml/config.go b/core/chains/evm/config/toml/config.go index d35f9bd0a3..3e35bb4b55 100644 --- a/core/chains/evm/config/toml/config.go +++ b/core/chains/evm/config/toml/config.go @@ -358,6 +358,7 @@ type Chain struct { OperatorFactoryAddress *types.EIP55Address RPCDefaultBatchSize *uint32 RPCBlockQueryDelay *uint16 + FinalizedBlockOffset *uint32 Transactions Transactions `toml:",omitempty"` BalanceMonitor BalanceMonitor `toml:",omitempty"` @@ -389,6 +390,11 @@ func (c *Chain) ValidateConfig() (err error) { Msg: "must be greater than or equal to 1"}) } + if *c.FinalizedBlockOffset > *c.HeadTracker.HistoryDepth { + err = multierr.Append(err, commonconfig.ErrInvalid{Name: "HeadTracker.HistoryDepth", Value: *c.HeadTracker.HistoryDepth, + Msg: "must be greater than or equal to FinalizedBlockOffset"}) + } + // AutoPurge configs depend on ChainType so handling validation on per chain basis if c.Transactions.AutoPurge.Enabled != nil && *c.Transactions.AutoPurge.Enabled { chainType := c.ChainType.ChainType() @@ -842,6 +848,8 @@ type NodePool struct { NodeIsSyncingEnabled *bool FinalizedBlockPollInterval *commonconfig.Duration Errors ClientErrors `toml:",omitempty"` + EnforceRepeatableRead *bool + DeathDeclarationDelay *commonconfig.Duration } func (p *NodePool) setFrom(f *NodePool) { @@ -866,6 +874,14 @@ func (p *NodePool) setFrom(f *NodePool) { if v := f.FinalizedBlockPollInterval; v != nil { p.FinalizedBlockPollInterval = v } + + if v := f.EnforceRepeatableRead; v != nil { + p.EnforceRepeatableRead = v + } + + if v := f.DeathDeclarationDelay; v != nil { + p.DeathDeclarationDelay = v + } p.Errors.setFrom(&f.Errors) } diff --git a/core/chains/evm/config/toml/defaults.go b/core/chains/evm/config/toml/defaults.go index e006babfb6..38eef40bf7 100644 --- a/core/chains/evm/config/toml/defaults.go +++ b/core/chains/evm/config/toml/defaults.go @@ -161,6 +161,9 @@ func (c *Chain) SetFrom(f *Chain) { if v := f.RPCBlockQueryDelay; v != nil { c.RPCBlockQueryDelay = v } + if v := f.FinalizedBlockOffset; v != nil { + c.FinalizedBlockOffset = v + } c.Transactions.setFrom(&f.Transactions) c.BalanceMonitor.setFrom(&f.BalanceMonitor) diff --git a/core/chains/evm/config/toml/defaults/fallback.toml b/core/chains/evm/config/toml/defaults/fallback.toml index 7f19cb9dcf..2fe6edd54c 100644 --- a/core/chains/evm/config/toml/defaults/fallback.toml +++ b/core/chains/evm/config/toml/defaults/fallback.toml @@ -15,6 +15,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '3m' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -69,6 +70,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 diff --git a/core/chains/evm/forwarders/forwarder_manager_test.go b/core/chains/evm/forwarders/forwarder_manager_test.go index be8513f592..c3fae5292a 100644 --- a/core/chains/evm/forwarders/forwarder_manager_test.go +++ b/core/chains/evm/forwarders/forwarder_manager_test.go @@ -23,6 +23,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/forwarders" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" @@ -75,7 +76,8 @@ func TestFwdMgr_MaybeForwardTransaction(t *testing.T) { RpcBatchSize: 2, KeepFinalizedBlocksDepth: 1000, } - lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.FixtureChainID, db, lggr), evmClient, lggr, lpOpts) + ht := headtracker.NewSimulatedHeadTracker(evmClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) + lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.FixtureChainID, db, lggr), evmClient, lggr, ht, lpOpts) fwdMgr := forwarders.NewFwdMgr(db, evmClient, lp, lggr, evmcfg.EVM()) fwdMgr.ORM = forwarders.NewORM(db) @@ -136,7 +138,8 @@ func TestFwdMgr_AccountUnauthorizedToForward_SkipsForwarding(t *testing.T) { RpcBatchSize: 2, KeepFinalizedBlocksDepth: 1000, } - lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.FixtureChainID, db, lggr), evmClient, lggr, lpOpts) + ht := headtracker.NewSimulatedHeadTracker(evmClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) + lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.FixtureChainID, db, lggr), evmClient, lggr, ht, lpOpts) fwdMgr := forwarders.NewFwdMgr(db, evmClient, lp, lggr, evmcfg.EVM()) fwdMgr.ORM = forwarders.NewORM(db) @@ -201,7 +204,8 @@ func TestFwdMgr_InvalidForwarderForOCR2FeedsStates(t *testing.T) { RpcBatchSize: 2, KeepFinalizedBlocksDepth: 1000, } - lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.FixtureChainID, db, lggr), evmClient, lggr, lpOpts) + ht := headtracker.NewSimulatedHeadTracker(evmClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) + lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.FixtureChainID, db, lggr), evmClient, lggr, ht, lpOpts) fwdMgr := forwarders.NewFwdMgr(db, evmClient, lp, lggr, evmcfg.EVM()) fwdMgr.ORM = forwarders.NewORM(db) diff --git a/core/chains/evm/forwarders/mocks/orm.go b/core/chains/evm/forwarders/mocks/orm.go index 04e252fa7d..7cffdf171c 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/chains/evm/gas/arbitrum_estimator_test.go b/core/chains/evm/gas/arbitrum_estimator_test.go index 289025fcda..cbc6d5d37f 100644 --- a/core/chains/evm/gas/arbitrum_estimator_test.go +++ b/core/chains/evm/gas/arbitrum_estimator_test.go @@ -55,10 +55,11 @@ func TestArbitrumEstimator(t *testing.T) { t.Run("calling GetLegacyGas on unstarted estimator returns error", func(t *testing.T) { feeEstimatorClient := mocks.NewFeeEstimatorClient(t) - l1Oracle := rollups.NewArbitrumL1GasOracle(logger.Test(t), feeEstimatorClient) + l1Oracle, err := rollups.NewArbitrumL1GasOracle(logger.Test(t), feeEstimatorClient) + require.NoError(t, err) o := gas.NewArbitrumEstimator(logger.Test(t), &arbConfig{}, feeEstimatorClient, l1Oracle) - _, _, err := o.GetLegacyGas(tests.Context(t), calldata, gasLimit, maxGasPrice) + _, _, err = o.GetLegacyGas(tests.Context(t), calldata, gasLimit, maxGasPrice) assert.EqualError(t, err, "estimator is not started") }) @@ -68,7 +69,8 @@ func TestArbitrumEstimator(t *testing.T) { zeros.Write(common.BigToHash(big.NewInt(123455)).Bytes()) t.Run("calling GetLegacyGas on started estimator returns estimates", func(t *testing.T) { feeEstimatorClient := mocks.NewFeeEstimatorClient(t) - l1Oracle := rollups.NewArbitrumL1GasOracle(logger.Test(t), feeEstimatorClient) + l1Oracle, err := rollups.NewArbitrumL1GasOracle(logger.Test(t), feeEstimatorClient) + require.NoError(t, err) feeEstimatorClient.On("CallContext", mock.Anything, mock.Anything, "eth_gasPrice").Return(nil).Run(func(args mock.Arguments) { res := args.Get(1).(*hexutil.Big) @@ -93,7 +95,8 @@ func TestArbitrumEstimator(t *testing.T) { t.Run("gas price is lower than user specified max gas price", func(t *testing.T) { feeEstimatorClient := mocks.NewFeeEstimatorClient(t) - l1Oracle := rollups.NewArbitrumL1GasOracle(logger.Test(t), feeEstimatorClient) + l1Oracle, err := rollups.NewArbitrumL1GasOracle(logger.Test(t), feeEstimatorClient) + require.NoError(t, err) o := gas.NewArbitrumEstimator(logger.Test(t), &arbConfig{}, feeEstimatorClient, l1Oracle) @@ -119,7 +122,8 @@ func TestArbitrumEstimator(t *testing.T) { t.Run("gas price is lower than global max gas price", func(t *testing.T) { feeEstimatorClient := mocks.NewFeeEstimatorClient(t) - l1Oracle := rollups.NewArbitrumL1GasOracle(logger.Test(t), feeEstimatorClient) + l1Oracle, err := rollups.NewArbitrumL1GasOracle(logger.Test(t), feeEstimatorClient) + require.NoError(t, err) o := gas.NewArbitrumEstimator(logger.Test(t), &arbConfig{}, feeEstimatorClient, l1Oracle) @@ -144,16 +148,18 @@ func TestArbitrumEstimator(t *testing.T) { t.Run("calling BumpLegacyGas on unstarted arbitrum estimator returns error", func(t *testing.T) { feeEstimatorClient := mocks.NewFeeEstimatorClient(t) - l1Oracle := rollups.NewArbitrumL1GasOracle(logger.Test(t), feeEstimatorClient) + l1Oracle, err := rollups.NewArbitrumL1GasOracle(logger.Test(t), feeEstimatorClient) + require.NoError(t, err) o := gas.NewArbitrumEstimator(logger.Test(t), &arbConfig{}, feeEstimatorClient, l1Oracle) - _, _, err := o.BumpLegacyGas(tests.Context(t), assets.NewWeiI(42), gasLimit, assets.NewWeiI(10), nil) + _, _, err = o.BumpLegacyGas(tests.Context(t), assets.NewWeiI(42), gasLimit, assets.NewWeiI(10), nil) assert.EqualError(t, err, "estimator is not started") }) t.Run("calling GetLegacyGas on started estimator if initial call failed returns error", func(t *testing.T) { feeEstimatorClient := mocks.NewFeeEstimatorClient(t) - l1Oracle := rollups.NewArbitrumL1GasOracle(logger.Test(t), feeEstimatorClient) + l1Oracle, err := rollups.NewArbitrumL1GasOracle(logger.Test(t), feeEstimatorClient) + require.NoError(t, err) o := gas.NewArbitrumEstimator(logger.Test(t), &arbConfig{}, feeEstimatorClient, l1Oracle) @@ -168,35 +174,38 @@ func TestArbitrumEstimator(t *testing.T) { servicetest.RunHealthy(t, o) - _, _, err := o.GetLegacyGas(tests.Context(t), calldata, gasLimit, maxGasPrice) + _, _, err = o.GetLegacyGas(tests.Context(t), calldata, gasLimit, maxGasPrice) assert.EqualError(t, err, "failed to estimate gas; gas price not set") }) t.Run("calling GetDynamicFee always returns error", func(t *testing.T) { feeEstimatorClient := mocks.NewFeeEstimatorClient(t) - l1Oracle := rollups.NewArbitrumL1GasOracle(logger.Test(t), feeEstimatorClient) + l1Oracle, err := rollups.NewArbitrumL1GasOracle(logger.Test(t), feeEstimatorClient) + require.NoError(t, err) o := gas.NewArbitrumEstimator(logger.Test(t), &arbConfig{}, feeEstimatorClient, l1Oracle) - _, err := o.GetDynamicFee(tests.Context(t), maxGasPrice) + _, err = o.GetDynamicFee(tests.Context(t), maxGasPrice) assert.EqualError(t, err, "dynamic fees are not implemented for this estimator") }) t.Run("calling BumpDynamicFee always returns error", func(t *testing.T) { feeEstimatorClient := mocks.NewFeeEstimatorClient(t) - l1Oracle := rollups.NewArbitrumL1GasOracle(logger.Test(t), feeEstimatorClient) + l1Oracle, err := rollups.NewArbitrumL1GasOracle(logger.Test(t), feeEstimatorClient) + require.NoError(t, err) o := gas.NewArbitrumEstimator(logger.Test(t), &arbConfig{}, feeEstimatorClient, l1Oracle) fee := gas.DynamicFee{ FeeCap: assets.NewWeiI(42), TipCap: assets.NewWeiI(5), } - _, err := o.BumpDynamicFee(tests.Context(t), fee, maxGasPrice, nil) + _, err = o.BumpDynamicFee(tests.Context(t), fee, maxGasPrice, nil) assert.EqualError(t, err, "dynamic fees are not implemented for this estimator") }) t.Run("limit computes", func(t *testing.T) { feeEstimatorClient := mocks.NewFeeEstimatorClient(t) - l1Oracle := rollups.NewArbitrumL1GasOracle(logger.Test(t), feeEstimatorClient) + l1Oracle, err := rollups.NewArbitrumL1GasOracle(logger.Test(t), feeEstimatorClient) + require.NoError(t, err) feeEstimatorClient.On("CallContext", mock.Anything, mock.Anything, "eth_gasPrice").Return(nil).Run(func(args mock.Arguments) { res := args.Get(1).(*hexutil.Big) @@ -232,7 +241,8 @@ func TestArbitrumEstimator(t *testing.T) { t.Run("limit exceeds max", func(t *testing.T) { feeEstimatorClient := mocks.NewFeeEstimatorClient(t) - l1Oracle := rollups.NewArbitrumL1GasOracle(logger.Test(t), feeEstimatorClient) + l1Oracle, err := rollups.NewArbitrumL1GasOracle(logger.Test(t), feeEstimatorClient) + require.NoError(t, err) feeEstimatorClient.On("CallContext", mock.Anything, mock.Anything, "eth_gasPrice").Return(nil).Run(func(args mock.Arguments) { res := args.Get(1).(*hexutil.Big) diff --git a/core/chains/evm/gas/mocks/config.go b/core/chains/evm/gas/mocks/config.go index 5daac21176..b4a5e40f2f 100644 --- a/core/chains/evm/gas/mocks/config.go +++ b/core/chains/evm/gas/mocks/config.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. 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 1213a71742..8c8693413f 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/chains/evm/gas/mocks/evm_fee_estimator.go b/core/chains/evm/gas/mocks/evm_fee_estimator.go index 5179a2560d..7382133561 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/chains/evm/gas/mocks/fee_estimator_client.go b/core/chains/evm/gas/mocks/fee_estimator_client.go index d81120e2ed..d375b478a7 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/chains/evm/gas/models.go b/core/chains/evm/gas/models.go index 00357aac70..f5b7a7f96a 100644 --- a/core/chains/evm/gas/models.go +++ b/core/chains/evm/gas/models.go @@ -52,7 +52,7 @@ type feeEstimatorClient interface { } // NewEstimator returns the estimator for a given config -func NewEstimator(lggr logger.Logger, ethClient feeEstimatorClient, cfg Config, geCfg evmconfig.GasEstimator) EvmFeeEstimator { +func NewEstimator(lggr logger.Logger, ethClient feeEstimatorClient, cfg Config, geCfg evmconfig.GasEstimator) (EvmFeeEstimator, error) { bh := geCfg.BlockHistory() s := geCfg.Mode() lggr.Infow(fmt.Sprintf("Initializing EVM gas estimator in mode: %s", s), @@ -80,13 +80,21 @@ func NewEstimator(lggr logger.Logger, ethClient feeEstimatorClient, cfg Config, var l1Oracle rollups.L1Oracle lggr.Infow("Checking if chain type is roll up", "chainType", cfg.ChainType(), "isRollUp", rollups.IsRollupWithL1Support(cfg.ChainType())) if rollups.IsRollupWithL1Support(cfg.ChainType()) { - l1Oracle = rollups.NewL1GasOracle(lggr, ethClient, cfg.ChainType()) + var err error + l1Oracle, err = rollups.NewL1GasOracle(lggr, ethClient, cfg.ChainType()) + if err != nil { + return nil, fmt.Errorf("failed to initialize L1 oracle: %w", err) + } } var newEstimator func(logger.Logger) EvmEstimator switch s { case "Arbitrum": + arbOracle, err := rollups.NewArbitrumL1GasOracle(lggr, ethClient) + if err != nil { + return nil, fmt.Errorf("failed to initialize Arbitrum L1 oracle: %w", err) + } newEstimator = func(l logger.Logger) EvmEstimator { - return NewArbitrumEstimator(lggr, geCfg, ethClient, rollups.NewArbitrumL1GasOracle(lggr, ethClient)) + return NewArbitrumEstimator(lggr, geCfg, ethClient, arbOracle) } case "BlockHistory": newEstimator = func(l logger.Logger) EvmEstimator { @@ -106,7 +114,7 @@ func NewEstimator(lggr logger.Logger, ethClient feeEstimatorClient, cfg Config, return NewFixedPriceEstimator(geCfg, ethClient, bh, lggr, l1Oracle) } } - return NewEvmFeeEstimator(lggr, newEstimator, df, geCfg) + return NewEvmFeeEstimator(lggr, newEstimator, df, geCfg), nil } // DynamicFee encompasses both FeeCap and TipCap for EIP1559 transactions diff --git a/core/chains/evm/gas/models_test.go b/core/chains/evm/gas/models_test.go index 059d0ed3d0..92ea901596 100644 --- a/core/chains/evm/gas/models_test.go +++ b/core/chains/evm/gas/models_test.go @@ -65,7 +65,8 @@ func TestWrappedEvmEstimator(t *testing.T) { assert.Nil(t, l1Oracle) // expect l1Oracle - oracle := rollups.NewL1GasOracle(lggr, nil, chaintype.ChainOptimismBedrock) + oracle, err := rollups.NewL1GasOracle(lggr, nil, chaintype.ChainOptimismBedrock) + require.NoError(t, err) // cast oracle to L1Oracle interface estimator = gas.NewEvmFeeEstimator(lggr, getEst, false, geCfg) diff --git a/core/chains/evm/gas/rollups/arbitrum_l1_oracle.go b/core/chains/evm/gas/rollups/arbitrum_l1_oracle.go index 9e15cfe4c9..eb8dae3de2 100644 --- a/core/chains/evm/gas/rollups/arbitrum_l1_oracle.go +++ b/core/chains/evm/gas/rollups/arbitrum_l1_oracle.go @@ -71,7 +71,7 @@ const ( ArbGasInfo_getPricesInArbGas = "02199f34" ) -func NewArbitrumL1GasOracle(lggr logger.Logger, ethClient l1OracleClient) *arbitrumL1Oracle { +func NewArbitrumL1GasOracle(lggr logger.Logger, ethClient l1OracleClient) (*arbitrumL1Oracle, error) { var l1GasPriceAddress, gasPriceMethod, l1GasCostAddress, gasCostMethod string var l1GasPriceMethodAbi, l1GasCostMethodAbi abi.ABI var gasPriceErr, gasCostErr error @@ -84,10 +84,10 @@ func NewArbitrumL1GasOracle(lggr logger.Logger, ethClient l1OracleClient) *arbit l1GasCostMethodAbi, gasCostErr = abi.JSON(strings.NewReader(GasEstimateL1ComponentAbiString)) if gasPriceErr != nil { - panic("Failed to parse L1 gas price method ABI for chain: arbitrum") + return nil, fmt.Errorf("failed to parse L1 gas price method ABI for chain: arbitrum: %w", gasPriceErr) } if gasCostErr != nil { - panic("Failed to parse L1 gas cost method ABI for chain: arbitrum") + return nil, fmt.Errorf("failed to parse L1 gas cost method ABI for chain: arbitrum: %w", gasCostErr) } return &arbitrumL1Oracle{ @@ -106,7 +106,7 @@ func NewArbitrumL1GasOracle(lggr logger.Logger, ethClient l1OracleClient) *arbit chInitialised: make(chan struct{}), chStop: make(chan struct{}), chDone: make(chan struct{}), - } + }, nil } func (o *arbitrumL1Oracle) Name() string { diff --git a/core/chains/evm/gas/rollups/l1_oracle.go b/core/chains/evm/gas/rollups/l1_oracle.go index 97cc673154..234ddd9fdc 100644 --- a/core/chains/evm/gas/rollups/l1_oracle.go +++ b/core/chains/evm/gas/rollups/l1_oracle.go @@ -52,20 +52,24 @@ func IsRollupWithL1Support(chainType chaintype.ChainType) bool { return slices.Contains(supportedChainTypes, chainType) } -func NewL1GasOracle(lggr logger.Logger, ethClient l1OracleClient, chainType chaintype.ChainType) L1Oracle { +func NewL1GasOracle(lggr logger.Logger, ethClient l1OracleClient, chainType chaintype.ChainType) (L1Oracle, error) { if !IsRollupWithL1Support(chainType) { - return nil + return nil, nil } var l1Oracle L1Oracle + var err error switch chainType { case chaintype.ChainOptimismBedrock, chaintype.ChainKroma, chaintype.ChainScroll: - l1Oracle = NewOpStackL1GasOracle(lggr, ethClient, chainType) + l1Oracle, err = NewOpStackL1GasOracle(lggr, ethClient, chainType) case chaintype.ChainArbitrum: - l1Oracle = NewArbitrumL1GasOracle(lggr, ethClient) + l1Oracle, err = NewArbitrumL1GasOracle(lggr, ethClient) case chaintype.ChainZkSync: l1Oracle = NewZkSyncL1GasOracle(lggr, ethClient) default: - panic(fmt.Sprintf("Received unspported chaintype %s", chainType)) + return nil, fmt.Errorf("received unsupported chaintype %s", chainType) } - return l1Oracle + if err != nil { + return nil, fmt.Errorf("failed to initialize L1 oracle for chaintype %s: %w", chainType, err) + } + return l1Oracle, nil } diff --git a/core/chains/evm/gas/rollups/l1_oracle_abi.go b/core/chains/evm/gas/rollups/l1_oracle_abi.go index dc18e43c98..fa5d9c8539 100644 --- a/core/chains/evm/gas/rollups/l1_oracle_abi.go +++ b/core/chains/evm/gas/rollups/l1_oracle_abi.go @@ -12,6 +12,10 @@ const GasEstimateL1ComponentAbiString = `[{"inputs":[{"internalType":"address"," const L1BaseFeeAbiString = `[{"inputs":[],"name":"l1BaseFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]` const GetL1FeeAbiString = `[{"inputs":[{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"getL1Fee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]` -// ABIs for OP Stack Ecotone GasPriceOracle methods needed to calculated encoded gas price +// ABIs for OP Stack GasPriceOracle methods needed to calculated encoded gas price const OPIsEcotoneAbiString = `[{"inputs":[],"name":"isEcotone","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]` -const OPGetL1GasUsedAbiString = `[{"inputs":[{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"getL1GasUsed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]` +const OPIsFjordAbiString = `[{"inputs":[],"name":"isFjord","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]` +const OPBaseFeeScalarAbiString = `[{"inputs":[],"name":"baseFeeScalar","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"}]` +const OPBlobBaseFeeAbiString = `[{"inputs":[],"name":"blobBaseFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]` +const OPBlobBaseFeeScalarAbiString = `[{"inputs":[],"name":"blobBaseFeeScalar","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"}]` +const OPDecimalsAbiString = `[{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"}]` diff --git a/core/chains/evm/gas/rollups/l1_oracle_test.go b/core/chains/evm/gas/rollups/l1_oracle_test.go index 800a3558d1..7a28523d39 100644 --- a/core/chains/evm/gas/rollups/l1_oracle_test.go +++ b/core/chains/evm/gas/rollups/l1_oracle_test.go @@ -2,7 +2,6 @@ package rollups import ( "encoding/hex" - "errors" "math/big" "strings" "testing" @@ -31,7 +30,9 @@ func TestL1Oracle(t *testing.T) { t.Run("Unsupported ChainType returns nil", func(t *testing.T) { ethClient := mocks.NewL1OracleClient(t) - assert.Nil(t, NewL1GasOracle(logger.Test(t), ethClient, chaintype.ChainCelo)) + oracle, err := NewL1GasOracle(logger.Test(t), ethClient, chaintype.ChainCelo) + require.NoError(t, err) + assert.Nil(t, oracle) }) } @@ -41,9 +42,10 @@ func TestL1Oracle_GasPrice(t *testing.T) { t.Run("Calling GasPrice on unstarted L1Oracle returns error", func(t *testing.T) { ethClient := mocks.NewL1OracleClient(t) - oracle := NewL1GasOracle(logger.Test(t), ethClient, chaintype.ChainOptimismBedrock) + oracle, err := NewL1GasOracle(logger.Test(t), ethClient, chaintype.ChainOptimismBedrock) + require.NoError(t, err) - _, err := oracle.GasPrice(tests.Context(t)) + _, err = oracle.GasPrice(tests.Context(t)) assert.EqualError(t, err, "L1GasOracle is not started; cannot estimate gas") }) @@ -63,7 +65,8 @@ func TestL1Oracle_GasPrice(t *testing.T) { assert.Nil(t, blockNumber) }).Return(common.BigToHash(l1BaseFee).Bytes(), nil) - oracle := NewL1GasOracle(logger.Test(t), ethClient, chaintype.ChainArbitrum) + oracle, err := NewL1GasOracle(logger.Test(t), ethClient, chaintype.ChainArbitrum) + require.NoError(t, err) servicetest.RunHealthy(t, oracle) gasPrice, err := oracle.GasPrice(tests.Context(t)) @@ -78,19 +81,7 @@ func TestL1Oracle_GasPrice(t *testing.T) { l1GasPriceMethodAbi, err := abi.JSON(strings.NewReader(L1BaseFeeAbiString)) require.NoError(t, err) - isEcotoneAbiString, err := abi.JSON(strings.NewReader(OPIsEcotoneAbiString)) - require.NoError(t, err) - - ethClient := mocks.NewL1OracleClient(t) - ethClient.On("CallContract", mock.Anything, mock.IsType(ethereum.CallMsg{}), mock.IsType(&big.Int{})).Run(func(args mock.Arguments) { - callMsg := args.Get(1).(ethereum.CallMsg) - blockNumber := args.Get(2).(*big.Int) - var payload []byte - payload, err = isEcotoneAbiString.Pack("isEcotone") - require.NoError(t, err) - require.Equal(t, payload, callMsg.Data) - assert.Nil(t, blockNumber) - }).Return(nil, errors.New("not ecotone")).Once() + ethClient := setupUpgradeCheck(t, KromaGasOracleAddress, false, false) // Ecotone, Fjord disabled ethClient.On("CallContract", mock.Anything, mock.IsType(ethereum.CallMsg{}), mock.IsType(&big.Int{})).Run(func(args mock.Arguments) { callMsg := args.Get(1).(ethereum.CallMsg) @@ -102,7 +93,8 @@ func TestL1Oracle_GasPrice(t *testing.T) { assert.Nil(t, blockNumber) }).Return(common.BigToHash(l1BaseFee).Bytes(), nil) - oracle := newOpStackL1GasOracle(logger.Test(t), ethClient, chaintype.ChainKroma, KromaGasOracleAddress) + oracle, err := NewL1GasOracle(logger.Test(t), ethClient, chaintype.ChainKroma) + require.NoError(t, err) servicetest.RunHealthy(t, oracle) gasPrice, err := oracle.GasPrice(tests.Context(t)) @@ -117,19 +109,7 @@ func TestL1Oracle_GasPrice(t *testing.T) { l1GasPriceMethodAbi, err := abi.JSON(strings.NewReader(L1BaseFeeAbiString)) require.NoError(t, err) - isEcotoneAbiString, err := abi.JSON(strings.NewReader(OPIsEcotoneAbiString)) - require.NoError(t, err) - - ethClient := mocks.NewL1OracleClient(t) - ethClient.On("CallContract", mock.Anything, mock.IsType(ethereum.CallMsg{}), mock.IsType(&big.Int{})).Run(func(args mock.Arguments) { - callMsg := args.Get(1).(ethereum.CallMsg) - blockNumber := args.Get(2).(*big.Int) - var payload []byte - payload, err = isEcotoneAbiString.Pack("isEcotone") - require.NoError(t, err) - require.Equal(t, payload, callMsg.Data) - assert.Nil(t, blockNumber) - }).Return(nil, errors.New("not ecotone")).Once() + ethClient := setupUpgradeCheck(t, OPGasOracleAddress, false, false) // Ecotone, Fjord disabled ethClient.On("CallContract", mock.Anything, mock.IsType(ethereum.CallMsg{}), mock.IsType(&big.Int{})).Run(func(args mock.Arguments) { callMsg := args.Get(1).(ethereum.CallMsg) @@ -141,7 +121,8 @@ func TestL1Oracle_GasPrice(t *testing.T) { assert.Nil(t, blockNumber) }).Return(common.BigToHash(l1BaseFee).Bytes(), nil) - oracle := newOpStackL1GasOracle(logger.Test(t), ethClient, chaintype.ChainOptimismBedrock, OPGasOracleAddress) + oracle, err := NewL1GasOracle(logger.Test(t), ethClient, chaintype.ChainOptimismBedrock) + require.NoError(t, err) servicetest.RunHealthy(t, oracle) gasPrice, err := oracle.GasPrice(tests.Context(t)) @@ -155,19 +136,7 @@ func TestL1Oracle_GasPrice(t *testing.T) { l1GasPriceMethodAbi, err := abi.JSON(strings.NewReader(L1BaseFeeAbiString)) require.NoError(t, err) - isEcotoneAbiString, err := abi.JSON(strings.NewReader(OPIsEcotoneAbiString)) - require.NoError(t, err) - - ethClient := mocks.NewL1OracleClient(t) - ethClient.On("CallContract", mock.Anything, mock.IsType(ethereum.CallMsg{}), mock.IsType(&big.Int{})).Run(func(args mock.Arguments) { - callMsg := args.Get(1).(ethereum.CallMsg) - blockNumber := args.Get(2).(*big.Int) - var payload []byte - payload, err = isEcotoneAbiString.Pack("isEcotone") - require.NoError(t, err) - require.Equal(t, payload, callMsg.Data) - assert.Nil(t, blockNumber) - }).Return(nil, errors.New("not ecotone")).Once() + ethClient := setupUpgradeCheck(t, ScrollGasOracleAddress, false, false) // Ecotone, Fjord disabled ethClient.On("CallContract", mock.Anything, mock.IsType(ethereum.CallMsg{}), mock.IsType(&big.Int{})).Run(func(args mock.Arguments) { callMsg := args.Get(1).(ethereum.CallMsg) @@ -179,9 +148,9 @@ func TestL1Oracle_GasPrice(t *testing.T) { assert.Nil(t, blockNumber) }).Return(common.BigToHash(l1BaseFee).Bytes(), nil) - oracle := NewL1GasOracle(logger.Test(t), ethClient, chaintype.ChainScroll) - require.NoError(t, oracle.Start(tests.Context(t))) - t.Cleanup(func() { assert.NoError(t, oracle.Close()) }) + oracle, err := NewL1GasOracle(logger.Test(t), ethClient, chaintype.ChainScroll) + require.NoError(t, err) + servicetest.RunHealthy(t, oracle) gasPrice, err := oracle.GasPrice(tests.Context(t)) require.NoError(t, err) @@ -216,9 +185,9 @@ func TestL1Oracle_GasPrice(t *testing.T) { assert.Nil(t, blockNumber) }).Return(common.BigToHash(gasPerPubByteL2).Bytes(), nil) - oracle := NewL1GasOracle(logger.Test(t), ethClient, chaintype.ChainZkSync) - require.NoError(t, oracle.Start(tests.Context(t))) - t.Cleanup(func() { assert.NoError(t, oracle.Close()) }) + oracle, err := NewL1GasOracle(logger.Test(t), ethClient, chaintype.ChainZkSync) + require.NoError(t, err) + servicetest.RunHealthy(t, oracle) gasPrice, err := oracle.GasPrice(tests.Context(t)) require.NoError(t, err) @@ -260,7 +229,8 @@ func TestL1Oracle_GetGasCost(t *testing.T) { require.Equal(t, blockNum, blockNumber) }).Return(result, nil) - oracle := NewL1GasOracle(logger.Test(t), ethClient, chaintype.ChainArbitrum) + oracle, err := NewL1GasOracle(logger.Test(t), ethClient, chaintype.ChainArbitrum) + require.NoError(t, err) gasCost, err := oracle.GetGasCost(tests.Context(t), tx, blockNum) require.NoError(t, err) @@ -272,9 +242,10 @@ func TestL1Oracle_GetGasCost(t *testing.T) { tx := types.NewTx(&types.LegacyTx{}) ethClient := mocks.NewL1OracleClient(t) - oracle := NewL1GasOracle(logger.Test(t), ethClient, chaintype.ChainKroma) + oracle, err := NewL1GasOracle(logger.Test(t), ethClient, chaintype.ChainKroma) + require.NoError(t, err) - _, err := oracle.GetGasCost(tests.Context(t), tx, blockNum) + _, err = oracle.GetGasCost(tests.Context(t), tx, blockNum) require.Error(t, err, "L1 gas cost not supported for this chain: kroma") }) @@ -306,7 +277,8 @@ func TestL1Oracle_GetGasCost(t *testing.T) { require.Equal(t, blockNum, blockNumber) }).Return(common.BigToHash(l1GasCost).Bytes(), nil) - oracle := NewL1GasOracle(logger.Test(t), ethClient, chaintype.ChainOptimismBedrock) + oracle, err := NewL1GasOracle(logger.Test(t), ethClient, chaintype.ChainOptimismBedrock) + require.NoError(t, err) gasCost, err := oracle.GetGasCost(tests.Context(t), tx, blockNum) require.NoError(t, err) @@ -341,7 +313,8 @@ func TestL1Oracle_GetGasCost(t *testing.T) { require.Equal(t, blockNum, blockNumber) }).Return(common.BigToHash(l1GasCost).Bytes(), nil) - oracle := NewL1GasOracle(logger.Test(t), ethClient, chaintype.ChainScroll) + oracle, err := NewL1GasOracle(logger.Test(t), ethClient, chaintype.ChainScroll) + require.NoError(t, err) gasCost, err := oracle.GetGasCost(tests.Context(t), tx, blockNum) require.NoError(t, err) diff --git a/core/chains/evm/gas/rollups/mocks/da_price_reader.go b/core/chains/evm/gas/rollups/mocks/da_price_reader.go deleted file mode 100644 index 4157eb1494..0000000000 --- a/core/chains/evm/gas/rollups/mocks/da_price_reader.go +++ /dev/null @@ -1,59 +0,0 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. - -package mocks - -import ( - context "context" - big "math/big" - - mock "github.com/stretchr/testify/mock" -) - -// DAPriceReader is an autogenerated mock type for the daPriceReader type -type DAPriceReader struct { - mock.Mock -} - -// GetDAGasPrice provides a mock function with given fields: ctx -func (_m *DAPriceReader) GetDAGasPrice(ctx context.Context) (*big.Int, error) { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for GetDAGasPrice") - } - - var r0 *big.Int - var r1 error - if rf, ok := ret.Get(0).(func(context.Context) (*big.Int, error)); ok { - return rf(ctx) - } - if rf, ok := ret.Get(0).(func(context.Context) *big.Int); ok { - r0 = rf(ctx) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*big.Int) - } - } - - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(ctx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// NewDAPriceReader creates a new instance of DAPriceReader. 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 NewDAPriceReader(t interface { - mock.TestingT - Cleanup(func()) -}) *DAPriceReader { - mock := &DAPriceReader{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/core/chains/evm/gas/rollups/mocks/l1_oracle.go b/core/chains/evm/gas/rollups/mocks/l1_oracle.go index 79d4d64ecd..f9f0a34b8a 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks 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 146f2cd680..9225c247ca 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/chains/evm/gas/rollups/op_l1_oracle.go b/core/chains/evm/gas/rollups/op_l1_oracle.go index 1977d139f9..ade7604a44 100644 --- a/core/chains/evm/gas/rollups/op_l1_oracle.go +++ b/core/chains/evm/gas/rollups/op_l1_oracle.go @@ -27,64 +27,76 @@ import ( ) // Reads L2-specific precompiles and caches the l1GasPrice set by the L2. -type OptimismL1Oracle struct { +type optimismL1Oracle struct { services.StateMachine client l1OracleClient pollPeriod time.Duration logger logger.SugaredLogger chainType chaintype.ChainType - l1OracleAddress string - gasPriceMethod string - l1GasPriceMethodAbi abi.ABI - l1GasPriceMu sync.RWMutex - l1GasPrice priceEntry - - gasCostMethod string - l1GasCostMethodAbi abi.ABI + l1OracleAddress string + l1GasPriceMu sync.RWMutex + l1GasPrice priceEntry + isEcotone bool + isFjord bool + upgradeCheckTs time.Time chInitialised chan struct{} chStop services.StopChan chDone chan struct{} - isEcotoneMethodAbi abi.ABI - - l1BaseFeeCalldata []byte - isEcotoneCalldata []byte - getL1GasUsedCalldata []byte - getL1FeeCalldata []byte - - isEcotone bool - isEcotoneCheckTs int64 + getL1FeeMethodAbi abi.ABI + l1BaseFeeCalldata []byte + baseFeeScalarCalldata []byte + blobBaseFeeCalldata []byte + blobBaseFeeScalarCalldata []byte + decimalsCalldata []byte + isEcotoneCalldata []byte + isEcotoneMethodAbi abi.ABI + isFjordCalldata []byte + isFjordMethodAbi abi.ABI } const ( - // OPStackGasOracle_isEcotone fetches if the OP Stack GasPriceOracle contract has upgraded to Ecotone - OPStackGasOracle_isEcotone = "isEcotone" - // OPStackGasOracle_getL1GasUsed fetches the l1 gas used for given tx bytes - OPStackGasOracle_getL1GasUsed = "getL1GasUsed" - // OPStackGasOracle_isEcotonePollingPeriod is the interval to poll if chain has upgraded to Ecotone - // Set to poll every 4 hours - OPStackGasOracle_isEcotonePollingPeriod = 14400 - // OPStackGasOracleAddress is the address of the precompiled contract that exists on OP stack chain. - // OPStackGasOracle_l1BaseFee fetches the l1 base fee set in the OP Stack GasPriceOracle contract - // OPStackGasOracle_l1BaseFee is a hex encoded call to: - // `function l1BaseFee() external view returns (uint256);` - OPStackGasOracle_l1BaseFee = "l1BaseFee" - // OPStackGasOracle_getL1Fee fetches the l1 fee for given tx bytes - // OPStackGasOracle_getL1Fee is a hex encoded call to: + // upgradePollingPeriod is the interval to poll if chain has been upgraded + upgradePollingPeriod = 4 * time.Hour + // isEcotone fetches if the OP Stack GasPriceOracle contract has upgraded to Ecotone + isEcotoneMethod = "isEcotone" + // isFjord fetches if the OP Stack GasPriceOracle contract has upgraded to Fjord + isFjordMethod = "isFjord" + // getL1Fee fetches the l1 fee for given tx bytes + // getL1Fee is a hex encoded call to: // `function getL1Fee(bytes) external view returns (uint256);` - OPStackGasOracle_getL1Fee = "getL1Fee" - // This is the case for Optimism and Base. + getL1FeeMethod = "getL1Fee" + // l1BaseFee fetches the l1 base fee set in the OP Stack GasPriceOracle contract + // l1BaseFee is a hex encoded call to: + // `function l1BaseFee() external view returns (uint256);` + l1BaseFeeMethod = "l1BaseFee" + // baseFeeScalar fetches the l1 base fee scalar for gas price calculation + // baseFeeScalar is a hex encoded call to: + // `function baseFeeScalar() public view returns (uint32);` + baseFeeScalarMethod = "baseFeeScalar" + // blobBaseFee fetches the l1 blob base fee for gas price calculation + // blobBaseFee is a hex encoded call to: + // `function blobBaseFee() public view returns (uint256);` + blobBaseFeeMethod = "blobBaseFee" + // blobBaseFeeScalar fetches the l1 blob base fee scalar for gas price calculation + // blobBaseFeeScalar is a hex encoded call to: + // `function blobBaseFeeScalar() public view returns (uint32);` + blobBaseFeeScalarMethod = "blobBaseFeeScalar" + // decimals fetches the number of decimals used in the scalar for gas price calculation + // decimals is a hex encoded call to: + // `function decimals() public pure returns (uint256);` + decimalsMethod = "decimals" + // OPGasOracleAddress is the address of the precompiled contract that exists on Optimism and Base. OPGasOracleAddress = "0x420000000000000000000000000000000000000F" - // GasOracleAddress is the address of the precompiled contract that exists on Kroma chain. - // This is the case for Kroma. + // KromaGasOracleAddress is the address of the precompiled contract that exists on Kroma. KromaGasOracleAddress = "0x4200000000000000000000000000000000000005" - // ScrollGasOracleAddress is the address of the precompiled contract that exists on Scroll chain. + // ScrollGasOracleAddress is the address of the precompiled contract that exists on Scroll. ScrollGasOracleAddress = "0x5300000000000000000000000000000000000002" ) -func NewOpStackL1GasOracle(lggr logger.Logger, ethClient l1OracleClient, chainType chaintype.ChainType) *OptimismL1Oracle { +func NewOpStackL1GasOracle(lggr logger.Logger, ethClient l1OracleClient, chainType chaintype.ChainType) (*optimismL1Oracle, error) { var precompileAddress string switch chainType { case chaintype.ChainOptimismBedrock: @@ -94,106 +106,128 @@ func NewOpStackL1GasOracle(lggr logger.Logger, ethClient l1OracleClient, chainTy case chaintype.ChainScroll: precompileAddress = ScrollGasOracleAddress default: - panic(fmt.Sprintf("Received unspported chaintype %s", chainType)) + return nil, fmt.Errorf("received unsupported chaintype %s", chainType) } return newOpStackL1GasOracle(lggr, ethClient, chainType, precompileAddress) } -func newOpStackL1GasOracle(lggr logger.Logger, ethClient l1OracleClient, chainType chaintype.ChainType, precompileAddress string) *OptimismL1Oracle { - var l1OracleAddress, gasPriceMethod, gasCostMethod string - var l1GasPriceMethodAbi, l1GasCostMethodAbi abi.ABI - var gasPriceErr, gasCostErr error - - l1OracleAddress = precompileAddress - gasPriceMethod = OPStackGasOracle_l1BaseFee - l1GasPriceMethodAbi, gasPriceErr = abi.JSON(strings.NewReader(L1BaseFeeAbiString)) - gasCostMethod = OPStackGasOracle_getL1Fee - l1GasCostMethodAbi, gasCostErr = abi.JSON(strings.NewReader(GetL1FeeAbiString)) - - if gasPriceErr != nil { - panic(fmt.Sprintf("Failed to parse L1 gas price method ABI for chain: %s", chainType)) - } - if gasCostErr != nil { - panic(fmt.Sprintf("Failed to parse L1 gas cost method ABI for chain: %s", chainType)) +func newOpStackL1GasOracle(lggr logger.Logger, ethClient l1OracleClient, chainType chaintype.ChainType, precompileAddress string) (*optimismL1Oracle, error) { + getL1FeeMethodAbi, err := abi.JSON(strings.NewReader(GetL1FeeAbiString)) + if err != nil { + return nil, fmt.Errorf("failed to parse L1 gas cost method ABI for chain: %s", chainType) } // encode calldata for each method; these calldata will remain the same for each call, we can encode them just once + // Encode calldata for l1BaseFee method l1BaseFeeMethodAbi, err := abi.JSON(strings.NewReader(L1BaseFeeAbiString)) if err != nil { - panic(fmt.Errorf("failed to parse GasPriceOracle %s() method ABI for chain: %s; %w", OPStackGasOracle_l1BaseFee, chainType, err)) + return nil, fmt.Errorf("failed to parse GasPriceOracle %s() method ABI for chain: %s; %w", l1BaseFeeMethod, chainType, err) } - l1BaseFeeCalldata, err := l1BaseFeeMethodAbi.Pack(OPStackGasOracle_l1BaseFee) + l1BaseFeeCalldata, err := l1BaseFeeMethodAbi.Pack(l1BaseFeeMethod) if err != nil { - panic(fmt.Errorf("failed to parse GasPriceOracle %s() calldata for chain: %s; %w", OPStackGasOracle_l1BaseFee, chainType, err)) + return nil, fmt.Errorf("failed to parse GasPriceOracle %s() calldata for chain: %s; %w", l1BaseFeeMethod, chainType, err) } + // Encode calldata for isEcotone method isEcotoneMethodAbi, err := abi.JSON(strings.NewReader(OPIsEcotoneAbiString)) if err != nil { - panic(fmt.Errorf("failed to parse GasPriceOracle %s() method ABI for chain: %s; %w", OPStackGasOracle_isEcotone, chainType, err)) + return nil, fmt.Errorf("failed to parse GasPriceOracle %s() method ABI for chain: %s; %w", isEcotoneMethod, chainType, err) } - isEcotoneCalldata, err := isEcotoneMethodAbi.Pack(OPStackGasOracle_isEcotone) + isEcotoneCalldata, err := isEcotoneMethodAbi.Pack(isEcotoneMethod) if err != nil { - panic(fmt.Errorf("failed to parse GasPriceOracle %s() calldata for chain: %s; %w", OPStackGasOracle_isEcotone, chainType, err)) + return nil, fmt.Errorf("failed to parse GasPriceOracle %s() calldata for chain: %s; %w", isEcotoneMethod, chainType, err) } - getL1GasUsedMethodAbi, err := abi.JSON(strings.NewReader(OPGetL1GasUsedAbiString)) + // Encode calldata for isFjord method + isFjordMethodAbi, err := abi.JSON(strings.NewReader(OPIsFjordAbiString)) if err != nil { - panic(fmt.Errorf("failed to parse GasPriceOracle %s() method ABI for chain: %s; %w", OPStackGasOracle_getL1GasUsed, chainType, err)) + return nil, fmt.Errorf("failed to parse GasPriceOracle %s() method ABI for chain: %s; %w", isFjordMethod, chainType, err) } - getL1GasUsedCalldata, err := getL1GasUsedMethodAbi.Pack(OPStackGasOracle_getL1GasUsed, []byte{0x1}) + isFjordCalldata, err := isFjordMethodAbi.Pack(isFjordMethod) if err != nil { - panic(fmt.Errorf("failed to parse GasPriceOracle %s() calldata for chain: %s; %w", OPStackGasOracle_getL1GasUsed, chainType, err)) + return nil, fmt.Errorf("failed to parse GasPriceOracle %s() calldata for chain: %s; %w", isFjordMethod, chainType, err) } - getL1FeeMethodAbi, err := abi.JSON(strings.NewReader(GetL1FeeAbiString)) + // Encode calldata for baseFeeScalar method + baseFeeScalarMethodAbi, err := abi.JSON(strings.NewReader(OPBaseFeeScalarAbiString)) if err != nil { - panic(fmt.Errorf("failed to parse GasPriceOracle %s() method ABI for chain: %s; %w", OPStackGasOracle_getL1Fee, chainType, err)) + return nil, fmt.Errorf("failed to parse GasPriceOracle %s() method ABI for chain: %s; %w", baseFeeScalarMethod, chainType, err) } - getL1FeeCalldata, err := getL1FeeMethodAbi.Pack(OPStackGasOracle_getL1Fee, []byte{0x1}) + baseFeeScalarCalldata, err := baseFeeScalarMethodAbi.Pack(baseFeeScalarMethod) if err != nil { - panic(fmt.Errorf("failed to parse GasPriceOracle %s() calldata for chain: %s; %w", OPStackGasOracle_getL1Fee, chainType, err)) + return nil, fmt.Errorf("failed to parse GasPriceOracle %s() calldata for chain: %s; %w", baseFeeScalarMethod, chainType, err) } - return &OptimismL1Oracle{ + // Encode calldata for blobBaseFee method + blobBaseFeeMethodAbi, err := abi.JSON(strings.NewReader(OPBlobBaseFeeAbiString)) + if err != nil { + return nil, fmt.Errorf("failed to parse GasPriceOracle %s() method ABI for chain: %s; %w", blobBaseFeeMethod, chainType, err) + } + blobBaseFeeCalldata, err := blobBaseFeeMethodAbi.Pack(blobBaseFeeMethod) + if err != nil { + return nil, fmt.Errorf("failed to parse GasPriceOracle %s() calldata for chain: %s; %w", blobBaseFeeMethod, chainType, err) + } + + // Encode calldata for blobBaseFeeScalar method + blobBaseFeeScalarMethodAbi, err := abi.JSON(strings.NewReader(OPBlobBaseFeeScalarAbiString)) + if err != nil { + return nil, fmt.Errorf("failed to parse GasPriceOracle %s() method ABI for chain: %s; %w", blobBaseFeeScalarMethod, chainType, err) + } + blobBaseFeeScalarCalldata, err := blobBaseFeeScalarMethodAbi.Pack(blobBaseFeeScalarMethod) + if err != nil { + return nil, fmt.Errorf("failed to parse GasPriceOracle %s() calldata for chain: %s; %w", blobBaseFeeScalarMethod, chainType, err) + } + + // Encode calldata for decimals method + decimalsMethodAbi, err := abi.JSON(strings.NewReader(OPDecimalsAbiString)) + if err != nil { + return nil, fmt.Errorf("failed to parse GasPriceOracle %s() method ABI for chain: %s; %w", decimalsMethod, chainType, err) + } + decimalsCalldata, err := decimalsMethodAbi.Pack(decimalsMethod) + if err != nil { + return nil, fmt.Errorf("failed to parse GasPriceOracle %s() calldata for chain: %s; %w", decimalsMethod, chainType, err) + } + + return &optimismL1Oracle{ client: ethClient, pollPeriod: PollPeriod, - logger: logger.Sugared(logger.Named(lggr, "L1GasOracle(optimismBedrock)")), + logger: logger.Sugared(logger.Named(lggr, fmt.Sprintf("L1GasOracle(%s)", chainType))), chainType: chainType, - l1OracleAddress: l1OracleAddress, - gasPriceMethod: gasPriceMethod, - l1GasPriceMethodAbi: l1GasPriceMethodAbi, - gasCostMethod: gasCostMethod, - l1GasCostMethodAbi: l1GasCostMethodAbi, + l1OracleAddress: precompileAddress, + isEcotone: false, + isFjord: false, + upgradeCheckTs: time.Time{}, chInitialised: make(chan struct{}), chStop: make(chan struct{}), chDone: make(chan struct{}), - isEcotoneMethodAbi: isEcotoneMethodAbi, - - l1BaseFeeCalldata: l1BaseFeeCalldata, - isEcotoneCalldata: isEcotoneCalldata, - getL1GasUsedCalldata: getL1GasUsedCalldata, - getL1FeeCalldata: getL1FeeCalldata, - - isEcotone: false, - isEcotoneCheckTs: 0, - } + getL1FeeMethodAbi: getL1FeeMethodAbi, + l1BaseFeeCalldata: l1BaseFeeCalldata, + baseFeeScalarCalldata: baseFeeScalarCalldata, + blobBaseFeeCalldata: blobBaseFeeCalldata, + blobBaseFeeScalarCalldata: blobBaseFeeScalarCalldata, + decimalsCalldata: decimalsCalldata, + isEcotoneCalldata: isEcotoneCalldata, + isEcotoneMethodAbi: isEcotoneMethodAbi, + isFjordCalldata: isFjordCalldata, + isFjordMethodAbi: isFjordMethodAbi, + }, nil } -func (o *OptimismL1Oracle) Name() string { +func (o *optimismL1Oracle) Name() string { return o.logger.Name() } -func (o *OptimismL1Oracle) Start(ctx context.Context) error { +func (o *optimismL1Oracle) Start(ctx context.Context) error { return o.StartOnce(o.Name(), func() error { go o.run() <-o.chInitialised return nil }) } -func (o *OptimismL1Oracle) Close() error { +func (o *optimismL1Oracle) Close() error { return o.StopOnce(o.Name(), func() error { close(o.chStop) <-o.chDone @@ -201,11 +235,11 @@ func (o *OptimismL1Oracle) Close() error { }) } -func (o *OptimismL1Oracle) HealthReport() map[string]error { +func (o *optimismL1Oracle) HealthReport() map[string]error { return map[string]error{o.Name(): o.Healthy()} } -func (o *OptimismL1Oracle) run() { +func (o *optimismL1Oracle) run() { defer close(o.chDone) t := o.refresh() @@ -220,7 +254,7 @@ func (o *OptimismL1Oracle) run() { } } } -func (o *OptimismL1Oracle) refresh() (t *time.Timer) { +func (o *optimismL1Oracle) refresh() (t *time.Timer) { t, err := o.refreshWithError() if err != nil { o.SvcErrBuffer.Append(err) @@ -228,7 +262,7 @@ func (o *OptimismL1Oracle) refresh() (t *time.Timer) { return } -func (o *OptimismL1Oracle) refreshWithError() (t *time.Timer, err error) { +func (o *optimismL1Oracle) refreshWithError() (t *time.Timer, err error) { t = time.NewTimer(utils.WithJitter(o.pollPeriod)) ctx, cancel := o.chStop.CtxCancel(evmclient.ContextWithDefaultTimeout()) @@ -245,7 +279,7 @@ func (o *OptimismL1Oracle) refreshWithError() (t *time.Timer, err error) { return } -func (o *OptimismL1Oracle) GasPrice(_ context.Context) (l1GasPrice *assets.Wei, err error) { +func (o *optimismL1Oracle) GasPrice(_ context.Context) (l1GasPrice *assets.Wei, err error) { var timestamp time.Time ok := o.IfStarted(func() { o.l1GasPriceMu.RLock() @@ -269,7 +303,7 @@ func (o *OptimismL1Oracle) GasPrice(_ context.Context) (l1GasPrice *assets.Wei, // Gets the L1 gas cost for the provided transaction at the specified block num // If block num is not provided, the value on the latest block num is used -func (o *OptimismL1Oracle) GetGasCost(ctx context.Context, tx *gethtypes.Transaction, blockNum *big.Int) (*assets.Wei, error) { +func (o *optimismL1Oracle) GetGasCost(ctx context.Context, tx *gethtypes.Transaction, blockNum *big.Int) (*assets.Wei, error) { ctx, cancel := context.WithTimeout(ctx, client.QueryTimeout) defer cancel() var callData, b []byte @@ -282,7 +316,7 @@ func (o *OptimismL1Oracle) GetGasCost(ctx context.Context, tx *gethtypes.Transac if encodedtx, err = tx.MarshalBinary(); err != nil { return nil, fmt.Errorf("failed to marshal tx for gas cost estimation: %w", err) } - if callData, err = o.l1GasCostMethodAbi.Pack(o.gasCostMethod, encodedtx); err != nil { + if callData, err = o.getL1FeeMethodAbi.Pack(getL1FeeMethod, encodedtx); err != nil { return nil, fmt.Errorf("failed to pack calldata for %s L1 gas cost estimation method: %w", o.chainType, err) } @@ -308,54 +342,89 @@ func (o *OptimismL1Oracle) GetGasCost(ctx context.Context, tx *gethtypes.Transac return assets.NewWei(l1GasCost), nil } -func (o *OptimismL1Oracle) GetDAGasPrice(ctx context.Context) (*big.Int, error) { - isEcotone, err := o.checkIsEcotone(ctx) +func (o *optimismL1Oracle) GetDAGasPrice(ctx context.Context) (*big.Int, error) { + err := o.checkForUpgrade(ctx) if err != nil { return nil, err } - - o.logger.Infof("Chain isEcotone result: %t", isEcotone) - - if isEcotone { - return o.getEcotoneGasPrice(ctx) + if o.isFjord || o.isEcotone { + return o.getEcotoneFjordGasPrice(ctx) } return o.getV1GasPrice(ctx) } -func (o *OptimismL1Oracle) checkIsEcotone(ctx context.Context) (bool, error) { - // if chain is already Ecotone, NOOP - if o.isEcotone { - return true, nil +// Checks oracle flags for Ecotone and Fjord upgrades +func (o *optimismL1Oracle) checkForUpgrade(ctx context.Context) error { + // if chain is already Fjord (the latest upgrade), NOOP + // need to continue to check if not on latest upgrade + if o.isFjord { + return nil } // if time since last check has not exceeded polling period, NOOP - if time.Now().Unix()-o.isEcotoneCheckTs < OPStackGasOracle_isEcotonePollingPeriod { - return false, nil + if time.Since(o.upgradeCheckTs) < upgradePollingPeriod { + return nil } - o.isEcotoneCheckTs = time.Now().Unix() - - l1OracleAddress := common.HexToAddress(o.l1OracleAddress) - // confirmed with OP team that isEcotone() is the canonical way to check if the chain has upgraded - b, err := o.client.CallContract(ctx, ethereum.CallMsg{ - To: &l1OracleAddress, - Data: o.isEcotoneCalldata, - }, nil) - - // if the chain has not upgraded to Ecotone, the isEcotone call will revert, this would be expected - if err != nil { - o.logger.Infof("isEcotone() call failed, this can happen if chain has not upgraded: %v", err) - return false, nil + o.upgradeCheckTs = time.Now() + rpcBatchCalls := []rpc.BatchElem{ + { + Method: "eth_call", + Args: []any{ + map[string]interface{}{ + "from": common.Address{}, + "to": o.l1OracleAddress, + "data": hexutil.Bytes(o.isFjordCalldata), + }, + "latest", + }, + Result: new(string), + }, + { + Method: "eth_call", + Args: []any{ + map[string]interface{}{ + "from": common.Address{}, + "to": o.l1OracleAddress, + "data": hexutil.Bytes(o.isEcotoneCalldata), + }, + "latest", + }, + Result: new(string), + }, } - - res, err := o.isEcotoneMethodAbi.Unpack(OPStackGasOracle_isEcotone, b) + err := o.client.BatchCallContext(ctx, rpcBatchCalls) if err != nil { - return false, fmt.Errorf("failed to unpack isEcotone() return data: %w", err) + return fmt.Errorf("check upgrade batch call failed: %w", err) + } + // These calls are expected to revert if chain has not upgraded. Ignore non-nil Error field. + if rpcBatchCalls[0].Error == nil { + result := *(rpcBatchCalls[0].Result.(*string)) + if b, decodeErr := hexutil.Decode(result); decodeErr == nil { + if res, unpackErr := o.isFjordMethodAbi.Unpack(isFjordMethod, b); unpackErr == nil { + o.isFjord = res[0].(bool) + } else { + o.logger.Errorw("failed to unpack results", "method", isFjordMethod, "hex", result, "error", unpackErr) + } + } else { + o.logger.Errorw("failed to decode bytes", "method", isFjordMethod, "hex", result, "error", decodeErr) + } + } + if rpcBatchCalls[1].Error == nil { + result := *(rpcBatchCalls[1].Result.(*string)) + if b, decodeErr := hexutil.Decode(result); decodeErr == nil { + if res, unpackErr := o.isEcotoneMethodAbi.Unpack(isEcotoneMethod, b); unpackErr == nil { + o.isEcotone = res[0].(bool) + } else { + o.logger.Errorw("failed to unpack results", "method", isEcotoneMethod, "hex", result, "error", unpackErr) + } + } else { + o.logger.Errorw("failed to decode bytes", "method", isEcotoneMethod, "hex", result, "error", decodeErr) + } } - o.isEcotone = res[0].(bool) - return o.isEcotone, nil + return nil } -func (o *OptimismL1Oracle) getV1GasPrice(ctx context.Context) (*big.Int, error) { +func (o *optimismL1Oracle) getV1GasPrice(ctx context.Context) (*big.Int, error) { l1OracleAddress := common.HexToAddress(o.l1OracleAddress) b, err := o.client.CallContract(ctx, ethereum.CallMsg{ To: &l1OracleAddress, @@ -371,7 +440,9 @@ func (o *OptimismL1Oracle) getV1GasPrice(ctx context.Context) (*big.Int, error) return new(big.Int).SetBytes(b), nil } -func (o *OptimismL1Oracle) getEcotoneGasPrice(ctx context.Context) (*big.Int, error) { +// Returns the scaled gas price using baseFeeScalar, l1BaseFee, blobBaseFeeScalar, and blobBaseFee fields from the oracle +// Confirmed the same calculation is used to determine gas price for both Ecotone and Fjord +func (o *optimismL1Oracle) getEcotoneFjordGasPrice(ctx context.Context) (*big.Int, error) { rpcBatchCalls := []rpc.BatchElem{ { Method: "eth_call", @@ -379,7 +450,43 @@ func (o *OptimismL1Oracle) getEcotoneGasPrice(ctx context.Context) (*big.Int, er map[string]interface{}{ "from": common.Address{}, "to": o.l1OracleAddress, - "data": hexutil.Bytes(o.getL1GasUsedCalldata), + "data": hexutil.Bytes(o.l1BaseFeeCalldata), + }, + "latest", + }, + Result: new(string), + }, + { + Method: "eth_call", + Args: []any{ + map[string]interface{}{ + "from": common.Address{}, + "to": o.l1OracleAddress, + "data": hexutil.Bytes(o.baseFeeScalarCalldata), + }, + "latest", + }, + Result: new(string), + }, + { + Method: "eth_call", + Args: []any{ + map[string]interface{}{ + "from": common.Address{}, + "to": o.l1OracleAddress, + "data": hexutil.Bytes(o.blobBaseFeeCalldata), + }, + "latest", + }, + Result: new(string), + }, + { + Method: "eth_call", + Args: []any{ + map[string]interface{}{ + "from": common.Address{}, + "to": o.l1OracleAddress, + "data": hexutil.Bytes(o.blobBaseFeeScalarCalldata), }, "latest", }, @@ -391,7 +498,7 @@ func (o *OptimismL1Oracle) getEcotoneGasPrice(ctx context.Context) (*big.Int, er map[string]interface{}{ "from": common.Address{}, "to": o.l1OracleAddress, - "data": hexutil.Bytes(o.getL1FeeCalldata), + "data": hexutil.Bytes(o.decimalsCalldata), }, "latest", }, @@ -401,31 +508,75 @@ func (o *OptimismL1Oracle) getEcotoneGasPrice(ctx context.Context) (*big.Int, er err := o.client.BatchCallContext(ctx, rpcBatchCalls) if err != nil { - return nil, fmt.Errorf("getEcotoneGasPrice batch call failed: %w", err) + return nil, fmt.Errorf("fetch gas price parameters batch call failed: %w", err) } if rpcBatchCalls[0].Error != nil { - return nil, fmt.Errorf("%s call failed in a batch: %w", OPStackGasOracle_getL1GasUsed, err) + return nil, fmt.Errorf("%s call failed in a batch: %w", l1BaseFeeMethod, err) } if rpcBatchCalls[1].Error != nil { - return nil, fmt.Errorf("%s call failed in a batch: %w", OPStackGasOracle_getL1Fee, err) + return nil, fmt.Errorf("%s call failed in a batch: %w", baseFeeScalarMethod, err) + } + if rpcBatchCalls[2].Error != nil { + return nil, fmt.Errorf("%s call failed in a batch: %w", blobBaseFeeMethod, err) + } + if rpcBatchCalls[3].Error != nil { + return nil, fmt.Errorf("%s call failed in a batch: %w", blobBaseFeeScalarMethod, err) + } + if rpcBatchCalls[4].Error != nil { + return nil, fmt.Errorf("%s call failed in a batch: %w", decimalsMethod, err) } - l1GasUsedResult := *(rpcBatchCalls[0].Result.(*string)) - l1FeeResult := *(rpcBatchCalls[1].Result.(*string)) + // Extract values from responses + l1BaseFeeResult := *(rpcBatchCalls[0].Result.(*string)) + baseFeeScalarResult := *(rpcBatchCalls[1].Result.(*string)) + blobBaseFeeResult := *(rpcBatchCalls[2].Result.(*string)) + blobBaseFeeScalarResult := *(rpcBatchCalls[3].Result.(*string)) + decimalsResult := *(rpcBatchCalls[4].Result.(*string)) - l1GasUsedBytes, err := hexutil.Decode(l1GasUsedResult) + // Decode the responses into bytes + l1BaseFeeBytes, err := hexutil.Decode(l1BaseFeeResult) + if err != nil { + return nil, fmt.Errorf("failed to decode %s rpc result: %w", l1BaseFeeMethod, err) + } + baseFeeScalarBytes, err := hexutil.Decode(baseFeeScalarResult) if err != nil { - return nil, fmt.Errorf("failed to decode %s rpc result: %w", OPStackGasOracle_getL1GasUsed, err) + return nil, fmt.Errorf("failed to decode %s rpc result: %w", baseFeeScalarMethod, err) } - l1FeeBytes, err := hexutil.Decode(l1FeeResult) + blobBaseFeeBytes, err := hexutil.Decode(blobBaseFeeResult) if err != nil { - return nil, fmt.Errorf("failed to decode %s rpc result: %w", OPStackGasOracle_getL1Fee, err) + return nil, fmt.Errorf("failed to decode %s rpc result: %w", blobBaseFeeMethod, err) } + blobBaseFeeScalarBytes, err := hexutil.Decode(blobBaseFeeScalarResult) + if err != nil { + return nil, fmt.Errorf("failed to decode %s rpc result: %w", blobBaseFeeScalarMethod, err) + } + decimalsBytes, err := hexutil.Decode(decimalsResult) + if err != nil { + return nil, fmt.Errorf("failed to decode %s rpc result: %w", decimalsMethod, err) + } + + // Convert bytes to big int for calculations + l1BaseFee := new(big.Int).SetBytes(l1BaseFeeBytes) + baseFeeScalar := new(big.Int).SetBytes(baseFeeScalarBytes) + blobBaseFee := new(big.Int).SetBytes(blobBaseFeeBytes) + blobBaseFeeScalar := new(big.Int).SetBytes(blobBaseFeeScalarBytes) + decimals := new(big.Int).SetBytes(decimalsBytes) + + o.logger.Debugw("gas price parameters", "l1BaseFee", l1BaseFee, "baseFeeScalar", baseFeeScalar, "blobBaseFee", blobBaseFee, "blobBaseFeeScalar", blobBaseFeeScalar, "decimals", decimals) + + // Scaled gas price = baseFee * 16 * baseFeeScalar + blobBaseFee * blobBaseFeeScalar + scaledBaseFee := new(big.Int).Mul(l1BaseFee, baseFeeScalar) + scaledBaseFee = new(big.Int).Mul(scaledBaseFee, big.NewInt(16)) + scaledBlobBaseFee := new(big.Int).Mul(blobBaseFee, blobBaseFeeScalar) + scaledGasPrice := new(big.Int).Add(scaledBaseFee, scaledBlobBaseFee) - l1GasUsed := new(big.Int).SetBytes(l1GasUsedBytes) - l1Fee := new(big.Int).SetBytes(l1FeeBytes) + // Gas price = scaled gas price / (16 * 10 ^ decimals) + // This formula is extracted from the gas cost methods in the precompile contract + // Note: The Fjord calculation in the contract uses estimated size instead of gas used which is why we have to scale down by (16 * 10 ^ decimals) as well + // Ecotone: https://github.com/ethereum-optimism/optimism/blob/71b93116738ee98c9f8713b1a5dfe626ce06c1b2/packages/contracts-bedrock/src/L2/GasPriceOracle.sol#L192 + // Fjord: https://github.com/ethereum-optimism/optimism/blob/71b93116738ee98c9f8713b1a5dfe626ce06c1b2/packages/contracts-bedrock/src/L2/GasPriceOracle.sol#L229-L230 + scale := new(big.Int).Exp(big.NewInt(10), decimals, nil) + scale = new(big.Int).Mul(scale, big.NewInt(16)) - // for the same tx byte, l1Fee / l1GasUsed will give the l1 gas price - // note this price is per l1 gas, not l1 data byte - return new(big.Int).Div(l1Fee, l1GasUsed), nil + return new(big.Int).Div(scaledGasPrice, scale), nil } diff --git a/core/chains/evm/gas/rollups/op_l1_oracle_test.go b/core/chains/evm/gas/rollups/op_l1_oracle_test.go index 86fdbe0a62..f5f009f1ea 100644 --- a/core/chains/evm/gas/rollups/op_l1_oracle_test.go +++ b/core/chains/evm/gas/rollups/op_l1_oracle_test.go @@ -22,26 +22,28 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas/rollups/mocks" ) -func TestDAPriceReader_ReadV1GasPrice(t *testing.T) { +func TestOPL1Oracle_ReadV1GasPrice(t *testing.T) { t.Parallel() testCases := []struct { name string isEcotoneError bool - returnBadData bool + isFjordError bool }{ { - name: "calling isEcotone returns false, fetches l1BaseFee", + name: "calling isEcotone and isFjord returns false, fetches l1BaseFee", isEcotoneError: false, + isFjordError: false, }, { - name: "calling isEcotone when chain has not made Ecotone upgrade, fetches l1BaseFee", - isEcotoneError: true, + name: "calling isEcotone returns false and IsFjord errors when chain has not made Fjord upgrade, fetches l1BaseFee", + isEcotoneError: false, + isFjordError: true, }, { - name: "calling isEcotone returns bad data, returns error", - isEcotoneError: false, - returnBadData: true, + name: "calling isEcotone and isFjord when chain has not made Ecotone upgrade, fetches l1BaseFee", + isEcotoneError: true, + isFjordError: true, }, } @@ -52,117 +54,252 @@ func TestDAPriceReader_ReadV1GasPrice(t *testing.T) { l1BaseFeeMethodAbi, err := abi.JSON(strings.NewReader(L1BaseFeeAbiString)) require.NoError(t, err) - l1BaseFeeCalldata, err := l1BaseFeeMethodAbi.Pack(OPStackGasOracle_l1BaseFee) + l1BaseFeeCalldata, err := l1BaseFeeMethodAbi.Pack(l1BaseFeeMethod) require.NoError(t, err) + // IsFjord calldata + isFjordMethodAbi, err := abi.JSON(strings.NewReader(OPIsFjordAbiString)) + require.NoError(t, err) + isFjordCalldata, err := isFjordMethodAbi.Pack(isFjordMethod) + require.NoError(t, err) + + // IsEcotone calldata isEcotoneMethodAbi, err := abi.JSON(strings.NewReader(OPIsEcotoneAbiString)) require.NoError(t, err) - isEcotoneCalldata, err := isEcotoneMethodAbi.Pack(OPStackGasOracle_isEcotone) + isEcotoneCalldata, err := isEcotoneMethodAbi.Pack(isEcotoneMethod) require.NoError(t, err) ethClient := mocks.NewL1OracleClient(t) - call := ethClient.On("CallContract", mock.Anything, mock.IsType(ethereum.CallMsg{}), mock.IsType(&big.Int{})).Run(func(args mock.Arguments) { + ethClient.On("BatchCallContext", mock.Anything, mock.IsType([]rpc.BatchElem{})).Run(func(args mock.Arguments) { + rpcElements := args.Get(1).([]rpc.BatchElem) + require.Equal(t, 2, len(rpcElements)) + for _, rE := range rpcElements { + require.Equal(t, "eth_call", rE.Method) + require.Equal(t, oracleAddress, rE.Args[0].(map[string]interface{})["to"]) + require.Equal(t, "latest", rE.Args[1]) + } + require.Equal(t, hexutil.Bytes(isFjordCalldata), rpcElements[0].Args[0].(map[string]interface{})["data"]) + require.Equal(t, hexutil.Bytes(isEcotoneCalldata), rpcElements[1].Args[0].(map[string]interface{})["data"]) + isUpgraded := "0x0000000000000000000000000000000000000000000000000000000000000000" + if tc.isFjordError { + rpcElements[0].Error = fmt.Errorf("test error") + } else { + rpcElements[0].Result = &isUpgraded + } + if tc.isEcotoneError { + rpcElements[1].Error = fmt.Errorf("test error") + } else { + rpcElements[1].Result = &isUpgraded + } + }).Return(nil).Once() + + ethClient.On("CallContract", mock.Anything, mock.IsType(ethereum.CallMsg{}), mock.IsType(&big.Int{})).Run(func(args mock.Arguments) { callMsg := args.Get(1).(ethereum.CallMsg) blockNumber := args.Get(2).(*big.Int) - require.Equal(t, isEcotoneCalldata, callMsg.Data) + require.Equal(t, l1BaseFeeCalldata, callMsg.Data) require.Equal(t, oracleAddress, callMsg.To.String()) assert.Nil(t, blockNumber) - }) - - if tc.returnBadData { - call.Return([]byte{0x2, 0x2}, nil).Once() - } else if tc.isEcotoneError { - call.Return(nil, fmt.Errorf("test error")).Once() - } else { - call.Return(isEcotoneMethodAbi.Methods["isEcotone"].Outputs.Pack(false)).Once() - } - - if !tc.returnBadData { - ethClient.On("CallContract", mock.Anything, mock.IsType(ethereum.CallMsg{}), mock.IsType(&big.Int{})).Run(func(args mock.Arguments) { - callMsg := args.Get(1).(ethereum.CallMsg) - blockNumber := args.Get(2).(*big.Int) - require.Equal(t, l1BaseFeeCalldata, callMsg.Data) - require.Equal(t, oracleAddress, callMsg.To.String()) - assert.Nil(t, blockNumber) - }).Return(common.BigToHash(l1BaseFee).Bytes(), nil).Once() - } - - oracle := newOpStackL1GasOracle(logger.Test(t), ethClient, chaintype.ChainOptimismBedrock, oracleAddress) + }).Return(common.BigToHash(l1BaseFee).Bytes(), nil).Once() + + oracle, err := newOpStackL1GasOracle(logger.Test(t), ethClient, chaintype.ChainOptimismBedrock, oracleAddress) + require.NoError(t, err) gasPrice, err := oracle.GetDAGasPrice(tests.Context(t)) - if tc.returnBadData { - assert.Error(t, err) - } else { - require.NoError(t, err) - assert.Equal(t, l1BaseFee, gasPrice) - } + require.NoError(t, err) + assert.Equal(t, l1BaseFee, gasPrice) }) } } -func setupIsEcotone(t *testing.T, oracleAddress string) *mocks.L1OracleClient { +func setupUpgradeCheck(t *testing.T, oracleAddress string, isFjord, isEcotone bool) *mocks.L1OracleClient { + trueHex := "0x0000000000000000000000000000000000000000000000000000000000000001" + falseHex := "0x0000000000000000000000000000000000000000000000000000000000000000" + boolToHexMap := map[bool]*string{ + true: &trueHex, + false: &falseHex, + } + // IsFjord calldata + isFjordMethodAbi, err := abi.JSON(strings.NewReader(OPIsFjordAbiString)) + require.NoError(t, err) + isFjordCalldata, err := isFjordMethodAbi.Pack(isFjordMethod) + require.NoError(t, err) + + // IsEcotone calldata isEcotoneMethodAbi, err := abi.JSON(strings.NewReader(OPIsEcotoneAbiString)) require.NoError(t, err) - isEcotoneCalldata, err := isEcotoneMethodAbi.Pack(OPStackGasOracle_isEcotone) + isEcotoneCalldata, err := isEcotoneMethodAbi.Pack(isEcotoneMethod) require.NoError(t, err) ethClient := mocks.NewL1OracleClient(t) - ethClient.On("CallContract", mock.Anything, mock.IsType(ethereum.CallMsg{}), mock.IsType(&big.Int{})).Run(func(args mock.Arguments) { - callMsg := args.Get(1).(ethereum.CallMsg) - blockNumber := args.Get(2).(*big.Int) - require.Equal(t, isEcotoneCalldata, callMsg.Data) - require.Equal(t, oracleAddress, callMsg.To.String()) - assert.Nil(t, blockNumber) - }).Return(isEcotoneMethodAbi.Methods["isEcotone"].Outputs.Pack(true)).Once() + ethClient.On("BatchCallContext", mock.Anything, mock.IsType([]rpc.BatchElem{})).Run(func(args mock.Arguments) { + rpcElements := args.Get(1).([]rpc.BatchElem) + require.Equal(t, 2, len(rpcElements)) + for _, rE := range rpcElements { + require.Equal(t, "eth_call", rE.Method) + require.Equal(t, oracleAddress, rE.Args[0].(map[string]interface{})["to"]) + require.Equal(t, "latest", rE.Args[1]) + } + require.Equal(t, hexutil.Bytes(isFjordCalldata), rpcElements[0].Args[0].(map[string]interface{})["data"]) + require.Equal(t, hexutil.Bytes(isEcotoneCalldata), rpcElements[1].Args[0].(map[string]interface{})["data"]) + + rpcElements[0].Result = boolToHexMap[isFjord] + rpcElements[1].Result = boolToHexMap[isEcotone] + }).Return(nil).Once() return ethClient } -func TestDAPriceReader_ReadEcotoneGasPrice(t *testing.T) { - l1BaseFee := big.NewInt(100) +func mockBatchContractCall(t *testing.T, ethClient *mocks.L1OracleClient, oracleAddress string, baseFeeVal, baseFeeScalarVal, blobBaseFeeVal, blobBaseFeeScalarVal, decimalsVal *big.Int) { + // L1 base fee calldata + l1BaseFeeMethodAbi, err := abi.JSON(strings.NewReader(L1BaseFeeAbiString)) + require.NoError(t, err) + l1BaseFeeCalldata, err := l1BaseFeeMethodAbi.Pack(l1BaseFeeMethod) + require.NoError(t, err) + + // L1 base fee scalar calldata + l1BaseFeeScalarMethodAbi, err := abi.JSON(strings.NewReader(OPBaseFeeScalarAbiString)) + require.NoError(t, err) + l1BaseFeeScalarCalldata, err := l1BaseFeeScalarMethodAbi.Pack(baseFeeScalarMethod) + require.NoError(t, err) + + // Blob base fee calldata + blobBaseFeeMethodAbi, err := abi.JSON(strings.NewReader(OPBlobBaseFeeAbiString)) + require.NoError(t, err) + blobBaseFeeCalldata, err := blobBaseFeeMethodAbi.Pack(blobBaseFeeMethod) + require.NoError(t, err) + + // Blob base fee scalar calldata + blobBaseFeeScalarMethodAbi, err := abi.JSON(strings.NewReader(OPBlobBaseFeeScalarAbiString)) + require.NoError(t, err) + blobBaseFeeScalarCalldata, err := blobBaseFeeScalarMethodAbi.Pack(blobBaseFeeScalarMethod) + require.NoError(t, err) + + // Decimals calldata + decimalsMethodAbi, err := abi.JSON(strings.NewReader(OPDecimalsAbiString)) + require.NoError(t, err) + decimalsCalldata, err := decimalsMethodAbi.Pack(decimalsMethod) + require.NoError(t, err) + + ethClient.On("BatchCallContext", mock.Anything, mock.IsType([]rpc.BatchElem{})).Run(func(args mock.Arguments) { + rpcElements := args.Get(1).([]rpc.BatchElem) + require.Equal(t, 5, len(rpcElements)) + + for _, rE := range rpcElements { + require.Equal(t, "eth_call", rE.Method) + require.Equal(t, oracleAddress, rE.Args[0].(map[string]interface{})["to"]) + require.Equal(t, "latest", rE.Args[1]) + } + + require.Equal(t, hexutil.Bytes(l1BaseFeeCalldata), rpcElements[0].Args[0].(map[string]interface{})["data"]) + require.Equal(t, hexutil.Bytes(l1BaseFeeScalarCalldata), rpcElements[1].Args[0].(map[string]interface{})["data"]) + require.Equal(t, hexutil.Bytes(blobBaseFeeCalldata), rpcElements[2].Args[0].(map[string]interface{})["data"]) + require.Equal(t, hexutil.Bytes(blobBaseFeeScalarCalldata), rpcElements[3].Args[0].(map[string]interface{})["data"]) + require.Equal(t, hexutil.Bytes(decimalsCalldata), rpcElements[4].Args[0].(map[string]interface{})["data"]) + + res1 := common.BigToHash(baseFeeVal).Hex() + res2 := common.BigToHash(baseFeeScalarVal).Hex() + res3 := common.BigToHash(blobBaseFeeVal).Hex() + res4 := common.BigToHash(blobBaseFeeScalarVal).Hex() + res5 := common.BigToHash(decimalsVal).Hex() + rpcElements[0].Result = &res1 + rpcElements[1].Result = &res2 + rpcElements[2].Result = &res3 + rpcElements[3].Result = &res4 + rpcElements[4].Result = &res5 + }).Return(nil).Once() +} + +func TestOPL1Oracle_CalculateEcotoneGasPrice(t *testing.T) { + baseFee := big.NewInt(100000000) + blobBaseFee := big.NewInt(25000000) + baseFeeScalar := big.NewInt(10) + blobBaseFeeScalar := big.NewInt(5) + decimals := big.NewInt(6) oracleAddress := common.HexToAddress("0x1234").String() t.Parallel() t.Run("correctly fetches weighted gas price if chain has upgraded to Ecotone", func(t *testing.T) { - ethClient := setupIsEcotone(t, oracleAddress) - getL1GasUsedMethodAbi, err := abi.JSON(strings.NewReader(OPGetL1GasUsedAbiString)) + ethClient := setupUpgradeCheck(t, oracleAddress, false, true) + mockBatchContractCall(t, ethClient, oracleAddress, baseFee, baseFeeScalar, blobBaseFee, blobBaseFeeScalar, decimals) + + oracle, err := newOpStackL1GasOracle(logger.Test(t), ethClient, chaintype.ChainOptimismBedrock, oracleAddress) require.NoError(t, err) - getL1GasUsedCalldata, err := getL1GasUsedMethodAbi.Pack(OPStackGasOracle_getL1GasUsed, []byte{0x1}) + gasPrice, err := oracle.GetDAGasPrice(tests.Context(t)) require.NoError(t, err) + scaledGasPrice := big.NewInt(16125000000) // baseFee * scalar * 16 + blobBaseFee * scalar + scale := big.NewInt(16000000) // Scaled by 16 * 10 ^ decimals + expectedGasPrice := new(big.Int).Div(scaledGasPrice, scale) + assert.Equal(t, expectedGasPrice, gasPrice) + }) - getL1FeeMethodAbi, err := abi.JSON(strings.NewReader(GetL1FeeAbiString)) + t.Run("fetching Ecotone price but rpc returns bad data", func(t *testing.T) { + ethClient := setupUpgradeCheck(t, oracleAddress, false, true) + ethClient.On("BatchCallContext", mock.Anything, mock.IsType([]rpc.BatchElem{})).Run(func(args mock.Arguments) { + rpcElements := args.Get(1).([]rpc.BatchElem) + var badData = "zzz" + rpcElements[0].Result = &badData + rpcElements[1].Result = &badData + }).Return(nil).Once() + + oracle, err := newOpStackL1GasOracle(logger.Test(t), ethClient, chaintype.ChainOptimismBedrock, oracleAddress) require.NoError(t, err) - getL1FeeCalldata, err := getL1FeeMethodAbi.Pack(OPStackGasOracle_getL1Fee, []byte{0x1}) + _, err = oracle.GetDAGasPrice(tests.Context(t)) + assert.Error(t, err) + }) + + t.Run("fetching Ecotone price but rpc parent call errors", func(t *testing.T) { + ethClient := setupUpgradeCheck(t, oracleAddress, false, true) + ethClient.On("BatchCallContext", mock.Anything, mock.IsType([]rpc.BatchElem{})).Return(fmt.Errorf("revert")).Once() + + oracle, err := newOpStackL1GasOracle(logger.Test(t), ethClient, chaintype.ChainOptimismBedrock, oracleAddress) require.NoError(t, err) + _, err = oracle.GetDAGasPrice(tests.Context(t)) + assert.Error(t, err) + }) + t.Run("fetching Ecotone price but one of the sub rpc call errors", func(t *testing.T) { + ethClient := setupUpgradeCheck(t, oracleAddress, false, true) ethClient.On("BatchCallContext", mock.Anything, mock.IsType([]rpc.BatchElem{})).Run(func(args mock.Arguments) { rpcElements := args.Get(1).([]rpc.BatchElem) - require.Equal(t, 2, len(rpcElements)) + res := common.BigToHash(baseFee).Hex() + rpcElements[0].Result = &res + rpcElements[1].Error = fmt.Errorf("revert") + }).Return(nil).Once() - for _, rE := range rpcElements { - require.Equal(t, "eth_call", rE.Method) - require.Equal(t, oracleAddress, rE.Args[0].(map[string]interface{})["to"]) - require.Equal(t, "latest", rE.Args[1]) - } + oracle, err := newOpStackL1GasOracle(logger.Test(t), ethClient, chaintype.ChainOptimismBedrock, oracleAddress) + require.NoError(t, err) + _, err = oracle.GetDAGasPrice(tests.Context(t)) + assert.Error(t, err) + }) +} - require.Equal(t, hexutil.Bytes(getL1GasUsedCalldata), rpcElements[0].Args[0].(map[string]interface{})["data"]) - require.Equal(t, hexutil.Bytes(getL1FeeCalldata), rpcElements[1].Args[0].(map[string]interface{})["data"]) +func TestOPL1Oracle_CalculateFjordGasPrice(t *testing.T) { + baseFee := big.NewInt(100000000) + blobBaseFee := big.NewInt(25000000) + baseFeeScalar := big.NewInt(10) + blobBaseFeeScalar := big.NewInt(5) + decimals := big.NewInt(6) + oracleAddress := common.HexToAddress("0x1234").String() - res1 := common.BigToHash(big.NewInt(1)).Hex() - res2 := common.BigToHash(l1BaseFee).Hex() - rpcElements[0].Result = &res1 - rpcElements[1].Result = &res2 - }).Return(nil).Once() + t.Parallel() - oracle := newOpStackL1GasOracle(logger.Test(t), ethClient, chaintype.ChainOptimismBedrock, oracleAddress) + t.Run("correctly fetches gas price if chain has upgraded to Fjord", func(t *testing.T) { + ethClient := setupUpgradeCheck(t, oracleAddress, true, true) + mockBatchContractCall(t, ethClient, oracleAddress, baseFee, baseFeeScalar, blobBaseFee, blobBaseFeeScalar, decimals) + + oracle, err := newOpStackL1GasOracle(logger.Test(t), ethClient, chaintype.ChainOptimismBedrock, oracleAddress) + require.NoError(t, err) gasPrice, err := oracle.GetDAGasPrice(tests.Context(t)) require.NoError(t, err) - assert.Equal(t, l1BaseFee, gasPrice) + scaledGasPrice := big.NewInt(16125000000) // baseFee * scalar * 16 + blobBaseFee * scalar + scale := big.NewInt(16000000) // Scaled by 16 * 10 ^ decimals + expectedGasPrice := new(big.Int).Div(scaledGasPrice, scale) + assert.Equal(t, expectedGasPrice, gasPrice) }) - t.Run("fetching Ecotone price but rpc returns bad data", func(t *testing.T) { - ethClient := setupIsEcotone(t, oracleAddress) + t.Run("fetching Fjord price but rpc returns bad data", func(t *testing.T) { + ethClient := setupUpgradeCheck(t, oracleAddress, true, true) ethClient.On("BatchCallContext", mock.Anything, mock.IsType([]rpc.BatchElem{})).Run(func(args mock.Arguments) { rpcElements := args.Get(1).([]rpc.BatchElem) var badData = "zzz" @@ -170,31 +307,34 @@ func TestDAPriceReader_ReadEcotoneGasPrice(t *testing.T) { rpcElements[1].Result = &badData }).Return(nil).Once() - oracle := newOpStackL1GasOracle(logger.Test(t), ethClient, chaintype.ChainOptimismBedrock, oracleAddress) - _, err := oracle.GetDAGasPrice(tests.Context(t)) + oracle, err := newOpStackL1GasOracle(logger.Test(t), ethClient, chaintype.ChainOptimismBedrock, oracleAddress) + require.NoError(t, err) + _, err = oracle.GetDAGasPrice(tests.Context(t)) assert.Error(t, err) }) - t.Run("fetching Ecotone price but rpc parent call errors", func(t *testing.T) { - ethClient := setupIsEcotone(t, oracleAddress) + t.Run("fetching Fjord price but rpc parent call errors", func(t *testing.T) { + ethClient := setupUpgradeCheck(t, oracleAddress, true, true) ethClient.On("BatchCallContext", mock.Anything, mock.IsType([]rpc.BatchElem{})).Return(fmt.Errorf("revert")).Once() - oracle := newOpStackL1GasOracle(logger.Test(t), ethClient, chaintype.ChainOptimismBedrock, oracleAddress) - _, err := oracle.GetDAGasPrice(tests.Context(t)) + oracle, err := newOpStackL1GasOracle(logger.Test(t), ethClient, chaintype.ChainOptimismBedrock, oracleAddress) + require.NoError(t, err) + _, err = oracle.GetDAGasPrice(tests.Context(t)) assert.Error(t, err) }) - t.Run("fetching Ecotone price but one of the sub rpc call errors", func(t *testing.T) { - ethClient := setupIsEcotone(t, oracleAddress) + t.Run("fetching Fjord price but one of the sub rpc call errors", func(t *testing.T) { + ethClient := setupUpgradeCheck(t, oracleAddress, true, true) ethClient.On("BatchCallContext", mock.Anything, mock.IsType([]rpc.BatchElem{})).Run(func(args mock.Arguments) { rpcElements := args.Get(1).([]rpc.BatchElem) - res := common.BigToHash(l1BaseFee).Hex() + res := common.BigToHash(baseFee).Hex() rpcElements[0].Result = &res rpcElements[1].Error = fmt.Errorf("revert") }).Return(nil).Once() - oracle := newOpStackL1GasOracle(logger.Test(t), ethClient, chaintype.ChainOptimismBedrock, oracleAddress) - _, err := oracle.GetDAGasPrice(tests.Context(t)) + oracle, err := newOpStackL1GasOracle(logger.Test(t), ethClient, chaintype.ChainOptimismBedrock, oracleAddress) + require.NoError(t, err) + _, err = oracle.GetDAGasPrice(tests.Context(t)) assert.Error(t, err) }) } diff --git a/core/chains/evm/gas/rollups/zkSync_l1_oracle.go b/core/chains/evm/gas/rollups/zkSync_l1_oracle.go index 00ee91b3f8..2517bd5edf 100644 --- a/core/chains/evm/gas/rollups/zkSync_l1_oracle.go +++ b/core/chains/evm/gas/rollups/zkSync_l1_oracle.go @@ -185,7 +185,7 @@ func (o *zkSyncL1Oracle) GasPrice(_ context.Context) (l1GasPrice *assets.Wei, er func (o *zkSyncL1Oracle) GetGasCost(ctx context.Context, tx *gethtypes.Transaction, blockNum *big.Int) (*assets.Wei, error) { //Unused method, so not implemented // And its not possible to know gas consumption of a transaction before its executed, since zkSync only posts the state difference - panic("unimplemented") + return nil, fmt.Errorf("unimplemented") } // GetL2GasPrice calls SystemContract.gasPrice() on the zksync system precompile contract. diff --git a/core/chains/evm/headtracker/head_broadcaster_test.go b/core/chains/evm/headtracker/head_broadcaster_test.go index 2da41de877..7ac61ab34b 100644 --- a/core/chains/evm/headtracker/head_broadcaster_test.go +++ b/core/chains/evm/headtracker/head_broadcaster_test.go @@ -60,8 +60,7 @@ func TestHeadBroadcaster_Subscribe(t *testing.T) { chchHeaders <- args.Get(1).(chan<- *evmtypes.Head) }). Return(sub, nil) - // 2 for initial and 2 for backfill - ethClient.On("HeadByNumber", mock.Anything, mock.Anything).Return(testutils.Head(1), nil).Times(4) + ethClient.On("HeadByNumber", mock.Anything, mock.Anything).Return(testutils.Head(1), nil) sub.On("Unsubscribe").Return() sub.On("Err").Return(nil) diff --git a/core/chains/evm/headtracker/head_listener_test.go b/core/chains/evm/headtracker/head_listener_test.go index 2e1a9c81d5..29b090bbff 100644 --- a/core/chains/evm/headtracker/head_listener_test.go +++ b/core/chains/evm/headtracker/head_listener_test.go @@ -70,7 +70,7 @@ func Test_HeadListener_HappyPath(t *testing.T) { done := func() { doneAwaiter.ItHappened() } - go hl.ListenForNewHeads(handler, done) + go hl.ListenForNewHeads(func() {}, handler, done) subscribeAwaiter.AwaitOrFail(t, tests.WaitTimeout(t)) require.Eventually(t, hl.Connected, tests.WaitTimeout(t), tests.TestInterval) @@ -129,7 +129,7 @@ func Test_HeadListener_NotReceivingHeads(t *testing.T) { done := func() { doneAwaiter.ItHappened() } - go hl.ListenForNewHeads(handler, done) + go hl.ListenForNewHeads(func() {}, handler, done) subscribeAwaiter.AwaitOrFail(t, tests.WaitTimeout(t)) @@ -190,7 +190,7 @@ func Test_HeadListener_SubscriptionErr(t *testing.T) { subscribeAwaiter.ItHappened() }) go func() { - hl.ListenForNewHeads(hnh, done) + hl.ListenForNewHeads(func() {}, hnh, done) }() // Put a head on the channel to ensure we test all code paths diff --git a/core/chains/evm/headtracker/head_saver_test.go b/core/chains/evm/headtracker/head_saver_test.go index 9be9f838d0..43e79235e9 100644 --- a/core/chains/evm/headtracker/head_saver_test.go +++ b/core/chains/evm/headtracker/head_saver_test.go @@ -47,6 +47,7 @@ type config struct { finalityDepth uint32 blockEmissionIdleWarningThreshold time.Duration finalityTagEnabled bool + finalizedBlockOffset uint32 } func (c *config) FinalityDepth() uint32 { return c.finalityDepth } @@ -58,6 +59,10 @@ func (c *config) FinalityTagEnabled() bool { return c.finalityTagEnabled } +func (c *config) FinalizedBlockOffset() uint32 { + return c.finalizedBlockOffset +} + type saverOpts struct { headTrackerConfig *headTrackerConfig } diff --git a/core/chains/evm/headtracker/head_tracker.go b/core/chains/evm/headtracker/head_tracker.go index 357c4dae99..d6c2cdc64e 100644 --- a/core/chains/evm/headtracker/head_tracker.go +++ b/core/chains/evm/headtracker/head_tracker.go @@ -49,7 +49,10 @@ func (*nullTracker) Ready() error { return nil } func (*nullTracker) HealthReport() map[string]error { return map[string]error{} } func (*nullTracker) Name() string { return "" } func (*nullTracker) SetLogLevel(zapcore.Level) {} -func (*nullTracker) Backfill(ctx context.Context, headWithChain, latestFinalized *evmtypes.Head) (err error) { +func (*nullTracker) Backfill(ctx context.Context, headWithChain *evmtypes.Head) (err error) { return nil } func (*nullTracker) LatestChain() *evmtypes.Head { return nil } +func (*nullTracker) LatestAndFinalizedBlock(ctx context.Context) (latest, finalized *evmtypes.Head, err error) { + return nil, nil, nil +} diff --git a/core/chains/evm/headtracker/head_tracker_test.go b/core/chains/evm/headtracker/head_tracker_test.go index 4da8d27c55..81ba3ea85f 100644 --- a/core/chains/evm/headtracker/head_tracker_test.go +++ b/core/chains/evm/headtracker/head_tracker_test.go @@ -3,6 +3,7 @@ package headtracker_test import ( "context" "errors" + "fmt" "math/big" "slices" "sync" @@ -20,17 +21,19 @@ import ( "go.uber.org/zap/zaptest/observer" "golang.org/x/exp/maps" + "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox/mailboxtest" + "github.com/jmoiron/sqlx" commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" - "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" - "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox/mailboxtest" "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" htmocks "github.com/smartcontractkit/chainlink/v2/common/headtracker/mocks" commontypes "github.com/smartcontractkit/chainlink/v2/common/headtracker/types" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" @@ -60,6 +63,12 @@ func TestHeadTracker_New(t *testing.T) { ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(testutils.Head(0), nil) // finalized ethClient.On("HeadByNumber", mock.Anything, big.NewInt(0)).Return(testutils.Head(0), nil) + mockEth := &testutils.MockEth{ + EthClient: ethClient, + } + ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything). + Maybe(). + Return(mockEth.NewSub(t), nil) orm := headtracker.NewORM(*testutils.FixtureChainID, db) assert.Nil(t, orm.IdempotentInsertHead(tests.Context(t), testutils.Head(1))) @@ -71,9 +80,10 @@ func TestHeadTracker_New(t *testing.T) { ht := createHeadTracker(t, ethClient, evmcfg.EVM(), evmcfg.EVM().HeadTracker(), orm) ht.Start(t) - latest := ht.headSaver.LatestChain() - require.NotNil(t, latest) - assert.Equal(t, last.Number, latest.Number) + tests.AssertEventually(t, func() bool { + latest := ht.headSaver.LatestChain() + return latest != nil && last.Number == latest.Number + }) } func TestHeadTracker_MarkFinalized_MarksAndTrimsTable(t *testing.T) { @@ -126,7 +136,8 @@ func TestHeadTracker_Get(t *testing.T) { {"nil no initial", nil, nil, big.NewInt(0)}, } - for _, test := range cases { + for i := range cases { + test := cases[i] t.Run(test.name, func(t *testing.T) { db := pgtest.NewSqlxDB(t) config := testutils.NewTestChainScopedConfig(t, nil) @@ -146,9 +157,9 @@ func TestHeadTracker_Get(t *testing.T) { }, func(ctx context.Context, ch chan<- *evmtypes.Head) error { return nil }, ) - ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(testutils.Head(0), nil) + ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(testutils.Head(0), nil).Maybe() - fnCall := ethClient.On("HeadByNumber", mock.Anything, mock.Anything) + fnCall := ethClient.On("HeadByNumber", mock.Anything, mock.Anything).Maybe() fnCall.RunFn = func(args mock.Arguments) { num := args.Get(1).(*big.Int) fnCall.ReturnArguments = mock.Arguments{testutils.Head(num.Int64()), nil} @@ -166,7 +177,10 @@ func TestHeadTracker_Get(t *testing.T) { assert.NoError(t, err) } - assert.Equal(t, test.want, ht.headSaver.LatestChain().ToInt()) + tests.AssertEventually(t, func() bool { + latest := ht.headSaver.LatestChain().ToInt() + return latest != nil && test.want.Cmp(latest) == 0 + }) }) } } @@ -226,19 +240,12 @@ func TestHeadTracker_Start(t *testing.T) { } }) orm := headtracker.NewORM(*testutils.FixtureChainID, db) - ethClient := testutils.NewEthClientMockWithDefaultChain(t) + ethClient := evmtest.NewEthClientMockWithDefaultChain(t) + mockEth := &testutils.MockEth{EthClient: ethClient} + sub := mockEth.NewSub(t) + ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything).Return(sub, nil).Maybe() return createHeadTracker(t, ethClient, config.EVM(), config.EVM().HeadTracker(), orm) } - - t.Run("Fail start if context was canceled", func(t *testing.T) { - ctx, cancel := context.WithCancel(tests.Context(t)) - ht := newHeadTracker(t, opts{}) - ht.ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Run(func(args mock.Arguments) { - cancel() - }).Return(testutils.Head(0), context.Canceled) - err := ht.headTracker.Start(ctx) - require.ErrorIs(t, err, context.Canceled) - }) t.Run("Starts even if failed to get initialHead", func(t *testing.T) { ht := newHeadTracker(t, opts{}) ht.ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(testutils.Head(0), errors.New("failed to get init head")) @@ -268,20 +275,6 @@ func TestHeadTracker_Start(t *testing.T) { ht.Start(t) tests.AssertLogEventually(t, ht.observer, "Error handling initial head") }) - t.Run("Logs error if finality gap is too big", func(t *testing.T) { - ht := newHeadTracker(t, opts{FinalityTagEnable: ptr(true), FinalityTagBypass: ptr(false), MaxAllowedFinalityDepth: ptr(uint32(10))}) - head := testutils.Head(1000) - ht.ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(head, nil).Once() - ht.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(testutils.Head(989), nil).Once() - ht.ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything).Return(nil, errors.New("failed to connect")).Maybe() - ht.Start(t) - tests.AssertEventually(t, func() bool { - // must exactly match the error passed to logger - field := zap.String("err", "failed to calculate latest finalized head: gap between latest finalized block (989) and current head (1000) is too large (> 10)") - filtered := ht.observer.FilterMessage("Error handling initial head").FilterField(field) - return filtered.Len() > 0 - }) - }) t.Run("Happy path (finality tag)", func(t *testing.T) { head := testutils.Head(1000) ht := newHeadTracker(t, opts{FinalityTagEnable: ptr(true), FinalityTagBypass: ptr(false)}) @@ -877,10 +870,23 @@ func TestHeadTracker_Backfill(t *testing.T) { ctx := tests.Context(t) type opts struct { - Heads []evmtypes.Head + Heads []evmtypes.Head + FinalityTagEnabled bool + FinalizedBlockOffset uint32 + FinalityDepth uint32 + MaxAllowedFinalityDepth uint32 } newHeadTrackerUniverse := func(t *testing.T, opts opts) *headTrackerUniverse { - evmcfg := testutils.NewTestChainScopedConfig(t, nil) + evmcfg := testutils.NewTestChainScopedConfig(t, func(c *toml.EVMConfig) { + c.FinalityTagEnabled = ptr(opts.FinalityTagEnabled) + c.FinalizedBlockOffset = ptr(opts.FinalizedBlockOffset) + c.FinalityDepth = ptr(opts.FinalityDepth) + c.HeadTracker.FinalityTagBypass = ptr(false) + if opts.MaxAllowedFinalityDepth > 0 { + c.HeadTracker.MaxAllowedFinalityDepth = ptr(opts.MaxAllowedFinalityDepth) + } + }) + db := pgtest.NewSqlxDB(t) orm := headtracker.NewORM(*testutils.FixtureChainID, db) for i := range opts.Heads { @@ -894,26 +900,44 @@ func TestHeadTracker_Backfill(t *testing.T) { return ht } + t.Run("returns error if failed to get latestFinalized block", func(t *testing.T) { + htu := newHeadTrackerUniverse(t, opts{FinalityTagEnabled: true}) + const expectedError = "failed to fetch latest finalized block" + htu.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(nil, errors.New(expectedError)).Once() + + err := htu.headTracker.Backfill(ctx, &h12) + require.ErrorContains(t, err, expectedError) + }) t.Run("returns error if latestFinalized is not valid", func(t *testing.T) { - htu := newHeadTrackerUniverse(t, opts{}) + htu := newHeadTrackerUniverse(t, opts{FinalityTagEnabled: true}) + htu.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(nil, nil).Once() + + err := htu.headTracker.Backfill(ctx, &h12) + require.EqualError(t, err, "failed to calculate finalized block: failed to get valid latest finalized block") + }) + t.Run("Returns error if finality gap is too big", func(t *testing.T) { + htu := newHeadTrackerUniverse(t, opts{FinalityTagEnabled: true, MaxAllowedFinalityDepth: 2}) + htu.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(&h9, nil).Once() - err := htu.headTracker.Backfill(ctx, &h12, nil) - require.EqualError(t, err, "can not perform backfill without a valid latestFinalized head") + err := htu.headTracker.Backfill(ctx, &h12) + require.EqualError(t, err, "gap between latest finalized block (9) and current head (12) is too large (> 2)") }) t.Run("Returns error if finalized head is ahead of canonical", func(t *testing.T) { - htu := newHeadTrackerUniverse(t, opts{}) + htu := newHeadTrackerUniverse(t, opts{FinalityTagEnabled: true}) + htu.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(&h14Orphaned, nil).Once() - err := htu.headTracker.Backfill(ctx, &h12, &h14Orphaned) + err := htu.headTracker.Backfill(ctx, &h12) require.EqualError(t, err, "invariant violation: expected head of canonical chain to be ahead of the latestFinalized") }) t.Run("Returns error if finalizedHead is not present in the canonical chain", func(t *testing.T) { - htu := newHeadTrackerUniverse(t, opts{Heads: heads}) + htu := newHeadTrackerUniverse(t, opts{Heads: heads, FinalityTagEnabled: true}) + htu.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(&h14Orphaned, nil).Once() - err := htu.headTracker.Backfill(ctx, &h15, &h14Orphaned) + err := htu.headTracker.Backfill(ctx, &h15) require.EqualError(t, err, "expected finalized block to be present in canonical chain") }) t.Run("Marks all blocks in chain that are older than finalized", func(t *testing.T) { - htu := newHeadTrackerUniverse(t, opts{Heads: heads}) + htu := newHeadTrackerUniverse(t, opts{Heads: heads, FinalityTagEnabled: true}) assertFinalized := func(expectedFinalized bool, msg string, heads ...evmtypes.Head) { for _, h := range heads { @@ -922,18 +946,20 @@ func TestHeadTracker_Backfill(t *testing.T) { } } - err := htu.headTracker.Backfill(ctx, &h15, &h14) + htu.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(&h14, nil).Once() + err := htu.headTracker.Backfill(ctx, &h15) require.NoError(t, err) assertFinalized(true, "expected heads to be marked as finalized after backfill", h14, h13, h12, h11) assertFinalized(false, "expected heads to remain unfinalized", h15, head10) }) t.Run("fetches a missing head", func(t *testing.T) { - htu := newHeadTrackerUniverse(t, opts{Heads: heads}) + htu := newHeadTrackerUniverse(t, opts{Heads: heads, FinalityTagEnabled: true}) + htu.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(&h9, nil).Once() htu.ethClient.On("HeadByHash", mock.Anything, head10.Hash). Return(&head10, nil) - err := htu.headTracker.Backfill(ctx, &h12, &h9) + err := htu.headTracker.Backfill(ctx, &h12) require.NoError(t, err) h := htu.headSaver.Chain(h12.Hash) @@ -950,16 +976,16 @@ func TestHeadTracker_Backfill(t *testing.T) { require.NoError(t, err) assert.Equal(t, int64(10), writtenHead.Number) }) - t.Run("fetches only heads that are missing", func(t *testing.T) { - htu := newHeadTrackerUniverse(t, opts{Heads: heads}) + htu := newHeadTrackerUniverse(t, opts{Heads: heads, FinalityTagEnabled: true}) + htu.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(&head8, nil).Once() htu.ethClient.On("HeadByHash", mock.Anything, head10.Hash). Return(&head10, nil) htu.ethClient.On("HeadByHash", mock.Anything, head8.Hash). Return(&head8, nil) - err := htu.headTracker.Backfill(ctx, &h15, &head8) + err := htu.headTracker.Backfill(ctx, &h15) require.NoError(t, err) h := htu.headSaver.Chain(h15.Hash) @@ -971,7 +997,8 @@ func TestHeadTracker_Backfill(t *testing.T) { }) t.Run("abandons backfill and returns error if the eth node returns not found", func(t *testing.T) { - htu := newHeadTrackerUniverse(t, opts{Heads: heads}) + htu := newHeadTrackerUniverse(t, opts{Heads: heads, FinalityTagEnabled: true}) + htu.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(&head8, nil).Once() htu.ethClient.On("HeadByHash", mock.Anything, head10.Hash). Return(&head10, nil). Once() @@ -979,9 +1006,9 @@ func TestHeadTracker_Backfill(t *testing.T) { Return(nil, ethereum.NotFound). Once() - err := htu.headTracker.Backfill(ctx, &h12, &head8) + err := htu.headTracker.Backfill(ctx, &h12) require.Error(t, err) - require.EqualError(t, err, "fetchAndSaveHead failed: not found") + require.ErrorContains(t, err, "fetchAndSaveHead failed: not found") h := htu.headSaver.Chain(h12.Hash) @@ -991,7 +1018,8 @@ func TestHeadTracker_Backfill(t *testing.T) { }) t.Run("abandons backfill and returns error if the context time budget is exceeded", func(t *testing.T) { - htu := newHeadTrackerUniverse(t, opts{Heads: heads}) + htu := newHeadTrackerUniverse(t, opts{Heads: heads, FinalityTagEnabled: true}) + htu.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(&head8, nil).Once() htu.ethClient.On("HeadByHash", mock.Anything, head10.Hash). Return(&head10, nil) lctx, cancel := context.WithCancel(ctx) @@ -1000,9 +1028,9 @@ func TestHeadTracker_Backfill(t *testing.T) { cancel() }) - err := htu.headTracker.Backfill(lctx, &h12, &head8) + err := htu.headTracker.Backfill(lctx, &h12) require.Error(t, err) - require.EqualError(t, err, "fetchAndSaveHead failed: context canceled") + require.ErrorContains(t, err, "fetchAndSaveHead failed: context canceled") h := htu.headSaver.Chain(h12.Hash) @@ -1010,17 +1038,17 @@ func TestHeadTracker_Backfill(t *testing.T) { assert.Equal(t, 4, int(h.ChainLength())) assert.Equal(t, int64(9), h.EarliestInChain().BlockNumber()) }) - t.Run("abandons backfill and returns error when fetching a block by hash fails, indicating a reorg", func(t *testing.T) { - htu := newHeadTrackerUniverse(t, opts{}) + htu := newHeadTrackerUniverse(t, opts{FinalityTagEnabled: true}) + htu.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(&h11, nil).Once() htu.ethClient.On("HeadByHash", mock.Anything, h14.Hash).Return(&h14, nil).Once() htu.ethClient.On("HeadByHash", mock.Anything, h13.Hash).Return(&h13, nil).Once() htu.ethClient.On("HeadByHash", mock.Anything, h12.Hash).Return(nil, errors.New("not found")).Once() - err := htu.headTracker.Backfill(ctx, &h15, &h11) + err := htu.headTracker.Backfill(ctx, &h15) require.Error(t, err) - require.EqualError(t, err, "fetchAndSaveHead failed: not found") + require.ErrorContains(t, err, "fetchAndSaveHead failed: not found") h := htu.headSaver.Chain(h14.Hash) @@ -1029,9 +1057,10 @@ func TestHeadTracker_Backfill(t *testing.T) { assert.Equal(t, int64(13), h.EarliestInChain().BlockNumber()) }) t.Run("marks head as finalized, if latestHead = finalizedHead (0 finality depth)", func(t *testing.T) { - htu := newHeadTrackerUniverse(t, opts{Heads: []evmtypes.Head{h15}}) + htu := newHeadTrackerUniverse(t, opts{Heads: []evmtypes.Head{h15}, FinalityTagEnabled: true}) finalizedH15 := h15 // copy h15 to have different addresses - err := htu.headTracker.Backfill(ctx, &h15, &finalizedH15) + htu.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(&finalizedH15, nil).Once() + err := htu.headTracker.Backfill(ctx, &h15) require.NoError(t, err) h := htu.headSaver.LatestChain() @@ -1042,12 +1071,215 @@ func TestHeadTracker_Backfill(t *testing.T) { assert.Equal(t, h15.BlockNumber(), h.BlockNumber()) assert.Equal(t, h15.Hash, h.Hash) }) + t.Run("marks block as finalized according to FinalizedBlockOffset (finality tag)", func(t *testing.T) { + htu := newHeadTrackerUniverse(t, opts{Heads: []evmtypes.Head{h15}, FinalityTagEnabled: true, FinalizedBlockOffset: 2}) + htu.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(&h14, nil).Once() + // calculateLatestFinalizedBlock fetches blocks at LatestFinalized - FinalizedBlockOffset + htu.ethClient.On("HeadByNumber", mock.Anything, big.NewInt(h12.Number)).Return(&h12, nil).Once() + // backfill from 15 to 12 + htu.ethClient.On("HeadByHash", mock.Anything, h12.Hash).Return(&h12, nil).Once() + htu.ethClient.On("HeadByHash", mock.Anything, h13.Hash).Return(&h13, nil).Once() + htu.ethClient.On("HeadByHash", mock.Anything, h14.Hash).Return(&h14, nil).Once() + err := htu.headTracker.Backfill(ctx, &h15) + require.NoError(t, err) + + h := htu.headSaver.LatestChain() + // h - must contain 15, 14, 13, 12 and only 12 is finalized + assert.Equal(t, 4, int(h.ChainLength())) + for ; h.Hash != h12.Hash; h = h.Parent { + assert.False(t, h.IsFinalized) + } + + assert.True(t, h.IsFinalized) + assert.Equal(t, h12.BlockNumber(), h.BlockNumber()) + assert.Equal(t, h12.Hash, h.Hash) + }) + t.Run("marks block as finalized according to FinalizedBlockOffset (finality depth)", func(t *testing.T) { + htu := newHeadTrackerUniverse(t, opts{Heads: []evmtypes.Head{h15}, FinalityDepth: 1, FinalizedBlockOffset: 2}) + htu.ethClient.On("HeadByNumber", mock.Anything, big.NewInt(12)).Return(&h12, nil).Once() + + // backfill from 15 to 12 + htu.ethClient.On("HeadByHash", mock.Anything, h14.Hash).Return(&h14, nil).Once() + htu.ethClient.On("HeadByHash", mock.Anything, h13.Hash).Return(&h13, nil).Once() + htu.ethClient.On("HeadByHash", mock.Anything, h12.Hash).Return(&h12, nil).Once() + err := htu.headTracker.Backfill(ctx, &h15) + require.NoError(t, err) + + h := htu.headSaver.LatestChain() + // h - must contain 15, 14, 13, 12 and only 12 is finalized + assert.Equal(t, 4, int(h.ChainLength())) + for ; h.Hash != h12.Hash; h = h.Parent { + assert.False(t, h.IsFinalized) + } + + assert.True(t, h.IsFinalized) + assert.Equal(t, h12.BlockNumber(), h.BlockNumber()) + assert.Equal(t, h12.Hash, h.Hash) + }) + t.Run("marks block as finalized according to FinalizedBlockOffset even with instant finality", func(t *testing.T) { + htu := newHeadTrackerUniverse(t, opts{Heads: []evmtypes.Head{h15}, FinalityDepth: 0, FinalizedBlockOffset: 2}) + htu.ethClient.On("HeadByNumber", mock.Anything, big.NewInt(13)).Return(&h13, nil).Once() + + // backfill from 15 to 13 + htu.ethClient.On("HeadByHash", mock.Anything, h14.Hash).Return(&h14, nil).Once() + htu.ethClient.On("HeadByHash", mock.Anything, h13.Hash).Return(&h13, nil).Once() + err := htu.headTracker.Backfill(ctx, &h15) + require.NoError(t, err) + + h := htu.headSaver.LatestChain() + // h - must contain 15, 14, 13, only 13 is finalized + assert.Equal(t, 3, int(h.ChainLength())) + for ; h.Hash != h13.Hash; h = h.Parent { + assert.False(t, h.IsFinalized) + } + + assert.True(t, h.IsFinalized) + assert.Equal(t, h13.BlockNumber(), h.BlockNumber()) + assert.Equal(t, h13.Hash, h.Hash) + }) +} + +func TestHeadTracker_LatestAndFinalizedBlock(t *testing.T) { + t.Parallel() + + ctx := tests.Context(t) + + h11 := testutils.Head(11) + h11.ParentHash = utils.NewHash() + + h12 := testutils.Head(12) + h12.ParentHash = h11.Hash + + h13 := testutils.Head(13) + h13.ParentHash = h12.Hash + + type opts struct { + Heads []evmtypes.Head + FinalityTagEnabled bool + FinalizedBlockOffset uint32 + FinalityDepth uint32 + } + + newHeadTrackerUniverse := func(t *testing.T, opts opts) *headTrackerUniverse { + evmcfg := testutils.NewTestChainScopedConfig(t, func(c *toml.EVMConfig) { + c.FinalityTagEnabled = ptr(opts.FinalityTagEnabled) + c.FinalizedBlockOffset = ptr(opts.FinalizedBlockOffset) + c.FinalityDepth = ptr(opts.FinalityDepth) + }) + + db := pgtest.NewSqlxDB(t) + orm := headtracker.NewORM(*testutils.FixtureChainID, db) + for i := range opts.Heads { + require.NoError(t, orm.IdempotentInsertHead(tests.Context(t), &opts.Heads[i])) + } + ethClient := evmtest.NewEthClientMock(t) + ethClient.On("ConfiguredChainID", mock.Anything).Return(testutils.FixtureChainID, nil) + ht := createHeadTracker(t, ethClient, evmcfg.EVM(), evmcfg.EVM().HeadTracker(), orm) + _, err := ht.headSaver.Load(tests.Context(t), 0) + require.NoError(t, err) + return ht + } + t.Run("returns error if failed to get latest block", func(t *testing.T) { + htu := newHeadTrackerUniverse(t, opts{FinalityTagEnabled: true}) + const expectedError = "failed to fetch latest block" + htu.ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(nil, errors.New(expectedError)).Once() + + _, _, err := htu.headTracker.LatestAndFinalizedBlock(ctx) + require.ErrorContains(t, err, expectedError) + }) + t.Run("returns error if latest block is invalid", func(t *testing.T) { + htu := newHeadTrackerUniverse(t, opts{FinalityTagEnabled: true}) + htu.ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(nil, nil).Once() + + _, _, err := htu.headTracker.LatestAndFinalizedBlock(ctx) + require.ErrorContains(t, err, "expected latest block to be valid") + }) + t.Run("returns error if failed to get latest finalized (finality tag)", func(t *testing.T) { + htu := newHeadTrackerUniverse(t, opts{FinalityTagEnabled: true}) + htu.ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(h13, nil).Once() + const expectedError = "failed to get latest finalized block" + htu.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(nil, fmt.Errorf(expectedError)).Once() + + _, _, err := htu.headTracker.LatestAndFinalizedBlock(ctx) + require.ErrorContains(t, err, expectedError) + }) + t.Run("returns error if latest finalized block is not valid (finality tag)", func(t *testing.T) { + htu := newHeadTrackerUniverse(t, opts{FinalityTagEnabled: true}) + htu.ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(h13, nil).Once() + htu.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(nil, nil).Once() + + _, _, err := htu.headTracker.LatestAndFinalizedBlock(ctx) + require.ErrorContains(t, err, "failed to get valid latest finalized block") + }) + t.Run("returns latest finalized block as is if FinalizedBlockOffset is 0 (finality tag)", func(t *testing.T) { + htu := newHeadTrackerUniverse(t, opts{FinalityTagEnabled: true}) + htu.ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(h13, nil).Once() + htu.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(h11, nil).Once() + + actualL, actualLF, err := htu.headTracker.LatestAndFinalizedBlock(ctx) + require.NoError(t, err) + assert.Equal(t, actualL, h13) + assert.Equal(t, actualLF, h11) + }) + t.Run("returns latest finalized block with offset from cache (finality tag)", func(t *testing.T) { + htu := newHeadTrackerUniverse(t, opts{FinalityTagEnabled: true, FinalizedBlockOffset: 1, Heads: []evmtypes.Head{*h13, *h12, *h11}}) + htu.ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(h13, nil).Once() + htu.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(h12, nil).Once() + + actualL, actualLF, err := htu.headTracker.LatestAndFinalizedBlock(ctx) + require.NoError(t, err) + assert.Equal(t, actualL.Number, h13.Number) + assert.Equal(t, actualLF.Number, h11.Number) + }) + t.Run("returns latest finalized block with offset from RPC (finality tag)", func(t *testing.T) { + htu := newHeadTrackerUniverse(t, opts{FinalityTagEnabled: true, FinalizedBlockOffset: 2, Heads: []evmtypes.Head{*h13, *h12, *h11}}) + htu.ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(h13, nil).Once() + htu.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(h12, nil).Once() + h10 := testutils.Head(10) + htu.ethClient.On("HeadByNumber", mock.Anything, big.NewInt(10)).Return(h10, nil).Once() + + actualL, actualLF, err := htu.headTracker.LatestAndFinalizedBlock(ctx) + require.NoError(t, err) + assert.Equal(t, actualL.Number, h13.Number) + assert.Equal(t, actualLF.Number, h10.Number) + }) + t.Run("returns current head for both latest and finalized for FD = 0 (finality depth)", func(t *testing.T) { + htu := newHeadTrackerUniverse(t, opts{}) + htu.ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(h13, nil).Once() + + actualL, actualLF, err := htu.headTracker.LatestAndFinalizedBlock(ctx) + require.NoError(t, err) + assert.Equal(t, actualL.Number, h13.Number) + assert.Equal(t, actualLF.Number, h13.Number) + }) + t.Run("returns latest finalized block with offset from cache (finality depth)", func(t *testing.T) { + htu := newHeadTrackerUniverse(t, opts{FinalityDepth: 1, FinalizedBlockOffset: 1, Heads: []evmtypes.Head{*h13, *h12, *h11}}) + htu.ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(h13, nil).Once() + + actualL, actualLF, err := htu.headTracker.LatestAndFinalizedBlock(ctx) + require.NoError(t, err) + assert.Equal(t, actualL.Number, h13.Number) + assert.Equal(t, actualLF.Number, h11.Number) + }) + t.Run("returns latest finalized block with offset from RPC (finality depth)", func(t *testing.T) { + htu := newHeadTrackerUniverse(t, opts{FinalityDepth: 1, FinalizedBlockOffset: 2, Heads: []evmtypes.Head{*h13, *h12, *h11}}) + htu.ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(h13, nil).Once() + h10 := testutils.Head(10) + htu.ethClient.On("HeadByNumber", mock.Anything, big.NewInt(10)).Return(h10, nil).Once() + + actualL, actualLF, err := htu.headTracker.LatestAndFinalizedBlock(ctx) + require.NoError(t, err) + assert.Equal(t, actualL.Number, h13.Number) + assert.Equal(t, actualLF.Number, h10.Number) + }) } // BenchmarkHeadTracker_Backfill - benchmarks HeadTracker's Backfill with focus on efficiency after initial // backfill on start up func BenchmarkHeadTracker_Backfill(b *testing.B) { - evmcfg := testutils.NewTestChainScopedConfig(b, nil) + evmcfg := testutils.NewTestChainScopedConfig(b, func(c *toml.EVMConfig) { + c.FinalityTagEnabled = ptr(true) + }) db := pgtest.NewSqlxDB(b) chainID := big.NewInt(evmclient.NullClientChainID) orm := headtracker.NewORM(*chainID, db) @@ -1068,15 +1300,17 @@ func BenchmarkHeadTracker_Backfill(b *testing.B) { number := hash.Big().Int64() return makeBlock(number), nil }) + ethClient.On("LatestFinalizedBlock", mock.Anything).Return(finalized, nil).Once() // run initial backfill to populate the database - err := ht.headTracker.Backfill(ctx, latest, finalized) + err := ht.headTracker.Backfill(ctx, latest) require.NoError(b, err) b.ResetTimer() // focus benchmark on processing of a new latest block for i := 0; i < b.N; i++ { latest = makeBlock(int64(finalityDepth + i)) finalized = makeBlock(int64(i + 1)) - err := ht.headTracker.Backfill(ctx, latest, finalized) + ethClient.On("LatestFinalizedBlock", mock.Anything).Return(finalized, nil).Once() + err := ht.headTracker.Backfill(ctx, latest) require.NoError(b, err) } } @@ -1129,8 +1363,8 @@ type headTrackerUniverse struct { ethClient *evmclimocks.Client } -func (u *headTrackerUniverse) Backfill(ctx context.Context, head, finalizedHead *evmtypes.Head) error { - return u.headTracker.Backfill(ctx, head, finalizedHead) +func (u *headTrackerUniverse) Backfill(ctx context.Context, head *evmtypes.Head) error { + return u.headTracker.Backfill(ctx, head) } func (u *headTrackerUniverse) Start(t *testing.T) { diff --git a/core/chains/evm/headtracker/simulated_head_tracker.go b/core/chains/evm/headtracker/simulated_head_tracker.go new file mode 100644 index 0000000000..e1e550de99 --- /dev/null +++ b/core/chains/evm/headtracker/simulated_head_tracker.go @@ -0,0 +1,53 @@ +package headtracker + +import ( + "context" + "fmt" + "math/big" + + evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" + evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" +) + +// simulatedHeadTracker - simplified version of HeadTracker that works with simulated backed +type simulatedHeadTracker struct { + ec evmclient.Client + useFinalityTag bool + finalityDepth int64 +} + +func NewSimulatedHeadTracker(ec evmclient.Client, useFinalityTag bool, finalityDepth int64) *simulatedHeadTracker { + return &simulatedHeadTracker{ + ec: ec, + useFinalityTag: useFinalityTag, + finalityDepth: finalityDepth, + } +} + +func (ht *simulatedHeadTracker) LatestAndFinalizedBlock(ctx context.Context) (*evmtypes.Head, *evmtypes.Head, error) { + latest, err := ht.ec.HeadByNumber(ctx, nil) + if err != nil { + return nil, nil, err + } + + if latest == nil { + return nil, nil, fmt.Errorf("expected latest block to be valid") + } + + var finalizedBlock *evmtypes.Head + if ht.useFinalityTag { + finalizedBlock, err = ht.ec.LatestFinalizedBlock(ctx) + } else { + finalizedBlock, err = ht.ec.HeadByNumber(ctx, big.NewInt(max(latest.Number-ht.finalityDepth, 0))) + } + + if err != nil { + return nil, nil, fmt.Errorf("simulatedHeadTracker failed to get finalized block") + } + + if finalizedBlock == nil { + return nil, nil, fmt.Errorf("expected finalized block to be valid") + } + + return latest, finalizedBlock, nil +} diff --git a/core/chains/evm/keystore/mocks/eth.go b/core/chains/evm/keystore/mocks/eth.go index 9c11551e2c..df46adb202 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. 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 3ab5e35050..31e268cd85 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/chains/evm/log/mocks/broadcast.go b/core/chains/evm/log/mocks/broadcast.go index 1f1817474e..c7dfb90055 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/chains/evm/log/mocks/broadcaster.go b/core/chains/evm/log/mocks/broadcaster.go index 221456c508..4571ba4bab 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/chains/evm/logpoller/helper_test.go b/core/chains/evm/logpoller/helper_test.go index 3b2a10df6c..3f589d84d5 100644 --- a/core/chains/evm/logpoller/helper_test.go +++ b/core/chains/evm/logpoller/helper_test.go @@ -23,6 +23,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/log_emitter" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" @@ -68,10 +69,11 @@ func SetupTH(t testing.TB, opts logpoller.Opts) TestHarness { head := esc.Backend().Blockchain().CurrentHeader() esc.Backend().Blockchain().SetFinalized(head) + headTracker := headtracker.NewSimulatedHeadTracker(esc, opts.UseFinalityTag, opts.FinalityDepth) if opts.PollPeriod == 0 { opts.PollPeriod = 1 * time.Hour } - lp := logpoller.NewLogPoller(o, esc, lggr, opts) + lp := logpoller.NewLogPoller(o, esc, lggr, headTracker, opts) emitterAddress1, _, emitter1, err := log_emitter.DeployLogEmitter(owner, ec) require.NoError(t, err) emitterAddress2, _, emitter2, err := log_emitter.DeployLogEmitter(owner, ec) diff --git a/core/chains/evm/logpoller/log_poller.go b/core/chains/evm/logpoller/log_poller.go index 1c8996a46e..333c5b70f8 100644 --- a/core/chains/evm/logpoller/log_poller.go +++ b/core/chains/evm/logpoller/log_poller.go @@ -27,6 +27,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/types/query" "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink-common/pkg/utils/mathutil" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" @@ -88,6 +89,10 @@ type Client interface { ConfiguredChainID() *big.Int } +type HeadTracker interface { + LatestAndFinalizedBlock(ctx context.Context) (latest, finalized *evmtypes.Head, err error) +} + var ( _ LogPollerTest = &logPoller{} ErrReplayRequestAborted = pkgerrors.New("aborted, replay request cancelled") @@ -100,6 +105,7 @@ type logPoller struct { services.StateMachine ec Client orm ORM + headTracker HeadTracker lggr logger.SugaredLogger pollPeriod time.Duration // poll period set by block production rate useFinalityTag bool // indicates whether logPoller should use chain's finality or pick a fixed depth for finality @@ -150,11 +156,12 @@ type Opts struct { // // How fast that can be done depends largely on network speed and DB, but even for the fastest // support chain, polygon, which has 2s block times, we need RPCs roughly with <= 500ms latency -func NewLogPoller(orm ORM, ec Client, lggr logger.Logger, opts Opts) *logPoller { +func NewLogPoller(orm ORM, ec Client, lggr logger.Logger, headTracker HeadTracker, opts Opts) *logPoller { return &logPoller{ stopCh: make(chan struct{}), ec: ec, orm: orm, + headTracker: headTracker, lggr: logger.Sugared(logger.Named(lggr, "LogPoller")), replayStart: make(chan int64), replayComplete: make(chan error), @@ -1007,33 +1014,15 @@ func (lp *logPoller) PollAndSaveLogs(ctx context.Context, currentBlockNumber int } } -// Returns information about latestBlock, latestFinalizedBlockNumber -// If finality tag is not enabled, latestFinalizedBlockNumber is calculated as latestBlockNumber - lp.finalityDepth (configured param) -// Otherwise, we return last finalized block number returned from chain +// Returns information about latestBlock, latestFinalizedBlockNumber provided by HeadTracker func (lp *logPoller) latestBlocks(ctx context.Context) (*evmtypes.Head, int64, error) { - // If finality is not enabled, we can only fetch the latest block - if !lp.useFinalityTag { - // Example: - // finalityDepth = 2 - // Blocks: 1->2->3->4->5(latestBlock) - // latestFinalizedBlockNumber would be 3 - latestBlock, err := lp.ec.HeadByNumber(ctx, nil) - if err != nil { - return nil, 0, err - } - // If chain has fewer blocks than finalityDepth, return 0 - return latestBlock, mathutil.Max(latestBlock.Number-lp.finalityDepth, 0), nil - } - - // If finality is enabled, we need to get the latest and finalized blocks. - blocks, err := lp.batchFetchBlocks(ctx, []string{rpc.LatestBlockNumber.String(), rpc.FinalizedBlockNumber.String()}, 2) + latest, finalized, err := lp.headTracker.LatestAndFinalizedBlock(ctx) if err != nil { - return nil, 0, err + return nil, 0, fmt.Errorf("failed to get latest and latest finalized block from HeadTracker: %w", err) } - latest := blocks[0] - finalized := blocks[1] - lp.lggr.Debugw("Latest blocks read from chain", "latest", latest.Number, "finalized", finalized.Number) - return latest, finalized.Number, nil + + lp.lggr.Debugw("Latest blocks read from chain", "latest", latest.Number, "finalized", finalized.BlockNumber()) + return latest, finalized.BlockNumber(), nil } // Find the first place where our chain and their chain have the same block, diff --git a/core/chains/evm/logpoller/log_poller_internal_test.go b/core/chains/evm/logpoller/log_poller_internal_test.go index bc29510587..448710b93f 100644 --- a/core/chains/evm/logpoller/log_poller_internal_test.go +++ b/core/chains/evm/logpoller/log_poller_internal_test.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "math/big" - "reflect" "strings" "sync" "testing" @@ -22,10 +21,13 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" + htMocks "github.com/smartcontractkit/chainlink/v2/common/headtracker/mocks" 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" @@ -73,7 +75,7 @@ func TestLogPoller_RegisterFilter(t *testing.T) { RpcBatchSize: 2, KeepFinalizedBlocksDepth: 1000, } - lp := NewLogPoller(orm, nil, lggr, lpOpts) + lp := NewLogPoller(orm, nil, lggr, nil, lpOpts) // We expect a zero Filter if nothing registered yet. f := lp.Filter(nil, nil, nil) @@ -208,8 +210,10 @@ func TestLogPoller_BackupPollerStartup(t *testing.T) { db := pgtest.NewSqlxDB(t) orm := NewORM(chainID, db, lggr) latestBlock := int64(4) + const finalityDepth = 2 - head := evmtypes.Head{Number: latestBlock} + head := &evmtypes.Head{Number: latestBlock} + finalizedHead := &evmtypes.Head{Number: latestBlock - finalityDepth} events := []common.Hash{EmitterABI.Events["Log1"].ID} log1 := types.Log{ Index: 0, @@ -222,20 +226,22 @@ func TestLogPoller_BackupPollerStartup(t *testing.T) { } ec := evmclimocks.NewClient(t) - ec.On("HeadByNumber", mock.Anything, mock.Anything).Return(&head, nil) ec.On("FilterLogs", mock.Anything, mock.Anything).Return([]types.Log{log1}, nil) ec.On("ConfiguredChainID").Return(chainID, nil) + headTracker := htMocks.NewHeadTracker[*evmtypes.Head, common.Hash](t) + headTracker.On("LatestAndFinalizedBlock", mock.Anything).Return(head, finalizedHead, nil) + ctx := testutils.Context(t) lpOpts := Opts{ PollPeriod: time.Hour, - FinalityDepth: 2, + FinalityDepth: finalityDepth, BackfillBatchSize: 3, RpcBatchSize: 2, KeepFinalizedBlocksDepth: 1000, BackupPollerBlockDelay: 0, } - lp := NewLogPoller(orm, ec, lggr, lpOpts) + lp := NewLogPoller(orm, ec, lggr, headTracker, lpOpts) lp.BackupPollAndSaveLogs(ctx) assert.Equal(t, int64(0), lp.backupPollerNextBlock) assert.Equal(t, 1, observedLogs.FilterMessageSnippet("ran before first successful log poller run").Len()) @@ -309,7 +315,14 @@ func TestLogPoller_Replay(t *testing.T) { KeepFinalizedBlocksDepth: 20, BackupPollerBlockDelay: 0, } - lp := NewLogPoller(orm, ec, lggr, lpOpts) + headTracker := htMocks.NewHeadTracker[*evmtypes.Head, common.Hash](t) + + headTracker.On("LatestAndFinalizedBlock", mock.Anything).Return(func(ctx context.Context) (*evmtypes.Head, *evmtypes.Head, error) { + headCopy := head + finalized := &evmtypes.Head{Number: headCopy.Number - lpOpts.FinalityDepth} + return &headCopy, finalized, nil + }) + lp := NewLogPoller(orm, ec, lggr, headTracker, lpOpts) { ctx := testutils.Context(t) @@ -533,10 +546,6 @@ func (lp *logPoller) reset() { func Test_latestBlockAndFinalityDepth(t *testing.T) { lggr := logger.Test(t) - chainID := testutils.FixtureChainID - db := pgtest.NewSqlxDB(t) - orm := NewORM(chainID, db, lggr) - ctx := testutils.Context(t) lpOpts := Opts{ PollPeriod: time.Hour, @@ -545,71 +554,27 @@ func Test_latestBlockAndFinalityDepth(t *testing.T) { KeepFinalizedBlocksDepth: 20, } - t.Run("pick latest block from chain and use finality from config with finality disabled", func(t *testing.T) { - head := evmtypes.Head{Number: 4} - - lpOpts.UseFinalityTag = false - lpOpts.FinalityDepth = int64(3) - ec := evmclimocks.NewClient(t) - ec.On("HeadByNumber", mock.Anything, mock.Anything).Return(&head, nil) + t.Run("headTracker returns an error", func(t *testing.T) { + headTracker := htMocks.NewHeadTracker[*evmtypes.Head, common.Hash](t) + const expectedError = "finalized block is not available yet" + headTracker.On("LatestAndFinalizedBlock", mock.Anything).Return(&evmtypes.Head{}, &evmtypes.Head{}, fmt.Errorf(expectedError)) - lp := NewLogPoller(orm, ec, lggr, lpOpts) - latestBlock, lastFinalizedBlockNumber, err := lp.latestBlocks(ctx) - require.NoError(t, err) - require.Equal(t, latestBlock.Number, head.Number) - require.Equal(t, lpOpts.FinalityDepth, latestBlock.Number-lastFinalizedBlockNumber) + lp := NewLogPoller(nil, nil, lggr, headTracker, lpOpts) + _, _, err := lp.latestBlocks(tests.Context(t)) + require.ErrorContains(t, err, expectedError) }) - - t.Run("finality tags in use", func(t *testing.T) { - t.Run("client returns data properly", func(t *testing.T) { - expectedLatestBlockNumber := int64(20) - expectedLastFinalizedBlockNumber := int64(12) - ec := evmclimocks.NewClient(t) - ec.On("BatchCallContext", mock.Anything, mock.MatchedBy(func(b []rpc.BatchElem) bool { - return len(b) == 2 && - reflect.DeepEqual(b[0].Args, []interface{}{"latest", false}) && - reflect.DeepEqual(b[1].Args, []interface{}{"finalized", false}) - })).Return(nil).Run(func(args mock.Arguments) { - elems := args.Get(1).([]rpc.BatchElem) - // Latest block details - *(elems[0].Result.(*evmtypes.Head)) = evmtypes.Head{Number: expectedLatestBlockNumber, Hash: utils.RandomBytes32()} - // Finalized block details - *(elems[1].Result.(*evmtypes.Head)) = evmtypes.Head{Number: expectedLastFinalizedBlockNumber, Hash: utils.RandomBytes32()} - }) - - lpOpts.UseFinalityTag = true - lp := NewLogPoller(orm, ec, lggr, lpOpts) - - latestBlock, lastFinalizedBlockNumber, err := lp.latestBlocks(ctx) - require.NoError(t, err) - require.Equal(t, expectedLatestBlockNumber, latestBlock.Number) - require.Equal(t, expectedLastFinalizedBlockNumber, lastFinalizedBlockNumber) - }) - - t.Run("client returns error for at least one of the calls", func(t *testing.T) { - ec := evmclimocks.NewClient(t) - ec.On("BatchCallContext", mock.Anything, mock.Anything).Return(nil).Run(func(args mock.Arguments) { - elems := args.Get(1).([]rpc.BatchElem) - // Latest block details - *(elems[0].Result.(*evmtypes.Head)) = evmtypes.Head{Number: 10} - // Finalized block details - elems[1].Error = fmt.Errorf("some error") - }) - - lpOpts.UseFinalityTag = true - lp := NewLogPoller(orm, ec, lggr, lpOpts) - _, _, err := lp.latestBlocks(ctx) - require.Error(t, err) - }) - - t.Run("BatchCall returns an error", func(t *testing.T) { - ec := evmclimocks.NewClient(t) - ec.On("BatchCallContext", mock.Anything, mock.Anything).Return(fmt.Errorf("some error")) - lpOpts.UseFinalityTag = true - lp := NewLogPoller(orm, ec, lggr, lpOpts) - _, _, err := lp.latestBlocks(ctx) - require.Error(t, err) - }) + t.Run("headTracker returns valid chain", func(t *testing.T) { + headTracker := htMocks.NewHeadTracker[*evmtypes.Head, common.Hash](t) + finalizedBlock := &evmtypes.Head{Number: 2, IsFinalized: true} + head := &evmtypes.Head{Number: 10} + headTracker.On("LatestAndFinalizedBlock", mock.Anything).Return(head, finalizedBlock, nil) + + lp := NewLogPoller(nil, nil, lggr, headTracker, lpOpts) + latestBlock, finalizedBlockNumber, err := lp.latestBlocks(tests.Context(t)) + require.NoError(t, err) + require.NotNil(t, latestBlock) + assert.Equal(t, head.BlockNumber(), latestBlock.BlockNumber()) + assert.Equal(t, finalizedBlock.Number, finalizedBlockNumber) }) } @@ -653,7 +618,7 @@ func Test_FetchBlocks(t *testing.T) { errors.New("Received unfinalized block 9 while expecting finalized block (latestFinalizedBlockNumber = 5)"), }} - lp := NewLogPoller(orm, ec, lggr, lpOpts) + lp := NewLogPoller(orm, ec, lggr, nil, lpOpts) for _, tc := range cases { for _, lp.useFinalityTag = range []bool{false, true} { blockValidationReq := latestBlock @@ -693,7 +658,7 @@ func benchmarkFilter(b *testing.B, nFilters, nAddresses, nEvents int) { RpcBatchSize: 2, KeepFinalizedBlocksDepth: 1000, } - lp := NewLogPoller(nil, nil, lggr, lpOpts) + lp := NewLogPoller(nil, nil, lggr, nil, lpOpts) for i := 0; i < nFilters; i++ { var addresses []common.Address var events []common.Hash diff --git a/core/chains/evm/logpoller/log_poller_test.go b/core/chains/evm/logpoller/log_poller_test.go index 6ef1692150..c83efc83b3 100644 --- a/core/chains/evm/logpoller/log_poller_test.go +++ b/core/chains/evm/logpoller/log_poller_test.go @@ -28,7 +28,9 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" commonutils "github.com/smartcontractkit/chainlink-common/pkg/utils" + htMocks "github.com/smartcontractkit/chainlink/v2/common/headtracker/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" @@ -717,7 +719,9 @@ func TestLogPoller_SynchronizedWithGeth(t *testing.T) { RpcBatchSize: 2, KeepFinalizedBlocksDepth: 1000, } - lp := logpoller.NewLogPoller(orm, client.NewSimulatedBackendClient(t, ec, chainID), lggr, lpOpts) + simulatedClient := client.NewSimulatedBackendClient(t, ec, chainID) + ht := headtracker.NewSimulatedHeadTracker(simulatedClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) + lp := logpoller.NewLogPoller(orm, simulatedClient, lggr, ht, lpOpts) for i := 0; i < finalityDepth; i++ { // Have enough blocks that we could reorg the full finalityDepth-1. ec.Commit() } @@ -1493,7 +1497,7 @@ func TestLogPoller_DBErrorHandling(t *testing.T) { RpcBatchSize: 2, KeepFinalizedBlocksDepth: 1000, } - lp := logpoller.NewLogPoller(o, client.NewSimulatedBackendClient(t, ec, chainID2), lggr, lpOpts) + lp := logpoller.NewLogPoller(o, client.NewSimulatedBackendClient(t, ec, chainID2), lggr, nil, lpOpts) err = lp.Replay(ctx, 5) // block number too high require.ErrorContains(t, err, "Invalid replay block number") @@ -1548,7 +1552,8 @@ func TestTooManyLogResults(t *testing.T) { RpcBatchSize: 10, KeepFinalizedBlocksDepth: 1000, } - lp := logpoller.NewLogPoller(o, ec, lggr, lpOpts) + headTracker := htMocks.NewHeadTracker[*evmtypes.Head, common.Hash](t) + lp := logpoller.NewLogPoller(o, ec, lggr, headTracker, lpOpts) expected := []int64{10, 5, 2, 1} clientErr := client.JsonError{ @@ -1557,9 +1562,13 @@ func TestTooManyLogResults(t *testing.T) { Message: "query returned more than 10000 results. Try with this block range [0x100E698, 0x100E6D4].", } + // Simulate currentBlock = 300 + head := &evmtypes.Head{Number: 300} + finalized := &evmtypes.Head{Number: head.Number - lpOpts.FinalityDepth} + headTracker.On("LatestAndFinalizedBlock", mock.Anything).Return(head, finalized, nil).Once() call1 := ec.On("HeadByNumber", mock.Anything, mock.Anything).Return(func(ctx context.Context, blockNumber *big.Int) (*evmtypes.Head, error) { if blockNumber == nil { - return &evmtypes.Head{Number: 300}, nil // Simulate currentBlock = 300 + require.FailNow(t, "unexpected call to get current head") } return &evmtypes.Head{Number: blockNumber.Int64()}, nil }) @@ -1601,9 +1610,12 @@ func TestTooManyLogResults(t *testing.T) { // Now jump to block 500, but return error no matter how small the block range gets. // Should exit the loop with a critical error instead of hanging. + head = &evmtypes.Head{Number: 500} + finalized = &evmtypes.Head{Number: head.Number - lpOpts.FinalityDepth} + headTracker.On("LatestAndFinalizedBlock", mock.Anything).Return(head, finalized, nil).Once() call1.On("HeadByNumber", mock.Anything, mock.Anything).Return(func(ctx context.Context, blockNumber *big.Int) (*evmtypes.Head, error) { if blockNumber == nil { - return &evmtypes.Head{Number: 500}, nil // Simulate currentBlock = 300 + require.FailNow(t, "unexpected call to get current head") } return &evmtypes.Head{Number: blockNumber.Int64()}, nil }) @@ -1938,7 +1950,7 @@ func TestFindLCA(t *testing.T) { KeepFinalizedBlocksDepth: 1000, } - lp := logpoller.NewLogPoller(orm, ec, lggr, lpOpts) + lp := logpoller.NewLogPoller(orm, ec, lggr, nil, lpOpts) t.Run("Fails, if failed to select oldest block", func(t *testing.T) { _, err := lp.FindLCA(ctx) require.ErrorContains(t, err, "failed to select the latest block") diff --git a/core/chains/evm/logpoller/mocks/log_poller.go b/core/chains/evm/logpoller/mocks/log_poller.go index 3c8f445df5..76442c8e94 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/chains/evm/logpoller/orm.go b/core/chains/evm/logpoller/orm.go index 1d24976073..9cbb21a606 100644 --- a/core/chains/evm/logpoller/orm.go +++ b/core/chains/evm/logpoller/orm.go @@ -101,9 +101,9 @@ func (o *DSORM) InsertBlock(ctx context.Context, blockHash common.Hash, blockNum if err != nil { return err } - query := `INSERT INTO evm.log_poller_blocks - (evm_chain_id, block_hash, block_number, block_timestamp, finalized_block_number, created_at) - VALUES (:evm_chain_id, :block_hash, :block_number, :block_timestamp, :finalized_block_number, NOW()) + query := `INSERT INTO evm.log_poller_blocks + (evm_chain_id, block_hash, block_number, block_timestamp, finalized_block_number, created_at) + VALUES (:evm_chain_id, :block_hash, :block_number, :block_timestamp, :finalized_block_number, NOW()) ON CONFLICT DO NOTHING` _, err = o.ds.NamedExecContext(ctx, query, args) return err @@ -165,7 +165,7 @@ func (o *DSORM) DeleteFilter(ctx context.Context, name string) error { // LoadFilters returns all filters for this chain func (o *DSORM) LoadFilters(ctx context.Context) (map[string]Filter, error) { query := `SELECT name, - ARRAY_AGG(DISTINCT address)::BYTEA[] AS addresses, + ARRAY_AGG(DISTINCT address)::BYTEA[] AS addresses, ARRAY_AGG(DISTINCT event)::BYTEA[] AS event_sigs, ARRAY_AGG(DISTINCT topic2 ORDER BY topic2) FILTER(WHERE topic2 IS NOT NULL) AS topic2, ARRAY_AGG(DISTINCT topic3 ORDER BY topic3) FILTER(WHERE topic3 IS NOT NULL) AS topic3, @@ -229,7 +229,7 @@ func (o *DSORM) SelectLatestLogByEventSigWithConfs(ctx context.Context, eventSig AND event_sig = :event_sig AND address = :address AND block_number <= %s - ORDER BY block_number desc, log_index DESC + ORDER BY block_number desc, log_index DESC LIMIT 1 `, nestedBlockNumberQuery(confs)) var l Log @@ -252,7 +252,7 @@ func (o *DSORM) DeleteBlocksBefore(ctx context.Context, end int64, limit int64) `DELETE FROM evm.log_poller_blocks WHERE block_number IN ( SELECT block_number FROM evm.log_poller_blocks - WHERE block_number <= $1 + WHERE block_number <= $1 AND evm_chain_id = $2 LIMIT $3 ) @@ -263,7 +263,7 @@ func (o *DSORM) DeleteBlocksBefore(ctx context.Context, end int64, limit int64) } return result.RowsAffected() } - result, err := o.ds.ExecContext(ctx, `DELETE FROM evm.log_poller_blocks + result, err := o.ds.ExecContext(ctx, `DELETE FROM evm.log_poller_blocks WHERE block_number <= $1 AND evm_chain_id = $2`, end, ubig.New(o.chainID)) if err != nil { return 0, err @@ -280,11 +280,11 @@ func (o *DSORM) DeleteLogsAndBlocksAfter(ctx context.Context, start int64) error // If not applied, these queries can become very slow. After some critical number // of logs, Postgres will try to scan all the logs in the index by block_number. // Latency without upper bound filter can be orders of magnitude higher for large number of logs. - _, err := o.ds.ExecContext(ctx, `DELETE FROM evm.log_poller_blocks + _, err := o.ds.ExecContext(ctx, `DELETE FROM evm.log_poller_blocks WHERE evm_chain_id = $1 AND block_number >= $2 - AND block_number <= (SELECT MAX(block_number) - FROM evm.log_poller_blocks + AND block_number <= (SELECT MAX(block_number) + FROM evm.log_poller_blocks WHERE evm_chain_id = $1)`, ubig.New(o.chainID), start) if err != nil { @@ -292,8 +292,8 @@ func (o *DSORM) DeleteLogsAndBlocksAfter(ctx context.Context, start int64) error return err } - _, err = o.ds.ExecContext(ctx, `DELETE FROM evm.logs - WHERE evm_chain_id = $1 + _, err = o.ds.ExecContext(ctx, `DELETE FROM evm.logs + WHERE evm_chain_id = $1 AND block_number >= $2 AND block_number <= (SELECT MAX(block_number) FROM evm.logs WHERE evm_chain_id = $1)`, ubig.New(o.chainID), start) @@ -335,7 +335,7 @@ func (o *DSORM) DeleteExpiredLogs(ctx context.Context, limit int64) (int64, erro } else { result, err = o.ds.ExecContext(ctx, `WITH r AS ( SELECT address, event, MAX(retention) AS retention - FROM evm.log_poller_filters WHERE evm_chain_id=$1 + FROM evm.log_poller_filters WHERE evm_chain_id=$1 GROUP BY evm_chain_id,address, event HAVING NOT 0 = ANY(ARRAY_AGG(retention)) ) DELETE FROM evm.logs l USING r WHERE l.evm_chain_id = $1 AND l.address=r.address AND l.event_sig=r.event @@ -387,10 +387,10 @@ func (o *DSORM) insertLogsWithinTx(ctx context.Context, logs []Log, tx sqlutil.D end = len(logs) } - query := `INSERT INTO evm.logs - (evm_chain_id, log_index, block_hash, block_number, block_timestamp, address, event_sig, topics, tx_hash, data, created_at) - VALUES - (:evm_chain_id, :log_index, :block_hash, :block_number, :block_timestamp, :address, :event_sig, :topics, :tx_hash, :data, NOW()) + query := `INSERT INTO evm.logs + (evm_chain_id, log_index, block_hash, block_number, block_timestamp, address, event_sig, topics, tx_hash, data, created_at) + VALUES + (:evm_chain_id, :log_index, :block_hash, :block_number, :block_timestamp, :address, :event_sig, :topics, :tx_hash, :data, NOW()) ON CONFLICT DO NOTHING` _, err := o.ds.NamedExecContext(ctx, query, logs[start:end]) @@ -425,10 +425,10 @@ func (o *DSORM) SelectLogsByBlockRange(ctx context.Context, start, end int64) ([ return nil, err } - query := `SELECT * FROM evm.logs + query := `SELECT * FROM evm.logs WHERE evm_chain_id = :evm_chain_id - AND block_number >= :start_block - AND block_number <= :end_block + AND block_number >= :start_block + AND block_number <= :end_block ORDER BY block_number, log_index` var logs []Log @@ -454,11 +454,11 @@ func (o *DSORM) SelectLogs(ctx context.Context, start, end int64, address common return nil, err } - query := `SELECT * FROM evm.logs - WHERE evm_chain_id = :evm_chain_id + query := `SELECT * FROM evm.logs + WHERE evm_chain_id = :evm_chain_id AND address = :address - AND event_sig = :event_sig - AND block_number >= :start_block + AND event_sig = :event_sig + AND block_number >= :start_block AND block_number <= :end_block ORDER BY block_number, log_index` @@ -486,7 +486,7 @@ func (o *DSORM) SelectLogsCreatedAfter(ctx context.Context, address common.Addre } query := fmt.Sprintf(` - SELECT * FROM evm.logs + SELECT * FROM evm.logs WHERE evm_chain_id = :evm_chain_id AND address = :address AND event_sig = :event_sig @@ -547,8 +547,8 @@ func (o *DSORM) GetBlocksRange(ctx context.Context, start int64, end int64) ([]L return nil, err } - query := `SELECT * FROM evm.log_poller_blocks - WHERE block_number >= :start_block + query := `SELECT * FROM evm.log_poller_blocks + WHERE block_number >= :start_block AND block_number <= :end_block AND evm_chain_id = :evm_chain_id ORDER BY block_number ASC` @@ -580,11 +580,11 @@ func (o *DSORM) SelectLatestLogEventSigsAddrsWithConfs(ctx context.Context, from query := fmt.Sprintf(` SELECT * FROM evm.logs WHERE (block_number, address, event_sig) IN ( - SELECT MAX(block_number), address, event_sig FROM evm.logs - WHERE evm_chain_id = :evm_chain_id - AND event_sig = ANY(:event_sig_array) - AND address = ANY(:address_array) - AND block_number > :start_block + SELECT MAX(block_number), address, event_sig FROM evm.logs + WHERE evm_chain_id = :evm_chain_id + AND event_sig = ANY(:event_sig_array) + AND address = ANY(:address_array) + AND block_number > :start_block AND block_number <= %s GROUP BY event_sig, address ) @@ -615,10 +615,10 @@ func (o *DSORM) SelectLatestBlockByEventSigsAddrsWithConfs(ctx context.Context, } query := fmt.Sprintf(` SELECT COALESCE(MAX(block_number), 0) FROM evm.logs - WHERE evm_chain_id = :evm_chain_id - AND event_sig = ANY(:event_sig_array) - AND address = ANY(:address_array) - AND block_number > :start_block + WHERE evm_chain_id = :evm_chain_id + AND event_sig = ANY(:event_sig_array) + AND address = ANY(:address_array) + AND block_number > :start_block AND block_number <= %s`, nestedBlockNumberQuery(confs)) var blockNumber int64 @@ -644,9 +644,9 @@ func (o *DSORM) SelectLogsDataWordRange(ctx context.Context, address common.Addr return nil, err } - query := fmt.Sprintf(`SELECT * FROM evm.logs + query := fmt.Sprintf(`SELECT * FROM evm.logs WHERE evm_chain_id = :evm_chain_id - AND address = :address + AND address = :address AND event_sig = :event_sig AND substring(data from 32*:word_index+1 for 32) >= :word_value_min AND substring(data from 32*:word_index+1 for 32) <= :word_value_max @@ -676,7 +676,7 @@ func (o *DSORM) SelectLogsDataWordGreaterThan(ctx context.Context, address commo } query := fmt.Sprintf(` - SELECT * FROM evm.logs + SELECT * FROM evm.logs WHERE evm_chain_id = :evm_chain_id AND address = :address AND event_sig = :event_sig @@ -707,7 +707,7 @@ func (o *DSORM) SelectLogsDataWordBetween(ctx context.Context, address common.Ad return nil, err } query := fmt.Sprintf(` - SELECT * FROM evm.logs + SELECT * FROM evm.logs WHERE evm_chain_id = :evm_chain_id AND address = :address AND event_sig = :event_sig @@ -741,7 +741,7 @@ func (o *DSORM) SelectIndexedLogsTopicGreaterThan(ctx context.Context, address c query := fmt.Sprintf(` SELECT * FROM evm.logs WHERE evm_chain_id = :evm_chain_id - AND address = :address + AND address = :address AND event_sig = :event_sig AND topics[:topic_index] >= :topic_value_min AND block_number <= %s @@ -771,7 +771,7 @@ func (o *DSORM) SelectIndexedLogsTopicRange(ctx context.Context, address common. } query := fmt.Sprintf(` - SELECT * FROM evm.logs + SELECT * FROM evm.logs WHERE evm_chain_id = :evm_chain_id AND address = :address AND event_sig = :event_sig @@ -803,7 +803,7 @@ func (o *DSORM) SelectIndexedLogs(ctx context.Context, address common.Address, e } query := fmt.Sprintf(` - SELECT * FROM evm.logs + SELECT * FROM evm.logs WHERE evm_chain_id = :evm_chain_id AND address = :address AND event_sig = :event_sig @@ -835,8 +835,8 @@ func (o *DSORM) SelectIndexedLogsByBlockRange(ctx context.Context, start, end in return nil, err } - query := `SELECT * FROM evm.logs - WHERE evm_chain_id = :evm_chain_id + query := `SELECT * FROM evm.logs + WHERE evm_chain_id = :evm_chain_id AND address = :address AND event_sig = :event_sig AND topics[:topic_index] = ANY(:topic_values) @@ -869,7 +869,7 @@ func (o *DSORM) SelectIndexedLogsCreatedAfter(ctx context.Context, address commo } query := fmt.Sprintf(` - SELECT * FROM evm.logs + SELECT * FROM evm.logs WHERE evm_chain_id = :evm_chain_id AND address = :address AND event_sig = :event_sig @@ -901,7 +901,7 @@ func (o *DSORM) SelectIndexedLogsByTxHash(ctx context.Context, address common.Ad return nil, err } - query := `SELECT * FROM evm.logs + query := `SELECT * FROM evm.logs WHERE evm_chain_id = :evm_chain_id AND address = :address AND event_sig = :event_sig @@ -943,7 +943,7 @@ func (o *DSORM) SelectIndexedLogsWithSigsExcluding(ctx context.Context, sigA, si AND address = :address AND event_sig = :sigA AND block_number BETWEEN :start_block AND :end_block - AND block_number <= %s + AND block_number <= %s EXCEPT SELECT a.* FROM evm.logs AS a INNER JOIN evm.logs B @@ -996,16 +996,16 @@ func (o *DSORM) FilteredLogs(ctx context.Context, filter query.KeyFilter, limitA func nestedBlockNumberQuery(confs evmtypes.Confirmations) string { if confs == evmtypes.Finalized { return ` - (SELECT finalized_block_number - FROM evm.log_poller_blocks - WHERE evm_chain_id = :evm_chain_id + (SELECT finalized_block_number + FROM evm.log_poller_blocks + WHERE evm_chain_id = :evm_chain_id ORDER BY block_number DESC LIMIT 1) ` } // Intentionally wrap with greatest() function and don't return negative block numbers when :confs > :block_number // It doesn't impact logic of the outer query, because block numbers are never less or equal to 0 (guarded by log_poller_blocks_block_number_check) return ` - (SELECT greatest(block_number - :confs, 0) - FROM evm.log_poller_blocks - WHERE evm_chain_id = :evm_chain_id + (SELECT greatest(block_number - :confs, 0) + FROM evm.log_poller_blocks + WHERE evm_chain_id = :evm_chain_id ORDER BY block_number DESC LIMIT 1) ` } diff --git a/core/chains/evm/mocks/balance_monitor.go b/core/chains/evm/mocks/balance_monitor.go index 7c55cd78eb..2bf4898ef2 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/chains/evm/txmgr/mocks/config.go b/core/chains/evm/txmgr/mocks/config.go index 5ec00e960a..efaaab68af 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/chains/evm/txmgr/mocks/evm_tx_store.go b/core/chains/evm/txmgr/mocks/evm_tx_store.go index 465681247e..5a699f71bf 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/chains/evm/txmgr/txmgr_test.go b/core/chains/evm/txmgr/txmgr_test.go index dac0ef29cd..40df5616c9 100644 --- a/core/chains/evm/txmgr/txmgr_test.go +++ b/core/chains/evm/txmgr/txmgr_test.go @@ -35,6 +35,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/forwarders" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" gasmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas/mocks" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/keystore" ksmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/keystore/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" @@ -59,7 +60,9 @@ func makeTestEvmTxm( RpcBatchSize: 2, KeepFinalizedBlocksDepth: 1000, } - lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.FixtureChainID, db, lggr), ethClient, lggr, lpOpts) + + ht := headtracker.NewSimulatedHeadTracker(ethClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) + lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.FixtureChainID, db, lggr), ethClient, lggr, ht, lpOpts) // logic for building components (from evm/evm_txm.go) ------- lggr.Infow("Initializing EVM transaction manager", @@ -97,7 +100,8 @@ func TestTxm_SendNativeToken_DoesNotSendToZero(t *testing.T) { keyStore := cltest.NewKeyStore(t, db).Eth() ethClient := testutils.NewEthClientMockWithDefaultChain(t) - estimator := gas.NewEstimator(logger.Test(t), ethClient, config, evmConfig.GasEstimator()) + estimator, err := gas.NewEstimator(logger.Test(t), ethClient, config, evmConfig.GasEstimator()) + require.NoError(t, err) txm, err := makeTestEvmTxm(t, db, ethClient, estimator, evmConfig, evmConfig.GasEstimator(), evmConfig.Transactions(), dbConfig, dbConfig.Listener(), keyStore) require.NoError(t, err) @@ -122,7 +126,8 @@ func TestTxm_CreateTransaction(t *testing.T) { ethClient := testutils.NewEthClientMockWithDefaultChain(t) - estimator := gas.NewEstimator(logger.Test(t), ethClient, config, evmConfig.GasEstimator()) + estimator, err := gas.NewEstimator(logger.Test(t), ethClient, config, evmConfig.GasEstimator()) + require.NoError(t, err) txm, err := makeTestEvmTxm(t, db, ethClient, estimator, evmConfig, evmConfig.GasEstimator(), evmConfig.Transactions(), dbConfig, dbConfig.Listener(), kst.Eth()) require.NoError(t, err) @@ -403,7 +408,8 @@ func TestTxm_CreateTransaction_OutOfEth(t *testing.T) { config, dbConfig, evmConfig := txmgr.MakeTestConfigs(t) ethClient := testutils.NewEthClientMockWithDefaultChain(t) - estimator := gas.NewEstimator(logger.Test(t), ethClient, config, evmConfig.GasEstimator()) + estimator, err := gas.NewEstimator(logger.Test(t), ethClient, config, evmConfig.GasEstimator()) + require.NoError(t, err) txm, err := makeTestEvmTxm(t, db, ethClient, estimator, evmConfig, evmConfig.GasEstimator(), evmConfig.Transactions(), dbConfig, dbConfig.Listener(), etKeyStore) require.NoError(t, err) @@ -494,7 +500,8 @@ func TestTxm_Lifecycle(t *testing.T) { keyChangeCh := make(chan struct{}) unsub := cltest.NewAwaiter() kst.On("SubscribeToKeyChanges", mock.Anything).Return(keyChangeCh, unsub.ItHappened) - estimator := gas.NewEstimator(logger.Test(t), ethClient, config, evmConfig.GasEstimator()) + estimator, err := gas.NewEstimator(logger.Test(t), ethClient, config, evmConfig.GasEstimator()) + require.NoError(t, err) txm, err := makeTestEvmTxm(t, db, ethClient, estimator, evmConfig, evmConfig.GasEstimator(), evmConfig.Transactions(), dbConfig, dbConfig.Listener(), kst) require.NoError(t, err) @@ -549,7 +556,8 @@ func TestTxm_Reset(t *testing.T) { ethClient.On("PendingNonceAt", mock.Anything, addr).Return(uint64(128), nil).Maybe() ethClient.On("PendingNonceAt", mock.Anything, addr2).Return(uint64(44), nil).Maybe() - estimator := gas.NewEstimator(logger.Test(t), ethClient, cfg.EVM(), cfg.EVM().GasEstimator()) + estimator, err := gas.NewEstimator(logger.Test(t), ethClient, cfg.EVM(), cfg.EVM().GasEstimator()) + require.NoError(t, err) txm, err := makeTestEvmTxm(t, db, ethClient, estimator, cfg.EVM(), cfg.EVM().GasEstimator(), cfg.EVM().Transactions(), gcfg.Database(), gcfg.Database().Listener(), kst.Eth()) require.NoError(t, err) @@ -603,11 +611,11 @@ func TestTxm_GetTransactionStatus(t *testing.T) { ethClient.On("PendingNonceAt", mock.Anything, mock.Anything).Return(uint64(0), nil).Maybe() feeEstimator := gasmocks.NewEvmFeeEstimator(t) feeEstimator.On("Start", mock.Anything).Return(nil).Once() + feeEstimator.On("Close", mock.Anything).Return(nil).Once() feeEstimator.On("OnNewLongestChain", mock.Anything, mock.Anything).Once() txm, err := makeTestEvmTxm(t, db, ethClient, feeEstimator, cfg.EVM(), cfg.EVM().GasEstimator(), cfg.EVM().Transactions(), gcfg.Database(), gcfg.Database().Listener(), ethKeyStore) require.NoError(t, err) - err = txm.Start(ctx) - require.NoError(t, err) + servicetest.Run(t, txm) head := &evmtypes.Head{ Hash: utils.NewHash(), diff --git a/core/chains/evm/types/models.go b/core/chains/evm/types/models.go index 2af5b81ccf..a9e5cd5841 100644 --- a/core/chains/evm/types/models.go +++ b/core/chains/evm/types/models.go @@ -118,16 +118,23 @@ func (h *Head) IsInChain(blockHash common.Hash) bool { // HashAtHeight returns the hash of the block at the given height, if it is in the chain. // If not in chain, returns the zero hash func (h *Head) HashAtHeight(blockNum int64) common.Hash { - for { + headAtHeight, err := h.HeadAtHeight(blockNum) + if err != nil { + return common.Hash{} + } + + return headAtHeight.BlockHash() +} + +func (h *Head) HeadAtHeight(blockNum int64) (commontypes.Head[common.Hash], error) { + for h != nil { if h.Number == blockNum { - return h.Hash - } - if h.Parent == nil { - break + return h, nil } + h = h.Parent } - return common.Hash{} + return nil, fmt.Errorf("failed to find head at height %d", blockNum) } // ChainLength returns the length of the chain followed by recursively looking up parents diff --git a/core/chains/evm/types/models_test.go b/core/chains/evm/types/models_test.go index e7b08efa94..01666d7bda 100644 --- a/core/chains/evm/types/models_test.go +++ b/core/chains/evm/types/models_test.go @@ -247,6 +247,26 @@ func TestHead_EarliestInChain(t *testing.T) { assert.Equal(t, int64(1), head.EarliestInChain().BlockNumber()) } +func TestHead_HeadAtHeight(t *testing.T) { + expectedResult := &evmtypes.Head{ + Hash: common.BigToHash(big.NewInt(10)), + Number: 2, + Parent: &evmtypes.Head{ + Number: 1, + }, + } + head := evmtypes.Head{ + Number: 3, + Parent: expectedResult, + } + + headAtHeight, err := head.HeadAtHeight(2) + require.NoError(t, err) + assert.Equal(t, expectedResult, headAtHeight) + _, err = head.HeadAtHeight(0) + assert.Error(t, err, "expected to get an error if head is not in the chain") +} + func TestHead_IsInChain(t *testing.T) { hash1 := utils.NewHash() hash2 := utils.NewHash() diff --git a/core/chains/legacyevm/chain.go b/core/chains/legacyevm/chain.go index 1c94e3d7df..b38cd2c450 100644 --- a/core/chains/legacyevm/chain.go +++ b/core/chains/legacyevm/chain.go @@ -245,7 +245,7 @@ func newChain(ctx context.Context, cfg *evmconfig.ChainScoped, nodes []*toml.Nod LogPrunePageSize: int64(cfg.EVM().LogPrunePageSize()), BackupPollerBlockDelay: int64(cfg.EVM().BackupLogPollerBlockDelay()), } - logPoller = logpoller.NewLogPoller(logpoller.NewObservedORM(chainID, opts.DS, l), client, l, lpOpts) + logPoller = logpoller.NewLogPoller(logpoller.NewObservedORM(chainID, opts.DS, l), client, l, headTracker, lpOpts) } } diff --git a/core/chains/legacyevm/evm_txm.go b/core/chains/legacyevm/evm_txm.go index df1f4248ce..cecfd4ffaf 100644 --- a/core/chains/legacyevm/evm_txm.go +++ b/core/chains/legacyevm/evm_txm.go @@ -43,7 +43,9 @@ func newEvmTxm( // build estimator from factory if opts.GenGasEstimator == nil { - estimator = gas.NewEstimator(lggr, client, cfg, cfg.GasEstimator()) + if estimator, err = gas.NewEstimator(lggr, client, cfg, cfg.GasEstimator()); err != nil { + return nil, nil, fmt.Errorf("failed to initialize estimator: %w", err) + } } else { estimator = opts.GenGasEstimator(chainID) } diff --git a/core/chains/legacyevm/mocks/chain.go b/core/chains/legacyevm/mocks/chain.go index 8f313ee45e..93f7c40fe0 100644 --- a/core/chains/legacyevm/mocks/chain.go +++ b/core/chains/legacyevm/mocks/chain.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/chains/legacyevm/mocks/legacy_chain_container.go b/core/chains/legacyevm/mocks/legacy_chain_container.go index 527544de60..ff73a4e139 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/cmd/app.go b/core/cmd/app.go index 378f7e8bd6..94acfc4d74 100644 --- a/core/cmd/app.go +++ b/core/cmd/app.go @@ -194,6 +194,7 @@ func NewApp(s *Shell) *cli.App { keysCommand("Cosmos", NewCosmosKeysClient(s)), keysCommand("Solana", NewSolanaKeysClient(s)), keysCommand("StarkNet", NewStarkNetKeysClient(s)), + keysCommand("Aptos", NewAptosKeysClient(s)), keysCommand("DKGSign", NewDKGSignKeysClient(s)), keysCommand("DKGEncrypt", NewDKGEncryptKeysClient(s)), diff --git a/core/cmd/aptos_keys_commands.go b/core/cmd/aptos_keys_commands.go new file mode 100644 index 0000000000..cabad34751 --- /dev/null +++ b/core/cmd/aptos_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/aptoskey" + "github.com/smartcontractkit/chainlink/v2/core/web/presenters" +) + +type AptosKeyPresenter struct { + JAID + presenters.AptosKeyResource +} + +// RenderTable implements TableRenderer +func (p AptosKeyPresenter) RenderTable(rt RendererTable) error { + headers := []string{"ID", "Aptos Public Key"} + rows := [][]string{p.ToRow()} + + if _, err := rt.Write([]byte("🔑 Aptos Keys\n")); err != nil { + return err + } + renderList(headers, rows, rt.Writer) + + return utils.JustError(rt.Write([]byte("\n"))) +} + +func (p *AptosKeyPresenter) ToRow() []string { + row := []string{ + p.ID, + p.PubKey, + } + + return row +} + +type AptosKeyPresenters []AptosKeyPresenter + +// RenderTable implements TableRenderer +func (ps AptosKeyPresenters) RenderTable(rt RendererTable) error { + headers := []string{"ID", "Aptos Public Key"} + rows := [][]string{} + + for _, p := range ps { + rows = append(rows, p.ToRow()) + } + + if _, err := rt.Write([]byte("🔑 Aptos Keys\n")); err != nil { + return err + } + renderList(headers, rows, rt.Writer) + + return utils.JustError(rt.Write([]byte("\n"))) +} + +func NewAptosKeysClient(s *Shell) KeysClient { + return newKeysClient[aptoskey.Key, AptosKeyPresenter, AptosKeyPresenters]("Aptos", s) +} diff --git a/core/cmd/aptos_keys_commands_test.go b/core/cmd/aptos_keys_commands_test.go new file mode 100644 index 0000000000..c88a8e539a --- /dev/null +++ b/core/cmd/aptos_keys_commands_test.go @@ -0,0 +1,175 @@ +package cmd_test + +import ( + "bytes" + "context" + "flag" + "fmt" + "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/aptoskey" + "github.com/smartcontractkit/chainlink/v2/core/web/presenters" +) + +func TestAptosKeyPresenter_RenderTable(t *testing.T) { + t.Parallel() + + var ( + id = "1" + pubKey = "somepubkey" + buffer = bytes.NewBufferString("") + r = cmd.RendererTable{Writer: buffer} + ) + + p := cmd.AptosKeyPresenter{ + JAID: cmd.JAID{ID: id}, + AptosKeyResource: presenters.AptosKeyResource{ + 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.AptosKeyPresenters{p} + require.NoError(t, ps.RenderTable(r)) + + output = buffer.String() + assert.Contains(t, output, id) + assert.Contains(t, output, pubKey) +} + +func TestShell_AptosKeys(t *testing.T) { + app := startNewApplicationV2(t, nil) + ks := app.GetKeyStore().Aptos() + 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()))) + } + requireAptosKeyCount(t, app, 0) + } + + t.Run("ListAptosKeys", func(tt *testing.T) { + defer cleanup() + ctx := testutils.Context(t) + client, r := app.NewShellAndRenderer() + key, err := app.GetKeyStore().Aptos().Create(ctx) + require.NoError(t, err) + requireAptosKeyCount(t, app, 1) + assert.Nil(t, cmd.NewAptosKeysClient(client).ListKeys(cltest.EmptyCLIContext())) + require.Equal(t, 1, len(r.Renders)) + keys := *r.Renders[0].(*cmd.AptosKeyPresenters) + assert.True(t, key.PublicKeyStr() == keys[0].PubKey) + }) + + t.Run("CreateAptosKey", func(tt *testing.T) { + defer cleanup() + client, _ := app.NewShellAndRenderer() + require.NoError(t, cmd.NewAptosKeysClient(client).CreateKey(nilContext)) + keys, err := app.GetKeyStore().Aptos().GetAll() + require.NoError(t, err) + require.Len(t, keys, 1) + }) + + t.Run("DeleteAptosKey", func(tt *testing.T) { + defer cleanup() + ctx := testutils.Context(t) + client, _ := app.NewShellAndRenderer() + key, err := app.GetKeyStore().Aptos().Create(ctx) + require.NoError(t, err) + requireAptosKeyCount(t, app, 1) + set := flag.NewFlagSet("test", 0) + flagSetApplyFromAction(cmd.NewAptosKeysClient(client).DeleteKey, set, "aptos") + + 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.NewAptosKeysClient(client).DeleteKey(c) + require.NoError(t, err) + requireAptosKeyCount(t, app, 0) + }) + + t.Run("ImportExportAptosKey", func(tt *testing.T) { + defer cleanup() + defer deleteKeyExportFile(t) + ctx := testutils.Context(t) + client, _ := app.NewShellAndRenderer() + + _, err := app.GetKeyStore().Aptos().Create(ctx) + require.NoError(t, err) + + keys := requireAptosKeyCount(t, app, 1) + key := keys[0] + keyName := keyNameForTest(t) + + // Export test invalid id + set := flag.NewFlagSet("test Aptos export", 0) + flagSetApplyFromAction(cmd.NewAptosKeysClient(client).ExportKey, set, "aptos") + + 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.NewAptosKeysClient(client).ExportKey(c) + require.Error(t, err, "Error exporting") + require.Error(t, utils.JustError(os.Stat(keyName))) + + // Export test + set = flag.NewFlagSet("test Aptos export", 0) + flagSetApplyFromAction(cmd.NewAptosKeysClient(client).ExportKey, set, "aptos") + + require.NoError(tt, set.Parse([]string{fmt.Sprint(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.NewAptosKeysClient(client).ExportKey(c)) + require.NoError(t, utils.JustError(os.Stat(keyName))) + + require.NoError(t, utils.JustError(app.GetKeyStore().Aptos().Delete(ctx, key.ID()))) + requireAptosKeyCount(t, app, 0) + + set = flag.NewFlagSet("test Aptos import", 0) + flagSetApplyFromAction(cmd.NewAptosKeysClient(client).ImportKey, set, "aptos") + + 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.NewAptosKeysClient(client).ImportKey(c)) + + requireAptosKeyCount(t, app, 1) + }) +} + +func requireAptosKeyCount(t *testing.T, app chainlink.Application, length int) []aptoskey.Key { + t.Helper() + keys, err := app.GetKeyStore().Aptos().GetAll() + require.NoError(t, err) + require.Len(t, keys, length) + return keys +} diff --git a/core/cmd/mocks/prompter.go b/core/cmd/mocks/prompter.go index 38c1d327fb..c2dff9fa61 100644 --- a/core/cmd/mocks/prompter.go +++ b/core/cmd/mocks/prompter.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/cmd/shell.go b/core/cmd/shell.go index e12b069277..c79fb70310 100644 --- a/core/cmd/shell.go +++ b/core/cmd/shell.go @@ -184,7 +184,7 @@ func (n ChainlinkAppFactory) NewApplication(ctx context.Context, cfg chainlink.G } // evm always enabled for backward compatibility // TODO BCF-2510 this needs to change in order to clear the path for EVM extraction - initOps := []chainlink.CoreRelayerChainInitFunc{chainlink.InitEVM(ctx, relayerFactory, evmFactoryCfg)} + initOps := []chainlink.CoreRelayerChainInitFunc{chainlink.InitDummy(ctx, relayerFactory), chainlink.InitEVM(ctx, relayerFactory, evmFactoryCfg)} if cfg.CosmosEnabled() { cosmosCfg := chainlink.CosmosFactoryConfig{ diff --git a/core/config/app_config.go b/core/config/app_config.go index 869477218d..112e242636 100644 --- a/core/config/app_config.go +++ b/core/config/app_config.go @@ -25,6 +25,7 @@ type AppConfig interface { CosmosEnabled() bool SolanaEnabled() bool StarkNetEnabled() bool + AptosEnabled() bool Validate() error ValidateDB() error diff --git a/core/config/docs/chains-evm.toml b/core/config/docs/chains-evm.toml index 30b89c596e..acd1cf3d31 100644 --- a/core/config/docs/chains-evm.toml +++ b/core/config/docs/chains-evm.toml @@ -86,6 +86,18 @@ RPCDefaultBatchSize = 250 # Default # available from the connected node via RPC, due to race conditions in the code of the remote ETH node. In this case you will get false # "zero" blocks that are missing transactions. RPCBlockQueryDelay = 1 # Default +# FinalizedBlockOffset defines the number of blocks by which the latest finalized block will be shifted/delayed. +# For example, suppose RPC returns block 100 as the latest finalized. In that case, the CL Node will treat block `100 - FinalizedBlockOffset` as the latest finalized block and `latest - FinalityDepth - FinalizedBlockOffset` in case of `FinalityTagEnabled = false.` +# With `EnforceRepeatableRead = true,` RPC is considered healthy only if its most recent finalized block is larger or equal to the highest finalized block observed by the CL Node minus `FinalizedBlockOffset.` +# Higher values of `FinalizedBlockOffset` with `EnforceRepeatableRead = true` reduce the number of false `FinalizedBlockOutOfSync` declarations on healthy RPCs that are slightly lagging behind due to network delays. +# This may increase the number of healthy RPCs and reduce the probability that the CL Node will not have any healthy alternatives to the active RPC. +# CAUTION: Setting this to values higher than 0 may delay transaction creation in products (e.g., CCIP, Automation) that base their decision on finalized on-chain events. +# PoS chains with `FinalityTagEnabled=true` and batched (epochs) blocks finalization (e.g., Ethereum Mainnet) must be treated with special care as a minor increase in the `FinalizedBlockOffset` may lead to significant delays. +# For example, let's say that `FinalizedBlockOffset = 1` and blocks are finalized in batches of 32. +# The latest finalized block on chain is 64, so block 63 is the latest finalized for CL Node. +# Block 64 will be treated as finalized by CL Node only when chain's latest finalized block is 65. As chain finalizes blocks in batches of 32, +# CL Node has to wait for a whole new batch to be finalized to treat block 64 as finalized. +FinalizedBlockOffset = 0 # Default [EVM.Transactions] # ForwardersEnabled enables or disables sending transactions through forwarder contracts. @@ -369,7 +381,17 @@ NodeIsSyncingEnabled = false # Default # # Set to 0 to disable. FinalizedBlockPollInterval = '5s' # Default - +# EnforceRepeatableRead defines if Core should only use RPCs whose most recently finalized block is greater or equal to +# `highest finalized block - FinalizedBlockOffset`. In other words, exclude RPCs lagging on latest finalized +# block. +# +# Set false to disable +EnforceRepeatableRead = false # Default +# DeathDeclarationDelay defines the minimum duration an RPC must be in unhealthy state before producing an error log message. +# Larger values might be helpful to reduce the noisiness of health checks like `EnforceRepeatableRead = true', which might be falsely +# trigger declaration of `FinalizedBlockOutOfSync` due to insignificant network delays in broadcasting of the finalized state among RPCs. +# RPC will not be picked to handle a request even if this option is set to a nonzero value. +DeathDeclarationDelay = '10s' # Default # **ADVANCED** # Errors enable the node to provide custom regex patterns to match against error messages from RPCs. [EVM.NodePool.Errors] diff --git a/core/config/mocks/telemetry_ingress.go b/core/config/mocks/telemetry_ingress.go index fb01cc29cd..43889566c7 100644 --- a/core/config/mocks/telemetry_ingress.go +++ b/core/config/mocks/telemetry_ingress.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/config/mocks/telemetry_ingress_endpoint.go b/core/config/mocks/telemetry_ingress_endpoint.go index e5f8c3106e..11eebd373e 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/gethwrappers/ccip/generated/evm_2_evm_multi_offramp/evm_2_evm_multi_offramp.go b/core/gethwrappers/ccip/generated/evm_2_evm_multi_offramp/evm_2_evm_multi_offramp.go index b2fcdc4670..edd06bf867 100644 --- a/core/gethwrappers/ccip/generated/evm_2_evm_multi_offramp/evm_2_evm_multi_offramp.go +++ b/core/gethwrappers/ccip/generated/evm_2_evm_multi_offramp/evm_2_evm_multi_offramp.go @@ -93,13 +93,12 @@ type EVM2EVMMultiOffRampUnblessedRoot struct { } type InternalAny2EVMRampMessage struct { - Header InternalRampMessageHeader - Sender []byte - Data []byte - Receiver common.Address - GasLimit *big.Int - TokenAmounts []ClientEVMTokenAmount - SourceTokenData [][]byte + Header InternalRampMessageHeader + Sender []byte + Data []byte + Receiver common.Address + GasLimit *big.Int + TokenAmounts []InternalRampTokenAmount } type InternalExecutionReportSingleChain struct { @@ -128,6 +127,13 @@ type InternalRampMessageHeader struct { Nonce uint64 } +type InternalRampTokenAmount struct { + SourcePoolAddress []byte + DestTokenAddress []byte + ExtraData []byte + Amount *big.Int +} + type InternalTokenPriceUpdate struct { SourceToken common.Address UsdPerToken *big.Int @@ -156,8 +162,8 @@ type MultiOCR3BaseOCRConfigArgs struct { } var EVM2EVMMultiOffRampMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAdminRegistry\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"nonceManager\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.StaticConfig\",\"name\":\"staticConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"onRamp\",\"type\":\"bytes\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.SourceChainConfigArgs[]\",\"name\":\"sourceChainConfigs\",\"type\":\"tuple[]\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"}],\"name\":\"AlreadyAttempted\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"}],\"name\":\"AlreadyExecuted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CanOnlySelfCall\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"expected\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"actual\",\"type\":\"bytes32\"}],\"name\":\"ConfigDigestMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"CursedByRMN\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"EmptyReport\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"error\",\"type\":\"bytes\"}],\"name\":\"ExecutionError\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"expected\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"actual\",\"type\":\"uint256\"}],\"name\":\"ForkedChain\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"enumMultiOCR3Base.InvalidConfigErrorType\",\"name\":\"errorType\",\"type\":\"uint8\"}],\"name\":\"InvalidConfig\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"expected\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"got\",\"type\":\"uint256\"}],\"name\":\"InvalidDataLength\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"encodedAddress\",\"type\":\"bytes\"}],\"name\":\"InvalidEVMAddress\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"min\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"max\",\"type\":\"uint64\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.Interval\",\"name\":\"interval\",\"type\":\"tuple\"}],\"name\":\"InvalidInterval\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"newLimit\",\"type\":\"uint256\"}],\"name\":\"InvalidManualExecutionGasLimit\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"messageDestChainSelector\",\"type\":\"uint64\"}],\"name\":\"InvalidMessageDestChainSelector\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"internalType\":\"enumInternal.MessageExecutionState\",\"name\":\"newState\",\"type\":\"uint8\"}],\"name\":\"InvalidNewState\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidProof\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidRoot\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"InvalidStaticConfig\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"LeavesCannotBeEmpty\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ManualExecutionGasLimitMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"ManualExecutionNotYetEnabled\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"errorReason\",\"type\":\"bytes\"}],\"name\":\"MessageValidationError\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NonUniqueSignatures\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"notPool\",\"type\":\"address\"}],\"name\":\"NotACompatiblePool\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OracleCannotBeZeroAddress\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"error\",\"type\":\"bytes\"}],\"name\":\"ReceiverError\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"merkleRoot\",\"type\":\"bytes32\"}],\"name\":\"RootAlreadyCommitted\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"RootNotCommitted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"SignaturesOutOfRegistration\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"SourceChainNotEnabled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"StaleCommitReport\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"ocrPluginType\",\"type\":\"uint8\"}],\"name\":\"StaticConfigCannotBeChanged\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"}],\"name\":\"TokenDataMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"error\",\"type\":\"bytes\"}],\"name\":\"TokenHandlingError\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnauthorizedSigner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnauthorizedTransmitter\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnexpectedTokenData\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"expected\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"actual\",\"type\":\"uint256\"}],\"name\":\"WrongMessageLength\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"WrongNumberOfSignatures\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroAddressNotAllowed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroChainSelectorNotAllowed\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"components\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"sourceToken\",\"type\":\"address\"},{\"internalType\":\"uint224\",\"name\":\"usdPerToken\",\"type\":\"uint224\"}],\"internalType\":\"structInternal.TokenPriceUpdate[]\",\"name\":\"tokenPriceUpdates\",\"type\":\"tuple[]\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint224\",\"name\":\"usdPerUnitGas\",\"type\":\"uint224\"}],\"internalType\":\"structInternal.GasPriceUpdate[]\",\"name\":\"gasPriceUpdates\",\"type\":\"tuple[]\"}],\"internalType\":\"structInternal.PriceUpdates\",\"name\":\"priceUpdates\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"min\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"max\",\"type\":\"uint64\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.Interval\",\"name\":\"interval\",\"type\":\"tuple\"},{\"internalType\":\"bytes32\",\"name\":\"merkleRoot\",\"type\":\"bytes32\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.MerkleRoot[]\",\"name\":\"merkleRoots\",\"type\":\"tuple[]\"}],\"indexed\":false,\"internalType\":\"structEVM2EVMMultiOffRamp.CommitReport\",\"name\":\"report\",\"type\":\"tuple\"}],\"name\":\"CommitReportAccepted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"ocrPluginType\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"F\",\"type\":\"uint8\"}],\"name\":\"ConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"permissionLessExecutionThresholdSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxTokenTransferGas\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPoolReleaseOrMintGas\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"messageValidator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"priceRegistry\",\"type\":\"address\"}],\"indexed\":false,\"internalType\":\"structEVM2EVMMultiOffRamp.DynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"}],\"name\":\"DynamicConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"enumInternal.MessageExecutionState\",\"name\":\"state\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"returnData\",\"type\":\"bytes\"}],\"name\":\"ExecutionStateChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"}],\"name\":\"RootRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"}],\"name\":\"SkippedAlreadyExecutedMessage\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint64\",\"name\":\"minSeqNr\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"onRamp\",\"type\":\"bytes\"}],\"indexed\":false,\"internalType\":\"structEVM2EVMMultiOffRamp.SourceChainConfig\",\"name\":\"sourceConfig\",\"type\":\"tuple\"}],\"name\":\"SourceChainConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"SourceChainSelectorAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAdminRegistry\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"nonceManager\",\"type\":\"address\"}],\"indexed\":false,\"internalType\":\"structEVM2EVMMultiOffRamp.StaticConfig\",\"name\":\"staticConfig\",\"type\":\"tuple\"}],\"name\":\"StaticConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint8\",\"name\":\"ocrPluginType\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"}],\"name\":\"Transmitted\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"onRamp\",\"type\":\"bytes\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.SourceChainConfigArgs[]\",\"name\":\"sourceChainConfigUpdates\",\"type\":\"tuple[]\"}],\"name\":\"applySourceChainConfigUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"sender\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structClient.EVMTokenAmount[]\",\"name\":\"destTokenAmounts\",\"type\":\"tuple[]\"}],\"internalType\":\"structClient.Any2EVMMessage\",\"name\":\"\",\"type\":\"tuple\"}],\"name\":\"ccipReceive\",\"outputs\":[],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[3]\",\"name\":\"reportContext\",\"type\":\"bytes32[3]\"},{\"internalType\":\"bytes\",\"name\":\"report\",\"type\":\"bytes\"},{\"internalType\":\"bytes32[]\",\"name\":\"rs\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32[]\",\"name\":\"ss\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32\",\"name\":\"rawVs\",\"type\":\"bytes32\"}],\"name\":\"commit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[3]\",\"name\":\"reportContext\",\"type\":\"bytes32[3]\"},{\"internalType\":\"bytes\",\"name\":\"report\",\"type\":\"bytes\"}],\"name\":\"execute\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"}],\"internalType\":\"structInternal.RampMessageHeader\",\"name\":\"header\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"sender\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structClient.EVMTokenAmount[]\",\"name\":\"tokenAmounts\",\"type\":\"tuple[]\"},{\"internalType\":\"bytes[]\",\"name\":\"sourceTokenData\",\"type\":\"bytes[]\"}],\"internalType\":\"structInternal.Any2EVMRampMessage\",\"name\":\"message\",\"type\":\"tuple\"},{\"internalType\":\"bytes[]\",\"name\":\"offchainTokenData\",\"type\":\"bytes[]\"}],\"name\":\"executeSingleMessage\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getDynamicConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"permissionLessExecutionThresholdSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxTokenTransferGas\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPoolReleaseOrMintGas\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"messageValidator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"priceRegistry\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.DynamicConfig\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"}],\"name\":\"getExecutionState\",\"outputs\":[{\"internalType\":\"enumInternal.MessageExecutionState\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getLatestPriceSequenceNumber\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"}],\"name\":\"getMerkleRoot\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"getSourceChainConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint64\",\"name\":\"minSeqNr\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"onRamp\",\"type\":\"bytes\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.SourceChainConfig\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getStaticConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAdminRegistry\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"nonceManager\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.StaticConfig\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"}],\"name\":\"isBlessed\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"ocrPluginType\",\"type\":\"uint8\"}],\"name\":\"latestConfigDetails\",\"outputs\":[{\"components\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"internalType\":\"uint8\",\"name\":\"F\",\"type\":\"uint8\"},{\"internalType\":\"uint8\",\"name\":\"n\",\"type\":\"uint8\"},{\"internalType\":\"bool\",\"name\":\"isSignatureVerificationEnabled\",\"type\":\"bool\"}],\"internalType\":\"structMultiOCR3Base.ConfigInfo\",\"name\":\"configInfo\",\"type\":\"tuple\"},{\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"}],\"internalType\":\"structMultiOCR3Base.OCRConfig\",\"name\":\"ocrConfig\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"}],\"internalType\":\"structInternal.RampMessageHeader\",\"name\":\"header\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"sender\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structClient.EVMTokenAmount[]\",\"name\":\"tokenAmounts\",\"type\":\"tuple[]\"},{\"internalType\":\"bytes[]\",\"name\":\"sourceTokenData\",\"type\":\"bytes[]\"}],\"internalType\":\"structInternal.Any2EVMRampMessage[]\",\"name\":\"messages\",\"type\":\"tuple[]\"},{\"internalType\":\"bytes[][]\",\"name\":\"offchainTokenData\",\"type\":\"bytes[][]\"},{\"internalType\":\"bytes32[]\",\"name\":\"proofs\",\"type\":\"bytes32[]\"},{\"internalType\":\"uint256\",\"name\":\"proofFlagBits\",\"type\":\"uint256\"}],\"internalType\":\"structInternal.ExecutionReportSingleChain[]\",\"name\":\"reports\",\"type\":\"tuple[]\"},{\"internalType\":\"uint256[][]\",\"name\":\"gasLimitOverrides\",\"type\":\"uint256[][]\"}],\"name\":\"manuallyExecute\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"merkleRoot\",\"type\":\"bytes32\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.UnblessedRoot[]\",\"name\":\"rootToReset\",\"type\":\"tuple[]\"}],\"name\":\"resetUnblessedRoots\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"permissionLessExecutionThresholdSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxTokenTransferGas\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPoolReleaseOrMintGas\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"messageValidator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"priceRegistry\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.DynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"}],\"name\":\"setDynamicConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"internalType\":\"uint8\",\"name\":\"ocrPluginType\",\"type\":\"uint8\"},{\"internalType\":\"uint8\",\"name\":\"F\",\"type\":\"uint8\"},{\"internalType\":\"bool\",\"name\":\"isSignatureVerificationEnabled\",\"type\":\"bool\"},{\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"}],\"internalType\":\"structMultiOCR3Base.OCRConfigArgs[]\",\"name\":\"ocrConfigArgs\",\"type\":\"tuple[]\"}],\"name\":\"setOCR3Configs\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", - Bin: "0x6101206040523480156200001257600080fd5b506040516200694f3803806200694f8339810160408190526200003591620005bd565b33806000816200008c5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000bf57620000bf81620001ee565b5050466080525060208201516001600160a01b03161580620000ec575060408201516001600160a01b0316155b8062000103575060608201516001600160a01b0316155b1562000122576040516342bcdf7f60e11b815260040160405180910390fd5b81516001600160401b03166000036200014e5760405163c656089560e01b815260040160405180910390fd5b81516001600160401b0390811660a052602080840180516001600160a01b0390811660c05260408087018051831660e0526060808901805185166101005283518a519098168852945184169587019590955251821690850152905116908201527f683eb52ee924eb817377cfa8f41f238f4bb7a877da5267869dfffbad85f564d89060800160405180910390a1620001e68162000299565b505062000a03565b336001600160a01b03821603620002485760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000083565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60005b8151811015620004eb576000828281518110620002bd57620002bd620007cc565b60200260200101519050600081600001519050806001600160401b0316600003620002fb5760405163c656089560e01b815260040160405180910390fd5b6001600160401b03811660009081526007602052604081206001810180549192916200032790620007e2565b80601f01602080910402602001604051908101604052809291908181526020018280546200035590620007e2565b8015620003a65780601f106200037a57610100808354040283529160200191620003a6565b820191906000526020600020905b8154815290600101906020018083116200038857829003601f168201915b50505050509050600084604001519050815160000362000449578051600003620003e3576040516342bcdf7f60e11b815260040160405180910390fd5b60018301620003f3828262000873565b508254610100600160481b0319166101001783556040516001600160401b03851681527ff4c1390c70e5c0f491ae1ccbc06f9117cbbadf2767b247b3bc203280f24c0fb99060200160405180910390a162000484565b8080519060200120828051906020012014620004845760405163c39a620560e01b81526001600160401b038516600482015260240162000083565b6020850151835460ff19169015151783556040516001600160401b038516907f4f49973170c548fddd4a48341b75e131818913f38f44d47af57e8735eee588ba90620004d29086906200093f565b60405180910390a250505050508060010190506200029c565b5050565b634e487b7160e01b600052604160045260246000fd5b604051608081016001600160401b03811182821017156200052a576200052a620004ef565b60405290565b604051606081016001600160401b03811182821017156200052a576200052a620004ef565b604051601f8201601f191681016001600160401b0381118282101715620005805762000580620004ef565b604052919050565b80516001600160401b0381168114620005a057600080fd5b919050565b80516001600160a01b0381168114620005a057600080fd5b60008082840360a0811215620005d257600080fd5b6080811215620005e157600080fd5b50620005ec62000505565b620005f78462000588565b8152602062000608818601620005a5565b818301526200061a60408601620005a5565b60408301526200062d60608601620005a5565b606083015260808501519193506001600160401b03808311156200065057600080fd5b828601925086601f8401126200066557600080fd5b8251818111156200067a576200067a620004ef565b8060051b6200068b84820162000555565b918252848101840191848101908a841115620006a657600080fd5b85870192505b83831015620007bb57825185811115620006c557600080fd5b8701601f196060828e0382011215620006dd57600080fd5b620006e762000530565b620006f489840162000588565b8152604083015180151581146200070a57600080fd5b818a01526060830151888111156200072157600080fd5b8084019350508d603f8401126200073757600080fd5b88830151888111156200074e576200074e620004ef565b620007608a84601f8401160162000555565b92508083528e60408286010111156200077857600080fd5b60005b818110156200079957848101604001518482018c01528a016200077b565b5060009083018a015260408101919091528352509185019190850190620006ac565b809750505050505050509250929050565b634e487b7160e01b600052603260045260246000fd5b600181811c90821680620007f757607f821691505b6020821081036200081857634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200086e576000816000526020600020601f850160051c81016020861015620008495750805b601f850160051c820191505b818110156200086a5782815560010162000855565b5050505b505050565b81516001600160401b038111156200088f576200088f620004ef565b620008a781620008a08454620007e2565b846200081e565b602080601f831160018114620008df5760008415620008c65750858301515b600019600386901b1c1916600185901b1785556200086a565b600085815260208120601f198616915b828110156200091057888601518255948401946001909101908401620008ef565b50858210156200092f5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b6020808252825460ff811615158383015260081c6001600160401b031660408301526060808301526001808401805460009392919084906200098181620007e2565b80608089015260a06001831660008114620009a55760018114620009c257620009f4565b60ff19841660a08b015260a083151560051b8b01019450620009f4565b85600052602060002060005b84811015620009eb5781548c8201850152908801908901620009ce565b8b0160a0019550505b50929998505050505050505050565b60805160a05160c05160e05161010051615edd62000a726000396000818161023e0152612be101526000818161020f0152612ecb0152600081816101e0015281816115ca01526116810152600081816101b00152612755015260008181611fb901526120050152615edd6000f3fe608060405234801561001057600080fd5b50600436106101775760003560e01c806385572ffb116100d8578063ccd37ba31161008c578063f2fde38b11610066578063f2fde38b146105b1578063f716f99f146105c4578063ff888fb1146105d757600080fd5b8063ccd37ba314610539578063d2a15d351461057e578063e9d68a8e1461059157600080fd5b8063a12a9870116100bd578063a12a9870146104f3578063bec0b14314610506578063c673e5841461051957600080fd5b806385572ffb146104ca5780638da5cb5b146104d857600080fd5b8063403b2d631161012f5780635ffb5ced116101145780635ffb5ced146103855780637437ff9f1461039857806379ba5097146104c257600080fd5b8063403b2d63146103525780635e36480c1461036557600080fd5b80632d04ab76116101605780632d04ab761461030e578063311cd513146103235780633f4b04aa1461033657600080fd5b806306285c691461017c578063181f5a77146102c5575b600080fd5b61026e60408051608081018252600080825260208201819052918101829052606081019190915260405180608001604052807f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff1681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316815250905090565b6040516102bc9190815167ffffffffffffffff1681526020808301516001600160a01b0390811691830191909152604080840151821690830152606092830151169181019190915260800190565b60405180910390f35b6103016040518060400160405280601d81526020017f45564d3245564d4d756c74694f666652616d7020312e362e302d64657600000081525081565b6040516102bc9190613f11565b61032161031c366004613fbc565b6105fa565b005b61032161033136600461406f565b6109c0565b600a5460405167ffffffffffffffff90911681526020016102bc565b6103216103603660046141f8565b610a29565b610378610373366004614297565b610bfb565b6040516102bc91906142f4565b610321610393366004614600565b610c51565b6104596040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a0810191909152506040805160c0810182526004546001600160a01b03808216835263ffffffff7401000000000000000000000000000000000000000083048116602085015278010000000000000000000000000000000000000000000000008304811694840194909452600160e01b90910490921660608201526005548216608082015260065490911660a082015290565b6040516102bc9190600060c0820190506001600160a01b03808451168352602084015163ffffffff808216602086015280604087015116604086015280606087015116606086015250508060808501511660808401528060a08501511660a08401525092915050565b610321610fa9565b610321610177366004614664565b6000546040516001600160a01b0390911681526020016102bc565b6103216105013660046146b8565b611067565b610321610514366004614a46565b61107b565b61052c610527366004614b82565b611220565b6040516102bc9190614be2565b610570610547366004614c57565b67ffffffffffffffff919091166000908152600960209081526040808320938352929052205490565b6040519081526020016102bc565b61032161058c366004614c81565b61137e565b6105a461059f366004614cf6565b611438565b6040516102bc9190614d11565b6103216105bf366004614d4c565b611521565b6103216105d2366004614dd1565b611532565b6105ea6105e5366004614f0f565b611574565b60405190151581526020016102bc565b600061060887890189615098565b8051515190915015158061062157508051602001515115155b1561072157600a5460208a01359067ffffffffffffffff808316911610156106e057600a805467ffffffffffffffff191667ffffffffffffffff831617905560065482516040517f3937306f0000000000000000000000000000000000000000000000000000000081526001600160a01b0390921691633937306f916106a9916004016152d6565b600060405180830381600087803b1580156106c357600080fd5b505af11580156106d7573d6000803e3d6000fd5b5050505061071f565b81602001515160000361071f576040517f2261116700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505b60005b8160200151518110156109095760008260200151828151811061074957610749615203565b6020026020010151905060008160000151905061076581611635565b600061077082611737565b602084015151815491925067ffffffffffffffff908116610100909204161415806107b2575060208084015190810151905167ffffffffffffffff9182169116115b156107fb57825160208401516040517feefb0cac0000000000000000000000000000000000000000000000000000000081526107f29291906004016152e9565b60405180910390fd5b604083015180610837576040517f504570e300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b835167ffffffffffffffff166000908152600960209081526040808320848452909152902054156108aa5783516040517f32cf0cbf00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff9091166004820152602481018290526044016107f2565b60208085015101516108bd906001615334565b825468ffffffffffffffff00191661010067ffffffffffffffff928316021790925592511660009081526009602090815260408083209483529390529190912042905550600101610724565b507f3a3950e13dd607cc37980db0ef14266c40d2bba9c01b2e44bfe549808883095d81604051610939919061535c565b60405180910390a16109b560008a8a8a8a8a8080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808e0282810182019093528d82529093508d92508c9182918501908490808284376000920191909152508b9250611797915050565b505050505050505050565b610a006109cf828401846153f9565b60408051600080825260208201909252906109fa565b60608152602001906001900390816109e55790505b50611b0e565b604080516000808252602082019092529050610a23600185858585866000611797565b50505050565b610a31611bbe565b60a08101516001600160a01b03161580610a53575080516001600160a01b0316155b15610a8a576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80516004805460208085018051604080880180516060808b0180516001600160a01b039b8c167fffffffffffffffff000000000000000000000000000000000000000000000000909a168a177401000000000000000000000000000000000000000063ffffffff988916021777ffffffffffffffffffffffffffffffffffffffffffffffff167801000000000000000000000000000000000000000000000000948816949094026001600160e01b031693909317600160e01b93871693909302929092179098556080808b0180516005805473ffffffffffffffffffffffffffffffffffffffff19908116928e1692909217905560a0808e01805160068054909416908f161790925586519a8b5297518716988a0198909852925185169388019390935251909216958501959095525185169383019390935251909216908201527f0da37fd00459f4f5f0b8210d31525e4910ae674b8bab34b561d146bb45773a4c9060c00160405180910390a150565b6000610c096001600461542e565b6002610c16608085615457565b67ffffffffffffffff16610c2a919061547e565b610c348585611c1a565b901c166003811115610c4857610c486142ca565b90505b92915050565b333014610c8a576040517f371a732800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805160008082526020820190925281610cc7565b6040805180820190915260008082526020820152815260200190600190039081610ca05790505b5060a08401515190915015610cff57610cfc8360a00151846020015185606001518660000151602001518760c0015187611c61565b90505b6040805160a0810182528451518152845160209081015167ffffffffffffffff1681830152808601518351600094840192610d3b929101613f11565b60408051601f19818403018152918152908252868101516020830152018390526005549091506001600160a01b03168015610e48576040517f08d450a10000000000000000000000000000000000000000000000000000000081526001600160a01b038216906308d450a190610db5908590600401615543565b600060405180830381600087803b158015610dcf57600080fd5b505af1925050508015610de0575060015b610e48573d808015610e0e576040519150601f19603f3d011682016040523d82523d6000602084013e610e13565b606091505b50806040517f09c253250000000000000000000000000000000000000000000000000000000081526004016107f29190613f11565b604085015151158015610e5d57506080850151155b80610e74575060608501516001600160a01b03163b155b80610eb457506060850151610eb2906001600160a01b03167f85572ffb00000000000000000000000000000000000000000000000000000000611d0e565b155b15610ec0575050505050565b60048054608087015160608801516040517f3cf9798300000000000000000000000000000000000000000000000000000000815260009485946001600160a01b031693633cf9798393610f1b938a9361138893929101615556565b6000604051808303816000875af1158015610f3a573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610f6291908101906155d7565b509150915081610fa057806040517f0a8d6e8c0000000000000000000000000000000000000000000000000000000081526004016107f29190613f11565b50505050505050565b6001546001600160a01b031633146110035760405162461bcd60e51b815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064016107f2565b600080543373ffffffffffffffffffffffffffffffffffffffff19808316821784556001805490911690556040516001600160a01b0390921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b61106f611bbe565b61107881611d2a565b50565b611083611fb6565b8151815181146110bf576040517f83e3f56400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b818110156112105760008482815181106110de576110de615203565b6020026020010151905060008160200151519050600085848151811061110657611106615203565b602002602001015190508051821461114a576040517f83e3f56400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b8281101561120157600082828151811061116957611169615203565b60200260200101519050806000141580156111a457508460200151828151811061119557611195615203565b60200260200101516080015181105b156111f85784516040517fc8e9605100000000000000000000000000000000000000000000000000000000815267ffffffffffffffff909116600482015260248101839052604481018290526064016107f2565b5060010161114d565b505050508060010190506110c2565b5061121b8383611b0e565b505050565b6112636040805160e081019091526000606082018181526080830182905260a0830182905260c08301919091528190815260200160608152602001606081525090565b60ff808316600090815260026020818152604092839020835160e081018552815460608201908152600183015480881660808401526101008104881660a0840152620100009004909616151560c08201529485529182018054845181840281018401909552808552929385830193909283018282801561130c57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116112ee575b505050505081526020016003820180548060200260200160405190810160405280929190818152602001828054801561136e57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611350575b5050505050815250509050919050565b611386611bbe565b60005b8181101561121b5760008383838181106113a5576113a5615203565b9050604002018036038101906113bb9190615631565b90506113ca8160200151611574565b61142f57805167ffffffffffffffff1660009081526009602090815260408083208285018051855290835281842093909355915191519182527f202f1139a3e334b6056064c0e9b19fd07e44a88d8f6e5ded571b24cf8c371f12910160405180910390a15b50600101611389565b60408051606080820183526000808352602080840182905283850183905267ffffffffffffffff8681168352600782529185902085519384018652805460ff8116151585526101009004909216908301526001810180549394929391928401916114a19061566a565b80601f01602080910402602001604051908101604052809291908181526020018280546114cd9061566a565b801561136e5780601f106114ef5761010080835404028352916020019161136e565b820191906000526020600020905b8154815290600101906020018083116114fd57505050919092525091949350505050565b611529611bbe565b61107881612037565b61153a611bbe565b60005b81518110156115705761156882828151811061155b5761155b615203565b60200260200101516120ed565b60010161153d565b5050565b6040805180820182523081526020810183815291517f4d61677100000000000000000000000000000000000000000000000000000000815290516001600160a01b039081166004830152915160248201526000917f00000000000000000000000000000000000000000000000000000000000000001690634d61677190604401602060405180830381865afa158015611611573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c4b91906156a4565b6040517f2cbc26bb000000000000000000000000000000000000000000000000000000008152608082901b77ffffffffffffffff000000000000000000000000000000001660048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632cbc26bb90602401602060405180830381865afa1580156116d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116f491906156a4565b15611078576040517ffdbd6a7200000000000000000000000000000000000000000000000000000000815267ffffffffffffffff821660048201526024016107f2565b67ffffffffffffffff81166000908152600760205260408120805460ff16610c4b576040517fed053c5900000000000000000000000000000000000000000000000000000000815267ffffffffffffffff841660048201526024016107f2565b60ff878116600090815260026020908152604080832081516080810183528154815260019091015480861693820193909352610100830485169181019190915262010000909104909216151560608301528735906117f68760a46156c1565b905082606001511561183e57845161180f90602061547e565b865161181c90602061547e565b6118279060a06156c1565b61183191906156c1565b61183b90826156c1565b90505b368114611880576040517f8e1192e1000000000000000000000000000000000000000000000000000000008152600481018290523660248201526044016107f2565b50815181146118c85781516040517f93df584c0000000000000000000000000000000000000000000000000000000081526004810191909152602481018290526044016107f2565b6118d0611fb6565b60ff808a166000908152600360209081526040808320338452825280832081518083019092528054808616835293949193909284019161010090910416600281111561191e5761191e6142ca565b600281111561192f5761192f6142ca565b905250905060028160200151600281111561194c5761194c6142ca565b1480156119a05750600260008b60ff1660ff168152602001908152602001600020600301816000015160ff168154811061198857611988615203565b6000918252602090912001546001600160a01b031633145b6119d6576040517fda0f08e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50816060015115611ab85760208201516119f19060016156d4565b60ff16855114611a2d576040517f71253a2500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8351855114611a68576040517fa75d88af00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008787604051611a7a9291906156ed565b604051908190038120611a91918b906020016156fd565b604051602081830303815290604052805190602001209050611ab68a82888888612431565b505b6040805182815260208a81013567ffffffffffffffff169082015260ff8b16917f198d6990ef96613a9026203077e422916918b03ff47f0be6bee7b02d8e139ef0910160405180910390a2505050505050505050565b8151600003611b48576040517ebf199700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805160408051600080825260208201909252911591905b8451811015611bb757611baf858281518110611b7d57611b7d615203565b602002602001015184611ba957858381518110611b9c57611b9c615203565b6020026020010151612648565b83612648565b600101611b5f565b5050505050565b6000546001600160a01b03163314611c185760405162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016107f2565b565b67ffffffffffffffff8216600090815260086020526040812081611c3f608085615711565b67ffffffffffffffff1681526020810191909152604001600020549392505050565b8560005b8751811015611d0357611cde888281518110611c8357611c83615203565b602002602001015160200151888888888681518110611ca457611ca4615203565b6020026020010151806020019051810190611cbf9190615738565b888781518110611cd157611cd1615203565b6020026020010151612e6a565b828281518110611cf057611cf0615203565b6020908102919091010152600101611c65565b509695505050505050565b6000611d19836131f8565b8015610c485750610c488383613244565b60005b8151811015611570576000828281518110611d4a57611d4a615203565b602002602001015190506000816000015190508067ffffffffffffffff16600003611da1576040517fc656089500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff81166000908152600760205260408120600181018054919291611dcc9061566a565b80601f0160208091040260200160405190810160405280929190818152602001828054611df89061566a565b8015611e455780601f10611e1a57610100808354040283529160200191611e45565b820191906000526020600020905b815481529060010190602001808311611e2857829003601f168201915b505050505090506000846040015190508151600003611efe578051600003611e99576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60018301611ea78282615835565b50825468ffffffffffffffff00191661010017835560405167ffffffffffffffff851681527ff4c1390c70e5c0f491ae1ccbc06f9117cbbadf2767b247b3bc203280f24c0fb99060200160405180910390a1611f51565b8080519060200120828051906020012014611f51576040517fc39a620500000000000000000000000000000000000000000000000000000000815267ffffffffffffffff851660048201526024016107f2565b6020850151835460ff191690151517835560405167ffffffffffffffff8516907f4f49973170c548fddd4a48341b75e131818913f38f44d47af57e8735eee588ba90611f9e9086906158f5565b60405180910390a25050505050806001019050611d2d565b467f000000000000000000000000000000000000000000000000000000000000000014611c18576040517f0f01ce850000000000000000000000000000000000000000000000000000000081527f000000000000000000000000000000000000000000000000000000000000000060048201524660248201526044016107f2565b336001600160a01b0382160361208f5760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016107f2565b6001805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b806040015160ff16600003612118576000604051631b3fab5160e11b81526004016107f291906159b1565b60208082015160ff8082166000908152600290935260408320600181015492939092839216900361218557606084015160018201805491151562010000027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffff9092169190911790556121da565b6060840151600182015460ff62010000909104161515901515146121da576040517f87f6037c00000000000000000000000000000000000000000000000000000000815260ff841660048201526024016107f2565b60a08401518051601f60ff82161115612209576001604051631b3fab5160e11b81526004016107f291906159b1565b61226f858560030180548060200260200160405190810160405280929190818152602001828054801561226557602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311612247575b50505050506132e6565b85606001511561239e576122dd8585600201805480602002602001604051908101604052809291908181526020018280548015612265576020028201919060005260206000209081546001600160a01b031681526001909101906020018083116122475750505050506132e6565b608086015180516122f79060028701906020840190613e1f565b5080516001850180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff1661010060ff841690810291909117909155601f1015612357576002604051631b3fab5160e11b81526004016107f291906159b1565b60408801516123679060036159cb565b60ff168160ff161161238f576003604051631b3fab5160e11b81526004016107f291906159b1565b61239b8783600161334f565b50505b6123aa8583600261334f565b81516123bf9060038601906020850190613e1f565b5060408681015160018501805460ff191660ff8316179055875180865560a089015192517fab8b1b57514019638d7b5ce9c638fe71366fe8e2be1c40a7a80f1733d0e9f54793612418938a939260028b019291906159e7565b60405180910390a1612429856134cf565b505050505050565b612439613e8d565b835160005b8181101561263e57600060018886846020811061245d5761245d615203565b61246a91901a601b6156d4565b89858151811061247c5761247c615203565b602002602001015189868151811061249657612496615203565b6020026020010151604051600081526020016040526040516124d4949392919093845260ff9290921660208401526040830152606082015260800190565b6020604051602081039080840390855afa1580156124f6573d6000803e3d6000fd5b505060408051601f1981015160ff808e166000908152600360209081528582206001600160a01b03851683528152858220858701909652855480841686529397509095509293928401916101009004166002811115612557576125576142ca565b6002811115612568576125686142ca565b9052509050600181602001516002811115612585576125856142ca565b146125bc576040517fca31867a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8051859060ff16601f81106125d3576125d3615203565b60200201511561260f576040517ff67bc7c400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600185826000015160ff16601f811061262a5761262a615203565b91151560209092020152505060010161243e565b5050505050505050565b815161265381611635565b600061265e82611737565b60208501515190915060008190036126a1576040517ebf199700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84604001515181146126df576040517f57e0e08300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008167ffffffffffffffff8111156126fa576126fa6140c3565b604051908082528060200260200182016040528015612723578160200160208202803683370190505b50905060005b828110156128985760008760200151828151811061274957612749615203565b602002602001015190507f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff1681600001516040015167ffffffffffffffff16146127dc57805160409081015190517f38432a2200000000000000000000000000000000000000000000000000000000815267ffffffffffffffff90911660048201526024016107f2565b612872818660010180546127ef9061566a565b80601f016020809104026020016040519081016040528092919081815260200182805461281b9061566a565b80156128685780601f1061283d57610100808354040283529160200191612868565b820191906000526020600020905b81548152906001019060200180831161284b57829003601f168201915b50505050506134eb565b83838151811061288457612884615203565b602090810291909101015250600101612729565b5060006128af858389606001518a6080015161363d565b9050806000036128f7576040517f7dd17a7e00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff861660048201526024016107f2565b8551151560005b848110156109b55760008960200151828151811061291e5761291e615203565b60200260200101519050600061293c89836000015160600151610bfb565b90506002816003811115612952576129526142ca565b036129a9578151606001516040805167ffffffffffffffff808d16825290921660208301527f3b575419319662b2a6f5e2467d84521517a3382b908eb3d557bb3fdb0c50e23c910160405180910390a15050612e62565b60008160038111156129bd576129bd6142ca565b14806129da575060038160038111156129d8576129d86142ca565b145b612a2b578151606001516040517f25507e7f00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff808c16600483015290911660248201526044016107f2565b8315612b0c5760045460009074010000000000000000000000000000000000000000900463ffffffff16612a5f874261542e565b1190508080612a7f57506003826003811115612a7d57612a7d6142ca565b145b612ac1576040517fa9cfc86200000000000000000000000000000000000000000000000000000000815267ffffffffffffffff8b1660048201526024016107f2565b8a8481518110612ad357612ad3615203565b6020026020010151600014612b06578a8481518110612af457612af4615203565b60200260200101518360800181815250505b50612b72565b6000816003811115612b2057612b206142ca565b14612b72578151606001516040517f3ef2a99c00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff808c16600483015290911660248201526044016107f2565b81516080015167ffffffffffffffff1615801590612ba157506000816003811115612b9f57612b9f6142ca565b145b15612c665781516080015160208301516040517fe0e03cae0000000000000000000000000000000000000000000000000000000081526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169263e0e03cae92612c18928e929190600401615a6d565b6020604051808303816000875af1158015612c37573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c5b91906156a4565b612c66575050612e62565b60008b604001518481518110612c7e57612c7e615203565b6020026020010151905080518360a001515114612ce2578251606001516040517f1cfe6d8b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff808d16600483015290911660248201526044016107f2565b612cf68a8460000151606001516001613693565b600080612d03858461373b565b91509150612d1a8c86600001516060015184613693565b868015612d3857506003826003811115612d3657612d366142ca565b145b8015612d5657506000846003811115612d5357612d536142ca565b14155b15612d93578451516040517f2b11b8d90000000000000000000000000000000000000000000000000000000081526107f291908390600401615a9a565b6003826003811115612da757612da76142ca565b14158015612dc757506002826003811115612dc457612dc46142ca565b14155b15612e08578451606001516040517f926c5a3e0000000000000000000000000000000000000000000000000000000081526107f2918e918590600401615ab3565b8451805160609091015160405167ffffffffffffffff918216918f16907f8c324ce1367b83031769f6a813e3bb4c117aba2185789d66b98b791405be6df290612e549087908790615ad9565b60405180910390a450505050505b6001016128fe565b60408051808201909152600080825260208201526000612e8d8460200151613983565b6040517fbbe4f6db0000000000000000000000000000000000000000000000000000000081526001600160a01b0380831660048301529192506000917f0000000000000000000000000000000000000000000000000000000000000000169063bbe4f6db90602401602060405180830381865afa158015612f12573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f369190615af9565b90506001600160a01b0381161580612f7e5750612f7c6001600160a01b0382167faff2afbf00000000000000000000000000000000000000000000000000000000611d0e565b155b15612fc0576040517fae9b4ce90000000000000000000000000000000000000000000000000000000081526001600160a01b03821660048201526024016107f2565b60008061308b633907753760e01b6040518061010001604052808d81526020018b67ffffffffffffffff1681526020018c6001600160a01b031681526020018e8152602001876001600160a01b031681526020018a6000015181526020018a6040015181526020018981525060405160240161303c9190615b16565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152600454859063ffffffff600160e01b9091041661138860846139c5565b5091509150816130b0578060405163e1cd550960e01b81526004016107f29190613f11565b80516020146130f85780516040517f78ef80240000000000000000000000000000000000000000000000000000000081526020600482015260248101919091526044016107f2565b60008180602001905181019061310e9190615bed565b604080516001600160a01b038d16602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b03167fa9059cbb000000000000000000000000000000000000000000000000000000001790526004549192506131a69187907801000000000000000000000000000000000000000000000000900463ffffffff1661138860846139c5565b509093509150826131cc578160405163e1cd550960e01b81526004016107f29190613f11565b604080518082019091526001600160a01b03909516855260208501525091925050509695505050505050565b6000613224827f01ffc9a700000000000000000000000000000000000000000000000000000000613244565b8015610c4b575061323d826001600160e01b0319613244565b1592915050565b604080516001600160e01b03198316602480830191909152825180830390910181526044909101909152602080820180516001600160e01b03167f01ffc9a700000000000000000000000000000000000000000000000000000000178152825160009392849283928392918391908a617530fa92503d915060005190508280156132cf575060208210155b80156132db5750600081115b979650505050505050565b60005b815181101561121b5760ff83166000908152600360205260408120835190919084908490811061331b5761331b615203565b6020908102919091018101516001600160a01b03168252810191909152604001600020805461ffff191690556001016132e9565b60005b82518160ff161015610a23576000838260ff168151811061337557613375615203565b6020026020010151905060006002811115613392576133926142ca565b60ff80871660009081526003602090815260408083206001600160a01b038716845290915290205461010090041660028111156133d1576133d16142ca565b146133f2576004604051631b3fab5160e11b81526004016107f291906159b1565b6001600160a01b038116613432576040517fd6c62c9b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405180604001604052808360ff168152602001846002811115613458576134586142ca565b905260ff80871660009081526003602090815260408083206001600160a01b0387168452825290912083518154931660ff198416811782559184015190929091839161ffff1916176101008360028111156134b5576134b56142ca565b021790555090505050806134c890615c06565b9050613352565b60ff811661107857600a805467ffffffffffffffff1916905550565b815160208082015160409283015192516000938493613531937f2425b0b9f9054c76ff151b0a175b18f37a4a4e82013a72e9f15c9caa095ed21f93909291889101615c25565b60408051601f1981840301815290829052805160209182012086518051888401516060808b0151908401516080808d0151950151959761357a9794969395929491939101615c58565b604051602081830303815290604052805190602001208560400151805190602001208660a001516040516020016135b19190615cab565b604051602081830303815290604052805190602001208760c001516040516020016135dc9190615d60565b60408051601f198184030181528282528051602091820120908301979097528101949094526060840192909252608083015260a082015260c081019190915260e0015b60405160208183030381529060405280519060200120905092915050565b60008061364b858585613aeb565b905061365681611574565b61366457600091505061368b565b67ffffffffffffffff86166000908152600960209081526040808320938352929052205490505b949350505050565b600060026136a2608085615457565b67ffffffffffffffff166136b6919061547e565b905060006136c48585611c1a565b9050816136d36001600461542e565b901b1916818360038111156136ea576136ea6142ca565b67ffffffffffffffff871660009081526008602052604081209190921b92909217918291613719608088615711565b67ffffffffffffffff1681526020810191909152604001600020555050505050565b6040517f5ffb5ced0000000000000000000000000000000000000000000000000000000081526000906060903090635ffb5ced9061377f9087908790600401615d73565b600060405180830381600087803b15801561379957600080fd5b505af19250505080156137aa575060015b613967573d8080156137d8576040519150601f19603f3d011682016040523d82523d6000602084013e6137dd565b606091505b5060006137e982615e98565b90507f0a8d6e8c000000000000000000000000000000000000000000000000000000006001600160e01b031982161480613833575063e1cd550960e01b6001600160e01b03198216145b8061384e575063046b337b60e51b6001600160e01b03198216145b8061388257507f78ef8024000000000000000000000000000000000000000000000000000000006001600160e01b03198216145b806138b657507f0c3b563c000000000000000000000000000000000000000000000000000000006001600160e01b03198216145b806138ea57507fae9b4ce9000000000000000000000000000000000000000000000000000000006001600160e01b03198216145b8061391e57507f09c25325000000000000000000000000000000000000000000000000000000006001600160e01b03198216145b1561392f575060039250905061397c565b8551516040517f2b11b8d90000000000000000000000000000000000000000000000000000000081526107f291908490600401615a9a565b50506040805160208101909152600081526002905b9250929050565b600081516020146139a9578160405163046b337b60e51b81526004016107f29190613f11565b610c4b828060200190518101906139c09190615bed565b613d8a565b6000606060008361ffff1667ffffffffffffffff8111156139e8576139e86140c3565b6040519080825280601f01601f191660200182016040528015613a12576020820181803683370190505b509150863b613a45577f0c3b563c0000000000000000000000000000000000000000000000000000000060005260046000fd5b5a85811015613a78577fafa32a2c0000000000000000000000000000000000000000000000000000000060005260046000fd5b8590036040810481038710613ab1577f37c3be290000000000000000000000000000000000000000000000000000000060005260046000fd5b505a6000808a5160208c0160008c8cf193505a900390503d84811115613ad45750835b808352806000602085013e50955095509592505050565b8251825160009190818303613b2c576040517f11a6b26400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6101018211801590613b4057506101018111155b613b5d576040516309bde33960e01b815260040160405180910390fd5b60001982820101610100811115613b87576040516309bde33960e01b815260040160405180910390fd5b80600003613bb45786600081518110613ba257613ba2615203565b60200260200101519350505050613d83565b60008167ffffffffffffffff811115613bcf57613bcf6140c3565b604051908082528060200260200182016040528015613bf8578160200160208202803683370190505b50905060008080805b85811015613d225760006001821b8b811603613c5c5788851015613c45578c5160018601958e918110613c3657613c36615203565b60200260200101519050613c7e565b8551600185019487918110613c3657613c36615203565b8b5160018401938d918110613c7357613c73615203565b602002602001015190505b600089861015613cae578d5160018701968f918110613c9f57613c9f615203565b60200260200101519050613cd0565b8651600186019588918110613cc557613cc5615203565b602002602001015190505b82851115613cf1576040516309bde33960e01b815260040160405180910390fd5b613cfb8282613dde565b878481518110613d0d57613d0d615203565b60209081029190910101525050600101613c01565b506001850382148015613d3457508683145b8015613d3f57508581145b613d5c576040516309bde33960e01b815260040160405180910390fd5b836001860381518110613d7157613d71615203565b60200260200101519750505050505050505b9392505050565b60006001600160a01b03821180613da2575061040082105b15613dda5760408051602081018490520160408051601f198184030181529082905263046b337b60e51b82526107f291600401613f11565b5090565b6000818310613df657613df18284613dfc565b610c48565b610c4883835b60408051600160208201529081018390526060810182905260009060800161361f565b828054828255906000526020600020908101928215613e81579160200282015b82811115613e81578251825473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03909116178255602090920191600190910190613e3f565b50613dda929150613eac565b604051806103e00160405280601f906020820280368337509192915050565b5b80821115613dda5760008155600101613ead565b60005b83811015613edc578181015183820152602001613ec4565b50506000910152565b60008151808452613efd816020860160208601613ec1565b601f01601f19169290920160200192915050565b602081526000610c486020830184613ee5565b8060608101831015610c4b57600080fd5b60008083601f840112613f4757600080fd5b50813567ffffffffffffffff811115613f5f57600080fd5b60208301915083602082850101111561397c57600080fd5b60008083601f840112613f8957600080fd5b50813567ffffffffffffffff811115613fa157600080fd5b6020830191508360208260051b850101111561397c57600080fd5b60008060008060008060008060e0898b031215613fd857600080fd5b613fe28a8a613f24565b9750606089013567ffffffffffffffff80821115613fff57600080fd5b61400b8c838d01613f35565b909950975060808b013591508082111561402457600080fd5b6140308c838d01613f77565b909750955060a08b013591508082111561404957600080fd5b506140568b828c01613f77565b999c989b50969995989497949560c00135949350505050565b60008060006080848603121561408457600080fd5b61408e8585613f24565b9250606084013567ffffffffffffffff8111156140aa57600080fd5b6140b686828701613f35565b9497909650939450505050565b634e487b7160e01b600052604160045260246000fd5b60405160c0810167ffffffffffffffff811182821017156140fc576140fc6140c3565b60405290565b60405160a0810167ffffffffffffffff811182821017156140fc576140fc6140c3565b6040805190810167ffffffffffffffff811182821017156140fc576140fc6140c3565b60405160e0810167ffffffffffffffff811182821017156140fc576140fc6140c3565b6040516060810167ffffffffffffffff811182821017156140fc576140fc6140c3565b604051601f8201601f1916810167ffffffffffffffff811182821017156141b7576141b76140c3565b604052919050565b6001600160a01b038116811461107857600080fd5b80356141df816141bf565b919050565b803563ffffffff811681146141df57600080fd5b600060c0828403121561420a57600080fd5b6142126140d9565b823561421d816141bf565b815261422b602084016141e4565b602082015261423c604084016141e4565b604082015261424d606084016141e4565b60608201526080830135614260816141bf565b608082015260a0830135614273816141bf565b60a08201529392505050565b803567ffffffffffffffff811681146141df57600080fd5b600080604083850312156142aa57600080fd5b6142b38361427f565b91506142c16020840161427f565b90509250929050565b634e487b7160e01b600052602160045260246000fd5b600481106142f0576142f06142ca565b9052565b60208101610c4b82846142e0565b600060a0828403121561431457600080fd5b61431c614102565b90508135815261432e6020830161427f565b602082015261433f6040830161427f565b60408201526143506060830161427f565b60608201526143616080830161427f565b608082015292915050565b600067ffffffffffffffff821115614386576143866140c3565b50601f01601f191660200190565b600082601f8301126143a557600080fd5b81356143b86143b38261436c565b61418e565b8181528460208386010111156143cd57600080fd5b816020850160208301376000918101602001919091529392505050565b600067ffffffffffffffff821115614404576144046140c3565b5060051b60200190565b600082601f83011261441f57600080fd5b8135602061442f6143b3836143ea565b82815260069290921b8401810191818101908684111561444e57600080fd5b8286015b84811015611d03576040818903121561446b5760008081fd5b614473614125565b813561447e816141bf565b81528185013585820152835291830191604001614452565b600082601f8301126144a757600080fd5b813560206144b76143b3836143ea565b82815260059290921b840181019181810190868411156144d657600080fd5b8286015b84811015611d0357803567ffffffffffffffff8111156144fa5760008081fd5b6145088986838b0101614394565b8452509183019183016144da565b6000610160828403121561452957600080fd5b614531614148565b905061453d8383614302565b815260a082013567ffffffffffffffff8082111561455a57600080fd5b61456685838601614394565b602084015260c084013591508082111561457f57600080fd5b61458b85838601614394565b604084015261459c60e085016141d4565b606084015261010084013560808401526101208401359150808211156145c157600080fd5b6145cd8583860161440e565b60a08401526101408401359150808211156145e757600080fd5b506145f484828501614496565b60c08301525092915050565b6000806040838503121561461357600080fd5b823567ffffffffffffffff8082111561462b57600080fd5b61463786838701614516565b9350602085013591508082111561464d57600080fd5b5061465a85828601614496565b9150509250929050565b60006020828403121561467657600080fd5b813567ffffffffffffffff81111561468d57600080fd5b820160a08185031215613d8357600080fd5b801515811461107857600080fd5b80356141df8161469f565b600060208083850312156146cb57600080fd5b823567ffffffffffffffff808211156146e357600080fd5b818501915085601f8301126146f757600080fd5b81356147056143b3826143ea565b81815260059190911b8301840190848101908883111561472457600080fd5b8585015b838110156147b4578035858111156147405760008081fd5b86016060818c03601f19018113156147585760008081fd5b61476061416b565b61476b8a840161427f565b815260408084013561477c8161469f565b828c01529183013591888311156147935760008081fd5b6147a18e8c85870101614394565b9082015285525050918601918601614728565b5098975050505050505050565b600082601f8301126147d257600080fd5b813560206147e26143b3836143ea565b82815260059290921b8401810191818101908684111561480157600080fd5b8286015b84811015611d0357803567ffffffffffffffff8111156148255760008081fd5b6148338986838b0101614516565b845250918301918301614805565b600082601f83011261485257600080fd5b813560206148626143b3836143ea565b82815260059290921b8401810191818101908684111561488157600080fd5b8286015b84811015611d0357803567ffffffffffffffff8111156148a55760008081fd5b6148b38986838b0101614496565b845250918301918301614885565b600082601f8301126148d257600080fd5b813560206148e26143b3836143ea565b8083825260208201915060208460051b87010193508684111561490457600080fd5b602086015b84811015611d035780358352918301918301614909565b600082601f83011261493157600080fd5b813560206149416143b3836143ea565b82815260059290921b8401810191818101908684111561496057600080fd5b8286015b84811015611d0357803567ffffffffffffffff808211156149855760008081fd5b818901915060a080601f19848d030112156149a05760008081fd5b6149a8614102565b6149b388850161427f565b8152604080850135848111156149c95760008081fd5b6149d78e8b838901016147c1565b8a84015250606080860135858111156149f05760008081fd5b6149fe8f8c838a0101614841565b8385015250608091508186013585811115614a195760008081fd5b614a278f8c838a01016148c1565b9184019190915250919093013590830152508352918301918301614964565b6000806040808486031215614a5a57600080fd5b833567ffffffffffffffff80821115614a7257600080fd5b614a7e87838801614920565b9450602091508186013581811115614a9557600080fd5b8601601f81018813614aa657600080fd5b8035614ab46143b3826143ea565b81815260059190911b8201840190848101908a831115614ad357600080fd5b8584015b83811015614b5f57803586811115614aef5760008081fd5b8501603f81018d13614b015760008081fd5b87810135614b116143b3826143ea565b81815260059190911b82018a0190898101908f831115614b315760008081fd5b928b01925b82841015614b4f5783358252928a0192908a0190614b36565b8652505050918601918601614ad7565b50809750505050505050509250929050565b803560ff811681146141df57600080fd5b600060208284031215614b9457600080fd5b610c4882614b71565b60008151808452602080850194506020840160005b83811015614bd75781516001600160a01b031687529582019590820190600101614bb2565b509495945050505050565b60208152600082518051602084015260ff602082015116604084015260ff604082015116606084015260608101511515608084015250602083015160c060a0840152614c3160e0840182614b9d565b90506040840151601f198483030160c0850152614c4e8282614b9d565b95945050505050565b60008060408385031215614c6a57600080fd5b614c738361427f565b946020939093013593505050565b60008060208385031215614c9457600080fd5b823567ffffffffffffffff80821115614cac57600080fd5b818501915085601f830112614cc057600080fd5b813581811115614ccf57600080fd5b8660208260061b8501011115614ce457600080fd5b60209290920196919550909350505050565b600060208284031215614d0857600080fd5b610c488261427f565b6020815281511515602082015267ffffffffffffffff60208301511660408201526000604083015160608084015261368b6080840182613ee5565b600060208284031215614d5e57600080fd5b8135613d83816141bf565b600082601f830112614d7a57600080fd5b81356020614d8a6143b3836143ea565b8083825260208201915060208460051b870101935086841115614dac57600080fd5b602086015b84811015611d03578035614dc4816141bf565b8352918301918301614db1565b60006020808385031215614de457600080fd5b823567ffffffffffffffff80821115614dfc57600080fd5b818501915085601f830112614e1057600080fd5b8135614e1e6143b3826143ea565b81815260059190911b83018401908481019088831115614e3d57600080fd5b8585015b838110156147b457803585811115614e5857600080fd5b860160c0818c03601f19011215614e6f5760008081fd5b614e776140d9565b8882013581526040614e8a818401614b71565b8a8301526060614e9b818501614b71565b8284015260809150614eae8285016146ad565b9083015260a08381013589811115614ec65760008081fd5b614ed48f8d83880101614d69565b838501525060c0840135915088821115614eee5760008081fd5b614efc8e8c84870101614d69565b9083015250845250918601918601614e41565b600060208284031215614f2157600080fd5b5035919050565b80356001600160e01b03811681146141df57600080fd5b600082601f830112614f5057600080fd5b81356020614f606143b3836143ea565b82815260069290921b84018101918181019086841115614f7f57600080fd5b8286015b84811015611d035760408189031215614f9c5760008081fd5b614fa4614125565b614fad8261427f565b8152614fba858301614f28565b81860152835291830191604001614f83565b600082601f830112614fdd57600080fd5b81356020614fed6143b3836143ea565b82815260079290921b8401810191818101908684111561500c57600080fd5b8286015b84811015611d0357808803608081121561502a5760008081fd5b61503261416b565b61503b8361427f565b8152604080601f19840112156150515760008081fd5b615059614125565b925061506687850161427f565b835261507381850161427f565b8388015281870192909252606083013591810191909152835291830191608001615010565b600060208083850312156150ab57600080fd5b823567ffffffffffffffff808211156150c357600080fd5b818501915060408083880312156150d957600080fd5b6150e1614125565b8335838111156150f057600080fd5b84016040818a03121561510257600080fd5b61510a614125565b81358581111561511957600080fd5b8201601f81018b1361512a57600080fd5b80356151386143b3826143ea565b81815260069190911b8201890190898101908d83111561515757600080fd5b928a01925b828410156151a75787848f0312156151745760008081fd5b61517c614125565b8435615187816141bf565b8152615194858d01614f28565b818d0152825292870192908a019061515c565b8452505050818701359350848411156151bf57600080fd5b6151cb8a858401614f3f565b81880152825250838501359150828211156151e557600080fd5b6151f188838601614fcc565b85820152809550505050505092915050565b634e487b7160e01b600052603260045260246000fd5b805160408084528151848201819052600092602091908201906060870190855b8181101561527057835180516001600160a01b031684528501516001600160e01b0316858401529284019291850191600101615239565b50508583015187820388850152805180835290840192506000918401905b808310156152ca578351805167ffffffffffffffff1683528501516001600160e01b03168583015292840192600192909201919085019061528e565b50979650505050505050565b602081526000610c486020830184615219565b67ffffffffffffffff8316815260608101613d836020830184805167ffffffffffffffff908116835260209182015116910152565b634e487b7160e01b600052601160045260246000fd5b67ffffffffffffffff8181168382160190808211156153555761535561531e565b5092915050565b60006020808352606084516040808487015261537b6060870183615219565b87850151878203601f19016040890152805180835290860193506000918601905b808310156147b457845167ffffffffffffffff8151168352878101516153db89850182805167ffffffffffffffff908116835260209182015116910152565b5084015182870152938601936001929092019160809091019061539c565b60006020828403121561540b57600080fd5b813567ffffffffffffffff81111561542257600080fd5b61368b84828501614920565b81810381811115610c4b57610c4b61531e565b634e487b7160e01b600052601260045260246000fd5b600067ffffffffffffffff8084168061547257615472615441565b92169190910692915050565b8082028115828204841417610c4b57610c4b61531e565b60008151808452602080850194506020840160005b83811015614bd757815180516001600160a01b0316885260209081015190880152604087019650908201906001016154aa565b8051825267ffffffffffffffff60208201511660208301526000604082015160a0604085015261551060a0850182613ee5565b9050606083015184820360608601526155298282613ee5565b91505060808301518482036080860152614c4e8282615495565b602081526000610c4860208301846154dd565b60808152600061556960808301876154dd565b61ffff9590951660208301525060408101929092526001600160a01b0316606090910152919050565b600082601f8301126155a357600080fd5b81516155b16143b38261436c565b8181528460208386010111156155c657600080fd5b61368b826020830160208701613ec1565b6000806000606084860312156155ec57600080fd5b83516155f78161469f565b602085015190935067ffffffffffffffff81111561561457600080fd5b61562086828701615592565b925050604084015190509250925092565b60006040828403121561564357600080fd5b61564b614125565b6156548361427f565b8152602083013560208201528091505092915050565b600181811c9082168061567e57607f821691505b60208210810361569e57634e487b7160e01b600052602260045260246000fd5b50919050565b6000602082840312156156b657600080fd5b8151613d838161469f565b80820180821115610c4b57610c4b61531e565b60ff8181168382160190811115610c4b57610c4b61531e565b8183823760009101908152919050565b828152606082602083013760800192915050565b600067ffffffffffffffff8084168061572c5761572c615441565b92169190910492915050565b60006020828403121561574a57600080fd5b815167ffffffffffffffff8082111561576257600080fd5b908301906060828603121561577657600080fd5b61577e61416b565b82518281111561578d57600080fd5b61579987828601615592565b8252506020830151828111156157ae57600080fd5b6157ba87828601615592565b6020830152506040830151828111156157d257600080fd5b6157de87828601615592565b60408301525095945050505050565b601f82111561121b576000816000526020600020601f850160051c810160208610156158165750805b601f850160051c820191505b8181101561242957828155600101615822565b815167ffffffffffffffff81111561584f5761584f6140c3565b6158638161585d845461566a565b846157ed565b602080601f83116001811461589857600084156158805750858301515b600019600386901b1c1916600185901b178555612429565b600085815260208120601f198616915b828110156158c7578886015182559484019460019091019084016158a8565b50858210156158e55787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60006020808352835460ff81161515602085015267ffffffffffffffff8160081c166040850152506001808501606080860152600081546159358161566a565b80608089015260a060018316600081146159565760018114615972576159a2565b60ff19841660a08b015260a083151560051b8b010194506159a2565b85600052602060002060005b848110156159995781548c820185015290880190890161597e565b8b0160a0019550505b50929998505050505050505050565b60208101600583106159c5576159c56142ca565b91905290565b60ff81811683821602908116908181146153555761535561531e565b600060a0820160ff88168352602087602085015260a0604085015281875480845260c086019150886000526020600020935060005b81811015615a415784546001600160a01b031683526001948501949284019201615a1c565b50508481036060860152615a558188614b9d565b935050505060ff831660808301529695505050505050565b600067ffffffffffffffff808616835280851660208401525060606040830152614c4e6060830184613ee5565b82815260406020820152600061368b6040830184613ee5565b67ffffffffffffffff8481168252831660208201526060810161368b60408301846142e0565b615ae381846142e0565b60406020820152600061368b6040830184613ee5565b600060208284031215615b0b57600080fd5b8151613d83816141bf565b6020815260008251610100806020850152615b35610120850183613ee5565b91506020850151615b52604086018267ffffffffffffffff169052565b5060408501516001600160a01b038116606086015250606085015160808501526080850151615b8c60a08601826001600160a01b03169052565b5060a0850151601f19808685030160c0870152615ba98483613ee5565b935060c08701519150808685030160e0870152615bc68483613ee5565b935060e0870151915080868503018387015250615be38382613ee5565b9695505050505050565b600060208284031215615bff57600080fd5b5051919050565b600060ff821660ff8103615c1c57615c1c61531e565b60010192915050565b848152600067ffffffffffffffff808616602084015280851660408401525060806060830152615be36080830184613ee5565b86815260c060208201526000615c7160c0830188613ee5565b6001600160a01b039690961660408301525067ffffffffffffffff9384166060820152608081019290925290911660a09091015292915050565b6020808252825182820181905260009190848201906040850190845b81811015615cfa57835180516001600160a01b031684526020908101519084015260408301938501939250600101615cc7565b50909695505050505050565b60008282518085526020808601955060208260051b8401016020860160005b84811015615d5357601f19868403018952615d41838351613ee5565b98840198925090830190600101615d25565b5090979650505050505050565b602081526000610c486020830184615d06565b60408152615dc460408201845180518252602081015167ffffffffffffffff808216602085015280604084015116604085015280606084015116606085015280608084015116608085015250505050565b600060208401516101608060e0850152615de26101a0850183613ee5565b915060408601517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08086850301610100870152615e1f8483613ee5565b935060608801519150615e3e6101208701836001600160a01b03169052565b608088015161014087015260a08801519150808685030183870152615e638483615495565b935060c0880151925080868503016101808701525050615e838282615d06565b9150508281036020840152614c4e8185615d06565b6000815160208301516001600160e01b031980821693506004831015615ec85780818460040360031b1b83161693505b50505091905056fea164736f6c6343000818000a", + ABI: "[{\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAdminRegistry\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"nonceManager\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.StaticConfig\",\"name\":\"staticConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"onRamp\",\"type\":\"bytes\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.SourceChainConfigArgs[]\",\"name\":\"sourceChainConfigs\",\"type\":\"tuple[]\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"}],\"name\":\"AlreadyAttempted\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"}],\"name\":\"AlreadyExecuted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CanOnlySelfCall\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"expected\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"actual\",\"type\":\"bytes32\"}],\"name\":\"ConfigDigestMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"CursedByRMN\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"EmptyReport\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"error\",\"type\":\"bytes\"}],\"name\":\"ExecutionError\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"expected\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"actual\",\"type\":\"uint256\"}],\"name\":\"ForkedChain\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"enumMultiOCR3Base.InvalidConfigErrorType\",\"name\":\"errorType\",\"type\":\"uint8\"}],\"name\":\"InvalidConfig\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"expected\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"got\",\"type\":\"uint256\"}],\"name\":\"InvalidDataLength\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"encodedAddress\",\"type\":\"bytes\"}],\"name\":\"InvalidEVMAddress\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"min\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"max\",\"type\":\"uint64\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.Interval\",\"name\":\"interval\",\"type\":\"tuple\"}],\"name\":\"InvalidInterval\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"newLimit\",\"type\":\"uint256\"}],\"name\":\"InvalidManualExecutionGasLimit\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"messageDestChainSelector\",\"type\":\"uint64\"}],\"name\":\"InvalidMessageDestChainSelector\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"internalType\":\"enumInternal.MessageExecutionState\",\"name\":\"newState\",\"type\":\"uint8\"}],\"name\":\"InvalidNewState\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidProof\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidRoot\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"InvalidStaticConfig\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"LeavesCannotBeEmpty\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ManualExecutionGasLimitMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"ManualExecutionNotYetEnabled\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"errorReason\",\"type\":\"bytes\"}],\"name\":\"MessageValidationError\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NonUniqueSignatures\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"notPool\",\"type\":\"address\"}],\"name\":\"NotACompatiblePool\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OracleCannotBeZeroAddress\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"error\",\"type\":\"bytes\"}],\"name\":\"ReceiverError\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"merkleRoot\",\"type\":\"bytes32\"}],\"name\":\"RootAlreadyCommitted\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"RootNotCommitted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"SignaturesOutOfRegistration\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"SourceChainNotEnabled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"StaleCommitReport\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"ocrPluginType\",\"type\":\"uint8\"}],\"name\":\"StaticConfigCannotBeChanged\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"}],\"name\":\"TokenDataMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"error\",\"type\":\"bytes\"}],\"name\":\"TokenHandlingError\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnauthorizedSigner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnauthorizedTransmitter\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnexpectedTokenData\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"expected\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"actual\",\"type\":\"uint256\"}],\"name\":\"WrongMessageLength\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"WrongNumberOfSignatures\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroAddressNotAllowed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroChainSelectorNotAllowed\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"components\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"sourceToken\",\"type\":\"address\"},{\"internalType\":\"uint224\",\"name\":\"usdPerToken\",\"type\":\"uint224\"}],\"internalType\":\"structInternal.TokenPriceUpdate[]\",\"name\":\"tokenPriceUpdates\",\"type\":\"tuple[]\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint224\",\"name\":\"usdPerUnitGas\",\"type\":\"uint224\"}],\"internalType\":\"structInternal.GasPriceUpdate[]\",\"name\":\"gasPriceUpdates\",\"type\":\"tuple[]\"}],\"internalType\":\"structInternal.PriceUpdates\",\"name\":\"priceUpdates\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"min\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"max\",\"type\":\"uint64\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.Interval\",\"name\":\"interval\",\"type\":\"tuple\"},{\"internalType\":\"bytes32\",\"name\":\"merkleRoot\",\"type\":\"bytes32\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.MerkleRoot[]\",\"name\":\"merkleRoots\",\"type\":\"tuple[]\"}],\"indexed\":false,\"internalType\":\"structEVM2EVMMultiOffRamp.CommitReport\",\"name\":\"report\",\"type\":\"tuple\"}],\"name\":\"CommitReportAccepted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"ocrPluginType\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"F\",\"type\":\"uint8\"}],\"name\":\"ConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"permissionLessExecutionThresholdSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxTokenTransferGas\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPoolReleaseOrMintGas\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"messageValidator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"priceRegistry\",\"type\":\"address\"}],\"indexed\":false,\"internalType\":\"structEVM2EVMMultiOffRamp.DynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"}],\"name\":\"DynamicConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"enumInternal.MessageExecutionState\",\"name\":\"state\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"returnData\",\"type\":\"bytes\"}],\"name\":\"ExecutionStateChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"}],\"name\":\"RootRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"}],\"name\":\"SkippedAlreadyExecutedMessage\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint64\",\"name\":\"minSeqNr\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"onRamp\",\"type\":\"bytes\"}],\"indexed\":false,\"internalType\":\"structEVM2EVMMultiOffRamp.SourceChainConfig\",\"name\":\"sourceConfig\",\"type\":\"tuple\"}],\"name\":\"SourceChainConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"SourceChainSelectorAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAdminRegistry\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"nonceManager\",\"type\":\"address\"}],\"indexed\":false,\"internalType\":\"structEVM2EVMMultiOffRamp.StaticConfig\",\"name\":\"staticConfig\",\"type\":\"tuple\"}],\"name\":\"StaticConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint8\",\"name\":\"ocrPluginType\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"}],\"name\":\"Transmitted\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"onRamp\",\"type\":\"bytes\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.SourceChainConfigArgs[]\",\"name\":\"sourceChainConfigUpdates\",\"type\":\"tuple[]\"}],\"name\":\"applySourceChainConfigUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"sender\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structClient.EVMTokenAmount[]\",\"name\":\"destTokenAmounts\",\"type\":\"tuple[]\"}],\"internalType\":\"structClient.Any2EVMMessage\",\"name\":\"\",\"type\":\"tuple\"}],\"name\":\"ccipReceive\",\"outputs\":[],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[3]\",\"name\":\"reportContext\",\"type\":\"bytes32[3]\"},{\"internalType\":\"bytes\",\"name\":\"report\",\"type\":\"bytes\"},{\"internalType\":\"bytes32[]\",\"name\":\"rs\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32[]\",\"name\":\"ss\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32\",\"name\":\"rawVs\",\"type\":\"bytes32\"}],\"name\":\"commit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[3]\",\"name\":\"reportContext\",\"type\":\"bytes32[3]\"},{\"internalType\":\"bytes\",\"name\":\"report\",\"type\":\"bytes\"}],\"name\":\"execute\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"}],\"internalType\":\"structInternal.RampMessageHeader\",\"name\":\"header\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"sender\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"bytes\",\"name\":\"sourcePoolAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"destTokenAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"extraData\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structInternal.RampTokenAmount[]\",\"name\":\"tokenAmounts\",\"type\":\"tuple[]\"}],\"internalType\":\"structInternal.Any2EVMRampMessage\",\"name\":\"message\",\"type\":\"tuple\"},{\"internalType\":\"bytes[]\",\"name\":\"offchainTokenData\",\"type\":\"bytes[]\"}],\"name\":\"executeSingleMessage\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getDynamicConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"permissionLessExecutionThresholdSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxTokenTransferGas\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPoolReleaseOrMintGas\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"messageValidator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"priceRegistry\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.DynamicConfig\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"}],\"name\":\"getExecutionState\",\"outputs\":[{\"internalType\":\"enumInternal.MessageExecutionState\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getLatestPriceSequenceNumber\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"}],\"name\":\"getMerkleRoot\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"getSourceChainConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint64\",\"name\":\"minSeqNr\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"onRamp\",\"type\":\"bytes\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.SourceChainConfig\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getStaticConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAdminRegistry\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"nonceManager\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.StaticConfig\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"}],\"name\":\"isBlessed\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"ocrPluginType\",\"type\":\"uint8\"}],\"name\":\"latestConfigDetails\",\"outputs\":[{\"components\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"internalType\":\"uint8\",\"name\":\"F\",\"type\":\"uint8\"},{\"internalType\":\"uint8\",\"name\":\"n\",\"type\":\"uint8\"},{\"internalType\":\"bool\",\"name\":\"isSignatureVerificationEnabled\",\"type\":\"bool\"}],\"internalType\":\"structMultiOCR3Base.ConfigInfo\",\"name\":\"configInfo\",\"type\":\"tuple\"},{\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"}],\"internalType\":\"structMultiOCR3Base.OCRConfig\",\"name\":\"ocrConfig\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"}],\"internalType\":\"structInternal.RampMessageHeader\",\"name\":\"header\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"sender\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"bytes\",\"name\":\"sourcePoolAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"destTokenAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"extraData\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structInternal.RampTokenAmount[]\",\"name\":\"tokenAmounts\",\"type\":\"tuple[]\"}],\"internalType\":\"structInternal.Any2EVMRampMessage[]\",\"name\":\"messages\",\"type\":\"tuple[]\"},{\"internalType\":\"bytes[][]\",\"name\":\"offchainTokenData\",\"type\":\"bytes[][]\"},{\"internalType\":\"bytes32[]\",\"name\":\"proofs\",\"type\":\"bytes32[]\"},{\"internalType\":\"uint256\",\"name\":\"proofFlagBits\",\"type\":\"uint256\"}],\"internalType\":\"structInternal.ExecutionReportSingleChain[]\",\"name\":\"reports\",\"type\":\"tuple[]\"},{\"internalType\":\"uint256[][]\",\"name\":\"gasLimitOverrides\",\"type\":\"uint256[][]\"}],\"name\":\"manuallyExecute\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"merkleRoot\",\"type\":\"bytes32\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.UnblessedRoot[]\",\"name\":\"rootToReset\",\"type\":\"tuple[]\"}],\"name\":\"resetUnblessedRoots\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"permissionLessExecutionThresholdSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxTokenTransferGas\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPoolReleaseOrMintGas\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"messageValidator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"priceRegistry\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.DynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"}],\"name\":\"setDynamicConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"internalType\":\"uint8\",\"name\":\"ocrPluginType\",\"type\":\"uint8\"},{\"internalType\":\"uint8\",\"name\":\"F\",\"type\":\"uint8\"},{\"internalType\":\"bool\",\"name\":\"isSignatureVerificationEnabled\",\"type\":\"bool\"},{\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"}],\"internalType\":\"structMultiOCR3Base.OCRConfigArgs[]\",\"name\":\"ocrConfigArgs\",\"type\":\"tuple[]\"}],\"name\":\"setOCR3Configs\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", + Bin: "0x6101206040523480156200001257600080fd5b506040516200690e3803806200690e8339810160408190526200003591620005bd565b33806000816200008c5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000bf57620000bf81620001ee565b5050466080525060208201516001600160a01b03161580620000ec575060408201516001600160a01b0316155b8062000103575060608201516001600160a01b0316155b1562000122576040516342bcdf7f60e11b815260040160405180910390fd5b81516001600160401b03166000036200014e5760405163c656089560e01b815260040160405180910390fd5b81516001600160401b0390811660a052602080840180516001600160a01b0390811660c05260408087018051831660e0526060808901805185166101005283518a519098168852945184169587019590955251821690850152905116908201527f683eb52ee924eb817377cfa8f41f238f4bb7a877da5267869dfffbad85f564d89060800160405180910390a1620001e68162000299565b505062000a03565b336001600160a01b03821603620002485760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000083565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60005b8151811015620004eb576000828281518110620002bd57620002bd620007cc565b60200260200101519050600081600001519050806001600160401b0316600003620002fb5760405163c656089560e01b815260040160405180910390fd5b6001600160401b03811660009081526007602052604081206001810180549192916200032790620007e2565b80601f01602080910402602001604051908101604052809291908181526020018280546200035590620007e2565b8015620003a65780601f106200037a57610100808354040283529160200191620003a6565b820191906000526020600020905b8154815290600101906020018083116200038857829003601f168201915b50505050509050600084604001519050815160000362000449578051600003620003e3576040516342bcdf7f60e11b815260040160405180910390fd5b60018301620003f3828262000873565b508254610100600160481b0319166101001783556040516001600160401b03851681527ff4c1390c70e5c0f491ae1ccbc06f9117cbbadf2767b247b3bc203280f24c0fb99060200160405180910390a162000484565b8080519060200120828051906020012014620004845760405163c39a620560e01b81526001600160401b038516600482015260240162000083565b6020850151835460ff19169015151783556040516001600160401b038516907f4f49973170c548fddd4a48341b75e131818913f38f44d47af57e8735eee588ba90620004d29086906200093f565b60405180910390a250505050508060010190506200029c565b5050565b634e487b7160e01b600052604160045260246000fd5b604051608081016001600160401b03811182821017156200052a576200052a620004ef565b60405290565b604051606081016001600160401b03811182821017156200052a576200052a620004ef565b604051601f8201601f191681016001600160401b0381118282101715620005805762000580620004ef565b604052919050565b80516001600160401b0381168114620005a057600080fd5b919050565b80516001600160a01b0381168114620005a057600080fd5b60008082840360a0811215620005d257600080fd5b6080811215620005e157600080fd5b50620005ec62000505565b620005f78462000588565b8152602062000608818601620005a5565b818301526200061a60408601620005a5565b60408301526200062d60608601620005a5565b606083015260808501519193506001600160401b03808311156200065057600080fd5b828601925086601f8401126200066557600080fd5b8251818111156200067a576200067a620004ef565b8060051b6200068b84820162000555565b918252848101840191848101908a841115620006a657600080fd5b85870192505b83831015620007bb57825185811115620006c557600080fd5b8701601f196060828e0382011215620006dd57600080fd5b620006e762000530565b620006f489840162000588565b8152604083015180151581146200070a57600080fd5b818a01526060830151888111156200072157600080fd5b8084019350508d603f8401126200073757600080fd5b88830151888111156200074e576200074e620004ef565b620007608a84601f8401160162000555565b92508083528e60408286010111156200077857600080fd5b60005b818110156200079957848101604001518482018c01528a016200077b565b5060009083018a015260408101919091528352509185019190850190620006ac565b809750505050505050509250929050565b634e487b7160e01b600052603260045260246000fd5b600181811c90821680620007f757607f821691505b6020821081036200081857634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200086e576000816000526020600020601f850160051c81016020861015620008495750805b601f850160051c820191505b818110156200086a5782815560010162000855565b5050505b505050565b81516001600160401b038111156200088f576200088f620004ef565b620008a781620008a08454620007e2565b846200081e565b602080601f831160018114620008df5760008415620008c65750858301515b600019600386901b1c1916600185901b1785556200086a565b600085815260208120601f198616915b828110156200091057888601518255948401946001909101908401620008ef565b50858210156200092f5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b6020808252825460ff811615158383015260081c6001600160401b031660408301526060808301526001808401805460009392919084906200098181620007e2565b80608089015260a06001831660008114620009a55760018114620009c257620009f4565b60ff19841660a08b015260a083151560051b8b01019450620009f4565b85600052602060002060005b84811015620009eb5781548c8201850152908801908901620009ce565b8b0160a0019550505b50929998505050505050505050565b60805160a05160c05160e05161010051615e9c62000a726000396000818161023e0152612c0d01526000818161020f0152612ef70152600081816101e0015281816115c5015261167c0152600081816101b00152612781015260008181611c5f0152611cab0152615e9c6000f3fe608060405234801561001057600080fd5b50600436106101775760003560e01c806385572ffb116100d8578063d2a15d351161008c578063f2fde38b11610066578063f2fde38b146105b1578063f716f99f146105c4578063ff888fb1146105d757600080fd5b8063d2a15d351461056b578063e9d68a8e1461057e578063ece670b61461059e57600080fd5b8063a12a9870116100bd578063a12a9870146104f3578063c673e58414610506578063ccd37ba31461052657600080fd5b806385572ffb146104ca5780638da5cb5b146104d857600080fd5b8063403b2d631161012f5780637437ff9f116101145780637437ff9f1461038557806379ba5097146104af5780637d4eef60146104b757600080fd5b8063403b2d63146103525780635e36480c1461036557600080fd5b80632d04ab76116101605780632d04ab761461030e578063311cd513146103235780633f4b04aa1461033657600080fd5b806306285c691461017c578063181f5a77146102c5575b600080fd5b61026e60408051608081018252600080825260208201819052918101829052606081019190915260405180608001604052807f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff1681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316815250905090565b6040516102bc9190815167ffffffffffffffff1681526020808301516001600160a01b0390811691830191909152604080840151821690830152606092830151169181019190915260800190565b60405180910390f35b6103016040518060400160405280601d81526020017f45564d3245564d4d756c74694f666652616d7020312e362e302d64657600000081525081565b6040516102bc9190613f10565b61032161031c366004613fbb565b6105fa565b005b61032161033136600461406e565b6109c0565b600a5460405167ffffffffffffffff90911681526020016102bc565b6103216103603660046141f7565b610a29565b610378610373366004614296565b610bfb565b6040516102bc91906142f3565b6104466040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a0810191909152506040805160c0810182526004546001600160a01b03808216835263ffffffff7401000000000000000000000000000000000000000083048116602085015278010000000000000000000000000000000000000000000000008304811694840194909452600160e01b90910490921660608201526005548216608082015260065490911660a082015290565b6040516102bc9190600060c0820190506001600160a01b03808451168352602084015163ffffffff808216602086015280604087015116604086015280606087015116606086015250508060808501511660808401528060a08501511660a08401525092915050565b610321610c51565b6103216104c53660046148f7565b610d0f565b610321610177366004614a22565b6000546040516001600160a01b0390911681526020016102bc565b610321610501366004614a76565b610eb4565b610519610514366004614b90565b610ec8565b6040516102bc9190614bf0565b61055d610534366004614c65565b67ffffffffffffffff919091166000908152600960209081526040808320938352929052205490565b6040519081526020016102bc565b610321610579366004614c8f565b611026565b61059161058c366004614d04565b6110e0565b6040516102bc9190614d1f565b6103216105ac366004614d5a565b6111c9565b6103216105bf366004614dbe565b61151c565b6103216105d2366004614e43565b61152d565b6105ea6105e5366004614f81565b61156f565b60405190151581526020016102bc565b60006106088789018961510a565b8051515190915015158061062157508051602001515115155b1561072157600a5460208a01359067ffffffffffffffff808316911610156106e057600a805467ffffffffffffffff191667ffffffffffffffff831617905560065482516040517f3937306f0000000000000000000000000000000000000000000000000000000081526001600160a01b0390921691633937306f916106a991600401615348565b600060405180830381600087803b1580156106c357600080fd5b505af11580156106d7573d6000803e3d6000fd5b5050505061071f565b81602001515160000361071f576040517f2261116700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505b60005b8160200151518110156109095760008260200151828151811061074957610749615275565b6020026020010151905060008160000151905061076581611630565b600061077082611732565b602084015151815491925067ffffffffffffffff908116610100909204161415806107b2575060208084015190810151905167ffffffffffffffff9182169116115b156107fb57825160208401516040517feefb0cac0000000000000000000000000000000000000000000000000000000081526107f292919060040161535b565b60405180910390fd5b604083015180610837576040517f504570e300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b835167ffffffffffffffff166000908152600960209081526040808320848452909152902054156108aa5783516040517f32cf0cbf00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff9091166004820152602481018290526044016107f2565b60208085015101516108bd9060016153a6565b825468ffffffffffffffff00191661010067ffffffffffffffff928316021790925592511660009081526009602090815260408083209483529390529190912042905550600101610724565b507f3a3950e13dd607cc37980db0ef14266c40d2bba9c01b2e44bfe549808883095d8160405161093991906153ce565b60405180910390a16109b560008a8a8a8a8a8080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808e0282810182019093528d82529093508d92508c9182918501908490808284376000920191909152508b9250611792915050565b505050505050505050565b610a006109cf8284018461546b565b60408051600080825260208201909252906109fa565b60608152602001906001900390816109e55790505b50611b09565b604080516000808252602082019092529050610a23600185858585866000611792565b50505050565b610a31611bb9565b60a08101516001600160a01b03161580610a53575080516001600160a01b0316155b15610a8a576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80516004805460208085018051604080880180516060808b0180516001600160a01b039b8c167fffffffffffffffff000000000000000000000000000000000000000000000000909a168a177401000000000000000000000000000000000000000063ffffffff988916021777ffffffffffffffffffffffffffffffffffffffffffffffff167801000000000000000000000000000000000000000000000000948816949094026001600160e01b031693909317600160e01b93871693909302929092179098556080808b0180516005805473ffffffffffffffffffffffffffffffffffffffff19908116928e1692909217905560a0808e01805160068054909416908f161790925586519a8b5297518716988a0198909852925185169388019390935251909216958501959095525185169383019390935251909216908201527f0da37fd00459f4f5f0b8210d31525e4910ae674b8bab34b561d146bb45773a4c9060c00160405180910390a150565b6000610c09600160046154a0565b6002610c166080856154c9565b67ffffffffffffffff16610c2a91906154f0565b610c348585611c15565b901c166003811115610c4857610c486142c9565b90505b92915050565b6001546001600160a01b03163314610cab5760405162461bcd60e51b815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064016107f2565b600080543373ffffffffffffffffffffffffffffffffffffffff19808316821784556001805490911690556040516001600160a01b0390921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b610d17611c5c565b815181518114610d53576040517f83e3f56400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b81811015610ea4576000848281518110610d7257610d72615275565b60200260200101519050600081602001515190506000858481518110610d9a57610d9a615275565b6020026020010151905080518214610dde576040517f83e3f56400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b82811015610e95576000828281518110610dfd57610dfd615275565b6020026020010151905080600014158015610e38575084602001518281518110610e2957610e29615275565b60200260200101516080015181105b15610e8c5784516040517fc8e9605100000000000000000000000000000000000000000000000000000000815267ffffffffffffffff909116600482015260248101839052604481018290526064016107f2565b50600101610de1565b50505050806001019050610d56565b50610eaf8383611b09565b505050565b610ebc611bb9565b610ec581611cdd565b50565b610f0b6040805160e081019091526000606082018181526080830182905260a0830182905260c08301919091528190815260200160608152602001606081525090565b60ff808316600090815260026020818152604092839020835160e081018552815460608201908152600183015480881660808401526101008104881660a0840152620100009004909616151560c082015294855291820180548451818402810184019095528085529293858301939092830182828015610fb457602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610f96575b505050505081526020016003820180548060200260200160405190810160405280929190818152602001828054801561101657602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610ff8575b5050505050815250509050919050565b61102e611bb9565b60005b81811015610eaf57600083838381811061104d5761104d615275565b9050604002018036038101906110639190615507565b9050611072816020015161156f565b6110d757805167ffffffffffffffff1660009081526009602090815260408083208285018051855290835281842093909355915191519182527f202f1139a3e334b6056064c0e9b19fd07e44a88d8f6e5ded571b24cf8c371f12910160405180910390a15b50600101611031565b60408051606080820183526000808352602080840182905283850183905267ffffffffffffffff8681168352600782529185902085519384018652805460ff81161515855261010090049092169083015260018101805493949293919284019161114990615540565b80601f016020809104026020016040519081016040528092919081815260200182805461117590615540565b80156110165780601f1061119757610100808354040283529160200191611016565b820191906000526020600020905b8154815290600101906020018083116111a557505050919092525091949350505050565b333014611202576040517f371a732800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080516000808252602082019092528161123f565b60408051808201909152600080825260208201528152602001906001900390816112185790505b5060a084015151909150156112725761126f8360a001518460200151856060015186600001516020015186611f69565b90505b6040805160a0810182528451518152845160209081015167ffffffffffffffff16818301528086015183516000948401926112ae929101613f10565b60408051601f19818403018152918152908252868101516020830152018390526005549091506001600160a01b031680156113bb576040517f08d450a10000000000000000000000000000000000000000000000000000000081526001600160a01b038216906308d450a19061132890859060040161561c565b600060405180830381600087803b15801561134257600080fd5b505af1925050508015611353575060015b6113bb573d808015611381576040519150601f19603f3d011682016040523d82523d6000602084013e611386565b606091505b50806040517f09c253250000000000000000000000000000000000000000000000000000000081526004016107f29190613f10565b6040850151511580156113d057506080850151155b806113e7575060608501516001600160a01b03163b155b8061142757506060850151611425906001600160a01b03167f85572ffb00000000000000000000000000000000000000000000000000000000612047565b155b15611433575050505050565b60048054608087015160608801516040517f3cf9798300000000000000000000000000000000000000000000000000000000815260009485946001600160a01b031693633cf979839361148e938a936113889392910161562f565b6000604051808303816000875af11580156114ad573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526114d5919081019061566b565b50915091508161151357806040517f0a8d6e8c0000000000000000000000000000000000000000000000000000000081526004016107f29190613f10565b50505050505050565b611524611bb9565b610ec581612063565b611535611bb9565b60005b815181101561156b5761156382828151811061155657611556615275565b6020026020010151612119565b600101611538565b5050565b6040805180820182523081526020810183815291517f4d61677100000000000000000000000000000000000000000000000000000000815290516001600160a01b039081166004830152915160248201526000917f00000000000000000000000000000000000000000000000000000000000000001690634d61677190604401602060405180830381865afa15801561160c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c4b9190615701565b6040517f2cbc26bb000000000000000000000000000000000000000000000000000000008152608082901b77ffffffffffffffff000000000000000000000000000000001660048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632cbc26bb90602401602060405180830381865afa1580156116cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116ef9190615701565b15610ec5576040517ffdbd6a7200000000000000000000000000000000000000000000000000000000815267ffffffffffffffff821660048201526024016107f2565b67ffffffffffffffff81166000908152600760205260408120805460ff16610c4b576040517fed053c5900000000000000000000000000000000000000000000000000000000815267ffffffffffffffff841660048201526024016107f2565b60ff878116600090815260026020908152604080832081516080810183528154815260019091015480861693820193909352610100830485169181019190915262010000909104909216151560608301528735906117f18760a461571e565b905082606001511561183957845161180a9060206154f0565b86516118179060206154f0565b6118229060a061571e565b61182c919061571e565b611836908261571e565b90505b36811461187b576040517f8e1192e1000000000000000000000000000000000000000000000000000000008152600481018290523660248201526044016107f2565b50815181146118c35781516040517f93df584c0000000000000000000000000000000000000000000000000000000081526004810191909152602481018290526044016107f2565b6118cb611c5c565b60ff808a1660009081526003602090815260408083203384528252808320815180830190925280548086168352939491939092840191610100909104166002811115611919576119196142c9565b600281111561192a5761192a6142c9565b9052509050600281602001516002811115611947576119476142c9565b14801561199b5750600260008b60ff1660ff168152602001908152602001600020600301816000015160ff168154811061198357611983615275565b6000918252602090912001546001600160a01b031633145b6119d1576040517fda0f08e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50816060015115611ab35760208201516119ec906001615731565b60ff16855114611a28576040517f71253a2500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8351855114611a63576040517fa75d88af00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008787604051611a7592919061574a565b604051908190038120611a8c918b9060200161575a565b604051602081830303815290604052805190602001209050611ab18a8288888861245d565b505b6040805182815260208a81013567ffffffffffffffff169082015260ff8b16917f198d6990ef96613a9026203077e422916918b03ff47f0be6bee7b02d8e139ef0910160405180910390a2505050505050505050565b8151600003611b43576040517ebf199700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805160408051600080825260208201909252911591905b8451811015611bb257611baa858281518110611b7857611b78615275565b602002602001015184611ba457858381518110611b9757611b97615275565b6020026020010151612674565b83612674565b600101611b5a565b5050505050565b6000546001600160a01b03163314611c135760405162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016107f2565b565b67ffffffffffffffff8216600090815260086020526040812081611c3a60808561576e565b67ffffffffffffffff1681526020810191909152604001600020549392505050565b467f000000000000000000000000000000000000000000000000000000000000000014611c13576040517f0f01ce850000000000000000000000000000000000000000000000000000000081527f000000000000000000000000000000000000000000000000000000000000000060048201524660248201526044016107f2565b60005b815181101561156b576000828281518110611cfd57611cfd615275565b602002602001015190506000816000015190508067ffffffffffffffff16600003611d54576040517fc656089500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff81166000908152600760205260408120600181018054919291611d7f90615540565b80601f0160208091040260200160405190810160405280929190818152602001828054611dab90615540565b8015611df85780601f10611dcd57610100808354040283529160200191611df8565b820191906000526020600020905b815481529060010190602001808311611ddb57829003601f168201915b505050505090506000846040015190508151600003611eb1578051600003611e4c576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60018301611e5a82826157dd565b50825468ffffffffffffffff00191661010017835560405167ffffffffffffffff851681527ff4c1390c70e5c0f491ae1ccbc06f9117cbbadf2767b247b3bc203280f24c0fb99060200160405180910390a1611f04565b8080519060200120828051906020012014611f04576040517fc39a620500000000000000000000000000000000000000000000000000000000815267ffffffffffffffff851660048201526024016107f2565b6020850151835460ff191690151517835560405167ffffffffffffffff8516907f4f49973170c548fddd4a48341b75e131818913f38f44d47af57e8735eee588ba90611f5190869061589d565b60405180910390a25050505050806001019050611ce0565b6060855167ffffffffffffffff811115611f8557611f856140c2565b604051908082528060200260200182016040528015611fca57816020015b6040805180820190915260008082526020820152815260200190600190039081611fa35790505b50905060005b865181101561203d57612018878281518110611fee57611fee615275565b602002602001015187878787868151811061200b5761200b615275565b6020026020010151612e96565b82828151811061202a5761202a615275565b6020908102919091010152600101611fd0565b5095945050505050565b600061205283613227565b8015610c485750610c488383613273565b336001600160a01b038216036120bb5760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016107f2565b6001805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b806040015160ff16600003612144576000604051631b3fab5160e11b81526004016107f29190615959565b60208082015160ff808216600090815260029093526040832060018101549293909283921690036121b157606084015160018201805491151562010000027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffff909216919091179055612206565b6060840151600182015460ff6201000090910416151590151514612206576040517f87f6037c00000000000000000000000000000000000000000000000000000000815260ff841660048201526024016107f2565b60a08401518051601f60ff82161115612235576001604051631b3fab5160e11b81526004016107f29190615959565b61229b858560030180548060200260200160405190810160405280929190818152602001828054801561229157602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311612273575b5050505050613315565b8560600151156123ca576123098585600201805480602002602001604051908101604052809291908181526020018280548015612291576020028201919060005260206000209081546001600160a01b03168152600190910190602001808311612273575050505050613315565b608086015180516123239060028701906020840190613e1e565b5080516001850180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff1661010060ff841690810291909117909155601f1015612383576002604051631b3fab5160e11b81526004016107f29190615959565b6040880151612393906003615973565b60ff168160ff16116123bb576003604051631b3fab5160e11b81526004016107f29190615959565b6123c78783600161337e565b50505b6123d68583600261337e565b81516123eb9060038601906020850190613e1e565b5060408681015160018501805460ff191660ff8316179055875180865560a089015192517fab8b1b57514019638d7b5ce9c638fe71366fe8e2be1c40a7a80f1733d0e9f54793612444938a939260028b0192919061598f565b60405180910390a1612455856134fe565b505050505050565b612465613e8c565b835160005b8181101561266a57600060018886846020811061248957612489615275565b61249691901a601b615731565b8985815181106124a8576124a8615275565b60200260200101518986815181106124c2576124c2615275565b602002602001015160405160008152602001604052604051612500949392919093845260ff9290921660208401526040830152606082015260800190565b6020604051602081039080840390855afa158015612522573d6000803e3d6000fd5b505060408051601f1981015160ff808e166000908152600360209081528582206001600160a01b03851683528152858220858701909652855480841686529397509095509293928401916101009004166002811115612583576125836142c9565b6002811115612594576125946142c9565b90525090506001816020015160028111156125b1576125b16142c9565b146125e8576040517fca31867a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8051859060ff16601f81106125ff576125ff615275565b60200201511561263b576040517ff67bc7c400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600185826000015160ff16601f811061265657612656615275565b91151560209092020152505060010161246a565b5050505050505050565b815161267f81611630565b600061268a82611732565b60208501515190915060008190036126cd576040517ebf199700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b846040015151811461270b576040517f57e0e08300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008167ffffffffffffffff811115612726576127266140c2565b60405190808252806020026020018201604052801561274f578160200160208202803683370190505b50905060005b828110156128c45760008760200151828151811061277557612775615275565b602002602001015190507f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff1681600001516040015167ffffffffffffffff161461280857805160409081015190517f38432a2200000000000000000000000000000000000000000000000000000000815267ffffffffffffffff90911660048201526024016107f2565b61289e8186600101805461281b90615540565b80601f016020809104026020016040519081016040528092919081815260200182805461284790615540565b80156128945780601f1061286957610100808354040283529160200191612894565b820191906000526020600020905b81548152906001019060200180831161287757829003601f168201915b505050505061351a565b8383815181106128b0576128b0615275565b602090810291909101015250600101612755565b5060006128db858389606001518a6080015161363c565b905080600003612923576040517f7dd17a7e00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff861660048201526024016107f2565b8551151560005b848110156109b55760008960200151828151811061294a5761294a615275565b60200260200101519050600061296889836000015160600151610bfb565b9050600281600381111561297e5761297e6142c9565b036129d5578151606001516040805167ffffffffffffffff808d16825290921660208301527f3b575419319662b2a6f5e2467d84521517a3382b908eb3d557bb3fdb0c50e23c910160405180910390a15050612e8e565b60008160038111156129e9576129e96142c9565b1480612a0657506003816003811115612a0457612a046142c9565b145b612a57578151606001516040517f25507e7f00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff808c16600483015290911660248201526044016107f2565b8315612b385760045460009074010000000000000000000000000000000000000000900463ffffffff16612a8b87426154a0565b1190508080612aab57506003826003811115612aa957612aa96142c9565b145b612aed576040517fa9cfc86200000000000000000000000000000000000000000000000000000000815267ffffffffffffffff8b1660048201526024016107f2565b8a8481518110612aff57612aff615275565b6020026020010151600014612b32578a8481518110612b2057612b20615275565b60200260200101518360800181815250505b50612b9e565b6000816003811115612b4c57612b4c6142c9565b14612b9e578151606001516040517f3ef2a99c00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff808c16600483015290911660248201526044016107f2565b81516080015167ffffffffffffffff1615801590612bcd57506000816003811115612bcb57612bcb6142c9565b145b15612c925781516080015160208301516040517fe0e03cae0000000000000000000000000000000000000000000000000000000081526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169263e0e03cae92612c44928e929190600401615a15565b6020604051808303816000875af1158015612c63573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c879190615701565b612c92575050612e8e565b60008b604001518481518110612caa57612caa615275565b6020026020010151905080518360a001515114612d0e578251606001516040517f1cfe6d8b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff808d16600483015290911660248201526044016107f2565b612d228a8460000151606001516001613692565b600080612d2f858461373a565b91509150612d468c86600001516060015184613692565b868015612d6457506003826003811115612d6257612d626142c9565b145b8015612d8257506000846003811115612d7f57612d7f6142c9565b14155b15612dbf578451516040517f2b11b8d90000000000000000000000000000000000000000000000000000000081526107f291908390600401615a42565b6003826003811115612dd357612dd36142c9565b14158015612df357506002826003811115612df057612df06142c9565b14155b15612e34578451606001516040517f926c5a3e0000000000000000000000000000000000000000000000000000000081526107f2918e918590600401615a5b565b8451805160609091015160405167ffffffffffffffff918216918f16907f8c324ce1367b83031769f6a813e3bb4c117aba2185789d66b98b791405be6df290612e809087908790615a81565b60405180910390a450505050505b60010161292a565b60408051808201909152600080825260208201526000612eb98760200151613982565b6040517fbbe4f6db0000000000000000000000000000000000000000000000000000000081526001600160a01b0380831660048301529192506000917f0000000000000000000000000000000000000000000000000000000000000000169063bbe4f6db90602401602060405180830381865afa158015612f3e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f629190615aa1565b90506001600160a01b0381161580612faa5750612fa86001600160a01b0382167faff2afbf00000000000000000000000000000000000000000000000000000000612047565b155b15612fec576040517fae9b4ce90000000000000000000000000000000000000000000000000000000081526001600160a01b03821660048201526024016107f2565b6000806130bb633907753760e01b6040518061010001604052808c81526020018a67ffffffffffffffff1681526020018b6001600160a01b031681526020018d606001518152602001876001600160a01b031681526020018d6000015181526020018d6040015181526020018981525060405160240161306c9190615abe565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152600454859063ffffffff600160e01b9091041661138860846139c4565b5091509150816130e0578060405163e1cd550960e01b81526004016107f29190613f10565b80516020146131285780516040517f78ef80240000000000000000000000000000000000000000000000000000000081526020600482015260248101919091526044016107f2565b60008180602001905181019061313e9190615b95565b604080516001600160a01b038c16602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b03167fa9059cbb000000000000000000000000000000000000000000000000000000001790526004549192506131d69187907801000000000000000000000000000000000000000000000000900463ffffffff1661138860846139c4565b509093509150826131fc578160405163e1cd550960e01b81526004016107f29190613f10565b604080518082019091526001600160a01b039095168552602085015250919250505095945050505050565b6000613253827f01ffc9a700000000000000000000000000000000000000000000000000000000613273565b8015610c4b575061326c826001600160e01b0319613273565b1592915050565b604080516001600160e01b03198316602480830191909152825180830390910181526044909101909152602080820180516001600160e01b03167f01ffc9a700000000000000000000000000000000000000000000000000000000178152825160009392849283928392918391908a617530fa92503d915060005190508280156132fe575060208210155b801561330a5750600081115b979650505050505050565b60005b8151811015610eaf5760ff83166000908152600360205260408120835190919084908490811061334a5761334a615275565b6020908102919091018101516001600160a01b03168252810191909152604001600020805461ffff19169055600101613318565b60005b82518160ff161015610a23576000838260ff16815181106133a4576133a4615275565b60200260200101519050600060028111156133c1576133c16142c9565b60ff80871660009081526003602090815260408083206001600160a01b03871684529091529020546101009004166002811115613400576134006142c9565b14613421576004604051631b3fab5160e11b81526004016107f29190615959565b6001600160a01b038116613461576040517fd6c62c9b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405180604001604052808360ff168152602001846002811115613487576134876142c9565b905260ff80871660009081526003602090815260408083206001600160a01b0387168452825290912083518154931660ff198416811782559184015190929091839161ffff1916176101008360028111156134e4576134e46142c9565b021790555090505050806134f790615bae565b9050613381565b60ff8116610ec557600a805467ffffffffffffffff1916905550565b815160208082015160409283015192516000938493613560937f2425b0b9f9054c76ff151b0a175b18f37a4a4e82013a72e9f15c9caa095ed21f93909291889101615bcd565b60408051601f1981840301815290829052805160209182012086518051888401516060808b0151908401516080808d015195015195976135a99794969395929491939101615c00565b604051602081830303815290604052805190602001208560400151805190602001208660a001516040516020016135e09190615cf7565b60408051601f198184030181528282528051602091820120908301969096528101939093526060830191909152608082015260a081019190915260c0015b60405160208183030381529060405280519060200120905092915050565b60008061364a858585613aea565b90506136558161156f565b61366357600091505061368a565b67ffffffffffffffff86166000908152600960209081526040808320938352929052205490505b949350505050565b600060026136a16080856154c9565b67ffffffffffffffff166136b591906154f0565b905060006136c38585611c15565b9050816136d2600160046154a0565b901b1916818360038111156136e9576136e96142c9565b67ffffffffffffffff871660009081526008602052604081209190921b9290921791829161371860808861576e565b67ffffffffffffffff1681526020810191909152604001600020555050505050565b6040517fece670b6000000000000000000000000000000000000000000000000000000008152600090606090309063ece670b69061377e9087908790600401615d57565b600060405180830381600087803b15801561379857600080fd5b505af19250505080156137a9575060015b613966573d8080156137d7576040519150601f19603f3d011682016040523d82523d6000602084013e6137dc565b606091505b5060006137e882615e57565b90507f0a8d6e8c000000000000000000000000000000000000000000000000000000006001600160e01b031982161480613832575063e1cd550960e01b6001600160e01b03198216145b8061384d575063046b337b60e51b6001600160e01b03198216145b8061388157507f78ef8024000000000000000000000000000000000000000000000000000000006001600160e01b03198216145b806138b557507f0c3b563c000000000000000000000000000000000000000000000000000000006001600160e01b03198216145b806138e957507fae9b4ce9000000000000000000000000000000000000000000000000000000006001600160e01b03198216145b8061391d57507f09c25325000000000000000000000000000000000000000000000000000000006001600160e01b03198216145b1561392e575060039250905061397b565b8551516040517f2b11b8d90000000000000000000000000000000000000000000000000000000081526107f291908490600401615a42565b50506040805160208101909152600081526002905b9250929050565b600081516020146139a8578160405163046b337b60e51b81526004016107f29190613f10565b610c4b828060200190518101906139bf9190615b95565b613d89565b6000606060008361ffff1667ffffffffffffffff8111156139e7576139e76140c2565b6040519080825280601f01601f191660200182016040528015613a11576020820181803683370190505b509150863b613a44577f0c3b563c0000000000000000000000000000000000000000000000000000000060005260046000fd5b5a85811015613a77577fafa32a2c0000000000000000000000000000000000000000000000000000000060005260046000fd5b8590036040810481038710613ab0577f37c3be290000000000000000000000000000000000000000000000000000000060005260046000fd5b505a6000808a5160208c0160008c8cf193505a900390503d84811115613ad35750835b808352806000602085013e50955095509592505050565b8251825160009190818303613b2b576040517f11a6b26400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6101018211801590613b3f57506101018111155b613b5c576040516309bde33960e01b815260040160405180910390fd5b60001982820101610100811115613b86576040516309bde33960e01b815260040160405180910390fd5b80600003613bb35786600081518110613ba157613ba1615275565b60200260200101519350505050613d82565b60008167ffffffffffffffff811115613bce57613bce6140c2565b604051908082528060200260200182016040528015613bf7578160200160208202803683370190505b50905060008080805b85811015613d215760006001821b8b811603613c5b5788851015613c44578c5160018601958e918110613c3557613c35615275565b60200260200101519050613c7d565b8551600185019487918110613c3557613c35615275565b8b5160018401938d918110613c7257613c72615275565b602002602001015190505b600089861015613cad578d5160018701968f918110613c9e57613c9e615275565b60200260200101519050613ccf565b8651600186019588918110613cc457613cc4615275565b602002602001015190505b82851115613cf0576040516309bde33960e01b815260040160405180910390fd5b613cfa8282613ddd565b878481518110613d0c57613d0c615275565b60209081029190910101525050600101613c00565b506001850382148015613d3357508683145b8015613d3e57508581145b613d5b576040516309bde33960e01b815260040160405180910390fd5b836001860381518110613d7057613d70615275565b60200260200101519750505050505050505b9392505050565b60006001600160a01b03821180613da1575061040082105b15613dd95760408051602081018490520160408051601f198184030181529082905263046b337b60e51b82526107f291600401613f10565b5090565b6000818310613df557613df08284613dfb565b610c48565b610c4883835b60408051600160208201529081018390526060810182905260009060800161361e565b828054828255906000526020600020908101928215613e80579160200282015b82811115613e80578251825473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03909116178255602090920191600190910190613e3e565b50613dd9929150613eab565b604051806103e00160405280601f906020820280368337509192915050565b5b80821115613dd95760008155600101613eac565b60005b83811015613edb578181015183820152602001613ec3565b50506000910152565b60008151808452613efc816020860160208601613ec0565b601f01601f19169290920160200192915050565b602081526000610c486020830184613ee4565b8060608101831015610c4b57600080fd5b60008083601f840112613f4657600080fd5b50813567ffffffffffffffff811115613f5e57600080fd5b60208301915083602082850101111561397b57600080fd5b60008083601f840112613f8857600080fd5b50813567ffffffffffffffff811115613fa057600080fd5b6020830191508360208260051b850101111561397b57600080fd5b60008060008060008060008060e0898b031215613fd757600080fd5b613fe18a8a613f23565b9750606089013567ffffffffffffffff80821115613ffe57600080fd5b61400a8c838d01613f34565b909950975060808b013591508082111561402357600080fd5b61402f8c838d01613f76565b909750955060a08b013591508082111561404857600080fd5b506140558b828c01613f76565b999c989b50969995989497949560c00135949350505050565b60008060006080848603121561408357600080fd5b61408d8585613f23565b9250606084013567ffffffffffffffff8111156140a957600080fd5b6140b586828701613f34565b9497909650939450505050565b634e487b7160e01b600052604160045260246000fd5b60405160c0810167ffffffffffffffff811182821017156140fb576140fb6140c2565b60405290565b60405160a0810167ffffffffffffffff811182821017156140fb576140fb6140c2565b6040516080810167ffffffffffffffff811182821017156140fb576140fb6140c2565b6040516060810167ffffffffffffffff811182821017156140fb576140fb6140c2565b6040805190810167ffffffffffffffff811182821017156140fb576140fb6140c2565b604051601f8201601f1916810167ffffffffffffffff811182821017156141b6576141b66140c2565b604052919050565b6001600160a01b0381168114610ec557600080fd5b80356141de816141be565b919050565b803563ffffffff811681146141de57600080fd5b600060c0828403121561420957600080fd5b6142116140d8565b823561421c816141be565b815261422a602084016141e3565b602082015261423b604084016141e3565b604082015261424c606084016141e3565b6060820152608083013561425f816141be565b608082015260a0830135614272816141be565b60a08201529392505050565b803567ffffffffffffffff811681146141de57600080fd5b600080604083850312156142a957600080fd5b6142b28361427e565b91506142c06020840161427e565b90509250929050565b634e487b7160e01b600052602160045260246000fd5b600481106142ef576142ef6142c9565b9052565b60208101610c4b82846142df565b600067ffffffffffffffff82111561431b5761431b6140c2565b5060051b60200190565b600060a0828403121561433757600080fd5b61433f614101565b9050813581526143516020830161427e565b60208201526143626040830161427e565b60408201526143736060830161427e565b60608201526143846080830161427e565b608082015292915050565b600067ffffffffffffffff8211156143a9576143a96140c2565b50601f01601f191660200190565b600082601f8301126143c857600080fd5b81356143db6143d68261438f565b61418d565b8181528460208386010111156143f057600080fd5b816020850160208301376000918101602001919091529392505050565b600082601f83011261441e57600080fd5b8135602061442e6143d683614301565b82815260059290921b8401810191818101908684111561444d57600080fd5b8286015b8481101561452357803567ffffffffffffffff808211156144725760008081fd5b8189019150608080601f19848d0301121561448d5760008081fd5b614495614124565b87840135838111156144a75760008081fd5b6144b58d8a838801016143b7565b825250604080850135848111156144cc5760008081fd5b6144da8e8b838901016143b7565b8a84015250606080860135858111156144f35760008081fd5b6145018f8c838a01016143b7565b9284019290925294909201359381019390935250508352918301918301614451565b509695505050505050565b6000610140828403121561454157600080fd5b6145496140d8565b90506145558383614325565b815260a082013567ffffffffffffffff8082111561457257600080fd5b61457e858386016143b7565b602084015260c084013591508082111561459757600080fd5b6145a3858386016143b7565b60408401526145b460e085016141d3565b606084015261010084013560808401526101208401359150808211156145d957600080fd5b506145e68482850161440d565b60a08301525092915050565b600082601f83011261460357600080fd5b813560206146136143d683614301565b82815260059290921b8401810191818101908684111561463257600080fd5b8286015b8481101561452357803567ffffffffffffffff8111156146565760008081fd5b6146648986838b010161452e565b845250918301918301614636565b600082601f83011261468357600080fd5b813560206146936143d683614301565b82815260059290921b840181019181810190868411156146b257600080fd5b8286015b8481101561452357803567ffffffffffffffff8111156146d65760008081fd5b6146e48986838b01016143b7565b8452509183019183016146b6565b600082601f83011261470357600080fd5b813560206147136143d683614301565b82815260059290921b8401810191818101908684111561473257600080fd5b8286015b8481101561452357803567ffffffffffffffff8111156147565760008081fd5b6147648986838b0101614672565b845250918301918301614736565b600082601f83011261478357600080fd5b813560206147936143d683614301565b8083825260208201915060208460051b8701019350868411156147b557600080fd5b602086015b8481101561452357803583529183019183016147ba565b600082601f8301126147e257600080fd5b813560206147f26143d683614301565b82815260059290921b8401810191818101908684111561481157600080fd5b8286015b8481101561452357803567ffffffffffffffff808211156148365760008081fd5b818901915060a080601f19848d030112156148515760008081fd5b614859614101565b61486488850161427e565b81526040808501358481111561487a5760008081fd5b6148888e8b838901016145f2565b8a84015250606080860135858111156148a15760008081fd5b6148af8f8c838a01016146f2565b83850152506080915081860135858111156148ca5760008081fd5b6148d88f8c838a0101614772565b9184019190915250919093013590830152508352918301918301614815565b600080604080848603121561490b57600080fd5b833567ffffffffffffffff8082111561492357600080fd5b61492f878388016147d1565b945060209150818601358181111561494657600080fd5b8601601f8101881361495757600080fd5b80356149656143d682614301565b81815260059190911b8201840190848101908a83111561498457600080fd5b8584015b83811015614a10578035868111156149a05760008081fd5b8501603f81018d136149b25760008081fd5b878101356149c26143d682614301565b81815260059190911b82018a0190898101908f8311156149e25760008081fd5b928b01925b82841015614a005783358252928a0192908a01906149e7565b8652505050918601918601614988565b50809750505050505050509250929050565b600060208284031215614a3457600080fd5b813567ffffffffffffffff811115614a4b57600080fd5b820160a08185031215613d8257600080fd5b8015158114610ec557600080fd5b80356141de81614a5d565b60006020808385031215614a8957600080fd5b823567ffffffffffffffff80821115614aa157600080fd5b818501915085601f830112614ab557600080fd5b8135614ac36143d682614301565b81815260059190911b83018401908481019088831115614ae257600080fd5b8585015b83811015614b7257803585811115614afe5760008081fd5b86016060818c03601f1901811315614b165760008081fd5b614b1e614147565b614b298a840161427e565b8152604080840135614b3a81614a5d565b828c0152918301359188831115614b515760008081fd5b614b5f8e8c858701016143b7565b9082015285525050918601918601614ae6565b5098975050505050505050565b803560ff811681146141de57600080fd5b600060208284031215614ba257600080fd5b610c4882614b7f565b60008151808452602080850194506020840160005b83811015614be55781516001600160a01b031687529582019590820190600101614bc0565b509495945050505050565b60208152600082518051602084015260ff602082015116604084015260ff604082015116606084015260608101511515608084015250602083015160c060a0840152614c3f60e0840182614bab565b90506040840151601f198483030160c0850152614c5c8282614bab565b95945050505050565b60008060408385031215614c7857600080fd5b614c818361427e565b946020939093013593505050565b60008060208385031215614ca257600080fd5b823567ffffffffffffffff80821115614cba57600080fd5b818501915085601f830112614cce57600080fd5b813581811115614cdd57600080fd5b8660208260061b8501011115614cf257600080fd5b60209290920196919550909350505050565b600060208284031215614d1657600080fd5b610c488261427e565b6020815281511515602082015267ffffffffffffffff60208301511660408201526000604083015160608084015261368a6080840182613ee4565b60008060408385031215614d6d57600080fd5b823567ffffffffffffffff80821115614d8557600080fd5b614d918683870161452e565b93506020850135915080821115614da757600080fd5b50614db485828601614672565b9150509250929050565b600060208284031215614dd057600080fd5b8135613d82816141be565b600082601f830112614dec57600080fd5b81356020614dfc6143d683614301565b8083825260208201915060208460051b870101935086841115614e1e57600080fd5b602086015b84811015614523578035614e36816141be565b8352918301918301614e23565b60006020808385031215614e5657600080fd5b823567ffffffffffffffff80821115614e6e57600080fd5b818501915085601f830112614e8257600080fd5b8135614e906143d682614301565b81815260059190911b83018401908481019088831115614eaf57600080fd5b8585015b83811015614b7257803585811115614eca57600080fd5b860160c0818c03601f19011215614ee15760008081fd5b614ee96140d8565b8882013581526040614efc818401614b7f565b8a8301526060614f0d818501614b7f565b8284015260809150614f20828501614a6b565b9083015260a08381013589811115614f385760008081fd5b614f468f8d83880101614ddb565b838501525060c0840135915088821115614f605760008081fd5b614f6e8e8c84870101614ddb565b9083015250845250918601918601614eb3565b600060208284031215614f9357600080fd5b5035919050565b80356001600160e01b03811681146141de57600080fd5b600082601f830112614fc257600080fd5b81356020614fd26143d683614301565b82815260069290921b84018101918181019086841115614ff157600080fd5b8286015b84811015614523576040818903121561500e5760008081fd5b61501661416a565b61501f8261427e565b815261502c858301614f9a565b81860152835291830191604001614ff5565b600082601f83011261504f57600080fd5b8135602061505f6143d683614301565b82815260079290921b8401810191818101908684111561507e57600080fd5b8286015b8481101561452357808803608081121561509c5760008081fd5b6150a4614147565b6150ad8361427e565b8152604080601f19840112156150c35760008081fd5b6150cb61416a565b92506150d887850161427e565b83526150e581850161427e565b8388015281870192909252606083013591810191909152835291830191608001615082565b6000602080838503121561511d57600080fd5b823567ffffffffffffffff8082111561513557600080fd5b8185019150604080838803121561514b57600080fd5b61515361416a565b83358381111561516257600080fd5b84016040818a03121561517457600080fd5b61517c61416a565b81358581111561518b57600080fd5b8201601f81018b1361519c57600080fd5b80356151aa6143d682614301565b81815260069190911b8201890190898101908d8311156151c957600080fd5b928a01925b828410156152195787848f0312156151e65760008081fd5b6151ee61416a565b84356151f9816141be565b8152615206858d01614f9a565b818d0152825292870192908a01906151ce565b84525050508187013593508484111561523157600080fd5b61523d8a858401614fb1565b818801528252508385013591508282111561525757600080fd5b6152638883860161503e565b85820152809550505050505092915050565b634e487b7160e01b600052603260045260246000fd5b805160408084528151848201819052600092602091908201906060870190855b818110156152e257835180516001600160a01b031684528501516001600160e01b03168584015292840192918501916001016152ab565b50508583015187820388850152805180835290840192506000918401905b8083101561533c578351805167ffffffffffffffff1683528501516001600160e01b031685830152928401926001929092019190850190615300565b50979650505050505050565b602081526000610c48602083018461528b565b67ffffffffffffffff8316815260608101613d826020830184805167ffffffffffffffff908116835260209182015116910152565b634e487b7160e01b600052601160045260246000fd5b67ffffffffffffffff8181168382160190808211156153c7576153c7615390565b5092915050565b6000602080835260608451604080848701526153ed606087018361528b565b87850151878203601f19016040890152805180835290860193506000918601905b80831015614b7257845167ffffffffffffffff81511683528781015161544d89850182805167ffffffffffffffff908116835260209182015116910152565b5084015182870152938601936001929092019160809091019061540e565b60006020828403121561547d57600080fd5b813567ffffffffffffffff81111561549457600080fd5b61368a848285016147d1565b81810381811115610c4b57610c4b615390565b634e487b7160e01b600052601260045260246000fd5b600067ffffffffffffffff808416806154e4576154e46154b3565b92169190910692915050565b8082028115828204841417610c4b57610c4b615390565b60006040828403121561551957600080fd5b61552161416a565b61552a8361427e565b8152602083013560208201528091505092915050565b600181811c9082168061555457607f821691505b60208210810361557457634e487b7160e01b600052602260045260246000fd5b50919050565b805182526000602067ffffffffffffffff81840151168185015260408084015160a060408701526155ae60a0870182613ee4565b9050606085015186820360608801526155c78282613ee4565b608087810151898303918a01919091528051808352908601935060009250908501905b8083101561533c57835180516001600160a01b03168352860151868301529285019260019290920191908401906155ea565b602081526000610c48602083018461557a565b608081526000615642608083018761557a565b61ffff9590951660208301525060408101929092526001600160a01b0316606090910152919050565b60008060006060848603121561568057600080fd5b835161568b81614a5d565b602085015190935067ffffffffffffffff8111156156a857600080fd5b8401601f810186136156b957600080fd5b80516156c76143d68261438f565b8181528760208385010111156156dc57600080fd5b6156ed826020830160208601613ec0565b809450505050604084015190509250925092565b60006020828403121561571357600080fd5b8151613d8281614a5d565b80820180821115610c4b57610c4b615390565b60ff8181168382160190811115610c4b57610c4b615390565b8183823760009101908152919050565b828152606082602083013760800192915050565b600067ffffffffffffffff80841680615789576157896154b3565b92169190910492915050565b601f821115610eaf576000816000526020600020601f850160051c810160208610156157be5750805b601f850160051c820191505b81811015612455578281556001016157ca565b815167ffffffffffffffff8111156157f7576157f76140c2565b61580b816158058454615540565b84615795565b602080601f83116001811461584057600084156158285750858301515b600019600386901b1c1916600185901b178555612455565b600085815260208120601f198616915b8281101561586f57888601518255948401946001909101908401615850565b508582101561588d5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60006020808352835460ff81161515602085015267ffffffffffffffff8160081c166040850152506001808501606080860152600081546158dd81615540565b80608089015260a060018316600081146158fe576001811461591a5761594a565b60ff19841660a08b015260a083151560051b8b0101945061594a565b85600052602060002060005b848110156159415781548c8201850152908801908901615926565b8b0160a0019550505b50929998505050505050505050565b602081016005831061596d5761596d6142c9565b91905290565b60ff81811683821602908116908181146153c7576153c7615390565b600060a0820160ff88168352602087602085015260a0604085015281875480845260c086019150886000526020600020935060005b818110156159e95784546001600160a01b0316835260019485019492840192016159c4565b505084810360608601526159fd8188614bab565b935050505060ff831660808301529695505050505050565b600067ffffffffffffffff808616835280851660208401525060606040830152614c5c6060830184613ee4565b82815260406020820152600061368a6040830184613ee4565b67ffffffffffffffff8481168252831660208201526060810161368a60408301846142df565b615a8b81846142df565b60406020820152600061368a6040830184613ee4565b600060208284031215615ab357600080fd5b8151613d82816141be565b6020815260008251610100806020850152615add610120850183613ee4565b91506020850151615afa604086018267ffffffffffffffff169052565b5060408501516001600160a01b038116606086015250606085015160808501526080850151615b3460a08601826001600160a01b03169052565b5060a0850151601f19808685030160c0870152615b518483613ee4565b935060c08701519150808685030160e0870152615b6e8483613ee4565b935060e0870151915080868503018387015250615b8b8382613ee4565b9695505050505050565b600060208284031215615ba757600080fd5b5051919050565b600060ff821660ff8103615bc457615bc4615390565b60010192915050565b848152600067ffffffffffffffff808616602084015280851660408401525060806060830152615b8b6080830184613ee4565b86815260c060208201526000615c1960c0830188613ee4565b6001600160a01b039690961660408301525067ffffffffffffffff9384166060820152608081019290925290911660a09091015292915050565b600082825180855260208086019550808260051b84010181860160005b84811015615cea57601f19868403018952815160808151818652615c9682870182613ee4565b9150508582015185820387870152615cae8282613ee4565b91505060408083015186830382880152615cc88382613ee4565b6060948501519790940196909652505098840198925090830190600101615c70565b5090979650505050505050565b602081526000610c486020830184615c53565b60008282518085526020808601955060208260051b8401016020860160005b84811015615cea57601f19868403018952615d45838351613ee4565b98840198925090830190600101615d29565b604081526000835180516040840152602081015167ffffffffffffffff80821660608601528060408401511660808601528060608401511660a08601528060808401511660c086015250505060208401516101408060e0850152615dbf610180850183613ee4565b915060408601517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08086850301610100870152615dfc8483613ee4565b935060608801519150615e1b6101208701836001600160a01b03169052565b60808801518387015260a0880151925080868503016101608701525050615e428282615c53565b9150508281036020840152614c5c8185615d0a565b6000815160208301516001600160e01b031980821693506004831015615e875780818460040360031b1b83161693505b50505091905056fea164736f6c6343000818000a", } var EVM2EVMMultiOffRampABI = EVM2EVMMultiOffRampMetaData.ABI diff --git a/core/gethwrappers/ccip/generated/evm_2_evm_multi_onramp/evm_2_evm_multi_onramp.go b/core/gethwrappers/ccip/generated/evm_2_evm_multi_onramp/evm_2_evm_multi_onramp.go index fadeefb48b..4fdc2e8136 100644 --- a/core/gethwrappers/ccip/generated/evm_2_evm_multi_onramp/evm_2_evm_multi_onramp.go +++ b/core/gethwrappers/ccip/generated/evm_2_evm_multi_onramp/evm_2_evm_multi_onramp.go @@ -73,6 +73,7 @@ type EVM2EVMMultiOnRampDestChainDynamicConfig struct { GasMultiplierWeiPerEth uint64 NetworkFeeUSDCents uint32 EnforceOutOfOrder bool + ChainFamilySelector [4]byte } type EVM2EVMMultiOnRampDynamicConfig struct { @@ -120,25 +121,35 @@ type EVM2EVMMultiOnRampTokenTransferFeeConfigSingleTokenArgs struct { TokenTransferFeeConfig EVM2EVMMultiOnRampTokenTransferFeeConfig } -type InternalEVM2EVMMessage struct { +type InternalEVM2AnyRampMessage struct { + Header InternalRampMessageHeader + Sender common.Address + Data []byte + Receiver []byte + ExtraArgs []byte + FeeToken common.Address + FeeTokenAmount *big.Int + TokenAmounts []InternalRampTokenAmount +} + +type InternalRampMessageHeader struct { + MessageId [32]byte SourceChainSelector uint64 - Sender common.Address - Receiver common.Address + DestChainSelector uint64 SequenceNumber uint64 - GasLimit *big.Int - Strict bool Nonce uint64 - FeeToken common.Address - FeeTokenAmount *big.Int - Data []byte - TokenAmounts []ClientEVMTokenAmount - SourceTokenData [][]byte - MessageId [32]byte +} + +type InternalRampTokenAmount struct { + SourcePoolAddress []byte + DestTokenAddress []byte + ExtraData []byte + Amount *big.Int } var EVM2EVMMultiOnRampMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"linkToken\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint96\",\"name\":\"maxFeeJuelsPerMsg\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"nonceManager\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAdminRegistry\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.StaticConfig\",\"name\":\"staticConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"priceRegistry\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"messageValidator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"feeAggregator\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.DynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint16\",\"name\":\"maxNumberOfTokensPerMsg\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"maxDataBytes\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPerMsgGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"destGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"destGasPerPayloadByte\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"destDataAvailabilityOverheadGas\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"destGasPerDataAvailabilityByte\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"destDataAvailabilityMultiplierBps\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"defaultTokenFeeUSDCents\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"defaultTokenDestGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"defaultTokenDestBytesOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"defaultTxGasLimit\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"gasMultiplierWeiPerEth\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"networkFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"enforceOutOfOrder\",\"type\":\"bool\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.DestChainDynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"},{\"internalType\":\"address\",\"name\":\"prevOnRamp\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.DestChainConfigArgs[]\",\"name\":\"destChainConfigArgs\",\"type\":\"tuple[]\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"premiumMultiplierWeiPerEth\",\"type\":\"uint64\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.PremiumMultiplierWeiPerEthArgs[]\",\"name\":\"premiumMultiplierWeiPerEthArgs\",\"type\":\"tuple[]\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"minFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"deciBps\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"destGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"destBytesOverhead\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.TokenTransferFeeConfig\",\"name\":\"tokenTransferFeeConfig\",\"type\":\"tuple\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.TokenTransferFeeConfigSingleTokenArgs[]\",\"name\":\"tokenTransferFeeConfigs\",\"type\":\"tuple[]\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.TokenTransferFeeConfigArgs[]\",\"name\":\"tokenTransferFeeConfigArgs\",\"type\":\"tuple[]\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"CannotSendZeroTokens\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"CursedByRMN\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"}],\"name\":\"DestinationChainNotEnabled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ExtraArgOutOfOrderExecutionMustBeTrue\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"GetSupportedTokensFunctionalityRemovedCheckAdminRegistry\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidConfig\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"destBytesOverhead\",\"type\":\"uint32\"}],\"name\":\"InvalidDestBytesOverhead\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"}],\"name\":\"InvalidDestChainConfig\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"encodedAddress\",\"type\":\"bytes\"}],\"name\":\"InvalidEVMAddress\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidExtraArgsTag\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"msgFeeJuels\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxFeeJuelsPerMsg\",\"type\":\"uint256\"}],\"name\":\"MessageFeeTooHigh\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MessageGasLimitTooHigh\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"maxSize\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"actualSize\",\"type\":\"uint256\"}],\"name\":\"MessageTooLarge\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"errorReason\",\"type\":\"bytes\"}],\"name\":\"MessageValidationError\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MustBeCalledByRouter\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"NotAFeeToken\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByOwnerOrAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RouterMustSetOriginalSender\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"SourceTokenDataTooLarge\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnsupportedNumberOfTokens\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"UnsupportedToken\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newAdmin\",\"type\":\"address\"}],\"name\":\"AdminSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"strict\",\"type\":\"bool\"},{\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"feeToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"feeTokenAmount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structClient.EVMTokenAmount[]\",\"name\":\"tokenAmounts\",\"type\":\"tuple[]\"},{\"internalType\":\"bytes[]\",\"name\":\"sourceTokenData\",\"type\":\"bytes[]\"},{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"}],\"indexed\":false,\"internalType\":\"structInternal.EVM2EVMMessage\",\"name\":\"message\",\"type\":\"tuple\"}],\"name\":\"CCIPSendRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"linkToken\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint96\",\"name\":\"maxFeeJuelsPerMsg\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"nonceManager\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAdminRegistry\",\"type\":\"address\"}],\"indexed\":false,\"internalType\":\"structEVM2EVMMultiOnRamp.StaticConfig\",\"name\":\"staticConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"priceRegistry\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"messageValidator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"feeAggregator\",\"type\":\"address\"}],\"indexed\":false,\"internalType\":\"structEVM2EVMMultiOnRamp.DynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"}],\"name\":\"ConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint16\",\"name\":\"maxNumberOfTokensPerMsg\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"maxDataBytes\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPerMsgGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"destGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"destGasPerPayloadByte\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"destDataAvailabilityOverheadGas\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"destGasPerDataAvailabilityByte\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"destDataAvailabilityMultiplierBps\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"defaultTokenFeeUSDCents\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"defaultTokenDestGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"defaultTokenDestBytesOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"defaultTxGasLimit\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"gasMultiplierWeiPerEth\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"networkFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"enforceOutOfOrder\",\"type\":\"bool\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.DestChainDynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"},{\"internalType\":\"address\",\"name\":\"prevOnRamp\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"metadataHash\",\"type\":\"bytes32\"}],\"indexed\":false,\"internalType\":\"structEVM2EVMMultiOnRamp.DestChainConfig\",\"name\":\"destChainConfig\",\"type\":\"tuple\"}],\"name\":\"DestChainAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint16\",\"name\":\"maxNumberOfTokensPerMsg\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"maxDataBytes\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPerMsgGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"destGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"destGasPerPayloadByte\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"destDataAvailabilityOverheadGas\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"destGasPerDataAvailabilityByte\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"destDataAvailabilityMultiplierBps\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"defaultTokenFeeUSDCents\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"defaultTokenDestGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"defaultTokenDestBytesOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"defaultTxGasLimit\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"gasMultiplierWeiPerEth\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"networkFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"enforceOutOfOrder\",\"type\":\"bool\"}],\"indexed\":false,\"internalType\":\"structEVM2EVMMultiOnRamp.DestChainDynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"}],\"name\":\"DestChainDynamicConfigUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"feeToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"feeValueJuels\",\"type\":\"uint256\"}],\"name\":\"FeePaid\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"feeAggregator\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"feeToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"FeeTokenWithdrawn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"premiumMultiplierWeiPerEth\",\"type\":\"uint64\"}],\"name\":\"PremiumMultiplierWeiPerEthUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"TokenTransferFeeConfigDeleted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"minFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"deciBps\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"destGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"destBytesOverhead\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"}],\"indexed\":false,\"internalType\":\"structEVM2EVMMultiOnRamp.TokenTransferFeeConfig\",\"name\":\"tokenTransferFeeConfig\",\"type\":\"tuple\"}],\"name\":\"TokenTransferFeeConfigUpdated\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint16\",\"name\":\"maxNumberOfTokensPerMsg\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"maxDataBytes\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPerMsgGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"destGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"destGasPerPayloadByte\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"destDataAvailabilityOverheadGas\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"destGasPerDataAvailabilityByte\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"destDataAvailabilityMultiplierBps\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"defaultTokenFeeUSDCents\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"defaultTokenDestGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"defaultTokenDestBytesOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"defaultTxGasLimit\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"gasMultiplierWeiPerEth\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"networkFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"enforceOutOfOrder\",\"type\":\"bool\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.DestChainDynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"},{\"internalType\":\"address\",\"name\":\"prevOnRamp\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.DestChainConfigArgs[]\",\"name\":\"destChainConfigArgs\",\"type\":\"tuple[]\"}],\"name\":\"applyDestChainConfigUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"premiumMultiplierWeiPerEth\",\"type\":\"uint64\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.PremiumMultiplierWeiPerEthArgs[]\",\"name\":\"premiumMultiplierWeiPerEthArgs\",\"type\":\"tuple[]\"}],\"name\":\"applyPremiumMultiplierWeiPerEthUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"minFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"deciBps\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"destGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"destBytesOverhead\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.TokenTransferFeeConfig\",\"name\":\"tokenTransferFeeConfig\",\"type\":\"tuple\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.TokenTransferFeeConfigSingleTokenArgs[]\",\"name\":\"tokenTransferFeeConfigs\",\"type\":\"tuple[]\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.TokenTransferFeeConfigArgs[]\",\"name\":\"tokenTransferFeeConfigArgs\",\"type\":\"tuple[]\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.TokenTransferFeeConfigRemoveArgs[]\",\"name\":\"tokensToUseDefaultFeeConfigs\",\"type\":\"tuple[]\"}],\"name\":\"applyTokenTransferFeeConfigUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bytes\",\"name\":\"receiver\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structClient.EVMTokenAmount[]\",\"name\":\"tokenAmounts\",\"type\":\"tuple[]\"},{\"internalType\":\"address\",\"name\":\"feeToken\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"extraArgs\",\"type\":\"bytes\"}],\"internalType\":\"structClient.EVM2AnyMessage\",\"name\":\"message\",\"type\":\"tuple\"},{\"internalType\":\"uint256\",\"name\":\"feeTokenAmount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"originalSender\",\"type\":\"address\"}],\"name\":\"forwardFromRouter\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"}],\"name\":\"getDestChainConfig\",\"outputs\":[{\"components\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint16\",\"name\":\"maxNumberOfTokensPerMsg\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"maxDataBytes\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPerMsgGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"destGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"destGasPerPayloadByte\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"destDataAvailabilityOverheadGas\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"destGasPerDataAvailabilityByte\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"destDataAvailabilityMultiplierBps\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"defaultTokenFeeUSDCents\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"defaultTokenDestGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"defaultTokenDestBytesOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"defaultTxGasLimit\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"gasMultiplierWeiPerEth\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"networkFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"enforceOutOfOrder\",\"type\":\"bool\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.DestChainDynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"},{\"internalType\":\"address\",\"name\":\"prevOnRamp\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"metadataHash\",\"type\":\"bytes32\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.DestChainConfig\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getDynamicConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"priceRegistry\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"messageValidator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"feeAggregator\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.DynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"}],\"name\":\"getExpectedNextSequenceNumber\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bytes\",\"name\":\"receiver\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structClient.EVMTokenAmount[]\",\"name\":\"tokenAmounts\",\"type\":\"tuple[]\"},{\"internalType\":\"address\",\"name\":\"feeToken\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"extraArgs\",\"type\":\"bytes\"}],\"internalType\":\"structClient.EVM2AnyMessage\",\"name\":\"message\",\"type\":\"tuple\"}],\"name\":\"getFee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"feeTokenAmount\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"},{\"internalType\":\"contractIERC20\",\"name\":\"sourceToken\",\"type\":\"address\"}],\"name\":\"getPoolBySourceToken\",\"outputs\":[{\"internalType\":\"contractIPoolV1\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"getPremiumMultiplierWeiPerEth\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"premiumMultiplierWeiPerEth\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getStaticConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"linkToken\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint96\",\"name\":\"maxFeeJuelsPerMsg\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"nonceManager\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAdminRegistry\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.StaticConfig\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"name\":\"getSupportedTokens\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"getTokenTransferFeeConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"minFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"deciBps\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"destGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"destBytesOverhead\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.TokenTransferFeeConfig\",\"name\":\"tokenTransferFeeConfig\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"priceRegistry\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"messageValidator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"feeAggregator\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.DynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"}],\"name\":\"setDynamicConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"withdrawFeeTokens\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - Bin: "0x6101406040523480156200001257600080fd5b5060405162006b4538038062006b458339810160408190526200003591620014af565b33806000816200008c5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000bf57620000bf8162000214565b505085516001600160a01b031615905080620000e6575060208501516001600160401b0316155b80620000fd575060608501516001600160a01b0316155b8062000114575060808501516001600160a01b0316155b806200012b575060a08501516001600160a01b0316155b156200014a576040516306b7c75960e31b815260040160405180910390fd5b84516001600160a01b0390811660a090815260208701516001600160401b031660c05260408701516001600160601b031660809081526060880151831660e0528701518216610100528601511661012052620001a684620002bf565b620001b18362000482565b620001bc8262000a00565b60408051600080825260208201909252620002099183919062000202565b6040805180820190915260008082526020820152815260200190600190039081620001da5790505b5062000acc565b5050505050620018f7565b336001600160a01b038216036200026e5760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000083565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60208101516001600160a01b03161580620002e5575060608101516001600160a01b0316155b1562000304576040516306b7c75960e31b815260040160405180910390fd5b8051600280546001600160a01b039283166001600160a01b0319918216179091556020808401516003805491851691841691909117905560408085015160048054918616918516919091179055606080860151600580549187169190951617909355805160c0808201835260a080518716835290516001600160401b031693820193909352608080516001600160601b03168284015260e05186169482019490945261010051851693810193909352610120519093169082015290517f4012fe74115805c44a121d8f9edc3d234df8f05f53b52864b8e5e8a30384b8aa916200047791849082516001600160a01b0390811682526020808501516001600160401b0316818401526040808601516001600160601b03168185015260608087015184168186015260808088015185169086015260a0968701518416968501969096528451831660c085015290840151821660e084015283015181166101008301529190920151166101208201526101400190565b60405180910390a150565b60005b8151811015620009fc576000828281518110620004a657620004a6620015f5565b602002602001015190506000838381518110620004c757620004c7620015f5565b6020026020010151600001519050806001600160401b031660001480620004fe5750602082015161018001516001600160401b0316155b15620005295760405163c35aa79d60e01b81526001600160401b038216600482015260240162000083565b600060066000836001600160401b03166001600160401b0316815260200190815260200160002090506000836040015190506000604051806080016040528086602001518152602001836001600160a01b031681526020018460020160149054906101000a90046001600160401b03166001600160401b031681526020018460030154815250905080600001518360000160008201518160000160006101000a81548160ff02191690831515021790555060208201518160000160016101000a81548161ffff021916908361ffff16021790555060408201518160000160036101000a81548163ffffffff021916908363ffffffff16021790555060608201518160000160076101000a81548163ffffffff021916908363ffffffff160217905550608082015181600001600b6101000a81548163ffffffff021916908363ffffffff16021790555060a082015181600001600f6101000a81548161ffff021916908361ffff16021790555060c08201518160000160116101000a81548163ffffffff021916908363ffffffff16021790555060e08201518160000160156101000a81548161ffff021916908361ffff1602179055506101008201518160000160176101000a81548161ffff021916908361ffff1602179055506101208201518160000160196101000a81548161ffff021916908361ffff16021790555061014082015181600001601b6101000a81548163ffffffff021916908363ffffffff1602179055506101608201518160010160006101000a81548163ffffffff021916908363ffffffff1602179055506101808201518160010160046101000a8154816001600160401b0302191690836001600160401b031602179055506101a082015181600101600c6101000a8154816001600160401b0302191690836001600160401b031602179055506101c08201518160010160146101000a81548163ffffffff021916908363ffffffff1602179055506101e08201518160010160186101000a81548160ff02191690831515021790555090505082600301546000801b03620009195760c051604080517f8acd72527118c8324937b1a42e02cd246697c3b633f1742f3cae11de233722b360208201526001600160401b0392831691810191909152908516606082015230608082015260a00160408051601f1981840301815291905280516020909101206060820181905260038401556001600160a01b03821615620008d0576002830180546001600160a01b0319166001600160a01b0384161790555b836001600160401b03167f4be92415590c4554edbd7d3fbf308eeb6e5d00ababade0791f835b4a18416ed6846040516200090b91906200160b565b60405180910390a2620009eb565b60028301546001600160a01b03838116911614620009565760405163c35aa79d60e01b81526001600160401b038516600482015260240162000083565b60208560200151610160015163ffffffff161015620009a357602085015161016001516040516312766e0160e11b81526000600482015263ffffffff909116602482015260440162000083565b836001600160401b03167f1173725509a61aed633c58cd12e31c104cd26ca24f6c270c8ac81c5f6f12e8e98660200151604051620009e29190620017b5565b60405180910390a25b505050505080600101905062000485565b5050565b60005b8151811015620009fc57600082828151811062000a245762000a24620015f5565b6020026020010151600001519050600083838151811062000a495762000a49620015f5565b6020908102919091018101518101516001600160a01b03841660008181526007845260409081902080546001600160401b0319166001600160401b0385169081179091559051908152919350917fbb77da6f7210cdd16904228a9360133d1d7dfff99b1bc75f128da5b53e28f97d910160405180910390a2505060010162000a03565b60005b825181101562000d3957600083828151811062000af05762000af0620015f5565b6020026020010151905060008160000151905060005b82602001515181101562000d2a5760008360200151828151811062000b2f5762000b2f620015f5565b602002602001015160200151905060008460200151838151811062000b585762000b58620015f5565b60200260200101516000015190506020826080015163ffffffff16101562000bb15760808201516040516312766e0160e11b81526001600160a01b038316600482015263ffffffff909116602482015260440162000083565b6001600160401b03841660008181526008602090815260408083206001600160a01b0386168085529083529281902086518154938801518389015160608a015160808b015160a08c01511515600160901b0260ff60901b1963ffffffff928316600160701b021664ffffffffff60701b199383166a01000000000000000000000263ffffffff60501b1961ffff90961668010000000000000000029590951665ffffffffffff60401b19968416640100000000026001600160401b0319909b16939097169290921798909817939093169390931717919091161792909217909155519091907f94967ae9ea7729ad4f54021c1981765d2b1d954f7c92fbec340aa0a54f46b8b59062000d17908690600060c08201905063ffffffff80845116835280602085015116602084015261ffff60408501511660408401528060608501511660608401528060808501511660808401525060a0830151151560a083015292915050565b60405180910390a3505060010162000b06565b50505080600101905062000acf565b5060005b815181101562000dff57600082828151811062000d5e5762000d5e620015f5565b6020026020010151600001519050600083838151811062000d835762000d83620015f5565b6020908102919091018101518101516001600160401b03841660008181526008845260408082206001600160a01b038516808452955280822080546001600160981b03191690555192945090917f4de5b1bcbca6018c11303a2c3f4a4b4f22a1c741d8c4ba430d246ac06c5ddf8b9190a3505060010162000d3d565b505050565b634e487b7160e01b600052604160045260246000fd5b604051606081016001600160401b038111828210171562000e3f5762000e3f62000e04565b60405290565b60405161020081016001600160401b038111828210171562000e3f5762000e3f62000e04565b604080519081016001600160401b038111828210171562000e3f5762000e3f62000e04565b60405160c081016001600160401b038111828210171562000e3f5762000e3f62000e04565b604051601f8201601f191681016001600160401b038111828210171562000ee05762000ee062000e04565b604052919050565b80516001600160a01b038116811462000f0057600080fd5b919050565b80516001600160401b038116811462000f0057600080fd5b60006080828403121562000f3057600080fd5b604051608081016001600160401b038111828210171562000f555762000f5562000e04565b60405290508062000f668362000ee8565b815262000f766020840162000ee8565b602082015262000f896040840162000ee8565b604082015262000f9c6060840162000ee8565b60608201525092915050565b60006001600160401b0382111562000fc45762000fc462000e04565b5060051b60200190565b8051801515811462000f0057600080fd5b805161ffff8116811462000f0057600080fd5b805163ffffffff8116811462000f0057600080fd5b600082601f8301126200101957600080fd5b81516020620010326200102c8362000fa8565b62000eb5565b82815261024092830285018201928282019190878511156200105357600080fd5b8387015b85811015620012125780890382811215620010725760008081fd5b6200107c62000e1a565b620010878362000f05565b815261020080601f19840112156200109f5760008081fd5b620010a962000e45565b9250620010b888850162000fce565b83526040620010c981860162000fdf565b898501526060620010dc81870162000ff2565b828601526080620010ef81880162000ff2565b8287015260a091506200110482880162000ff2565b9086015260c06200111787820162000fdf565b8287015260e091506200112c82880162000ff2565b908601526101006200114087820162000fdf565b8287015261012091506200115682880162000fdf565b908601526101406200116a87820162000fdf565b8287015261016091506200118082880162000ff2565b908601526101806200119487820162000ff2565b828701526101a09150620011aa82880162000f05565b908601526101c0620011be87820162000f05565b828701526101e09150620011d482880162000ff2565b90860152620011e586840162000fce565b81860152508389840152620011fe610220860162000ee8565b908301525085525092840192810162001057565b5090979650505050505050565b600082601f8301126200123157600080fd5b81516020620012446200102c8362000fa8565b82815260069290921b840181019181810190868411156200126457600080fd5b8286015b84811015620012ba5760408189031215620012835760008081fd5b6200128d62000e6b565b620012988262000ee8565b8152620012a785830162000f05565b8186015283529183019160400162001268565b509695505050505050565b600082601f830112620012d757600080fd5b81516020620012ea6200102c8362000fa8565b82815260059290921b840181019181810190868411156200130a57600080fd5b8286015b84811015620012ba5780516001600160401b03808211156200132f57600080fd5b908801906040601f19838c0381018213156200134a57600080fd5b6200135462000e6b565b6200136189860162000f05565b815282850151848111156200137557600080fd5b8086019550508c603f8601126200138b57600080fd5b888501519350620013a06200102c8562000fa8565b84815260e09094028501830193898101908e861115620013bf57600080fd5b958401955b858710156200149857868f0360e0811215620013df57600080fd5b620013e962000e6b565b620013f48962000ee8565b815260c086830112156200140757600080fd5b6200141162000e90565b9150620014208d8a0162000ff2565b82526200142f878a0162000ff2565b8d8301526200144160608a0162000fdf565b878301526200145360808a0162000ff2565b60608301526200146660a08a0162000ff2565b60808301526200147960c08a0162000fce565b60a0830152808d0191909152825260e09690960195908a0190620013c4565b828b0152508752505050928401925083016200130e565b60008060008060008587036101a0811215620014ca57600080fd5b60c0811215620014d957600080fd5b50620014e462000e90565b620014ef8762000ee8565b8152620014ff6020880162000f05565b602082015260408701516001600160601b03811681146200151f57600080fd5b6040820152620015326060880162000ee8565b6060820152620015456080880162000ee8565b60808201526200155860a0880162000ee8565b60a082015294506200156e8760c0880162000f1d565b6101408701519094506001600160401b03808211156200158d57600080fd5b6200159b89838a0162001007565b9450610160880151915080821115620015b357600080fd5b620015c189838a016200121f565b9350610180880151915080821115620015d957600080fd5b50620015e888828901620012c5565b9150509295509295909350565b634e487b7160e01b600052603260045260246000fd5b815460ff81161515825261026082019061ffff600882901c8116602085015263ffffffff601883901c811660408601526200165360608601828560381c1663ffffffff169052565b6200166b60808601828560581c1663ffffffff169052565b6200168160a08601838560781c1661ffff169052565b6200169960c08601828560881c1663ffffffff169052565b620016af60e08601838560a81c1661ffff169052565b620016c66101008601838560b81c1661ffff169052565b620016dd6101208601838560c81c1661ffff169052565b620016f66101408601828560d81c1663ffffffff169052565b600186015463ffffffff8282161661016087015292506001600160401b03602084901c811661018087015291506200173f6101a08601838560601c166001600160401b03169052565b620017586101c08601828560a01c1663ffffffff169052565b506200176f6101e0850160ff8460c01c1615159052565b60028501546001600160a01b0381166102008601529150620017a26102208501828460a01c166001600160401b03169052565b5050600383015461024083015292915050565b81511515815261020081016020830151620017d6602084018261ffff169052565b506040830151620017ef604084018263ffffffff169052565b50606083015162001808606084018263ffffffff169052565b50608083015162001821608084018263ffffffff169052565b5060a08301516200183860a084018261ffff169052565b5060c08301516200185160c084018263ffffffff169052565b5060e08301516200186860e084018261ffff169052565b506101008381015161ffff9081169184019190915261012080850151909116908301526101408084015163ffffffff9081169184019190915261016080850151821690840152610180808501516001600160401b03908116918501919091526101a080860151909116908401526101c080850151909116908301526101e0928301511515929091019190915290565b60805160a05160c05160e0516101005161012051615195620019b0600039600081816102da01528181610f260152612adf0152600081816102ab01528181612ab7015261323e01526000818161027c01528181612a8d0152612bf30152600081816102180152818161265001528181612a2801526130f90152600081816101e901528181612a0301528181612eda0152612f8401526000818161024801528181612a5a0152818161304601526130b601526151956000f3fe608060405234801561001057600080fd5b50600436106101515760003560e01c806379ba5097116100cd578063a69c64c011610081578063df0aa9e911610066578063df0aa9e9146108c0578063f2fde38b146108d3578063fbca3b74146108e657600080fd5b8063a69c64c01461089a578063a6f3ab6c146108ad57600080fd5b80638da5cb5b116100b25780638da5cb5b146108635780639041be3d14610874578063932e53c81461088757600080fd5b806379ba5097146106f857806382b49eb01461070057600080fd5b80633a019940116101245780636def4ce7116101095780636def4ce7146103b65780637437ff9f14610672578063770e2dc4146106e557600080fd5b80633a0199401461038157806348a98aa41461038b57600080fd5b8063061877e31461015657806306285c69146101a7578063181f5a771461031757806320487ded14610360575b600080fd5b610189610164366004613945565b6001600160a01b031660009081526007602052604090205467ffffffffffffffff1690565b60405167ffffffffffffffff90911681526020015b60405180910390f35b61030a6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a08101919091526040518060c001604052807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff1681526020017f00000000000000000000000000000000000000000000000000000000000000006bffffffffffffffffffffffff1681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316815250905090565b60405161019e9190613962565b6103536040518060400160405280601c81526020017f45564d3245564d4d756c74694f6e52616d7020312e362e302d6465760000000081525081565b60405161019e9190613a1e565b61037361036e366004613a6a565b610906565b60405190815260200161019e565b610389610d27565b005b61039e610399366004613aba565b610eeb565b6040516001600160a01b03909116815260200161019e565b6106656103c4366004613af3565b604080516102808101825260006080820181815260a0830182905260c0830182905260e08301829052610100830182905261012083018290526101408301829052610160830182905261018083018290526101a083018290526101c083018290526101e08301829052610200830182905261022083018290526102408301829052610260830182905282526020820181905291810182905260608101919091525067ffffffffffffffff908116600090815260066020908152604091829020825161028081018452815460ff80821615156080840190815261ffff610100808504821660a087015263ffffffff63010000008604811660c08801526701000000000000008604811660e08801526b01000000000000000000000086048116918701919091526f01000000000000000000000000000000850482166101208701527101000000000000000000000000000000000085048116610140870152750100000000000000000000000000000000000000000085048216610160870152770100000000000000000000000000000000000000000000008504821661018087015279010000000000000000000000000000000000000000000000000085049091166101a08601527b0100000000000000000000000000000000000000000000000000000090930483166101c085015260018501548084166101e0860152640100000000810489166102008601526c010000000000000000000000008104891661022086015274010000000000000000000000000000000000000000808204909416610240860152780100000000000000000000000000000000000000000000000090049091161515610260840152825260028301546001600160a01b0381169483019490945290920490931691810191909152600390910154606082015290565b60405161019e9190613c41565b6106d860408051608081018252600080825260208201819052918101829052606081019190915250604080516080810182526002546001600160a01b03908116825260035481166020830152600454811692820192909252600554909116606082015290565b60405161019e9190613c8e565b6103896106f3366004613ec1565b610f9a565b610389610fb0565b61080361070e366004613aba565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a08101919091525067ffffffffffffffff9190911660009081526008602090815260408083206001600160a01b0394909416835292815290829020825160c081018452905463ffffffff8082168352640100000000820481169383019390935268010000000000000000810461ffff16938201939093526a01000000000000000000008304821660608201526e01000000000000000000000000000083049091166080820152720100000000000000000000000000000000000090910460ff16151560a082015290565b60405161019e9190600060c08201905063ffffffff80845116835280602085015116602084015261ffff60408501511660408401528060608501511660608401528060808501511660808401525060a0830151151560a083015292915050565b6000546001600160a01b031661039e565b610189610882366004613af3565b611079565b61038961089536600461413b565b6110bd565b6103896108a8366004614348565b6110d1565b6103896108bb366004614406565b6110e2565b6103736108ce36600461448b565b6110f3565b6103896108e1366004613945565b61157b565b6108f96108f4366004613af3565b61158c565b60405161019e91906144f7565b67ffffffffffffffff82166000908152600660205260408120805460ff1661096b576040517f99ac52f200000000000000000000000000000000000000000000000000000000815267ffffffffffffffff851660048201526024015b60405180910390fd5b600061099961097d6080860186614544565b6001850154640100000000900467ffffffffffffffff166115c0565b90506109ca856109ac6020870187614544565b84519091506109be6040890189614592565b9050856020015161172d565b60006007816109df6080880160608901613945565b6001600160a01b03168152602081019190915260400160009081205467ffffffffffffffff169150819003610a5c57610a1e6080860160608701613945565b6040517fa7499d200000000000000000000000000000000000000000000000000000000081526001600160a01b039091166004820152602401610962565b60035460009081906001600160a01b031663ffdb4b37610a8260808a0160608b01613945565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526001600160a01b03909116600482015267ffffffffffffffff8b1660248201526044016040805180830381865afa158015610aed573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b119190614608565b90925090506000808080610b2860408c018c614592565b90501115610b6357610b578b610b4460808d0160608e01613945565b87610b5260408f018f614592565b6118a3565b91945092509050610b9a565b6001880154610b979074010000000000000000000000000000000000000000900463ffffffff16662386f26fc10000614661565b92505b875460009077010000000000000000000000000000000000000000000000900461ffff1615610c0657610c038c6dffffffffffffffffffffffffffff607088901c16610be960208f018f614544565b90508e8060400190610bfb9190614592565b905086611c94565b90505b600089600101600c9054906101000a900467ffffffffffffffff1667ffffffffffffffff168463ffffffff168b600001600f9054906101000a900461ffff1661ffff168e8060200190610c599190614544565b610c64929150614661565b8c548c51610c87916b010000000000000000000000900463ffffffff1690614678565b610c919190614678565b610c9b9190614678565b610cb5906dffffffffffffffffffffffffffff8916614661565b610cbf9190614661565b90507bffffffffffffffffffffffffffffffffffffffffffffffffffffffff87168282610cf667ffffffffffffffff8c1689614661565b610d009190614678565b610d0a9190614678565b610d14919061468b565b9a50505050505050505050505b92915050565b600354604080517fcdc73d5100000000000000000000000000000000000000000000000000000000815290516000926001600160a01b03169163cdc73d5191600480830192869291908290030181865afa158015610d89573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610db191908101906146c6565b6005549091506001600160a01b031660005b8251811015610ee6576000838281518110610de057610de0614755565b60209081029190910101516040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529091506000906001600160a01b038316906370a0823190602401602060405180830381865afa158015610e4e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e729190614784565b90508015610edc57610e8e6001600160a01b0383168583611d9a565b816001600160a01b0316846001600160a01b03167f508d7d183612c18fc339b42618912b9fa3239f631dd7ec0671f950200a0fa66e83604051610ed391815260200190565b60405180910390a35b5050600101610dc3565b505050565b6040517fbbe4f6db0000000000000000000000000000000000000000000000000000000081526001600160a01b0382811660048301526000917f00000000000000000000000000000000000000000000000000000000000000009091169063bbe4f6db90602401602060405180830381865afa158015610f6f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f93919061479d565b9392505050565b610fa2611e1a565b610fac8282611e76565b5050565b6001546001600160a01b0316331461100a5760405162461bcd60e51b815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e6572000000000000000000006044820152606401610962565b60008054337fffffffffffffffffffffffff0000000000000000000000000000000000000000808316821784556001805490911690556040516001600160a01b0390921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b67ffffffffffffffff8082166000908152600660205260408120600201549091610d21917401000000000000000000000000000000000000000090041660016147ba565b6110c5611e1a565b6110ce81612256565b50565b6110d9611e1a565b6110ce81612849565b6110ea611e1a565b6110ce81612926565b67ffffffffffffffff841660009081526006602052604081208161111a8288888888612b3f565b905060005b8161014001515181101561151157600061113c6040890189614592565b8381811061114c5761114c614755565b90506040020180360381019061116291906147e2565b905080602001516000036111a2576040517f5cf0444900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006111b28a8360000151610eeb565b90506001600160a01b038116158061126857506040517f01ffc9a70000000000000000000000000000000000000000000000000000000081527faff2afbf0000000000000000000000000000000000000000000000000000000060048201526001600160a01b038216906301ffc9a790602401602060405180830381865afa158015611242573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611266919061481c565b155b156112ad5781516040517fbf16aab60000000000000000000000000000000000000000000000000000000081526001600160a01b039091166004820152602401610962565b6000816001600160a01b0316639a4575b96040518060a001604052808d80600001906112d99190614544565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050509082525067ffffffffffffffff8f166020808301919091526001600160a01b03808e16604080850191909152918901516060840152885116608090920191909152517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526113849190600401614839565b6000604051808303816000875af11580156113a3573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526113cb9190810190614906565b9050602081602001515111801561142c575067ffffffffffffffff8b16600090815260086020908152604080832086516001600160a01b0316845282529091205490820151516e01000000000000000000000000000090910463ffffffff16105b156114715782516040517f36f536ca0000000000000000000000000000000000000000000000000000000081526001600160a01b039091166004820152602401610962565b805161147c906133f7565b5060408051606081019091526001600160a01b03831660808201528060a0810160405160208183030381529060405281526020018260000151815260200182602001518152506040516020016114d29190614997565b60405160208183030381529060405285610160015185815181106114f8576114f8614755565b602002602001018190525050505080600101905061111f565b50611520818360030154613452565b61018082015260405167ffffffffffffffff8816907fc79f9c3e610deac14de4e704195fe17eab0983ee9916866bc04d16a00f54daa690611562908490614aa2565b60405180910390a261018001519150505b949350505050565b611583611e1a565b6110ce816135ad565b60606040517f9e7177c800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805180820190915260008082526020820152600083900361160157506040805180820190915267ffffffffffffffff8216815260006020820152610f93565b600061160d8486614bd7565b90507fe7e230f0000000000000000000000000000000000000000000000000000000007fffffffff0000000000000000000000000000000000000000000000000000000082160161167a576116658460048188614c1f565b8101906116729190614c49565b915050610f93565b7f6859a837000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008216016116fb5760408051808201909152806116db866004818a614c1f565b8101906116e89190614c81565b815260006020909101529150610f939050565b6040517f5247fdce00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff8516600090815260066020526040902080546301000000900463ffffffff168511156117a65780546040517f86933789000000000000000000000000000000000000000000000000000000008152630100000090910463ffffffff16600482015260248101869052604401610962565b8054670100000000000000900463ffffffff168411156117f2576040517f4c4fc93a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8054610100900461ffff16831115611836576040517f4c056b6a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60018101547801000000000000000000000000000000000000000000000000900460ff168015611864575081155b1561189b576040517fee433e9900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050505050565b6000808083815b81811015611c875760008787838181106118c6576118c6614755565b9050604002018036038101906118dc91906147e2565b905060006001600160a01b03166118f78c8360000151610eeb565b6001600160a01b0316036119455780516040517fbf16aab60000000000000000000000000000000000000000000000000000000081526001600160a01b039091166004820152602401610962565b67ffffffffffffffff8b16600090815260086020908152604080832084516001600160a01b03168452825291829020825160c081018452905463ffffffff8082168352640100000000820481169383019390935268010000000000000000810461ffff16938201939093526a01000000000000000000008304821660608201526e01000000000000000000000000000083049091166080820152720100000000000000000000000000000000000090910460ff16151560a08201819052611ab35767ffffffffffffffff8c1660009081526006602052604090208054611a5390790100000000000000000000000000000000000000000000000000900461ffff16662386f26fc10000614661565b611a5d9089614678565b8154909850611a91907b01000000000000000000000000000000000000000000000000000000900463ffffffff1688614c9a565b6001820154909750611aa99063ffffffff1687614c9a565b9550505050611c7f565b604081015160009061ffff1615611bcf5760008c6001600160a01b031684600001516001600160a01b031614611b725760035484516040517f4ab35b0b0000000000000000000000000000000000000000000000000000000081526001600160a01b039182166004820152911690634ab35b0b90602401602060405180830381865afa158015611b47573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b6b9190614cb7565b9050611b75565b508a5b620186a0836040015161ffff16611bb78660200151847bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1661366e90919063ffffffff16565b611bc19190614661565b611bcb919061468b565b9150505b6060820151611bde9088614c9a565b9650816080015186611bf09190614c9a565b8251909650600090611c0f9063ffffffff16662386f26fc10000614661565b905080821015611c2e57611c23818a614678565b985050505050611c7f565b6000836020015163ffffffff16662386f26fc10000611c4d9190614661565b905080831115611c6d57611c61818b614678565b99505050505050611c7f565b611c77838b614678565b995050505050505b6001016118aa565b5050955095509592505050565b60008063ffffffff8316611ca9608086614661565b611cb587610220614678565b611cbf9190614678565b611cc99190614678565b67ffffffffffffffff8816600090815260066020526040812080549293509171010000000000000000000000000000000000810463ffffffff1690611d2b907501000000000000000000000000000000000000000000900461ffff1685614661565b611d359190614678565b825490915077010000000000000000000000000000000000000000000000900461ffff16611d736dffffffffffffffffffffffffffff8a1683614661565b611d7d9190614661565b611d8d90655af3107a4000614661565b9998505050505050505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb00000000000000000000000000000000000000000000000000000000179052610ee69084906136ab565b6000546001600160a01b03163314611e745760405162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610962565b565b60005b825181101561217f576000838281518110611e9657611e96614755565b6020026020010151905060008160000151905060005b82602001515181101561217157600083602001518281518110611ed157611ed1614755565b6020026020010151602001519050600084602001518381518110611ef757611ef7614755565b60200260200101516000015190506020826080015163ffffffff161015611f675760808201516040517f24ecdc020000000000000000000000000000000000000000000000000000000081526001600160a01b038316600482015263ffffffff9091166024820152604401610962565b67ffffffffffffffff841660008181526008602090815260408083206001600160a01b0386168085529083529281902086518154938801518389015160608a015160808b015160a08c015115157201000000000000000000000000000000000000027fffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffff63ffffffff9283166e01000000000000000000000000000002167fffffffffffffffffffffffffff0000000000ffffffffffffffffffffffffffff9383166a0100000000000000000000027fffffffffffffffffffffffffffffffffffff00000000ffffffffffffffffffff61ffff9096166801000000000000000002959095167fffffffffffffffffffffffffffffffffffff000000000000ffffffffffffffff968416640100000000027fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000909b16939097169290921798909817939093169390931717919091161792909217909155519091907f94967ae9ea7729ad4f54021c1981765d2b1d954f7c92fbec340aa0a54f46b8b59061215f908690600060c08201905063ffffffff80845116835280602085015116602084015261ffff60408501511660408401528060608501511660608401528060808501511660808401525060a0830151151560a083015292915050565b60405180910390a35050600101611eac565b505050806001019050611e79565b5060005b8151811015610ee65760008282815181106121a0576121a0614755565b602002602001015160000151905060008383815181106121c2576121c2614755565b60209081029190910181015181015167ffffffffffffffff841660008181526008845260408082206001600160a01b038516808452955280822080547fffffffffffffffffffffffffff000000000000000000000000000000000000001690555192945090917f4de5b1bcbca6018c11303a2c3f4a4b4f22a1c741d8c4ba430d246ac06c5ddf8b9190a35050600101612183565b60005b8151811015610fac57600082828151811061227657612276614755565b60200260200101519050600083838151811061229457612294614755565b60200260200101516000015190508067ffffffffffffffff16600014806122cc57506020820151610180015167ffffffffffffffff16155b1561230f576040517fc35aa79d00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff82166004820152602401610962565b6000600660008367ffffffffffffffff1667ffffffffffffffff16815260200190815260200160002090506000836040015190506000604051806080016040528086602001518152602001836001600160a01b031681526020018460020160149054906101000a900467ffffffffffffffff1667ffffffffffffffff1681526020018460030154815250905080600001518360000160008201518160000160006101000a81548160ff02191690831515021790555060208201518160000160016101000a81548161ffff021916908361ffff16021790555060408201518160000160036101000a81548163ffffffff021916908363ffffffff16021790555060608201518160000160076101000a81548163ffffffff021916908363ffffffff160217905550608082015181600001600b6101000a81548163ffffffff021916908363ffffffff16021790555060a082015181600001600f6101000a81548161ffff021916908361ffff16021790555060c08201518160000160116101000a81548163ffffffff021916908363ffffffff16021790555060e08201518160000160156101000a81548161ffff021916908361ffff1602179055506101008201518160000160176101000a81548161ffff021916908361ffff1602179055506101208201518160000160196101000a81548161ffff021916908361ffff16021790555061014082015181600001601b6101000a81548163ffffffff021916908363ffffffff1602179055506101608201518160010160006101000a81548163ffffffff021916908363ffffffff1602179055506101808201518160010160046101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055506101a082015181600101600c6101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055506101c08201518160010160146101000a81548163ffffffff021916908363ffffffff1602179055506101e08201518160010160186101000a81548160ff02191690831515021790555090505082600301546000801b0361273957604080517f8acd72527118c8324937b1a42e02cd246697c3b633f1742f3cae11de233722b3602082015267ffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000811692820192909252908516606082015230608082015260a00160408051601f1981840301815291905280516020909101206060820181905260038401556001600160a01b038216156126f2576002830180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0384161790555b8367ffffffffffffffff167f4be92415590c4554edbd7d3fbf308eeb6e5d00ababade0791f835b4a18416ed68460405161272c9190614cd2565b60405180910390a2612839565b60028301546001600160a01b0383811691161461278e576040517fc35aa79d00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff85166004820152602401610962565b60208560200151610160015163ffffffff1610156127f257602085015161016001516040517f24ecdc020000000000000000000000000000000000000000000000000000000081526000600482015263ffffffff9091166024820152604401610962565b8367ffffffffffffffff167f1173725509a61aed633c58cd12e31c104cd26ca24f6c270c8ac81c5f6f12e8e986602001516040516128309190614e73565b60405180910390a25b5050505050806001019050612259565b60005b8151811015610fac57600082828151811061286957612869614755565b6020026020010151600001519050600083838151811061288b5761288b614755565b6020908102919091018101518101516001600160a01b03841660008181526007845260409081902080547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001667ffffffffffffffff85169081179091559051908152919350917fbb77da6f7210cdd16904228a9360133d1d7dfff99b1bc75f128da5b53e28f97d910160405180910390a2505060010161284c565b60208101516001600160a01b0316158061294b575060608101516001600160a01b0316155b15612982576040517f35be3ac800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8051600280547fffffffffffffffffffffffff00000000000000000000000000000000000000009081166001600160a01b03938416179091556020808401516003805484169185169190911790556040808501516004805485169186169190911790556060808601516005805490951690861617909355805160c0810182527f0000000000000000000000000000000000000000000000000000000000000000851681527f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff16928101929092527f00000000000000000000000000000000000000000000000000000000000000006bffffffffffffffffffffffff16828201527f00000000000000000000000000000000000000000000000000000000000000008416928201929092527f0000000000000000000000000000000000000000000000000000000000000000831660808201527f000000000000000000000000000000000000000000000000000000000000000090921660a0830152517f4012fe74115805c44a121d8f9edc3d234df8f05f53b52864b8e5e8a30384b8aa91612b34918490614e82565b60405180910390a150565b604080516101a08101825260008082526020820181905291810182905260608082018390526080820183905260a0820183905260c0820183905260e082018390526101008201839052610120820181905261014082018190526101608201526101808101919091526040517f2cbc26bb000000000000000000000000000000000000000000000000000000008152608086901b77ffffffffffffffff000000000000000000000000000000001660048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632cbc26bb90602401602060405180830381865afa158015612c42573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c66919061481c565b15612ca9576040517ffdbd6a7200000000000000000000000000000000000000000000000000000000815267ffffffffffffffff86166004820152602401610962565b6001600160a01b038216612ce9576040517fa4ec747900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002546001600160a01b03163314612d2d576040517f1c0a352900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b855460ff16612d74576040517f99ac52f200000000000000000000000000000000000000000000000000000000815267ffffffffffffffff86166004820152602401610962565b6000612da2612d866080870187614544565b60018a0154640100000000900467ffffffffffffffff166115c0565b90506000612db36040870187614592565b9150612dda905087612dc86020890189614544565b9050846000015184866020015161172d565b8015612ece576004546001600160a01b03168015612ecc576040517fe0a0e5060000000000000000000000000000000000000000000000000000000081526001600160a01b0382169063e0a0e50690612e39908b908b90600401614fe4565b600060405180830381600087803b158015612e5357600080fd5b505af1925050508015612e64575060015b612ecc573d808015612e92576040519150601f19603f3d011682016040523d82523d6000602084013e612e97565b606091505b50806040517f09c253250000000000000000000000000000000000000000000000000000000081526004016109629190613a1e565b505b60006001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016612f0a6080890160608a01613945565b6001600160a01b031603612f1f575084612ff2565b6003546001600160a01b03166241e5be612f3f60808a0160608b01613945565b60405160e083901b7fffffffff000000000000000000000000000000000000000000000000000000001681526001600160a01b039182166004820152602481018a90527f00000000000000000000000000000000000000000000000000000000000000009091166044820152606401602060405180830381865afa158015612fcb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fef9190614784565b90505b6130026080880160608901613945565b6001600160a01b03167f075a2720282fdf622141dae0b048ef90a21a7e57c134c76912d19d006b3b3f6f8260405161303c91815260200190565b60405180910390a27f00000000000000000000000000000000000000000000000000000000000000006bffffffffffffffffffffffff168111156130e3576040517f6a92a483000000000000000000000000000000000000000000000000000000008152600481018290526bffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000166024820152604401610962565b604080516101a08101825267ffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001681526001600160a01b038716602082015290810161317461313a8a80614544565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506133f792505050565b6001600160a01b031681526020018a600201601481819054906101000a900467ffffffffffffffff166131a690615102565b91906101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905567ffffffffffffffff1681526020018460000151815260200160001515815260200184602001516132b0576040517fea458c0c00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff8b1660048201526001600160a01b0388811660248301527f0000000000000000000000000000000000000000000000000000000000000000169063ea458c0c906044016020604051808303816000875af1158015613287573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132ab9190615129565b6132b3565b60005b67ffffffffffffffff1681526020016132d260808a0160608b01613945565b6001600160a01b031681526020018781526020018880602001906132f69190614544565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050509082525060200161333d60408a018a614592565b808060200260200160405190810160405280939291908181526020016000905b828210156133895761337a604083028601368190038101906147e2565b8152602001906001019061335d565b505050505081526020018367ffffffffffffffff8111156133ac576133ac613cca565b6040519080825280602002602001820160405280156133df57816020015b60608152602001906001900390816133ca5790505b50815260006020909101529998505050505050505050565b6000815160201461343657816040517f8d666f600000000000000000000000000000000000000000000000000000000081526004016109629190613a1e565b610d218280602001905181019061344d9190614784565b613790565b60008060001b8284602001518560400151866060015187608001518860a001518960c001518a60e001518b61010001516040516020016134e89897969594939291906001600160a01b039889168152968816602088015267ffffffffffffffff95861660408801526060870194909452911515608086015290921660a0840152921660c082015260e08101919091526101000190565b60405160208183030381529060405280519060200120856101200151805190602001208661014001516040516020016135219190615146565b6040516020818303038152906040528051906020012087610160015160405160200161354d9190615159565b60408051601f198184030181528282528051602091820120908301979097528101949094526060840192909252608083015260a082015260c081019190915260e00160405160208183030381529060405280519060200120905092915050565b336001600160a01b038216036136055760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610962565b600180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000670de0b6b3a76400006136a1837bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8616614661565b610f93919061468b565b6000613700826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166137fd9092919063ffffffff16565b805190915015610ee6578080602001905181019061371e919061481c565b610ee65760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610962565b60006001600160a01b038211806137a8575061040082105b156137f95760408051602081018490520160408051601f19818403018152908290527f8d666f6000000000000000000000000000000000000000000000000000000000825261096291600401613a1e565b5090565b6060611573848460008585600080866001600160a01b03168587604051613824919061516c565b60006040518083038185875af1925050503d8060008114613861576040519150601f19603f3d011682016040523d82523d6000602084013e613866565b606091505b509150915061387787838387613882565b979650505050505050565b606083156138f15782516000036138ea576001600160a01b0385163b6138ea5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610962565b5081611573565b61157383838151156139065781518083602001fd5b8060405162461bcd60e51b81526004016109629190613a1e565b6001600160a01b03811681146110ce57600080fd5b803561394081613920565b919050565b60006020828403121561395757600080fd5b8135610f9381613920565b60c08101610d2182846001600160a01b0380825116835267ffffffffffffffff60208301511660208401526bffffffffffffffffffffffff60408301511660408401528060608301511660608401528060808301511660808401528060a08301511660a0840152505050565b60005b838110156139e95781810151838201526020016139d1565b50506000910152565b60008151808452613a0a8160208601602086016139ce565b601f01601f19169290920160200192915050565b602081526000610f9360208301846139f2565b67ffffffffffffffff811681146110ce57600080fd5b803561394081613a31565b600060a08284031215613a6457600080fd5b50919050565b60008060408385031215613a7d57600080fd5b8235613a8881613a31565b9150602083013567ffffffffffffffff811115613aa457600080fd5b613ab085828601613a52565b9150509250929050565b60008060408385031215613acd57600080fd5b8235613ad881613a31565b91506020830135613ae881613920565b809150509250929050565b600060208284031215613b0557600080fd5b8135610f9381613a31565b8051151582526020810151613b2b602084018261ffff169052565b506040810151613b43604084018263ffffffff169052565b506060810151613b5b606084018263ffffffff169052565b506080810151613b73608084018263ffffffff169052565b5060a0810151613b8960a084018261ffff169052565b5060c0810151613ba160c084018263ffffffff169052565b5060e0810151613bb760e084018261ffff169052565b506101008181015161ffff9081169184019190915261012080830151909116908301526101408082015163ffffffff90811691840191909152610160808301518216908401526101808083015167ffffffffffffffff908116918501919091526101a080840151909116908401526101c080830151909116908301526101e0908101511515910152565b600061026082019050613c55828451613b10565b60208301516001600160a01b0316610200830152604083015167ffffffffffffffff166102208301526060909201516102409091015290565b60808101610d21828480516001600160a01b03908116835260208083015182169084015260408083015182169084015260609182015116910152565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff81118282101715613d1c57613d1c613cca565b60405290565b60405160c0810167ffffffffffffffff81118282101715613d1c57613d1c613cca565b6040516060810167ffffffffffffffff81118282101715613d1c57613d1c613cca565b604051610200810167ffffffffffffffff81118282101715613d1c57613d1c613cca565b604051601f8201601f1916810167ffffffffffffffff81118282101715613db557613db5613cca565b604052919050565b600067ffffffffffffffff821115613dd757613dd7613cca565b5060051b60200190565b803563ffffffff8116811461394057600080fd5b803561ffff8116811461394057600080fd5b80151581146110ce57600080fd5b803561394081613e07565b600082601f830112613e3157600080fd5b81356020613e46613e4183613dbd565b613d8c565b82815260069290921b84018101918181019086841115613e6557600080fd5b8286015b84811015613eb65760408189031215613e825760008081fd5b613e8a613cf9565b8135613e9581613a31565b815281850135613ea481613920565b81860152835291830191604001613e69565b509695505050505050565b60008060408385031215613ed457600080fd5b67ffffffffffffffff83351115613eea57600080fd5b83601f843585010112613efc57600080fd5b613f0c613e418435850135613dbd565b8335840180358083526020808401939260059290921b90910101861015613f3257600080fd5b602085358601015b85358601803560051b016020018110156141055767ffffffffffffffff81351115613f6457600080fd5b6040601f19823588358901018903011215613f7e57600080fd5b613f86613cf9565b613f996020833589358a01010135613a31565b863587018235016020810135825267ffffffffffffffff6040909101351115613fc157600080fd5b86358701823501604081013501603f81018913613fdd57600080fd5b613fed613e416020830135613dbd565b602082810135808352908201919060e00283016040018b101561400f57600080fd5b604083015b604060e06020860135028501018110156140ec5760e0818d03121561403857600080fd5b614040613cf9565b61404a8235613920565b8135815260c0601f19838f0301121561406257600080fd5b61406a613d22565b61407660208401613de1565b815261408460408401613de1565b602082015261409560608401613df5565b60408201526140a660808401613de1565b60608201526140b760a08401613de1565b60808201526140c960c0840135613e07565b60c083013560a0820152602082810191909152908452929092019160e001614014565b5060208481019190915292865250509283019201613f3a565b5092505067ffffffffffffffff6020840135111561412257600080fd5b6141328460208501358501613e20565b90509250929050565b6000602080838503121561414e57600080fd5b823567ffffffffffffffff81111561416557600080fd5b8301601f8101851361417657600080fd5b8035614184613e4182613dbd565b81815261024091820283018401918482019190888411156141a457600080fd5b938501935b8385101561433c57848903818112156141c25760008081fd5b6141ca613d45565b86356141d581613a31565b8152610200601f1983018113156141ec5760008081fd5b6141f4613d68565b9250614201898901613e15565b83526040614210818a01613df5565b8a8501526060614221818b01613de1565b828601526080614232818c01613de1565b8287015260a09150614245828c01613de1565b9086015260c06142568b8201613df5565b8287015260e09150614269828c01613de1565b9086015261010061427b8b8201613df5565b82870152610120915061428f828c01613df5565b908601526101406142a18b8201613df5565b8287015261016091506142b5828c01613de1565b908601526101806142c78b8201613de1565b828701526101a091506142db828c01613a47565b908601526101c06142ed8b8201613a47565b828701526101e09150614301828c01613de1565b908601526143108a8401613e15565b8186015250838a8401526143276102208a01613935565b908301525084525093840193918501916141a9565b50979650505050505050565b6000602080838503121561435b57600080fd5b823567ffffffffffffffff81111561437257600080fd5b8301601f8101851361438357600080fd5b8035614391613e4182613dbd565b81815260069190911b820183019083810190878311156143b057600080fd5b928401925b8284101561387757604084890312156143ce5760008081fd5b6143d6613cf9565b84356143e181613920565b8152848601356143f081613a31565b81870152825260409390930192908401906143b5565b60006080828403121561441857600080fd5b6040516080810181811067ffffffffffffffff8211171561443b5761443b613cca565b604052823561444981613920565b8152602083013561445981613920565b6020820152604083013561446c81613920565b6040820152606083013561447f81613920565b60608201529392505050565b600080600080608085870312156144a157600080fd5b84356144ac81613a31565b9350602085013567ffffffffffffffff8111156144c857600080fd5b6144d487828801613a52565b9350506040850135915060608501356144ec81613920565b939692955090935050565b6020808252825182820181905260009190848201906040850190845b818110156145385783516001600160a01b031683529284019291840191600101614513565b50909695505050505050565b6000808335601e1984360301811261455b57600080fd5b83018035915067ffffffffffffffff82111561457657600080fd5b60200191503681900382131561458b57600080fd5b9250929050565b6000808335601e198436030181126145a957600080fd5b83018035915067ffffffffffffffff8211156145c457600080fd5b6020019150600681901b360382131561458b57600080fd5b80517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8116811461394057600080fd5b6000806040838503121561461b57600080fd5b614624836145dc565b9150614132602084016145dc565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8082028115828204841417610d2157610d21614632565b80820180821115610d2157610d21614632565b6000826146c1577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b600060208083850312156146d957600080fd5b825167ffffffffffffffff8111156146f057600080fd5b8301601f8101851361470157600080fd5b805161470f613e4182613dbd565b81815260059190911b8201830190838101908783111561472e57600080fd5b928401925b8284101561387757835161474681613920565b82529284019290840190614733565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60006020828403121561479657600080fd5b5051919050565b6000602082840312156147af57600080fd5b8151610f9381613920565b67ffffffffffffffff8181168382160190808211156147db576147db614632565b5092915050565b6000604082840312156147f457600080fd5b6147fc613cf9565b823561480781613920565b81526020928301359281019290925250919050565b60006020828403121561482e57600080fd5b8151610f9381613e07565b602081526000825160a0602084015261485560c08401826139f2565b905067ffffffffffffffff602085015116604084015260408401516001600160a01b038082166060860152606086015160808601528060808701511660a086015250508091505092915050565b600082601f8301126148b357600080fd5b815167ffffffffffffffff8111156148cd576148cd613cca565b6148e06020601f19601f84011601613d8c565b8181528460208386010111156148f557600080fd5b6115738260208301602087016139ce565b60006020828403121561491857600080fd5b815167ffffffffffffffff8082111561493057600080fd5b908301906040828603121561494457600080fd5b61494c613cf9565b82518281111561495b57600080fd5b614967878286016148a2565b82525060208301518281111561497c57600080fd5b614988878286016148a2565b60208301525095945050505050565b6020815260008251606060208401526149b360808401826139f2565b90506020840151601f19808584030160408601526149d183836139f2565b92506040860151915080858403016060860152506149ef82826139f2565b95945050505050565b60008151808452602080850194506020840160005b83811015614a3d57815180516001600160a01b031688528301518388015260409096019590820190600101614a0d565b509495945050505050565b60008282518085526020808601955060208260051b8401016020860160005b84811015614a9557601f19868403018952614a838383516139f2565b98840198925090830190600101614a67565b5090979650505050505050565b60208152614abd60208201835167ffffffffffffffff169052565b60006020830151614ad960408401826001600160a01b03169052565b5060408301516001600160a01b038116606084015250606083015167ffffffffffffffff8116608084015250608083015160a083015260a0830151614b2260c084018215159052565b5060c083015167ffffffffffffffff811660e08401525060e0830151610100614b55818501836001600160a01b03169052565b840151610120848101919091528401516101a061014080860182905291925090614b836101c08601846139f2565b9250808601519050601f19610160818786030181880152614ba485846149f8565b945080880151925050610180818786030181880152614bc38584614a48565b970151959092019490945250929392505050565b7fffffffff000000000000000000000000000000000000000000000000000000008135818116916004851015614c175780818660040360031b1b83161692505b505092915050565b60008085851115614c2f57600080fd5b83861115614c3c57600080fd5b5050820193919092039150565b600060408284031215614c5b57600080fd5b614c63613cf9565b823581526020830135614c7581613e07565b60208201529392505050565b600060208284031215614c9357600080fd5b5035919050565b63ffffffff8181168382160190808211156147db576147db614632565b600060208284031215614cc957600080fd5b610f93826145dc565b815460ff81161515825261026082019061ffff600882901c8116602085015263ffffffff601883901c81166040860152614d1960608601828560381c1663ffffffff169052565b614d3060808601828560581c1663ffffffff169052565b614d4560a08601838560781c1661ffff169052565b614d5c60c08601828560881c1663ffffffff169052565b614d7160e08601838560a81c1661ffff169052565b614d876101008601838560b81c1661ffff169052565b614d9d6101208601838560c81c1661ffff169052565b614db56101408601828560d81c1663ffffffff169052565b600186015463ffffffff82821616610160870152925067ffffffffffffffff602084901c81166101808701529150614dff6101a08601838560601c1667ffffffffffffffff169052565b614e176101c08601828560a01c1663ffffffff169052565b50614e2d6101e0850160ff8460c01c1615159052565b60028501546001600160a01b0381166102008601529150614e606102208501828460a01c1667ffffffffffffffff169052565b5050600383015461024083015292915050565b6102008101610d218284613b10565b6101408101614eef82856001600160a01b0380825116835267ffffffffffffffff60208301511660208401526bffffffffffffffffffffffff60408301511660408401528060608301511660608401528060808301511660808401528060a08301511660a0840152505050565b82516001600160a01b0390811660c08401526020840151811660e084015260408401518116610100840152606084015116610120830152610f93565b6000808335601e19843603018112614f4257600080fd5b830160208101925035905067ffffffffffffffff811115614f6257600080fd5b80360382131561458b57600080fd5b818352818160208501375060006020828401015260006020601f19601f840116840101905092915050565b8183526000602080850194508260005b85811015614a3d578135614fbf81613920565b6001600160a01b03168752818301358388015260409687019690910190600101614fac565b600067ffffffffffffffff8085168352604060208401526150058485614f2b565b60a0604086015261501a60e086018284614f71565b91505061502a6020860186614f2b565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc080878503016060880152615060848385614f71565b935060408801359250601e1988360301831261507b57600080fd5b6020928801928301923591508482111561509457600080fd5b8160061b36038313156150a657600080fd5b808785030160808801526150bb848385614f9c565b94506150c960608901613935565b6001600160a01b03811660a089015293506150e76080890189614f2b565b94509250808786030160c08801525050613877838383614f71565b600067ffffffffffffffff80831681810361511f5761511f614632565b6001019392505050565b60006020828403121561513b57600080fd5b8151610f9381613a31565b602081526000610f9360208301846149f8565b602081526000610f936020830184614a48565b6000825161517e8184602087016139ce565b919091019291505056fea164736f6c6343000818000a", + ABI: "[{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"linkToken\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint96\",\"name\":\"maxFeeJuelsPerMsg\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"nonceManager\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAdminRegistry\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.StaticConfig\",\"name\":\"staticConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"priceRegistry\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"messageValidator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"feeAggregator\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.DynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint16\",\"name\":\"maxNumberOfTokensPerMsg\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"maxDataBytes\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPerMsgGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"destGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"destGasPerPayloadByte\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"destDataAvailabilityOverheadGas\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"destGasPerDataAvailabilityByte\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"destDataAvailabilityMultiplierBps\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"defaultTokenFeeUSDCents\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"defaultTokenDestGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"defaultTokenDestBytesOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"defaultTxGasLimit\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"gasMultiplierWeiPerEth\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"networkFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"enforceOutOfOrder\",\"type\":\"bool\"},{\"internalType\":\"bytes4\",\"name\":\"chainFamilySelector\",\"type\":\"bytes4\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.DestChainDynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"},{\"internalType\":\"address\",\"name\":\"prevOnRamp\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.DestChainConfigArgs[]\",\"name\":\"destChainConfigArgs\",\"type\":\"tuple[]\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"premiumMultiplierWeiPerEth\",\"type\":\"uint64\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.PremiumMultiplierWeiPerEthArgs[]\",\"name\":\"premiumMultiplierWeiPerEthArgs\",\"type\":\"tuple[]\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"minFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"deciBps\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"destGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"destBytesOverhead\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.TokenTransferFeeConfig\",\"name\":\"tokenTransferFeeConfig\",\"type\":\"tuple\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.TokenTransferFeeConfigSingleTokenArgs[]\",\"name\":\"tokenTransferFeeConfigs\",\"type\":\"tuple[]\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.TokenTransferFeeConfigArgs[]\",\"name\":\"tokenTransferFeeConfigArgs\",\"type\":\"tuple[]\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"CannotSendZeroTokens\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"CursedByRMN\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"}],\"name\":\"DestinationChainNotEnabled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ExtraArgOutOfOrderExecutionMustBeTrue\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"GetSupportedTokensFunctionalityRemovedCheckAdminRegistry\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidConfig\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"destBytesOverhead\",\"type\":\"uint32\"}],\"name\":\"InvalidDestBytesOverhead\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"}],\"name\":\"InvalidDestChainConfig\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"encodedAddress\",\"type\":\"bytes\"}],\"name\":\"InvalidEVMAddress\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidExtraArgsTag\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"msgFeeJuels\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxFeeJuelsPerMsg\",\"type\":\"uint256\"}],\"name\":\"MessageFeeTooHigh\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MessageGasLimitTooHigh\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"maxSize\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"actualSize\",\"type\":\"uint256\"}],\"name\":\"MessageTooLarge\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"errorReason\",\"type\":\"bytes\"}],\"name\":\"MessageValidationError\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MustBeCalledByRouter\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"NotAFeeToken\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByOwnerOrAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RouterMustSetOriginalSender\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"SourceTokenDataTooLarge\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnsupportedNumberOfTokens\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"UnsupportedToken\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newAdmin\",\"type\":\"address\"}],\"name\":\"AdminSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"}],\"internalType\":\"structInternal.RampMessageHeader\",\"name\":\"header\",\"type\":\"tuple\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"receiver\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"extraArgs\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"feeToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"feeTokenAmount\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"bytes\",\"name\":\"sourcePoolAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"destTokenAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"extraData\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structInternal.RampTokenAmount[]\",\"name\":\"tokenAmounts\",\"type\":\"tuple[]\"}],\"indexed\":false,\"internalType\":\"structInternal.EVM2AnyRampMessage\",\"name\":\"message\",\"type\":\"tuple\"}],\"name\":\"CCIPSendRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"linkToken\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint96\",\"name\":\"maxFeeJuelsPerMsg\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"nonceManager\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAdminRegistry\",\"type\":\"address\"}],\"indexed\":false,\"internalType\":\"structEVM2EVMMultiOnRamp.StaticConfig\",\"name\":\"staticConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"priceRegistry\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"messageValidator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"feeAggregator\",\"type\":\"address\"}],\"indexed\":false,\"internalType\":\"structEVM2EVMMultiOnRamp.DynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"}],\"name\":\"ConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint16\",\"name\":\"maxNumberOfTokensPerMsg\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"maxDataBytes\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPerMsgGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"destGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"destGasPerPayloadByte\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"destDataAvailabilityOverheadGas\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"destGasPerDataAvailabilityByte\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"destDataAvailabilityMultiplierBps\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"defaultTokenFeeUSDCents\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"defaultTokenDestGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"defaultTokenDestBytesOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"defaultTxGasLimit\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"gasMultiplierWeiPerEth\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"networkFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"enforceOutOfOrder\",\"type\":\"bool\"},{\"internalType\":\"bytes4\",\"name\":\"chainFamilySelector\",\"type\":\"bytes4\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.DestChainDynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"},{\"internalType\":\"address\",\"name\":\"prevOnRamp\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"metadataHash\",\"type\":\"bytes32\"}],\"indexed\":false,\"internalType\":\"structEVM2EVMMultiOnRamp.DestChainConfig\",\"name\":\"destChainConfig\",\"type\":\"tuple\"}],\"name\":\"DestChainAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint16\",\"name\":\"maxNumberOfTokensPerMsg\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"maxDataBytes\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPerMsgGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"destGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"destGasPerPayloadByte\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"destDataAvailabilityOverheadGas\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"destGasPerDataAvailabilityByte\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"destDataAvailabilityMultiplierBps\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"defaultTokenFeeUSDCents\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"defaultTokenDestGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"defaultTokenDestBytesOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"defaultTxGasLimit\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"gasMultiplierWeiPerEth\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"networkFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"enforceOutOfOrder\",\"type\":\"bool\"},{\"internalType\":\"bytes4\",\"name\":\"chainFamilySelector\",\"type\":\"bytes4\"}],\"indexed\":false,\"internalType\":\"structEVM2EVMMultiOnRamp.DestChainDynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"}],\"name\":\"DestChainDynamicConfigUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"feeToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"feeValueJuels\",\"type\":\"uint256\"}],\"name\":\"FeePaid\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"feeAggregator\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"feeToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"FeeTokenWithdrawn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"premiumMultiplierWeiPerEth\",\"type\":\"uint64\"}],\"name\":\"PremiumMultiplierWeiPerEthUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"TokenTransferFeeConfigDeleted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"minFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"deciBps\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"destGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"destBytesOverhead\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"}],\"indexed\":false,\"internalType\":\"structEVM2EVMMultiOnRamp.TokenTransferFeeConfig\",\"name\":\"tokenTransferFeeConfig\",\"type\":\"tuple\"}],\"name\":\"TokenTransferFeeConfigUpdated\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint16\",\"name\":\"maxNumberOfTokensPerMsg\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"maxDataBytes\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPerMsgGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"destGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"destGasPerPayloadByte\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"destDataAvailabilityOverheadGas\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"destGasPerDataAvailabilityByte\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"destDataAvailabilityMultiplierBps\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"defaultTokenFeeUSDCents\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"defaultTokenDestGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"defaultTokenDestBytesOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"defaultTxGasLimit\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"gasMultiplierWeiPerEth\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"networkFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"enforceOutOfOrder\",\"type\":\"bool\"},{\"internalType\":\"bytes4\",\"name\":\"chainFamilySelector\",\"type\":\"bytes4\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.DestChainDynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"},{\"internalType\":\"address\",\"name\":\"prevOnRamp\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.DestChainConfigArgs[]\",\"name\":\"destChainConfigArgs\",\"type\":\"tuple[]\"}],\"name\":\"applyDestChainConfigUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"premiumMultiplierWeiPerEth\",\"type\":\"uint64\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.PremiumMultiplierWeiPerEthArgs[]\",\"name\":\"premiumMultiplierWeiPerEthArgs\",\"type\":\"tuple[]\"}],\"name\":\"applyPremiumMultiplierWeiPerEthUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"minFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"deciBps\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"destGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"destBytesOverhead\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.TokenTransferFeeConfig\",\"name\":\"tokenTransferFeeConfig\",\"type\":\"tuple\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.TokenTransferFeeConfigSingleTokenArgs[]\",\"name\":\"tokenTransferFeeConfigs\",\"type\":\"tuple[]\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.TokenTransferFeeConfigArgs[]\",\"name\":\"tokenTransferFeeConfigArgs\",\"type\":\"tuple[]\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.TokenTransferFeeConfigRemoveArgs[]\",\"name\":\"tokensToUseDefaultFeeConfigs\",\"type\":\"tuple[]\"}],\"name\":\"applyTokenTransferFeeConfigUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bytes\",\"name\":\"receiver\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structClient.EVMTokenAmount[]\",\"name\":\"tokenAmounts\",\"type\":\"tuple[]\"},{\"internalType\":\"address\",\"name\":\"feeToken\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"extraArgs\",\"type\":\"bytes\"}],\"internalType\":\"structClient.EVM2AnyMessage\",\"name\":\"message\",\"type\":\"tuple\"},{\"internalType\":\"uint256\",\"name\":\"feeTokenAmount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"originalSender\",\"type\":\"address\"}],\"name\":\"forwardFromRouter\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"}],\"name\":\"getDestChainConfig\",\"outputs\":[{\"components\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint16\",\"name\":\"maxNumberOfTokensPerMsg\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"maxDataBytes\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPerMsgGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"destGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"destGasPerPayloadByte\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"destDataAvailabilityOverheadGas\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"destGasPerDataAvailabilityByte\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"destDataAvailabilityMultiplierBps\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"defaultTokenFeeUSDCents\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"defaultTokenDestGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"defaultTokenDestBytesOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"defaultTxGasLimit\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"gasMultiplierWeiPerEth\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"networkFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"enforceOutOfOrder\",\"type\":\"bool\"},{\"internalType\":\"bytes4\",\"name\":\"chainFamilySelector\",\"type\":\"bytes4\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.DestChainDynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"},{\"internalType\":\"address\",\"name\":\"prevOnRamp\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"metadataHash\",\"type\":\"bytes32\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.DestChainConfig\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getDynamicConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"priceRegistry\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"messageValidator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"feeAggregator\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.DynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"}],\"name\":\"getExpectedNextSequenceNumber\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bytes\",\"name\":\"receiver\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structClient.EVMTokenAmount[]\",\"name\":\"tokenAmounts\",\"type\":\"tuple[]\"},{\"internalType\":\"address\",\"name\":\"feeToken\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"extraArgs\",\"type\":\"bytes\"}],\"internalType\":\"structClient.EVM2AnyMessage\",\"name\":\"message\",\"type\":\"tuple\"}],\"name\":\"getFee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"feeTokenAmount\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"},{\"internalType\":\"contractIERC20\",\"name\":\"sourceToken\",\"type\":\"address\"}],\"name\":\"getPoolBySourceToken\",\"outputs\":[{\"internalType\":\"contractIPoolV1\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"getPremiumMultiplierWeiPerEth\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"premiumMultiplierWeiPerEth\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getStaticConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"linkToken\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint96\",\"name\":\"maxFeeJuelsPerMsg\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"nonceManager\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAdminRegistry\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.StaticConfig\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"name\":\"getSupportedTokens\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"getTokenTransferFeeConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"minFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"deciBps\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"destGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"destBytesOverhead\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.TokenTransferFeeConfig\",\"name\":\"tokenTransferFeeConfig\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"priceRegistry\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"messageValidator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"feeAggregator\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.DynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"}],\"name\":\"setDynamicConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"withdrawFeeTokens\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x6101406040523480156200001257600080fd5b506040516200740738038062007407833981016040819052620000359162001528565b33806000816200008c5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000bf57620000bf8162000214565b505085516001600160a01b031615905080620000e6575060208501516001600160401b0316155b80620000fd575060608501516001600160a01b0316155b8062000114575060808501516001600160a01b0316155b806200012b575060a08501516001600160a01b0316155b156200014a576040516306b7c75960e31b815260040160405180910390fd5b84516001600160a01b0390811660a090815260208701516001600160401b031660c05260408701516001600160601b031660809081526060880151831660e0528701518216610100528601511661012052620001a684620002bf565b620001b18362000482565b620001bc8262000a4b565b60408051600080825260208201909252620002099183919062000202565b6040805180820190915260008082526020820152815260200190600190039081620001da5790505b5062000b17565b50505050506200199a565b336001600160a01b038216036200026e5760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000083565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60208101516001600160a01b03161580620002e5575060608101516001600160a01b0316155b1562000304576040516306b7c75960e31b815260040160405180910390fd5b8051600280546001600160a01b039283166001600160a01b0319918216179091556020808401516003805491851691841691909117905560408085015160048054918616918516919091179055606080860151600580549187169190951617909355805160c0808201835260a080518716835290516001600160401b031693820193909352608080516001600160601b03168284015260e05186169482019490945261010051851693810193909352610120519093169082015290517f4012fe74115805c44a121d8f9edc3d234df8f05f53b52864b8e5e8a30384b8aa916200047791849082516001600160a01b0390811682526020808501516001600160401b0316818401526040808601516001600160601b03168185015260608087015184168186015260808088015185169086015260a0968701518416968501969096528451831660c085015290840151821660e084015283015181166101008301529190920151166101208201526101400190565b60405180910390a150565b60005b815181101562000a47576000828281518110620004a657620004a66200166e565b602002602001015190506000838381518110620004c757620004c76200166e565b6020026020010151600001519050806001600160401b031660001480620004fe5750602082015161018001516001600160401b0316155b80620005245750602082015161020001516001600160e01b031916630a04b54b60e21b14155b156200054f5760405163c35aa79d60e01b81526001600160401b038216600482015260240162000083565b600060066000836001600160401b03166001600160401b0316815260200190815260200160002090506000836040015190506000604051806080016040528086602001518152602001836001600160a01b031681526020018460020160149054906101000a90046001600160401b03166001600160401b031681526020018460030154815250905080600001518360000160008201518160000160006101000a81548160ff02191690831515021790555060208201518160000160016101000a81548161ffff021916908361ffff16021790555060408201518160000160036101000a81548163ffffffff021916908363ffffffff16021790555060608201518160000160076101000a81548163ffffffff021916908363ffffffff160217905550608082015181600001600b6101000a81548163ffffffff021916908363ffffffff16021790555060a082015181600001600f6101000a81548161ffff021916908361ffff16021790555060c08201518160000160116101000a81548163ffffffff021916908363ffffffff16021790555060e08201518160000160156101000a81548161ffff021916908361ffff1602179055506101008201518160000160176101000a81548161ffff021916908361ffff1602179055506101208201518160000160196101000a81548161ffff021916908361ffff16021790555061014082015181600001601b6101000a81548163ffffffff021916908363ffffffff1602179055506101608201518160010160006101000a81548163ffffffff021916908363ffffffff1602179055506101808201518160010160046101000a8154816001600160401b0302191690836001600160401b031602179055506101a082015181600101600c6101000a8154816001600160401b0302191690836001600160401b031602179055506101c08201518160010160146101000a81548163ffffffff021916908363ffffffff1602179055506101e08201518160010160186101000a81548160ff0219169083151502179055506102008201518160010160196101000a81548163ffffffff021916908360e01c021790555090505082600301546000801b03620009645760c051604080517f130ac867e79e2789f923760a88743d292acdf7002139a588206e2260f73f732160208201526001600160401b0392831691810191909152908516606082015230608082015260a00160408051601f1981840301815291905280516020909101206060820181905260038401556001600160a01b038216156200091b576002830180546001600160a01b0319166001600160a01b0384161790555b836001600160401b03167f66951b52bb33e5d462611460f7aa53069005b173e002d9224c15fb986e4ead8f8460405162000956919062001684565b60405180910390a262000a36565b60028301546001600160a01b03838116911614620009a15760405163c35aa79d60e01b81526001600160401b038516600482015260240162000083565b60208560200151610160015163ffffffff161015620009ee57602085015161016001516040516312766e0160e11b81526000600482015263ffffffff909116602482015260440162000083565b836001600160401b03167fd42d3a670a4f1ab5d8703efa22bb17041446365cfcfa33980ced685a080cf7cb866020015160405162000a2d919062001843565b60405180910390a25b505050505080600101905062000485565b5050565b60005b815181101562000a4757600082828151811062000a6f5762000a6f6200166e565b6020026020010151600001519050600083838151811062000a945762000a946200166e565b6020908102919091018101518101516001600160a01b03841660008181526007845260409081902080546001600160401b0319166001600160401b0385169081179091559051908152919350917fbb77da6f7210cdd16904228a9360133d1d7dfff99b1bc75f128da5b53e28f97d910160405180910390a2505060010162000a4e565b60005b825181101562000d8457600083828151811062000b3b5762000b3b6200166e565b6020026020010151905060008160000151905060005b82602001515181101562000d755760008360200151828151811062000b7a5762000b7a6200166e565b602002602001015160200151905060008460200151838151811062000ba35762000ba36200166e565b60200260200101516000015190506020826080015163ffffffff16101562000bfc5760808201516040516312766e0160e11b81526001600160a01b038316600482015263ffffffff909116602482015260440162000083565b6001600160401b03841660008181526008602090815260408083206001600160a01b0386168085529083529281902086518154938801518389015160608a015160808b015160a08c01511515600160901b0260ff60901b1963ffffffff928316600160701b021664ffffffffff60701b199383166a01000000000000000000000263ffffffff60501b1961ffff90961668010000000000000000029590951665ffffffffffff60401b19968416640100000000026001600160401b0319909b16939097169290921798909817939093169390931717919091161792909217909155519091907f94967ae9ea7729ad4f54021c1981765d2b1d954f7c92fbec340aa0a54f46b8b59062000d62908690600060c08201905063ffffffff80845116835280602085015116602084015261ffff60408501511660408401528060608501511660608401528060808501511660808401525060a0830151151560a083015292915050565b60405180910390a3505060010162000b51565b50505080600101905062000b1a565b5060005b815181101562000e4a57600082828151811062000da95762000da96200166e565b6020026020010151600001519050600083838151811062000dce5762000dce6200166e565b6020908102919091018101518101516001600160401b03841660008181526008845260408082206001600160a01b038516808452955280822080546001600160981b03191690555192945090917f4de5b1bcbca6018c11303a2c3f4a4b4f22a1c741d8c4ba430d246ac06c5ddf8b9190a3505060010162000d88565b505050565b634e487b7160e01b600052604160045260246000fd5b604051606081016001600160401b038111828210171562000e8a5762000e8a62000e4f565b60405290565b60405161022081016001600160401b038111828210171562000e8a5762000e8a62000e4f565b604080519081016001600160401b038111828210171562000e8a5762000e8a62000e4f565b60405160c081016001600160401b038111828210171562000e8a5762000e8a62000e4f565b604051601f8201601f191681016001600160401b038111828210171562000f2b5762000f2b62000e4f565b604052919050565b80516001600160a01b038116811462000f4b57600080fd5b919050565b80516001600160401b038116811462000f4b57600080fd5b60006080828403121562000f7b57600080fd5b604051608081016001600160401b038111828210171562000fa05762000fa062000e4f565b60405290508062000fb18362000f33565b815262000fc16020840162000f33565b602082015262000fd46040840162000f33565b604082015262000fe76060840162000f33565b60608201525092915050565b60006001600160401b038211156200100f576200100f62000e4f565b5060051b60200190565b8051801515811462000f4b57600080fd5b805161ffff8116811462000f4b57600080fd5b805163ffffffff8116811462000f4b57600080fd5b80516001600160e01b03198116811462000f4b57600080fd5b600082601f8301126200107d57600080fd5b8151602062001096620010908362000ff3565b62000f00565b8281526102609283028501820192828201919087851115620010b757600080fd5b8387015b858110156200128b5780890382811215620010d65760008081fd5b620010e062000e65565b620010eb8362000f50565b815261022080601f1984011215620011035760008081fd5b6200110d62000e90565b92506200111c88850162001019565b835260406200112d8186016200102a565b898501526060620011408187016200103d565b828601526080620011538188016200103d565b8287015260a09150620011688288016200103d565b9086015260c06200117b8782016200102a565b8287015260e09150620011908288016200103d565b90860152610100620011a48782016200102a565b828701526101209150620011ba8288016200102a565b90860152610140620011ce8782016200102a565b828701526101609150620011e48288016200103d565b90860152610180620011f88782016200103d565b828701526101a091506200120e82880162000f50565b908601526101c06200122287820162000f50565b828701526101e09150620012388288016200103d565b908601526102006200124c87820162001019565b828701526200125d84880162001052565b818701525050838984015262001277610240860162000f33565b9083015250855250928401928101620010bb565b5090979650505050505050565b600082601f830112620012aa57600080fd5b81516020620012bd620010908362000ff3565b82815260069290921b84018101918181019086841115620012dd57600080fd5b8286015b84811015620013335760408189031215620012fc5760008081fd5b6200130662000eb6565b620013118262000f33565b81526200132085830162000f50565b81860152835291830191604001620012e1565b509695505050505050565b600082601f8301126200135057600080fd5b8151602062001363620010908362000ff3565b82815260059290921b840181019181810190868411156200138357600080fd5b8286015b84811015620013335780516001600160401b0380821115620013a857600080fd5b908801906040601f19838c038101821315620013c357600080fd5b620013cd62000eb6565b620013da89860162000f50565b81528285015184811115620013ee57600080fd5b8086019550508c603f8601126200140457600080fd5b88850151935062001419620010908562000ff3565b84815260e09094028501830193898101908e8611156200143857600080fd5b958401955b858710156200151157868f0360e08112156200145857600080fd5b6200146262000eb6565b6200146d8962000f33565b815260c086830112156200148057600080fd5b6200148a62000edb565b9150620014998d8a016200103d565b8252620014a8878a016200103d565b8d830152620014ba60608a016200102a565b87830152620014cc60808a016200103d565b6060830152620014df60a08a016200103d565b6080830152620014f260c08a0162001019565b60a0830152808d0191909152825260e09690960195908a01906200143d565b828b01525087525050509284019250830162001387565b60008060008060008587036101a08112156200154357600080fd5b60c08112156200155257600080fd5b506200155d62000edb565b620015688762000f33565b8152620015786020880162000f50565b602082015260408701516001600160601b03811681146200159857600080fd5b6040820152620015ab6060880162000f33565b6060820152620015be6080880162000f33565b6080820152620015d160a0880162000f33565b60a08201529450620015e78760c0880162000f68565b6101408701519094506001600160401b03808211156200160657600080fd5b6200161489838a016200106b565b94506101608801519150808211156200162c57600080fd5b6200163a89838a0162001298565b93506101808801519150808211156200165257600080fd5b5062001661888289016200133e565b9150509295509295909350565b634e487b7160e01b600052603260045260246000fd5b815460ff81161515825261028082019061ffff600882901c8116602085015263ffffffff601883901c81166040860152620016cc60608601828560381c1663ffffffff169052565b620016e460808601828560581c1663ffffffff169052565b620016fa60a08601838560781c1661ffff169052565b6200171260c08601828560881c1663ffffffff169052565b6200172860e08601838560a81c1661ffff169052565b6200173f6101008601838560b81c1661ffff169052565b620017566101208601838560c81c1661ffff169052565b6200176f6101408601828560d81c1663ffffffff169052565b600186015463ffffffff8282161661016087015292506001600160401b03602084901c81166101808701529150620017b86101a08601838560601c166001600160401b03169052565b620017d16101c08601828560a01c1663ffffffff169052565b50620017e86101e0850160ff8460c01c1615159052565b6001600160e01b0319601883901b1661020085015260028501546001600160a01b0381166102208601529150620018306102408501828460a01c166001600160401b03169052565b5050600383015461026083015292915050565b8151151581526102208101602083015162001864602084018261ffff169052565b5060408301516200187d604084018263ffffffff169052565b50606083015162001896606084018263ffffffff169052565b506080830151620018af608084018263ffffffff169052565b5060a0830151620018c660a084018261ffff169052565b5060c0830151620018df60c084018263ffffffff169052565b5060e0830151620018f660e084018261ffff169052565b506101008381015161ffff9081169184019190915261012080850151909116908301526101408084015163ffffffff9081169184019190915261016080850151821690840152610180808501516001600160401b03908116918501919091526101a080860151909116908401526101c080850151909116908301526101e080840151151590830152610200928301516001600160e01b031916929091019190915290565b60805160a05160c05160e05161010051610120516159b462001a53600039600081816102da015281816111820152612c640152600081816102ab01528181612c3c015261350001526000818161027c01528181612c120152612d77015260008181610218015281816123f501528181612bad01526135910152600081816101e901528181612b880152818161302901526130bb01526000818161024801528181612bdf0152818161317d01526131ed01526159b46000f3fe608060405234801561001057600080fd5b50600436106101515760003560e01c8063770e2dc4116100cd578063a69c64c011610081578063df0aa9e911610066578063df0aa9e9146108e5578063f2fde38b146108f8578063fbca3b741461090b57600080fd5b8063a69c64c0146108bf578063a6f3ab6c146108d257600080fd5b806382b49eb0116100b257806382b49eb0146107385780638da5cb5b1461089b5780639041be3d146108ac57600080fd5b8063770e2dc41461071d57806379ba50971461073057600080fd5b80633a019940116101245780636def4ce7116101095780636def4ce7146103b6578063714f5ec5146106975780637437ff9f146106aa57600080fd5b80633a0199401461038157806348a98aa41461038b57600080fd5b8063061877e31461015657806306285c69146101a7578063181f5a771461031757806320487ded14610360575b600080fd5b61018961016436600461418f565b6001600160a01b031660009081526007602052604090205467ffffffffffffffff1690565b60405167ffffffffffffffff90911681526020015b60405180910390f35b61030a6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a08101919091526040518060c001604052807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff1681526020017f00000000000000000000000000000000000000000000000000000000000000006bffffffffffffffffffffffff1681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316815250905090565b60405161019e91906141ac565b6103536040518060400160405280601c81526020017f45564d3245564d4d756c74694f6e52616d7020312e362e302d6465760000000081525081565b60405161019e9190614268565b61037361036e3660046142b4565b61092b565b60405190815260200161019e565b610389610f83565b005b61039e610399366004614304565b611147565b6040516001600160a01b03909116815260200161019e565b61068a6103c436600461433d565b604080516102a08101825260006080820181815260a0830182905260c0830182905260e08301829052610100830182905261012083018290526101408301829052610160830182905261018083018290526101a083018290526101c083018290526101e083018290526102008301829052610220830182905261024083018290526102608301829052610280830182905282526020820181905291810182905260608101919091525067ffffffffffffffff90811660009081526006602090815260409182902082516102a081018452815460ff80821615156080840190815261010080840461ffff90811660a08701526301000000850463ffffffff90811660c08801526701000000000000008604811660e0808901919091526b01000000000000000000000087048216938801939093526f0100000000000000000000000000000086048216610120880152710100000000000000000000000000000000008604811661014088015275010000000000000000000000000000000000000000008604821661016088015277010000000000000000000000000000000000000000000000860482166101808801527901000000000000000000000000000000000000000000000000008087049092166101a08801527b0100000000000000000000000000000000000000000000000000000090950485166101c087015260018701548086166101e088015264010000000081048b166102008801526c0100000000000000000000000081048b166102208801527401000000000000000000000000000000000000000080820490961661024088015278010000000000000000000000000000000000000000000000008104909416151561026087015290920490911b6001600160e01b031916610280840152825260028301546001600160a01b0381169483019490945290920490931691810191909152600390910154606082015290565b60405161019e91906144a0565b6103896106a536600461465b565b6111f6565b61071060408051608081018252600080825260208201819052918101829052606081019190915250604080516080810182526002546001600160a01b03908116825260035481166020830152600454811692820192909252600554909116606082015290565b60405161019e9190614880565b61038961072b366004614958565b61120a565b610389611220565b61083b610746366004614304565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a08101919091525067ffffffffffffffff9190911660009081526008602090815260408083206001600160a01b0394909416835292815290829020825160c081018452905463ffffffff8082168352640100000000820481169383019390935268010000000000000000810461ffff16938201939093526a01000000000000000000008304821660608201526e01000000000000000000000000000083049091166080820152720100000000000000000000000000000000000090910460ff16151560a082015290565b60405161019e9190600060c08201905063ffffffff80845116835280602085015116602084015261ffff60408501511660408401528060608501511660608401528060808501511660808401525060a0830151151560a083015292915050565b6000546001600160a01b031661039e565b6101896108ba36600461433d565b6112e9565b6103896108cd366004614bd2565b61132d565b6103896108e0366004614c90565b61133e565b6103736108f3366004614d15565b61134f565b61038961090636600461418f565b6117c7565b61091e61091936600461433d565b6117d8565b60405161019e9190614d81565b67ffffffffffffffff82166000908152600660205260408120805460ff16610990576040517f99ac52f200000000000000000000000000000000000000000000000000000000815267ffffffffffffffff851660048201526024015b60405180910390fd5b6109c1846109a16020860186614dce565b90506109b06040870187614e1c565b90506109bc8780614dce565b61180c565b60006007816109d6608087016060880161418f565b6001600160a01b03168152602081019190915260400160009081205467ffffffffffffffff169150819003610a5357610a15608085016060860161418f565b6040517fa7499d200000000000000000000000000000000000000000000000000000000081526001600160a01b039091166004820152602401610987565b60035460009081906001600160a01b031663ffdb4b37610a796080890160608a0161418f565b6040516001600160e01b031960e084901b1681526001600160a01b03909116600482015267ffffffffffffffff8a1660248201526044016040805180830381865afa158015610acc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610af09190614e7d565b90925090506000808080610b0760408b018b614e1c565b90501115610b4257610b368a610b2360808c0160608d0161418f565b87610b3160408e018e614e1c565b611934565b91945092509050610b79565b6001870154610b769074010000000000000000000000000000000000000000900463ffffffff16662386f26fc10000614ed6565b92505b865460009077010000000000000000000000000000000000000000000000900461ffff1615610be257610bdf8b6dffffffffffffffffffffffffffff607088901c16610bc860208e018e614dce565b9050610bd760408f018f614e1c565b905086611d10565b90505b875460009063ffffffff8516906f01000000000000000000000000000000900461ffff16610c1360208e018e614dce565b610c1e929150614ed6565b8a54610c3f91906b010000000000000000000000900463ffffffff16614eed565b610c499190614eed565b9050610edc610c5b60808d018d614dce565b8b604051806102200160405290816000820160009054906101000a900460ff161515151581526020016000820160019054906101000a900461ffff1661ffff1661ffff1681526020016000820160039054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016000820160079054906101000a900463ffffffff1663ffffffff1663ffffffff16815260200160008201600b9054906101000a900463ffffffff1663ffffffff1663ffffffff16815260200160008201600f9054906101000a900461ffff1661ffff1661ffff1681526020016000820160119054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016000820160159054906101000a900461ffff1661ffff1661ffff1681526020016000820160179054906101000a900461ffff1661ffff1661ffff1681526020016000820160199054906101000a900461ffff1661ffff1661ffff16815260200160008201601b9054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016001820160009054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016001820160049054906101000a900467ffffffffffffffff1667ffffffffffffffff1667ffffffffffffffff16815260200160018201600c9054906101000a900467ffffffffffffffff1667ffffffffffffffff1667ffffffffffffffff1681526020016001820160149054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016001820160189054906101000a900460ff161515151581526020016001820160199054906101000a900460e01b6001600160e01b0319166001600160e01b03191681525050611e16565b51610ee79082614eed565b60018a01549091506000906c01000000000000000000000000900467ffffffffffffffff16610f26836dffffffffffffffffffffffffffff8a16614ed6565b610f309190614ed6565b90506001600160e01b0388168382610f5267ffffffffffffffff8d168a614ed6565b610f5c9190614eed565b610f669190614eed565b610f709190614f00565b9a50505050505050505050505b92915050565b600354604080517fcdc73d5100000000000000000000000000000000000000000000000000000000815290516000926001600160a01b03169163cdc73d5191600480830192869291908290030181865afa158015610fe5573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261100d9190810190614f3b565b6005549091506001600160a01b031660005b825181101561114257600083828151811061103c5761103c614fca565b60209081029190910101516040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529091506000906001600160a01b038316906370a0823190602401602060405180830381865afa1580156110aa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110ce9190614ff9565b90508015611138576110ea6001600160a01b0383168583611ed1565b816001600160a01b0316846001600160a01b03167f508d7d183612c18fc339b42618912b9fa3239f631dd7ec0671f950200a0fa66e8360405161112f91815260200190565b60405180910390a35b505060010161101f565b505050565b6040517fbbe4f6db0000000000000000000000000000000000000000000000000000000081526001600160a01b0382811660048301526000917f00000000000000000000000000000000000000000000000000000000000000009091169063bbe4f6db90602401602060405180830381865afa1580156111cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111ef9190615012565b9392505050565b6111fe611f3c565b61120781611f98565b50565b611212611f3c565b61121c82826125ee565b5050565b6001546001600160a01b0316331461127a5760405162461bcd60e51b815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e6572000000000000000000006044820152606401610987565b60008054337fffffffffffffffffffffffff0000000000000000000000000000000000000000808316821784556001805490911690556040516001600160a01b0390921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b67ffffffffffffffff8082166000908152600660205260408120600201549091610f7d9174010000000000000000000000000000000000000000900416600161502f565b611335611f3c565b611207816129ce565b611346611f3c565b61120781612aab565b67ffffffffffffffff84166000908152600660205260408120816113768288888888612cc4565b905060005b8160e00151518110156117635760006113976040890189614e1c565b838181106113a7576113a7614fca565b9050604002018036038101906113bd9190615057565b905080602001516000036113fd576040517f5cf0444900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061140d8a8360000151611147565b90506001600160a01b03811615806114c357506040517f01ffc9a70000000000000000000000000000000000000000000000000000000081527faff2afbf0000000000000000000000000000000000000000000000000000000060048201526001600160a01b038216906301ffc9a790602401602060405180830381865afa15801561149d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114c19190615091565b155b156115085781516040517fbf16aab60000000000000000000000000000000000000000000000000000000081526001600160a01b039091166004820152602401610987565b6000816001600160a01b0316639a4575b96040518060a001604052808d80600001906115349190614dce565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050509082525067ffffffffffffffff8f166020808301919091526001600160a01b03808e16604080850191909152918901516060840152885116608090920191909152516001600160e01b031960e084901b1681526115c791906004016150ae565b6000604051808303816000875af11580156115e6573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261160e919081019061517b565b9050602081602001515111801561166f575067ffffffffffffffff8b16600090815260086020908152604080832086516001600160a01b0316845282529091205490820151516e01000000000000000000000000000090910463ffffffff16105b156116b45782516040517f36f536ca0000000000000000000000000000000000000000000000000000000081526001600160a01b039091166004820152602401610987565b600186015481516116e591790100000000000000000000000000000000000000000000000000900460e01b90613a20565b60408051608081019091526001600160a01b03831660a08201528060c081016040516020818303038152906040528152602001826000015181526020018260200151815260200184602001518152508560e00151858151811061174a5761174a614fca565b602002602001018190525050505080600101905061137b565b50611772818360030154613a5a565b81515260405167ffffffffffffffff8816907f0f07cd31e53232da9125e517f09550fdde74bf43d6a0a76ebd41674dafe2ab29906117b19084906152b0565b60405180910390a251519150505b949350505050565b6117cf611f3c565b61120781613b3c565b60606040517f9e7177c800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff8516600090815260066020526040902080546301000000900463ffffffff168511156118855780546040517f86933789000000000000000000000000000000000000000000000000000000008152630100000090910463ffffffff16600482015260248101869052604401610987565b8054610100900461ffff168411156118c9576040517f4c056b6a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001810154604080516020601f860181900481028201810190925284815261192c92790100000000000000000000000000000000000000000000000000900460e01b918690869081908401838280828437600092019190915250613a2092505050565b505050505050565b6000808083815b81811015611d0357600087878381811061195757611957614fca565b90506040020180360381019061196d9190615057565b905060006001600160a01b03166119888c8360000151611147565b6001600160a01b0316036119d65780516040517fbf16aab60000000000000000000000000000000000000000000000000000000081526001600160a01b039091166004820152602401610987565b67ffffffffffffffff8b16600090815260086020908152604080832084516001600160a01b03168452825291829020825160c081018452905463ffffffff8082168352640100000000820481169383019390935268010000000000000000810461ffff16938201939093526a01000000000000000000008304821660608201526e01000000000000000000000000000083049091166080820152720100000000000000000000000000000000000090910460ff16151560a08201819052611b445767ffffffffffffffff8c1660009081526006602052604090208054611ae490790100000000000000000000000000000000000000000000000000900461ffff16662386f26fc10000614ed6565b611aee9089614eed565b8154909850611b22907b01000000000000000000000000000000000000000000000000000000900463ffffffff16886153c6565b6001820154909750611b3a9063ffffffff16876153c6565b9550505050611cfb565b604081015160009061ffff1615611c4b5760008c6001600160a01b031684600001516001600160a01b031614611c035760035484516040517f4ab35b0b0000000000000000000000000000000000000000000000000000000081526001600160a01b039182166004820152911690634ab35b0b90602401602060405180830381865afa158015611bd8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bfc91906153e3565b9050611c06565b508a5b620186a0836040015161ffff16611c338660200151846001600160e01b0316613bfd90919063ffffffff16565b611c3d9190614ed6565b611c479190614f00565b9150505b6060820151611c5a90886153c6565b9650816080015186611c6c91906153c6565b8251909650600090611c8b9063ffffffff16662386f26fc10000614ed6565b905080821015611caa57611c9f818a614eed565b985050505050611cfb565b6000836020015163ffffffff16662386f26fc10000611cc99190614ed6565b905080831115611ce957611cdd818b614eed565b99505050505050611cfb565b611cf3838b614eed565b995050505050505b60010161193b565b5050955095509592505050565b60008063ffffffff8316611d2560e086614ed6565b611d31876101c0614eed565b611d3b9190614eed565b611d459190614eed565b67ffffffffffffffff8816600090815260066020526040812080549293509171010000000000000000000000000000000000810463ffffffff1690611da7907501000000000000000000000000000000000000000000900461ffff1685614ed6565b611db19190614eed565b825490915077010000000000000000000000000000000000000000000000900461ffff16611def6dffffffffffffffffffffffffffff8a1683614ed6565b611df99190614ed6565b611e0990655af3107a4000614ed6565b9998505050505050505050565b60408051808201909152600080825260208201526000611e3c8585856101800151613c25565b9050826060015163ffffffff1681600001511115611e86576040517f4c4fc93a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b826101e001518015611e9a57508060200151155b156117bf576040517fee433e9900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b03167fa9059cbb00000000000000000000000000000000000000000000000000000000179052611142908490613d9e565b6000546001600160a01b03163314611f965760405162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610987565b565b60005b815181101561121c576000828281518110611fb857611fb8614fca565b602002602001015190506000838381518110611fd657611fd6614fca565b60200260200101516000015190508067ffffffffffffffff166000148061200e57506020820151610180015167ffffffffffffffff16155b8061204c5750602082015161020001516001600160e01b0319167f2812d52c0000000000000000000000000000000000000000000000000000000014155b1561208f576040517fc35aa79d00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff82166004820152602401610987565b6000600660008367ffffffffffffffff1667ffffffffffffffff16815260200190815260200160002090506000836040015190506000604051806080016040528086602001518152602001836001600160a01b031681526020018460020160149054906101000a900467ffffffffffffffff1667ffffffffffffffff1681526020018460030154815250905080600001518360000160008201518160000160006101000a81548160ff02191690831515021790555060208201518160000160016101000a81548161ffff021916908361ffff16021790555060408201518160000160036101000a81548163ffffffff021916908363ffffffff16021790555060608201518160000160076101000a81548163ffffffff021916908363ffffffff160217905550608082015181600001600b6101000a81548163ffffffff021916908363ffffffff16021790555060a082015181600001600f6101000a81548161ffff021916908361ffff16021790555060c08201518160000160116101000a81548163ffffffff021916908363ffffffff16021790555060e08201518160000160156101000a81548161ffff021916908361ffff1602179055506101008201518160000160176101000a81548161ffff021916908361ffff1602179055506101208201518160000160196101000a81548161ffff021916908361ffff16021790555061014082015181600001601b6101000a81548163ffffffff021916908363ffffffff1602179055506101608201518160010160006101000a81548163ffffffff021916908363ffffffff1602179055506101808201518160010160046101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055506101a082015181600101600c6101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055506101c08201518160010160146101000a81548163ffffffff021916908363ffffffff1602179055506101e08201518160010160186101000a81548160ff0219169083151502179055506102008201518160010160196101000a81548163ffffffff021916908360e01c021790555090505082600301546000801b036124de57604080517f130ac867e79e2789f923760a88743d292acdf7002139a588206e2260f73f7321602082015267ffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000811692820192909252908516606082015230608082015260a00160408051601f1981840301815291905280516020909101206060820181905260038401556001600160a01b03821615612497576002830180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0384161790555b8367ffffffffffffffff167f66951b52bb33e5d462611460f7aa53069005b173e002d9224c15fb986e4ead8f846040516124d191906153fe565b60405180910390a26125de565b60028301546001600160a01b03838116911614612533576040517fc35aa79d00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff85166004820152602401610987565b60208560200151610160015163ffffffff16101561259757602085015161016001516040517f24ecdc020000000000000000000000000000000000000000000000000000000081526000600482015263ffffffff9091166024820152604401610987565b8367ffffffffffffffff167fd42d3a670a4f1ab5d8703efa22bb17041446365cfcfa33980ced685a080cf7cb86602001516040516125d591906155b4565b60405180910390a25b5050505050806001019050611f9b565b60005b82518110156128f757600083828151811061260e5761260e614fca565b6020026020010151905060008160000151905060005b8260200151518110156128e95760008360200151828151811061264957612649614fca565b602002602001015160200151905060008460200151838151811061266f5761266f614fca565b60200260200101516000015190506020826080015163ffffffff1610156126df5760808201516040517f24ecdc020000000000000000000000000000000000000000000000000000000081526001600160a01b038316600482015263ffffffff9091166024820152604401610987565b67ffffffffffffffff841660008181526008602090815260408083206001600160a01b0386168085529083529281902086518154938801518389015160608a015160808b015160a08c015115157201000000000000000000000000000000000000027fffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffff63ffffffff9283166e01000000000000000000000000000002167fffffffffffffffffffffffffff0000000000ffffffffffffffffffffffffffff9383166a0100000000000000000000027fffffffffffffffffffffffffffffffffffff00000000ffffffffffffffffffff61ffff9096166801000000000000000002959095167fffffffffffffffffffffffffffffffffffff000000000000ffffffffffffffff968416640100000000027fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000909b16939097169290921798909817939093169390931717919091161792909217909155519091907f94967ae9ea7729ad4f54021c1981765d2b1d954f7c92fbec340aa0a54f46b8b5906128d7908690600060c08201905063ffffffff80845116835280602085015116602084015261ffff60408501511660408401528060608501511660608401528060808501511660808401525060a0830151151560a083015292915050565b60405180910390a35050600101612624565b5050508060010190506125f1565b5060005b815181101561114257600082828151811061291857612918614fca565b6020026020010151600001519050600083838151811061293a5761293a614fca565b60209081029190910181015181015167ffffffffffffffff841660008181526008845260408082206001600160a01b038516808452955280822080547fffffffffffffffffffffffffff000000000000000000000000000000000000001690555192945090917f4de5b1bcbca6018c11303a2c3f4a4b4f22a1c741d8c4ba430d246ac06c5ddf8b9190a350506001016128fb565b60005b815181101561121c5760008282815181106129ee576129ee614fca565b60200260200101516000015190506000838381518110612a1057612a10614fca565b6020908102919091018101518101516001600160a01b03841660008181526007845260409081902080547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001667ffffffffffffffff85169081179091559051908152919350917fbb77da6f7210cdd16904228a9360133d1d7dfff99b1bc75f128da5b53e28f97d910160405180910390a250506001016129d1565b60208101516001600160a01b03161580612ad0575060608101516001600160a01b0316155b15612b07576040517f35be3ac800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8051600280547fffffffffffffffffffffffff00000000000000000000000000000000000000009081166001600160a01b03938416179091556020808401516003805484169185169190911790556040808501516004805485169186169190911790556060808601516005805490951690861617909355805160c0810182527f0000000000000000000000000000000000000000000000000000000000000000851681527f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff16928101929092527f00000000000000000000000000000000000000000000000000000000000000006bffffffffffffffffffffffff16828201527f00000000000000000000000000000000000000000000000000000000000000008416928201929092527f0000000000000000000000000000000000000000000000000000000000000000831660808201527f000000000000000000000000000000000000000000000000000000000000000090921660a0830152517f4012fe74115805c44a121d8f9edc3d234df8f05f53b52864b8e5e8a30384b8aa91612cb99184906155c3565b60405180910390a150565b604080516101a08101825260006101008201818152610120830182905261014083018290526101608301829052610180830182905282526020820181905260609282018390528282018390526080820183905260a0820181905260c082015260e08101919091526040517f2cbc26bb000000000000000000000000000000000000000000000000000000008152608086901b77ffffffffffffffff000000000000000000000000000000001660048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632cbc26bb90602401602060405180830381865afa158015612dc6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612dea9190615091565b15612e2d576040517ffdbd6a7200000000000000000000000000000000000000000000000000000000815267ffffffffffffffff86166004820152602401610987565b6001600160a01b038216612e6d576040517fa4ec747900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002546001600160a01b03163314612eb1576040517f1c0a352900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b855460ff16612ef8576040517f99ac52f200000000000000000000000000000000000000000000000000000000815267ffffffffffffffff86166004820152602401610987565b6000612f076040860186614e1c565b9150612f29905086612f1c6020880188614dce565b9050836109bc8980614dce565b801561301d576004546001600160a01b0316801561301b576040517fe0a0e5060000000000000000000000000000000000000000000000000000000081526001600160a01b0382169063e0a0e50690612f88908a908a90600401615730565b600060405180830381600087803b158015612fa257600080fd5b505af1925050508015612fb3575060015b61301b573d808015612fe1576040519150601f19603f3d011682016040523d82523d6000602084013e612fe6565b606091505b50806040517f09c253250000000000000000000000000000000000000000000000000000000081526004016109879190614268565b505b60006001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016613059608088016060890161418f565b6001600160a01b03160361306e575083613129565b6003546001600160a01b03166241e5be61308e6080890160608a0161418f565b60405160e083901b6001600160e01b03191681526001600160a01b039182166004820152602481018990527f00000000000000000000000000000000000000000000000000000000000000009091166044820152606401602060405180830381865afa158015613102573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131269190614ff9565b90505b613139608087016060880161418f565b6001600160a01b03167f075a2720282fdf622141dae0b048ef90a21a7e57c134c76912d19d006b3b3f6f8260405161317391815260200190565b60405180910390a27f00000000000000000000000000000000000000000000000000000000000000006bffffffffffffffffffffffff1681111561321a576040517f6a92a483000000000000000000000000000000000000000000000000000000008152600481018290526bffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000166024820152604401610987565b60006134b061322c6080890189614dce565b8b600001604051806102200160405290816000820160009054906101000a900460ff161515151581526020016000820160019054906101000a900461ffff1661ffff1661ffff1681526020016000820160039054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016000820160079054906101000a900463ffffffff1663ffffffff1663ffffffff16815260200160008201600b9054906101000a900463ffffffff1663ffffffff1663ffffffff16815260200160008201600f9054906101000a900461ffff1661ffff1661ffff1681526020016000820160119054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016000820160159054906101000a900461ffff1661ffff1661ffff1681526020016000820160179054906101000a900461ffff1661ffff1661ffff1681526020016000820160199054906101000a900461ffff1661ffff1661ffff16815260200160008201601b9054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016001820160009054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016001820160049054906101000a900467ffffffffffffffff1667ffffffffffffffff1667ffffffffffffffff16815260200160018201600c9054906101000a900467ffffffffffffffff1667ffffffffffffffff1667ffffffffffffffff1681526020016001820160149054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016001820160189054906101000a900460ff161515151581526020016001820160199054906101000a900460e01b6001600160e01b0319166001600160e01b03191681525050611e16565b60200151613570576040517fea458c0c00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff891660048201526001600160a01b0386811660248301527f0000000000000000000000000000000000000000000000000000000000000000169063ea458c0c906044016020604051808303816000875af1158015613549573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061356d919061584e565b90505b604080516101a081019091526000610100820181815267ffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000081166101208501528b811661014085015260028d018054939493849392610160850192916014916135f9917401000000000000000000000000000000000000000090041661586b565b91906101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905567ffffffffffffffff1681526020018567ffffffffffffffff168152508152602001876001600160a01b0316815260200189806020019061365f9190614dce565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505050908252506020016136a38a80614dce565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505050908252506020016139716136ed60808c018c614dce565b8e600001604051806102200160405290816000820160009054906101000a900460ff161515151581526020016000820160019054906101000a900461ffff1661ffff1661ffff1681526020016000820160039054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016000820160079054906101000a900463ffffffff1663ffffffff1663ffffffff16815260200160008201600b9054906101000a900463ffffffff1663ffffffff1663ffffffff16815260200160008201600f9054906101000a900461ffff1661ffff1661ffff1681526020016000820160119054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016000820160159054906101000a900461ffff1661ffff1661ffff1681526020016000820160179054906101000a900461ffff1661ffff1661ffff1681526020016000820160199054906101000a900461ffff1661ffff1661ffff16815260200160008201601b9054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016001820160009054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016001820160049054906101000a900467ffffffffffffffff1667ffffffffffffffff1667ffffffffffffffff16815260200160018201600c9054906101000a900467ffffffffffffffff1667ffffffffffffffff1667ffffffffffffffff1681526020016001820160149054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016001820160189054906101000a900460ff161515151581526020016001820160199054906101000a900460e01b6001600160e01b0319166001600160e01b03191681525050613e83565b815260200161398660808b0160608c0161418f565b6001600160a01b031681526020018881526020018567ffffffffffffffff8111156139b3576139b36144ed565b604051908082528060200260200182016040528015613a0f57816020015b6139fc6040518060800160405280606081526020016060815260200160608152602001600081525090565b8152602001906001900390816139d15790505b5090529a9950505050505050505050565b7fd7ed2ad4000000000000000000000000000000000000000000000000000000006001600160e01b031983160161121c5761114281613f03565b60008060001b82846020015185606001518660000151606001518760000151608001518860a001518960c00151604051602001613a9c96959493929190615892565b604051602081830303815290604052805190602001208560400151805190602001208660e00151604051602001613ad391906158e6565b60408051601f1981840301815282825280516020918201206080808c0151805190840120928501989098529183019590955260608201939093529384015260a083015260c082015260e00160405160208183030381529060405280519060200120905092915050565b336001600160a01b03821603613b945760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610987565b600180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000670de0b6b3a7640000613c1b836001600160e01b038616614ed6565b6111ef9190614f00565b60408051808201909152600080825260208201526000839003613c6657506040805180820190915267ffffffffffffffff82168152600060208201526111ef565b6000613c7284866158f9565b90506000613c838560048189615929565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509293505050506001600160e01b031982167fe7e230f00000000000000000000000000000000000000000000000000000000001613d085780806020019051810190613cff9190615953565b925050506111ef565b7f6859a837000000000000000000000000000000000000000000000000000000006001600160e01b0319831601613d6c57604051806040016040528082806020019051810190613d589190614ff9565b8152600060209091015292506111ef915050565b6040517f5247fdce00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000613df3826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316613f5e9092919063ffffffff16565b8051909150156111425780806020019051810190613e119190615091565b6111425760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610987565b6102008101516060907fd7ed2ad4000000000000000000000000000000000000000000000000000000006001600160e01b0319821601613efb57613ec8858585611e16565b604080518251602080830191909152909201511515908201526060016040516020818303038152906040529150506111ef565b509392505050565b60008151602014613f4257816040517f8d666f600000000000000000000000000000000000000000000000000000000081526004016109879190614268565b610f7d82806020019051810190613f599190614ff9565b613f6d565b60606117bf8484600085613fda565b60006001600160a01b03821180613f85575061040082105b15613fd65760408051602081018490520160408051601f19818403018152908290527f8d666f6000000000000000000000000000000000000000000000000000000000825261098791600401614268565b5090565b6060824710156140525760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608401610987565b600080866001600160a01b0316858760405161406e919061598b565b60006040518083038185875af1925050503d80600081146140ab576040519150601f19603f3d011682016040523d82523d6000602084013e6140b0565b606091505b50915091506140c1878383876140cc565b979650505050505050565b6060831561413b578251600003614134576001600160a01b0385163b6141345760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610987565b50816117bf565b6117bf83838151156141505781518083602001fd5b8060405162461bcd60e51b81526004016109879190614268565b6001600160a01b038116811461120757600080fd5b803561418a8161416a565b919050565b6000602082840312156141a157600080fd5b81356111ef8161416a565b60c08101610f7d82846001600160a01b0380825116835267ffffffffffffffff60208301511660208401526bffffffffffffffffffffffff60408301511660408401528060608301511660608401528060808301511660808401528060a08301511660a0840152505050565b60005b8381101561423357818101518382015260200161421b565b50506000910152565b60008151808452614254816020860160208601614218565b601f01601f19169290920160200192915050565b6020815260006111ef602083018461423c565b67ffffffffffffffff8116811461120757600080fd5b803561418a8161427b565b600060a082840312156142ae57600080fd5b50919050565b600080604083850312156142c757600080fd5b82356142d28161427b565b9150602083013567ffffffffffffffff8111156142ee57600080fd5b6142fa8582860161429c565b9150509250929050565b6000806040838503121561431757600080fd5b82356143228161427b565b915060208301356143328161416a565b809150509250929050565b60006020828403121561434f57600080fd5b81356111ef8161427b565b8051151582526020810151614375602084018261ffff169052565b50604081015161438d604084018263ffffffff169052565b5060608101516143a5606084018263ffffffff169052565b5060808101516143bd608084018263ffffffff169052565b5060a08101516143d360a084018261ffff169052565b5060c08101516143eb60c084018263ffffffff169052565b5060e081015161440160e084018261ffff169052565b506101008181015161ffff9081169184019190915261012080830151909116908301526101408082015163ffffffff90811691840191909152610160808301518216908401526101808083015167ffffffffffffffff908116918501919091526101a080840151909116908401526101c080830151909116908301526101e080820151151590830152610200908101516001600160e01b031916910152565b6000610280820190506144b482845161435a565b60208301516001600160a01b0316610220830152604083015167ffffffffffffffff166102408301526060909201516102609091015290565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040516060810167ffffffffffffffff8111828210171561453f5761453f6144ed565b60405290565b604051610220810167ffffffffffffffff8111828210171561453f5761453f6144ed565b6040805190810167ffffffffffffffff8111828210171561453f5761453f6144ed565b60405160c0810167ffffffffffffffff8111828210171561453f5761453f6144ed565b604051601f8201601f1916810167ffffffffffffffff811182821017156145d8576145d86144ed565b604052919050565b600067ffffffffffffffff8211156145fa576145fa6144ed565b5060051b60200190565b801515811461120757600080fd5b803561418a81614604565b803561ffff8116811461418a57600080fd5b803563ffffffff8116811461418a57600080fd5b80356001600160e01b03198116811461418a57600080fd5b6000602080838503121561466e57600080fd5b823567ffffffffffffffff81111561468557600080fd5b8301601f8101851361469657600080fd5b80356146a96146a4826145e0565b6145af565b81815261026091820283018401918482019190888411156146c957600080fd5b938501935b8385101561487457848903818112156146e75760008081fd5b6146ef61451c565b86356146fa8161427b565b8152610220601f1983018113156147115760008081fd5b614719614545565b9250614726898901614612565b83526040614735818a0161461d565b8a8501526060614746818b0161462f565b828601526080614757818c0161462f565b8287015260a0915061476a828c0161462f565b9086015260c061477b8b820161461d565b8287015260e0915061478e828c0161462f565b908601526101006147a08b820161461d565b8287015261012091506147b4828c0161461d565b908601526101406147c68b820161461d565b8287015261016091506147da828c0161462f565b908601526101806147ec8b820161462f565b828701526101a09150614800828c01614291565b908601526101c06148128b8201614291565b828701526101e09150614826828c0161462f565b908601526102006148388b8201614612565b82870152614847848c01614643565b818701525050838a84015261485f6102408a0161417f565b908301525084525093840193918501916146ce565b50979650505050505050565b60808101610f7d828480516001600160a01b03908116835260208083015182169084015260408083015182169084015260609182015116910152565b600082601f8301126148cd57600080fd5b813560206148dd6146a4836145e0565b82815260069290921b840181019181810190868411156148fc57600080fd5b8286015b8481101561494d57604081890312156149195760008081fd5b614921614569565b813561492c8161427b565b81528185013561493b8161416a565b81860152835291830191604001614900565b509695505050505050565b6000806040838503121561496b57600080fd5b67ffffffffffffffff8335111561498157600080fd5b83601f84358501011261499357600080fd5b6149a36146a484358501356145e0565b8335840180358083526020808401939260059290921b909101018610156149c957600080fd5b602085358601015b85358601803560051b01602001811015614b9c5767ffffffffffffffff813511156149fb57600080fd5b6040601f19823588358901018903011215614a1557600080fd5b614a1d614569565b614a306020833589358a0101013561427b565b863587018235016020810135825267ffffffffffffffff6040909101351115614a5857600080fd5b86358701823501604081013501603f81018913614a7457600080fd5b614a846146a460208301356145e0565b602082810135808352908201919060e00283016040018b1015614aa657600080fd5b604083015b604060e0602086013502850101811015614b835760e0818d031215614acf57600080fd5b614ad7614569565b614ae1823561416a565b8135815260c0601f19838f03011215614af957600080fd5b614b0161458c565b614b0d6020840161462f565b8152614b1b6040840161462f565b6020820152614b2c6060840161461d565b6040820152614b3d6080840161462f565b6060820152614b4e60a0840161462f565b6080820152614b6060c0840135614604565b60c083013560a0820152602082810191909152908452929092019160e001614aab565b50602084810191909152928652505092830192016149d1565b5092505067ffffffffffffffff60208401351115614bb957600080fd5b614bc984602085013585016148bc565b90509250929050565b60006020808385031215614be557600080fd5b823567ffffffffffffffff811115614bfc57600080fd5b8301601f81018513614c0d57600080fd5b8035614c1b6146a4826145e0565b81815260069190911b82018301908381019087831115614c3a57600080fd5b928401925b828410156140c15760408489031215614c585760008081fd5b614c60614569565b8435614c6b8161416a565b815284860135614c7a8161427b565b8187015282526040939093019290840190614c3f565b600060808284031215614ca257600080fd5b6040516080810181811067ffffffffffffffff82111715614cc557614cc56144ed565b6040528235614cd38161416a565b81526020830135614ce38161416a565b60208201526040830135614cf68161416a565b60408201526060830135614d098161416a565b60608201529392505050565b60008060008060808587031215614d2b57600080fd5b8435614d368161427b565b9350602085013567ffffffffffffffff811115614d5257600080fd5b614d5e8782880161429c565b935050604085013591506060850135614d768161416a565b939692955090935050565b6020808252825182820181905260009190848201906040850190845b81811015614dc25783516001600160a01b031683529284019291840191600101614d9d565b50909695505050505050565b6000808335601e19843603018112614de557600080fd5b83018035915067ffffffffffffffff821115614e0057600080fd5b602001915036819003821315614e1557600080fd5b9250929050565b6000808335601e19843603018112614e3357600080fd5b83018035915067ffffffffffffffff821115614e4e57600080fd5b6020019150600681901b3603821315614e1557600080fd5b80516001600160e01b038116811461418a57600080fd5b60008060408385031215614e9057600080fd5b614e9983614e66565b9150614bc960208401614e66565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8082028115828204841417610f7d57610f7d614ea7565b80820180821115610f7d57610f7d614ea7565b600082614f36577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b60006020808385031215614f4e57600080fd5b825167ffffffffffffffff811115614f6557600080fd5b8301601f81018513614f7657600080fd5b8051614f846146a4826145e0565b81815260059190911b82018301908381019087831115614fa357600080fd5b928401925b828410156140c1578351614fbb8161416a565b82529284019290840190614fa8565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60006020828403121561500b57600080fd5b5051919050565b60006020828403121561502457600080fd5b81516111ef8161416a565b67ffffffffffffffff81811683821601908082111561505057615050614ea7565b5092915050565b60006040828403121561506957600080fd5b615071614569565b823561507c8161416a565b81526020928301359281019290925250919050565b6000602082840312156150a357600080fd5b81516111ef81614604565b602081526000825160a060208401526150ca60c084018261423c565b905067ffffffffffffffff602085015116604084015260408401516001600160a01b038082166060860152606086015160808601528060808701511660a086015250508091505092915050565b600082601f83011261512857600080fd5b815167ffffffffffffffff811115615142576151426144ed565b6151556020601f19601f840116016145af565b81815284602083860101111561516a57600080fd5b6117bf826020830160208701614218565b60006020828403121561518d57600080fd5b815167ffffffffffffffff808211156151a557600080fd5b90830190604082860312156151b957600080fd5b6151c1614569565b8251828111156151d057600080fd5b6151dc87828601615117565b8252506020830151828111156151f157600080fd5b6151fd87828601615117565b60208301525095945050505050565b600082825180855260208086019550808260051b84010181860160005b848110156152a357601f1986840301895281516080815181865261524f8287018261423c565b9150508582015185820387870152615267828261423c565b91505060408083015186830382880152615281838261423c565b6060948501519790940196909652505098840198925090830190600101615229565b5090979650505050505050565b6020815261530160208201835180518252602081015167ffffffffffffffff808216602085015280604084015116604085015280606084015116606085015280608084015116608085015250505050565b6000602083015161531d60c08401826001600160a01b03169052565b5060408301516101808060e085015261533a6101a085018361423c565b91506060850151601f198086850301610100870152615359848361423c565b9350608087015191508086850301610120870152615377848361423c565b935060a087015191506153966101408701836001600160a01b03169052565b60c087015161016087015260e08701519150808685030183870152506153bc838261520c565b9695505050505050565b63ffffffff81811683821601908082111561505057615050614ea7565b6000602082840312156153f557600080fd5b6111ef82614e66565b815460ff81161515825261028082019061ffff600882901c8116602085015263ffffffff601883901c8116604086015261544560608601828560381c1663ffffffff169052565b61545c60808601828560581c1663ffffffff169052565b61547160a08601838560781c1661ffff169052565b61548860c08601828560881c1663ffffffff169052565b61549d60e08601838560a81c1661ffff169052565b6154b36101008601838560b81c1661ffff169052565b6154c96101208601838560c81c1661ffff169052565b6154e16101408601828560d81c1663ffffffff169052565b600186015463ffffffff82821616610160870152925067ffffffffffffffff602084901c8116610180870152915061552b6101a08601838560601c1667ffffffffffffffff169052565b6155436101c08601828560a01c1663ffffffff169052565b506155596101e0850160ff8460c01c1615159052565b601882901b6001600160e01b03191661020085015260028501546001600160a01b03811661022086015291506155a16102408501828460a01c1667ffffffffffffffff169052565b5050600383015461026083015292915050565b6102208101610f7d828461435a565b610140810161563082856001600160a01b0380825116835267ffffffffffffffff60208301511660208401526bffffffffffffffffffffffff60408301511660408401528060608301511660608401528060808301511660808401528060a08301511660a0840152505050565b82516001600160a01b0390811660c08401526020840151811660e0840152604084015181166101008401526060840151166101208301526111ef565b6000808335601e1984360301811261568357600080fd5b830160208101925035905067ffffffffffffffff8111156156a357600080fd5b803603821315614e1557600080fd5b818352818160208501375060006020828401015260006020601f19601f840116840101905092915050565b8183526000602080850194508260005b858110156157255781356157008161416a565b6001600160a01b031687528183013583880152604096870196909101906001016156ed565b509495945050505050565b600067ffffffffffffffff808516835260406020840152615751848561566c565b60a0604086015261576660e0860182846156b2565b915050615776602086018661566c565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0808785030160608801526157ac8483856156b2565b935060408801359250601e198836030183126157c757600080fd5b602092880192830192359150848211156157e057600080fd5b8160061b36038313156157f257600080fd5b808785030160808801526158078483856156dd565b94506158156060890161417f565b6001600160a01b03811660a08901529350615833608089018961566c565b94509250808786030160c088015250506140c18383836156b2565b60006020828403121561586057600080fd5b81516111ef8161427b565b600067ffffffffffffffff80831681810361588857615888614ea7565b6001019392505050565b60006001600160a01b03808916835260c060208401526158b560c084018961423c565b67ffffffffffffffff97881660408501529590961660608301525091909316608082015260a0019190915292915050565b6020815260006111ef602083018461520c565b6001600160e01b031981358181169160048510156159215780818660040360031b1b83161692505b505092915050565b6000808585111561593957600080fd5b8386111561594657600080fd5b5050820193919092039150565b60006040828403121561596557600080fd5b61596d614569565b82518152602083015161597f81614604565b60208201529392505050565b6000825161599d818460208701614218565b919091019291505056fea164736f6c6343000818000a", } var EVM2EVMMultiOnRampABI = EVM2EVMMultiOnRampMetaData.ABI @@ -794,7 +805,7 @@ func (it *EVM2EVMMultiOnRampCCIPSendRequestedIterator) Close() error { type EVM2EVMMultiOnRampCCIPSendRequested struct { DestChainSelector uint64 - Message InternalEVM2EVMMessage + Message InternalEVM2AnyRampMessage Raw types.Log } @@ -2209,7 +2220,7 @@ func (EVM2EVMMultiOnRampAdminSet) Topic() common.Hash { } func (EVM2EVMMultiOnRampCCIPSendRequested) Topic() common.Hash { - return common.HexToHash("0xc79f9c3e610deac14de4e704195fe17eab0983ee9916866bc04d16a00f54daa6") + return common.HexToHash("0x0f07cd31e53232da9125e517f09550fdde74bf43d6a0a76ebd41674dafe2ab29") } func (EVM2EVMMultiOnRampConfigSet) Topic() common.Hash { @@ -2217,11 +2228,11 @@ func (EVM2EVMMultiOnRampConfigSet) Topic() common.Hash { } func (EVM2EVMMultiOnRampDestChainAdded) Topic() common.Hash { - return common.HexToHash("0x4be92415590c4554edbd7d3fbf308eeb6e5d00ababade0791f835b4a18416ed6") + return common.HexToHash("0x66951b52bb33e5d462611460f7aa53069005b173e002d9224c15fb986e4ead8f") } func (EVM2EVMMultiOnRampDestChainDynamicConfigUpdated) Topic() common.Hash { - return common.HexToHash("0x1173725509a61aed633c58cd12e31c104cd26ca24f6c270c8ac81c5f6f12e8e9") + return common.HexToHash("0xd42d3a670a4f1ab5d8703efa22bb17041446365cfcfa33980ced685a080cf7cb") } func (EVM2EVMMultiOnRampFeePaid) Topic() common.Hash { diff --git a/core/gethwrappers/ccip/generated/message_hasher/message_hasher.go b/core/gethwrappers/ccip/generated/message_hasher/message_hasher.go index f41fb552d4..8fb8d08b6f 100644 --- a/core/gethwrappers/ccip/generated/message_hasher/message_hasher.go +++ b/core/gethwrappers/ccip/generated/message_hasher/message_hasher.go @@ -28,19 +28,13 @@ var ( _ = abi.ConvertType ) -type ClientEVMTokenAmount struct { - Token common.Address - Amount *big.Int -} - type InternalAny2EVMRampMessage struct { - Header InternalRampMessageHeader - Sender []byte - Data []byte - Receiver common.Address - GasLimit *big.Int - TokenAmounts []ClientEVMTokenAmount - SourceTokenData [][]byte + Header InternalRampMessageHeader + Sender []byte + Data []byte + Receiver common.Address + GasLimit *big.Int + TokenAmounts []InternalRampTokenAmount } type InternalRampMessageHeader struct { @@ -51,9 +45,16 @@ type InternalRampMessageHeader struct { Nonce uint64 } +type InternalRampTokenAmount struct { + SourcePoolAddress []byte + DestTokenAddress []byte + ExtraData []byte + Amount *big.Int +} + var MessageHasherMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"leafDomainSeparator\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"implicitMetadataHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"fixedSizeFieldsHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"dataHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"tokenAmountsHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"sourceTokenDataHash\",\"type\":\"bytes32\"}],\"name\":\"encodeFinalHashPreimage\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"sender\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"}],\"name\":\"encodeFixedSizeFieldsHashPreimage\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"any2EVMMessageHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"onRamp\",\"type\":\"bytes\"}],\"name\":\"encodeMetadataHashPreimage\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes[]\",\"name\":\"sourceTokenData\",\"type\":\"bytes[]\"}],\"name\":\"encodeSourceTokenDataHashPreimage\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structClient.EVMTokenAmount[]\",\"name\":\"tokenAmounts\",\"type\":\"tuple[]\"}],\"name\":\"encodeTokenAmountsHashPreimage\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"}],\"internalType\":\"structInternal.RampMessageHeader\",\"name\":\"header\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"sender\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structClient.EVMTokenAmount[]\",\"name\":\"tokenAmounts\",\"type\":\"tuple[]\"},{\"internalType\":\"bytes[]\",\"name\":\"sourceTokenData\",\"type\":\"bytes[]\"}],\"internalType\":\"structInternal.Any2EVMRampMessage\",\"name\":\"message\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"onRamp\",\"type\":\"bytes\"}],\"name\":\"hash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"}]", - Bin: "0x608060405234801561001057600080fd5b50610bb8806100206000396000f3fe608060405234801561001057600080fd5b50600436106100725760003560e01c8063bf0619ad11610050578063bf0619ad146100d0578063c2cfa165146100e3578063cd34ba82146100f657600080fd5b80633b61b52e14610077578063a1e747df1461009d578063a91d3aeb146100bd575b600080fd5b61008a6100853660046106e3565b610109565b6040519081526020015b60405180910390f35b6100b06100ab366004610812565b61011c565b60405161009491906108de565b6100b06100cb3660046108f1565b61014e565b6100b06100de366004610972565b610186565b6100b06100f13660046109b5565b6101bd565b6100b06101043660046109f2565b6101e6565b600061011583836101f9565b9392505050565b6060848484846040516020016101359493929190610a27565b6040516020818303038152906040529050949350505050565b606086868686868660405160200161016b96959493929190610a64565b60405160208183030381529060405290509695505050505050565b604080516020810188905290810186905260608181018690526080820185905260a0820184905260c082018390529060e00161016b565b6060816040516020016101d09190610ac4565b6040516020818303038152906040529050919050565b6060816040516020016101d09190610b46565b81516020808201516040928301519251600093849361023f937f2425b0b9f9054c76ff151b0a175b18f37a4a4e82013a72e9f15c9caa095ed21f93909291889101610a27565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815290829052805160209182012086518051888401516060808b0151908401516080808d015195015195976102a69794969395929491939101610a64565b604051602081830303815290604052805190602001208560400151805190602001208660a001516040516020016102dd9190610b46565b604051602081830303815290604052805190602001208760c001516040516020016103089190610ac4565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120908301979097528101949094526060840192909252608083015260a082015260c081019190915260e00160405160208183030381529060405280519060200120905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff811182821017156103d8576103d8610386565b60405290565b60405160e0810167ffffffffffffffff811182821017156103d8576103d8610386565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561044857610448610386565b604052919050565b803567ffffffffffffffff8116811461046857600080fd5b919050565b600060a0828403121561047f57600080fd5b60405160a0810181811067ffffffffffffffff821117156104a2576104a2610386565b604052823581529050806104b860208401610450565b60208201526104c960408401610450565b60408201526104da60608401610450565b60608201526104eb60808401610450565b60808201525092915050565b600082601f83011261050857600080fd5b813567ffffffffffffffff81111561052257610522610386565b61055360207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601610401565b81815284602083860101111561056857600080fd5b816020850160208301376000918101602001919091529392505050565b803573ffffffffffffffffffffffffffffffffffffffff8116811461046857600080fd5b600067ffffffffffffffff8211156105c3576105c3610386565b5060051b60200190565b600082601f8301126105de57600080fd5b813560206105f36105ee836105a9565b610401565b82815260069290921b8401810191818101908684111561061257600080fd5b8286015b84811015610658576040818903121561062f5760008081fd5b6106376103b5565b61064082610585565b81528185013585820152835291830191604001610616565b509695505050505050565b600082601f83011261067457600080fd5b813560206106846105ee836105a9565b82815260059290921b840181019181810190868411156106a357600080fd5b8286015b8481101561065857803567ffffffffffffffff8111156106c75760008081fd5b6106d58986838b01016104f7565b8452509183019183016106a7565b600080604083850312156106f657600080fd5b823567ffffffffffffffff8082111561070e57600080fd5b90840190610160828703121561072357600080fd5b61072b6103de565b610735878461046d565b815260a08301358281111561074957600080fd5b610755888286016104f7565b60208301525060c08301358281111561076d57600080fd5b610779888286016104f7565b60408301525061078b60e08401610585565b60608201526101008301356080820152610120830135828111156107ae57600080fd5b6107ba888286016105cd565b60a083015250610140830135828111156107d357600080fd5b6107df88828601610663565b60c083015250935060208501359150808211156107fb57600080fd5b50610808858286016104f7565b9150509250929050565b6000806000806080858703121561082857600080fd5b8435935061083860208601610450565b925061084660408601610450565b9150606085013567ffffffffffffffff81111561086257600080fd5b61086e878288016104f7565b91505092959194509250565b6000815180845260005b818110156108a057602081850181015186830182015201610884565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b602081526000610115602083018461087a565b60008060008060008060c0878903121561090a57600080fd5b86359550602087013567ffffffffffffffff81111561092857600080fd5b61093489828a016104f7565b95505061094360408801610585565b935061095160608801610450565b92506080870135915061096660a08801610450565b90509295509295509295565b60008060008060008060c0878903121561098b57600080fd5b505084359660208601359650604086013595606081013595506080810135945060a0013592509050565b6000602082840312156109c757600080fd5b813567ffffffffffffffff8111156109de57600080fd5b6109ea84828501610663565b949350505050565b600060208284031215610a0457600080fd5b813567ffffffffffffffff811115610a1b57600080fd5b6109ea848285016105cd565b848152600067ffffffffffffffff808616602084015280851660408401525060806060830152610a5a608083018461087a565b9695505050505050565b86815260c060208201526000610a7d60c083018861087a565b73ffffffffffffffffffffffffffffffffffffffff9690961660408301525067ffffffffffffffff9384166060820152608081019290925290911660a09091015292915050565b600060208083016020845280855180835260408601915060408160051b87010192506020870160005b82811015610b39577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0888603018452610b2785835161087a565b94509285019290850190600101610aed565b5092979650505050505050565b602080825282518282018190526000919060409081850190868401855b82811015610b9e578151805173ffffffffffffffffffffffffffffffffffffffff168552860151868501529284019290850190600101610b63565b509197965050505050505056fea164736f6c6343000818000a", + ABI: "[{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"leafDomainSeparator\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"implicitMetadataHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"fixedSizeFieldsHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"dataHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"tokenAmountsHash\",\"type\":\"bytes32\"}],\"name\":\"encodeFinalHashPreimage\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"sender\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"}],\"name\":\"encodeFixedSizeFieldsHashPreimage\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"any2EVMMessageHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"onRamp\",\"type\":\"bytes\"}],\"name\":\"encodeMetadataHashPreimage\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"sourcePoolAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"destTokenAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"extraData\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structInternal.RampTokenAmount[]\",\"name\":\"rampTokenAmounts\",\"type\":\"tuple[]\"}],\"name\":\"encodeTokenAmountsHashPreimage\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"}],\"internalType\":\"structInternal.RampMessageHeader\",\"name\":\"header\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"sender\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"bytes\",\"name\":\"sourcePoolAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"destTokenAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"extraData\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structInternal.RampTokenAmount[]\",\"name\":\"tokenAmounts\",\"type\":\"tuple[]\"}],\"internalType\":\"structInternal.Any2EVMRampMessage\",\"name\":\"message\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"onRamp\",\"type\":\"bytes\"}],\"name\":\"hash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"}]", + Bin: "0x608060405234801561001057600080fd5b50610af3806100206000396000f3fe608060405234801561001057600080fd5b50600436106100675760003560e01c806399df8d051161005057806399df8d05146100b6578063a1e747df14610101578063a91d3aeb1461011457600080fd5b8063902e94a01461006c5780639511afaa14610095575b600080fd5b61007f61007a3660046105d0565b610127565b60405161008c9190610671565b60405180910390f35b6100a86100a336600461074f565b610150565b60405190815260200161008c565b61007f6100c4366004610859565b604080516020810196909652858101949094526060850192909252608084015260a0808401919091528151808403909101815260c0909201905290565b61007f61010f366004610894565b610163565b61007f6101223660046108fc565b610195565b60608160405160200161013a919061097d565b6040516020818303038152906040529050919050565b600061015c83836101cd565b9392505050565b60608484848460405160200161017c9493929190610a49565b6040516020818303038152906040529050949350505050565b60608686868686866040516020016101b296959493929190610a86565b60405160208183030381529060405290509695505050505050565b815160208082015160409283015192516000938493610213937f2425b0b9f9054c76ff151b0a175b18f37a4a4e82013a72e9f15c9caa095ed21f93909291889101610a49565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815290829052805160209182012086518051888401516060808b0151908401516080808d0151950151959761027a9794969395929491939101610a86565b604051602081830303815290604052805190602001208560400151805190602001208660a001516040516020016102b1919061097d565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120908301969096528101939093526060830191909152608082015260a081019190915260c00160405160208183030381529060405280519060200120905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040516080810167ffffffffffffffff8111828210171561037c5761037c61032a565b60405290565b60405160c0810167ffffffffffffffff8111828210171561037c5761037c61032a565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156103ec576103ec61032a565b604052919050565b600082601f83011261040557600080fd5b813567ffffffffffffffff81111561041f5761041f61032a565b61045060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116016103a5565b81815284602083860101111561046557600080fd5b816020850160208301376000918101602001919091529392505050565b600082601f83011261049357600080fd5b8135602067ffffffffffffffff808311156104b0576104b061032a565b8260051b6104bf8382016103a5565b93845285810183019383810190888611156104d957600080fd5b84880192505b858310156105c4578235848111156104f75760008081fd5b88016080818b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe00181131561052d5760008081fd5b610535610359565b87830135878111156105475760008081fd5b6105558d8a838701016103f4565b8252506040808401358881111561056c5760008081fd5b61057a8e8b838801016103f4565b8a84015250606080850135898111156105935760008081fd5b6105a18f8c838901016103f4565b9284019290925293909201359281019290925250825291840191908401906104df565b98975050505050505050565b6000602082840312156105e257600080fd5b813567ffffffffffffffff8111156105f957600080fd5b61060584828501610482565b949350505050565b6000815180845260005b8181101561063357602081850181015186830182015201610617565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b60208152600061015c602083018461060d565b803567ffffffffffffffff8116811461069c57600080fd5b919050565b600060a082840312156106b357600080fd5b60405160a0810181811067ffffffffffffffff821117156106d6576106d661032a565b604052823581529050806106ec60208401610684565b60208201526106fd60408401610684565b604082015261070e60608401610684565b606082015261071f60808401610684565b60808201525092915050565b803573ffffffffffffffffffffffffffffffffffffffff8116811461069c57600080fd5b6000806040838503121561076257600080fd5b823567ffffffffffffffff8082111561077a57600080fd5b90840190610140828703121561078f57600080fd5b610797610382565b6107a187846106a1565b815260a0830135828111156107b557600080fd5b6107c1888286016103f4565b60208301525060c0830135828111156107d957600080fd5b6107e5888286016103f4565b6040830152506107f760e0840161072b565b606082015261010083013560808201526101208301358281111561081a57600080fd5b61082688828601610482565b60a0830152509350602085013591508082111561084257600080fd5b5061084f858286016103f4565b9150509250929050565b600080600080600060a0868803121561087157600080fd5b505083359560208501359550604085013594606081013594506080013592509050565b600080600080608085870312156108aa57600080fd5b843593506108ba60208601610684565b92506108c860408601610684565b9150606085013567ffffffffffffffff8111156108e457600080fd5b6108f0878288016103f4565b91505092959194509250565b60008060008060008060c0878903121561091557600080fd5b86359550602087013567ffffffffffffffff81111561093357600080fd5b61093f89828a016103f4565b95505061094e6040880161072b565b935061095c60608801610684565b92506080870135915061097160a08801610684565b90509295509295509295565b600060208083018184528085518083526040925060408601915060408160051b87010184880160005b83811015610a3b577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08984030185528151608081518186526109ea8287018261060d565b915050888201518582038a870152610a02828261060d565b9150508782015185820389870152610a1a828261060d565b606093840151969093019590955250948701949250908601906001016109a6565b509098975050505050505050565b848152600067ffffffffffffffff808616602084015280851660408401525060806060830152610a7c608083018461060d565b9695505050505050565b86815260c060208201526000610a9f60c083018861060d565b73ffffffffffffffffffffffffffffffffffffffff9690961660408301525067ffffffffffffffff9384166060820152608081019290925290911660a0909101529291505056fea164736f6c6343000818000a", } var MessageHasherABI = MessageHasherMetaData.ABI @@ -192,9 +193,9 @@ func (_MessageHasher *MessageHasherTransactorRaw) Transact(opts *bind.TransactOp return _MessageHasher.Contract.contract.Transact(opts, method, params...) } -func (_MessageHasher *MessageHasherCaller) EncodeFinalHashPreimage(opts *bind.CallOpts, leafDomainSeparator [32]byte, implicitMetadataHash [32]byte, fixedSizeFieldsHash [32]byte, dataHash [32]byte, tokenAmountsHash [32]byte, sourceTokenDataHash [32]byte) ([]byte, error) { +func (_MessageHasher *MessageHasherCaller) EncodeFinalHashPreimage(opts *bind.CallOpts, leafDomainSeparator [32]byte, implicitMetadataHash [32]byte, fixedSizeFieldsHash [32]byte, dataHash [32]byte, tokenAmountsHash [32]byte) ([]byte, error) { var out []interface{} - err := _MessageHasher.contract.Call(opts, &out, "encodeFinalHashPreimage", leafDomainSeparator, implicitMetadataHash, fixedSizeFieldsHash, dataHash, tokenAmountsHash, sourceTokenDataHash) + err := _MessageHasher.contract.Call(opts, &out, "encodeFinalHashPreimage", leafDomainSeparator, implicitMetadataHash, fixedSizeFieldsHash, dataHash, tokenAmountsHash) if err != nil { return *new([]byte), err @@ -206,12 +207,12 @@ func (_MessageHasher *MessageHasherCaller) EncodeFinalHashPreimage(opts *bind.Ca } -func (_MessageHasher *MessageHasherSession) EncodeFinalHashPreimage(leafDomainSeparator [32]byte, implicitMetadataHash [32]byte, fixedSizeFieldsHash [32]byte, dataHash [32]byte, tokenAmountsHash [32]byte, sourceTokenDataHash [32]byte) ([]byte, error) { - return _MessageHasher.Contract.EncodeFinalHashPreimage(&_MessageHasher.CallOpts, leafDomainSeparator, implicitMetadataHash, fixedSizeFieldsHash, dataHash, tokenAmountsHash, sourceTokenDataHash) +func (_MessageHasher *MessageHasherSession) EncodeFinalHashPreimage(leafDomainSeparator [32]byte, implicitMetadataHash [32]byte, fixedSizeFieldsHash [32]byte, dataHash [32]byte, tokenAmountsHash [32]byte) ([]byte, error) { + return _MessageHasher.Contract.EncodeFinalHashPreimage(&_MessageHasher.CallOpts, leafDomainSeparator, implicitMetadataHash, fixedSizeFieldsHash, dataHash, tokenAmountsHash) } -func (_MessageHasher *MessageHasherCallerSession) EncodeFinalHashPreimage(leafDomainSeparator [32]byte, implicitMetadataHash [32]byte, fixedSizeFieldsHash [32]byte, dataHash [32]byte, tokenAmountsHash [32]byte, sourceTokenDataHash [32]byte) ([]byte, error) { - return _MessageHasher.Contract.EncodeFinalHashPreimage(&_MessageHasher.CallOpts, leafDomainSeparator, implicitMetadataHash, fixedSizeFieldsHash, dataHash, tokenAmountsHash, sourceTokenDataHash) +func (_MessageHasher *MessageHasherCallerSession) EncodeFinalHashPreimage(leafDomainSeparator [32]byte, implicitMetadataHash [32]byte, fixedSizeFieldsHash [32]byte, dataHash [32]byte, tokenAmountsHash [32]byte) ([]byte, error) { + return _MessageHasher.Contract.EncodeFinalHashPreimage(&_MessageHasher.CallOpts, leafDomainSeparator, implicitMetadataHash, fixedSizeFieldsHash, dataHash, tokenAmountsHash) } func (_MessageHasher *MessageHasherCaller) EncodeFixedSizeFieldsHashPreimage(opts *bind.CallOpts, messageId [32]byte, sender []byte, receiver common.Address, sequenceNumber uint64, gasLimit *big.Int, nonce uint64) ([]byte, error) { @@ -258,9 +259,9 @@ func (_MessageHasher *MessageHasherCallerSession) EncodeMetadataHashPreimage(any return _MessageHasher.Contract.EncodeMetadataHashPreimage(&_MessageHasher.CallOpts, any2EVMMessageHash, sourceChainSelector, destChainSelector, onRamp) } -func (_MessageHasher *MessageHasherCaller) EncodeSourceTokenDataHashPreimage(opts *bind.CallOpts, sourceTokenData [][]byte) ([]byte, error) { +func (_MessageHasher *MessageHasherCaller) EncodeTokenAmountsHashPreimage(opts *bind.CallOpts, rampTokenAmounts []InternalRampTokenAmount) ([]byte, error) { var out []interface{} - err := _MessageHasher.contract.Call(opts, &out, "encodeSourceTokenDataHashPreimage", sourceTokenData) + err := _MessageHasher.contract.Call(opts, &out, "encodeTokenAmountsHashPreimage", rampTokenAmounts) if err != nil { return *new([]byte), err @@ -272,34 +273,12 @@ func (_MessageHasher *MessageHasherCaller) EncodeSourceTokenDataHashPreimage(opt } -func (_MessageHasher *MessageHasherSession) EncodeSourceTokenDataHashPreimage(sourceTokenData [][]byte) ([]byte, error) { - return _MessageHasher.Contract.EncodeSourceTokenDataHashPreimage(&_MessageHasher.CallOpts, sourceTokenData) +func (_MessageHasher *MessageHasherSession) EncodeTokenAmountsHashPreimage(rampTokenAmounts []InternalRampTokenAmount) ([]byte, error) { + return _MessageHasher.Contract.EncodeTokenAmountsHashPreimage(&_MessageHasher.CallOpts, rampTokenAmounts) } -func (_MessageHasher *MessageHasherCallerSession) EncodeSourceTokenDataHashPreimage(sourceTokenData [][]byte) ([]byte, error) { - return _MessageHasher.Contract.EncodeSourceTokenDataHashPreimage(&_MessageHasher.CallOpts, sourceTokenData) -} - -func (_MessageHasher *MessageHasherCaller) EncodeTokenAmountsHashPreimage(opts *bind.CallOpts, tokenAmounts []ClientEVMTokenAmount) ([]byte, error) { - var out []interface{} - err := _MessageHasher.contract.Call(opts, &out, "encodeTokenAmountsHashPreimage", tokenAmounts) - - if err != nil { - return *new([]byte), err - } - - out0 := *abi.ConvertType(out[0], new([]byte)).(*[]byte) - - return out0, err - -} - -func (_MessageHasher *MessageHasherSession) EncodeTokenAmountsHashPreimage(tokenAmounts []ClientEVMTokenAmount) ([]byte, error) { - return _MessageHasher.Contract.EncodeTokenAmountsHashPreimage(&_MessageHasher.CallOpts, tokenAmounts) -} - -func (_MessageHasher *MessageHasherCallerSession) EncodeTokenAmountsHashPreimage(tokenAmounts []ClientEVMTokenAmount) ([]byte, error) { - return _MessageHasher.Contract.EncodeTokenAmountsHashPreimage(&_MessageHasher.CallOpts, tokenAmounts) +func (_MessageHasher *MessageHasherCallerSession) EncodeTokenAmountsHashPreimage(rampTokenAmounts []InternalRampTokenAmount) ([]byte, error) { + return _MessageHasher.Contract.EncodeTokenAmountsHashPreimage(&_MessageHasher.CallOpts, rampTokenAmounts) } func (_MessageHasher *MessageHasherCaller) Hash(opts *bind.CallOpts, message InternalAny2EVMRampMessage, onRamp []byte) ([32]byte, error) { @@ -329,15 +308,13 @@ func (_MessageHasher *MessageHasher) Address() common.Address { } type MessageHasherInterface interface { - EncodeFinalHashPreimage(opts *bind.CallOpts, leafDomainSeparator [32]byte, implicitMetadataHash [32]byte, fixedSizeFieldsHash [32]byte, dataHash [32]byte, tokenAmountsHash [32]byte, sourceTokenDataHash [32]byte) ([]byte, error) + EncodeFinalHashPreimage(opts *bind.CallOpts, leafDomainSeparator [32]byte, implicitMetadataHash [32]byte, fixedSizeFieldsHash [32]byte, dataHash [32]byte, tokenAmountsHash [32]byte) ([]byte, error) EncodeFixedSizeFieldsHashPreimage(opts *bind.CallOpts, messageId [32]byte, sender []byte, receiver common.Address, sequenceNumber uint64, gasLimit *big.Int, nonce uint64) ([]byte, error) EncodeMetadataHashPreimage(opts *bind.CallOpts, any2EVMMessageHash [32]byte, sourceChainSelector uint64, destChainSelector uint64, onRamp []byte) ([]byte, error) - EncodeSourceTokenDataHashPreimage(opts *bind.CallOpts, sourceTokenData [][]byte) ([]byte, error) - - EncodeTokenAmountsHashPreimage(opts *bind.CallOpts, tokenAmounts []ClientEVMTokenAmount) ([]byte, error) + EncodeTokenAmountsHashPreimage(opts *bind.CallOpts, rampTokenAmounts []InternalRampTokenAmount) ([]byte, error) Hash(opts *bind.CallOpts, message InternalAny2EVMRampMessage, onRamp []byte) ([32]byte, error) diff --git a/core/gethwrappers/ccip/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/ccip/generation/generated-wrapper-dependency-versions-do-not-edit.txt index 02a4b18652..c4432d6860 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 @@ -9,14 +9,14 @@ ccip_config: ../../../contracts/solc/v0.8.24/CCIPConfig/CCIPConfig.abi ../../../ commit_store: ../../../contracts/solc/v0.8.24/CommitStore/CommitStore.abi ../../../contracts/solc/v0.8.24/CommitStore/CommitStore.bin ddc26c10c2a52b59624faae9005827b09b98db4566887a736005e8cc37cf8a51 commit_store_helper: ../../../contracts/solc/v0.8.24/CommitStoreHelper/CommitStoreHelper.abi ../../../contracts/solc/v0.8.24/CommitStoreHelper/CommitStoreHelper.bin ebd8aac686fa28a71d4212bcd25a28f8f640d50dce5e50498b2f6b8534890b69 ether_sender_receiver: ../../../contracts/solc/v0.8.24/EtherSenderReceiver/EtherSenderReceiver.abi ../../../contracts/solc/v0.8.24/EtherSenderReceiver/EtherSenderReceiver.bin 09510a3f773f108a3c231e8d202835c845ded862d071ec54c4f89c12d868b8de -evm_2_evm_multi_offramp: ../../../contracts/solc/v0.8.24/EVM2EVMMultiOffRamp/EVM2EVMMultiOffRamp.abi ../../../contracts/solc/v0.8.24/EVM2EVMMultiOffRamp/EVM2EVMMultiOffRamp.bin 09ac589a267ac59ae3fef7534bebe6da38c427c48b60b5304b9e55408e028337 -evm_2_evm_multi_onramp: ../../../contracts/solc/v0.8.24/EVM2EVMMultiOnRamp/EVM2EVMMultiOnRamp.abi ../../../contracts/solc/v0.8.24/EVM2EVMMultiOnRamp/EVM2EVMMultiOnRamp.bin 8edc6f93d7d5f045776cd843c0fae8b94a73835d2c939b2f94fa906ca5f26b97 +evm_2_evm_multi_offramp: ../../../contracts/solc/v0.8.24/EVM2EVMMultiOffRamp/EVM2EVMMultiOffRamp.abi ../../../contracts/solc/v0.8.24/EVM2EVMMultiOffRamp/EVM2EVMMultiOffRamp.bin 2ab5cd4acb0c0b7087397d0c99e928119ef9e174bfd55628e6b2daf14f2da1fe +evm_2_evm_multi_onramp: ../../../contracts/solc/v0.8.24/EVM2EVMMultiOnRamp/EVM2EVMMultiOnRamp.abi ../../../contracts/solc/v0.8.24/EVM2EVMMultiOnRamp/EVM2EVMMultiOnRamp.bin e304765672aeab78529edc35f6ee44779c098643fe3a7b4f3c0f33a9493016ac evm_2_evm_offramp: ../../../contracts/solc/v0.8.24/EVM2EVMOffRamp/EVM2EVMOffRamp.abi ../../../contracts/solc/v0.8.24/EVM2EVMOffRamp/EVM2EVMOffRamp.bin b6132cb22370d62b1b20174bbe832ec87df61f6ab65f7fe2515733bdd10a30f5 evm_2_evm_onramp: ../../../contracts/solc/v0.8.24/EVM2EVMOnRamp/EVM2EVMOnRamp.abi ../../../contracts/solc/v0.8.24/EVM2EVMOnRamp/EVM2EVMOnRamp.bin 383e9930fbc1b7fbb6554cc8857229d207fd6742e87c7fb1a37002347e8de8e2 lock_release_token_pool: ../../../contracts/solc/v0.8.24/LockReleaseTokenPool/LockReleaseTokenPool.abi ../../../contracts/solc/v0.8.24/LockReleaseTokenPool/LockReleaseTokenPool.bin c65c226e1e4d38414bd4a1b76fc8aca3cb3dd98df61268424c44564f455d3752 lock_release_token_pool_and_proxy: ../../../contracts/solc/v0.8.24/LockReleaseTokenPoolAndProxy/LockReleaseTokenPoolAndProxy.abi ../../../contracts/solc/v0.8.24/LockReleaseTokenPoolAndProxy/LockReleaseTokenPoolAndProxy.bin 8b929fab79d1caeea4c57e08cc523eb8ab45ec5c08f46da866b82c15ba94d9ad maybe_revert_message_receiver: ../../../contracts/solc/v0.8.24/MaybeRevertMessageReceiver/MaybeRevertMessageReceiver.abi ../../../contracts/solc/v0.8.24/MaybeRevertMessageReceiver/MaybeRevertMessageReceiver.bin d73956c26232ebcc4a5444429fa99cbefed960e323be9b5a24925885c2e477d5 -message_hasher: ../../../contracts/solc/v0.8.24/MessageHasher/MessageHasher.abi ../../../contracts/solc/v0.8.24/MessageHasher/MessageHasher.bin b16f2182c6f262f4864315c90d35efc0342f5ee3580896e9bca50a5b98d51282 +message_hasher: ../../../contracts/solc/v0.8.24/MessageHasher/MessageHasher.abi ../../../contracts/solc/v0.8.24/MessageHasher/MessageHasher.bin 9195ef458fee819595502b5b33263e4646f9526cbac0bd89cc322afe5a77ae19 mock_arm_contract: ../../../contracts/solc/v0.8.24/MockRMN1_0/MockRMN.abi ../../../contracts/solc/v0.8.24/MockRMN1_0/MockRMN.bin e7a3a6c3eda5fb882e16bcc2b4340f78523acb67907bcdcaf3c8ffc51488688e mock_usdc_token_messenger: ../../../contracts/solc/v0.8.24/MockE2EUSDCTokenMessenger/MockE2EUSDCTokenMessenger.abi ../../../contracts/solc/v0.8.24/MockE2EUSDCTokenMessenger/MockE2EUSDCTokenMessenger.bin e0cf17a38b438239fc6294ddca88f86b6c39e4542aefd9815b2d92987191b8bd mock_usdc_token_transmitter: ../../../contracts/solc/v0.8.24/MockE2EUSDCTransmitter/MockE2EUSDCTransmitter.abi ../../../contracts/solc/v0.8.24/MockE2EUSDCTransmitter/MockE2EUSDCTransmitter.bin 33bdad70822e889de7c720ed20085cf9cd3f8eba8b68f26bd6535197749595fe diff --git a/core/gethwrappers/ccip/mocks/commit_store_interface.go b/core/gethwrappers/ccip/mocks/commit_store_interface.go index c8347c5e5c..4674883067 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mock_contracts 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 15773a8762..3f7015b041 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mock_contracts 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 194a9f78b7..7fb51d91cf 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mock_contracts diff --git a/core/gethwrappers/ccip/mocks/link_token_interface.go b/core/gethwrappers/ccip/mocks/link_token_interface.go index 4bbf12c1fa..3685575131 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mock_contracts diff --git a/core/gethwrappers/ccip/mocks/price_registry_interface.go b/core/gethwrappers/ccip/mocks/price_registry_interface.go index 3b5818cd26..45e9214737 100644 --- a/core/gethwrappers/ccip/mocks/price_registry_interface.go +++ b/core/gethwrappers/ccip/mocks/price_registry_interface.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mock_contracts diff --git a/core/gethwrappers/ccip/mocks/v1_0_0/evm2_evm_off_ramp_interface.go b/core/gethwrappers/ccip/mocks/v1_0_0/evm2_evm_off_ramp_interface.go index 41242ffa2e..e44902633c 100644 --- a/core/gethwrappers/ccip/mocks/v1_0_0/evm2_evm_off_ramp_interface.go +++ b/core/gethwrappers/ccip/mocks/v1_0_0/evm2_evm_off_ramp_interface.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mock_contracts 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 6cd91e307e..1b191171ab 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mock_contracts 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 4614a99d0e..1a30ab139a 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mock_arbitrum_gateway_router 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 c79a5870df..d5e8233b71 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mock_arbitrum_inbox 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 cf94e6cca1..1e5ab1acb7 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mock_arbitrum_l1_bridge_adapter 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 597ab9155e..9232e2480e 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mock_arbitrum_l2_bridge_adapter 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 3c6ed7bc23..b8a498ca5a 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mock_arbitrum_rollup_core 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 2bcf1ae305..b6dda1589d 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mock_arbsys 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 11ccf51583..4ea59b596b 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mock_l2_arbitrum_gateway 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 404a1d276f..f57a2000d8 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mock_l2_arbitrum_messenger 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 5b04eaa49b..3392b5eed6 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mock_node_interface 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 6be12eee90..97ff5c4fb0 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mock_optimism_dispute_game_factory 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 3a1fe14eb1..dd8bc81475 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mock_optimism_l2_output_oracle 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 25202450f4..ebffa6ea86 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mock_optimism_portal 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 66f6e07a71..9a846a76ac 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mock_optimism_portal_2 diff --git a/core/internal/cltest/cltest.go b/core/internal/cltest/cltest.go index 5212b29fce..724af71b4a 100644 --- a/core/internal/cltest/cltest.go +++ b/core/internal/cltest/cltest.go @@ -66,6 +66,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" + "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/aptoskey" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/cosmoskey" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/csakey" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/dkgencryptkey" @@ -126,6 +127,7 @@ var ( DefaultP2PKey = p2pkey.MustNewV2XXXTestingOnly(big.NewInt(KeyBigIntSeed)) DefaultSolanaKey = solkey.MustNewInsecure(keystest.NewRandReaderFromSeed(KeyBigIntSeed)) DefaultStarkNetKey = starkkey.MustNewInsecure(keystest.NewRandReaderFromSeed(KeyBigIntSeed)) + DefaultAptosKey = aptoskey.MustNewInsecure(keystest.NewRandReaderFromSeed(KeyBigIntSeed)) DefaultVRFKey = vrfkey.MustNewV2XXXTestingOnly(big.NewInt(KeyBigIntSeed)) DefaultDKGSignKey = dkgsignkey.MustNewXXXTestingOnly(big.NewInt(KeyBigIntSeed)) DefaultDKGEncryptKey = dkgencryptkey.MustNewXXXTestingOnly(big.NewInt(KeyBigIntSeed)) @@ -387,7 +389,7 @@ func NewApplicationWithConfig(t testing.TB, cfg chainlink.GeneralConfig, flagsAn testCtx := testutils.Context(t) // evm alway enabled for backward compatibility - initOps := []chainlink.CoreRelayerChainInitFunc{chainlink.InitEVM(testCtx, relayerFactory, evmOpts)} + initOps := []chainlink.CoreRelayerChainInitFunc{chainlink.InitDummy(testCtx, relayerFactory), chainlink.InitEVM(testCtx, relayerFactory, evmOpts)} if cfg.CosmosEnabled() { cosmosCfg := chainlink.CosmosFactoryConfig{ diff --git a/core/internal/mocks/application.go b/core/internal/mocks/application.go index f845d46ca8..cbb8ebaaa0 100644 --- a/core/internal/mocks/application.go +++ b/core/internal/mocks/application.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/internal/mocks/flags.go b/core/internal/mocks/flags.go index 3f9e47095e..7d38a96d92 100644 --- a/core/internal/mocks/flags.go +++ b/core/internal/mocks/flags.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/internal/mocks/flux_aggregator.go b/core/internal/mocks/flux_aggregator.go index c1e35f41e2..1501e36e94 100644 --- a/core/internal/mocks/flux_aggregator.go +++ b/core/internal/mocks/flux_aggregator.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/internal/mocks/prometheus_backend.go b/core/internal/mocks/prometheus_backend.go index 81ff22d9d5..65cc739030 100644 --- a/core/internal/mocks/prometheus_backend.go +++ b/core/internal/mocks/prometheus_backend.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/internal/testutils/testutils.go b/core/internal/testutils/testutils.go index 6b4388fccf..45609488b4 100644 --- a/core/internal/testutils/testutils.go +++ b/core/internal/testutils/testutils.go @@ -367,15 +367,17 @@ func RequireLogMessage(t *testing.T, observedLogs *observer.ObservedLogs, msg st // // observedZapCore, observedLogs := observer.New(zap.DebugLevel) // lggr := logger.TestLogger(t, observedZapCore) -func WaitForLogMessage(t *testing.T, observedLogs *observer.ObservedLogs, msg string) { +func WaitForLogMessage(t *testing.T, observedLogs *observer.ObservedLogs, msg string) (le observer.LoggedEntry) { AssertEventually(t, func() bool { for _, l := range observedLogs.All() { if strings.Contains(l.Message, msg) { + le = l return true } } return false }) + return } // WaitForLogMessageCount waits until at least count log message containing the diff --git a/core/logger/logger_mock_test.go b/core/logger/logger_mock_test.go index 8d52734fe5..0c901c7eb2 100644 --- a/core/logger/logger_mock_test.go +++ b/core/logger/logger_mock_test.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package logger diff --git a/core/logger/mocks/logger.go b/core/logger/mocks/logger.go index 7e56bf8d22..da881894d1 100644 --- a/core/logger/mocks/logger.go +++ b/core/logger/mocks/logger.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 727954da03..d5dd252be7 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -26,10 +26,10 @@ require ( github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/chain-selectors v1.0.17 github.com/smartcontractkit/chainlink-automation v1.0.4 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20240625144005-556a7ba7b11c + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240702120320-563bf07487fe github.com/smartcontractkit/chainlink-vrf v0.0.0-20240222010609-cd67d123c772 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 - github.com/smartcontractkit/libocr v0.0.0-20240419185742-fd3cab206b2c + github.com/smartcontractkit/libocr v0.0.0-20240702141926-063ceef8c42e github.com/spf13/cobra v1.8.0 github.com/spf13/viper v1.18.2 github.com/stretchr/testify v1.9.0 @@ -279,13 +279,13 @@ require ( github.com/shirou/gopsutil/v3 v3.24.3 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240621143432-85370a54b141 // indirect - github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 // indirect - github.com/smartcontractkit/chainlink-feeds v0.0.0-20240523182126-1784bd4f3294 // indirect - github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240621151551-885054fb0f0e // indirect - github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240621143321-7e5949418911 // indirect + github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240702144807-761f63e7b527 // indirect + github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917 // indirect + github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240701154249-032706dcb7c8 // indirect + github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240625074951-06ab5e670dba // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 // indirect - github.com/smartcontractkit/wsrpc v0.8.1 // indirect + github.com/smartcontractkit/wsrpc v0.7.3 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.11.0 // indirect github.com/spf13/cast v1.6.0 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 01552ea368..479dee69e7 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -602,6 +602,7 @@ github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaU github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -1121,32 +1122,32 @@ github.com/smartcontractkit/chain-selectors v1.0.17 h1:otOlYUnutS8oQBEAi9RLQICqZ github.com/smartcontractkit/chain-selectors v1.0.17/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v1.0.4 h1:iyW181JjKHLNMnDleI8umfIfVVlwC7+n5izbLSFgjw8= github.com/smartcontractkit/chainlink-automation v1.0.4/go.mod h1:u4NbPZKJ5XiayfKHD/v3z3iflQWqvtdhj13jVZXj/cM= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240625144005-556a7ba7b11c h1:dR2y3uzuZ8cJOaMSij8LQIdySuImwxkiZFjZ62ML5S0= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240625144005-556a7ba7b11c/go.mod h1:L32xvCpk84Nglit64OhySPMP1tM3TTBK7Tw0qZl7Sd4= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240702120320-563bf07487fe h1:lYyWmjheglMu0y3JmfSqs9Dm4tZO34RmbUTOtQ4CadE= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240702120320-563bf07487fe/go.mod h1:EWvSuqIJUYXZLEHewC7WCaPylM2jyjF3Q36BZPS4MoI= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240621143432-85370a54b141 h1:TMOoYaeSDkkI3jkCH7lKHOZaLkeDuxFTNC+XblD6M0M= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240621143432-85370a54b141/go.mod h1:0UNuO3nDt9MFsZPaHJBEUolxVkN0iC69j1ccDp95e8k= -github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 h1:xFSv8561jsLtF6gYZr/zW2z5qUUAkcFkApin2mnbYTo= -github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540/go.mod h1:sjAmX8K2kbQhvDarZE1ZZgDgmHJ50s0BBc/66vKY2ek= -github.com/smartcontractkit/chainlink-feeds v0.0.0-20240523182126-1784bd4f3294 h1:4L1878goh1mqJx6JPv/TEeMAgYvbx7+D14QFSSu/DPs= -github.com/smartcontractkit/chainlink-feeds v0.0.0-20240523182126-1784bd4f3294/go.mod h1:jwVxhctE6BgLOSSsVq9wbREpZ8Ev34H+UBxeUhESZRs= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240621151551-885054fb0f0e h1:4dwcbcVrMne6wtOX/4yWdYJwk8iSm0INVNva/EhF35E= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240621151551-885054fb0f0e/go.mod h1:rJ4+TwNMciU2pvx6dsnUNnh4sPI5bUqgmYfhSrYhQ8Y= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240621143321-7e5949418911 h1:TSgCT8Uy+R4o3GwoWUqRCGNKeciub7r7+Aa3ylmg66c= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240621143321-7e5949418911/go.mod h1:OiWUTrrpSLLTMh7FINWjEh6mmDJCVPaC4yEsDCVaWdU= +github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240702144807-761f63e7b527 h1:Vs6myS+bpPwb8chUY7XxveJyhvejknhOmhDTddgsK5I= +github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240702144807-761f63e7b527/go.mod h1:KRK7KlAEpmORi+nJgT0vxQVWvlLEBQ6zgzXziZuKvUM= +github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917 h1:MD80ZRCTvxxJ8PBmhtrKoTnky8cVNYrCrIBLVRbrOM0= +github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917/go.mod h1:jwVxhctE6BgLOSSsVq9wbREpZ8Ev34H+UBxeUhESZRs= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240701154249-032706dcb7c8 h1:JkBap2v5AmU4H9LWVDGr6XKnnDwU0OzX4W7u9aq5PQg= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240701154249-032706dcb7c8/go.mod h1:NbXXQaNFskVMYRut0MvBlcHu/vDgipGMwYjamvjVB9Y= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240625074951-06ab5e670dba h1:YNSlhK5mobyAaw02LPGgIEuS3lXyCTXcc6oaV2L6uUI= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240625074951-06ab5e670dba/go.mod h1:UVFRacRkP7O7TQAzFmR52v5mUlxf+G1ovMlCQAB/cHU= github.com/smartcontractkit/chainlink-vrf v0.0.0-20240222010609-cd67d123c772 h1:LQmRsrzzaYYN3wEU1l5tWiccznhvbyGnu2N+wHSXZAo= github.com/smartcontractkit/chainlink-vrf v0.0.0-20240222010609-cd67d123c772/go.mod h1:Kn1Hape05UzFZ7bOUnm3GVsHzP0TNrVmpfXYNHdqGGs= github.com/smartcontractkit/go-plugin v0.0.0-20240208201424-b3b91517de16 h1:TFe+FvzxClblt6qRfqEhUfa4kFQx5UobuoFGO2W4mMo= github.com/smartcontractkit/go-plugin v0.0.0-20240208201424-b3b91517de16/go.mod h1:lBS5MtSSBZk0SHc66KACcjjlU6WzEVP/8pwz68aMkCI= github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f h1:hgJif132UCdjo8u43i7iPN1/MFnu49hv7lFGFftCHKU= github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f/go.mod h1:MvMXoufZAtqExNexqi4cjrNYE9MefKddKylxjS+//n0= -github.com/smartcontractkit/libocr v0.0.0-20240419185742-fd3cab206b2c h1:lIyMbTaF2H0Q71vkwZHX/Ew4KF2BxiKhqEXwF8rn+KI= -github.com/smartcontractkit/libocr v0.0.0-20240419185742-fd3cab206b2c/go.mod h1:fb1ZDVXACvu4frX3APHZaEBp0xi1DIm34DcA0CwTsZM= +github.com/smartcontractkit/libocr v0.0.0-20240702141926-063ceef8c42e h1:9ypZ/8aW8Vm497i1gXHcT96oNLiu88jbg9QdX+IUE3E= +github.com/smartcontractkit/libocr v0.0.0-20240702141926-063ceef8c42e/go.mod h1:fb1ZDVXACvu4frX3APHZaEBp0xi1DIm34DcA0CwTsZM= github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 h1:yiKnypAqP8l0OX0P3klzZ7SCcBUxy5KqTAKZmQOvSQE= github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1/go.mod h1:q6f4fe39oZPdsh1i57WznEZgxd8siidMaSFq3wdPmVg= github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 h1:Dai1bn+Q5cpeGMQwRdjOdVjG8mmFFROVkSKuUgBErRQ= github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1/go.mod h1:G5Sd/yzHWf26rQ+X0nG9E0buKPqRGPMJAfk2gwCzOOw= -github.com/smartcontractkit/wsrpc v0.8.1 h1:kk0SXLqWrWaZ3J6c7n8D0NZ2uTMBBBpG5dZZXZX8UGE= -github.com/smartcontractkit/wsrpc v0.8.1/go.mod h1:yfg8v8fPLXkb6Mcnx6Pm/snP6jJ0r5Kf762Yd1a/KpA= +github.com/smartcontractkit/wsrpc v0.7.3 h1:CKYZfawZShZGfvsQep1F9oBansnFk9ByZPCdTMpLphw= +github.com/smartcontractkit/wsrpc v0.7.3/go.mod h1:sj7QX2NQibhkhxTfs3KOhAj/5xwgqMipTvJVSssT9i0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= @@ -1376,6 +1377,7 @@ 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.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= +go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= diff --git a/core/services/blockhashstore/mocks/bhs.go b/core/services/blockhashstore/mocks/bhs.go index 43b4769c02..40b73aba0d 100644 --- a/core/services/blockhashstore/mocks/bhs.go +++ b/core/services/blockhashstore/mocks/bhs.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/blockhashstore/mocks/timer.go b/core/services/blockhashstore/mocks/timer.go index 46212b4c02..bf593f7691 100644 --- a/core/services/blockhashstore/mocks/timer.go +++ b/core/services/blockhashstore/mocks/timer.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/ccip/mocks/orm.go b/core/services/ccip/mocks/orm.go index ea6b07dc63..d3d7f89ba3 100644 --- a/core/services/ccip/mocks/orm.go +++ b/core/services/ccip/mocks/orm.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/ccipcapability/types/mocks/ccip_oracle.go b/core/services/ccipcapability/types/mocks/ccip_oracle.go index ebb3ad6801..7ac7cf5ffc 100644 --- a/core/services/ccipcapability/types/mocks/ccip_oracle.go +++ b/core/services/ccipcapability/types/mocks/ccip_oracle.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/ccipcapability/types/mocks/oracle_creator.go b/core/services/ccipcapability/types/mocks/oracle_creator.go index ce729d1afe..ed685e7312 100644 --- a/core/services/ccipcapability/types/mocks/oracle_creator.go +++ b/core/services/ccipcapability/types/mocks/oracle_creator.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/chainlink/application.go b/core/services/chainlink/application.go index 1a626bc07f..7a928c46b8 100644 --- a/core/services/chainlink/application.go +++ b/core/services/chainlink/application.go @@ -353,7 +353,7 @@ func NewApplication(opts ApplicationOpts) (Application, error) { jobORM = job.NewORM(opts.DS, pipelineORM, bridgeORM, keyStore, globalLogger) txmORM = txmgr.NewTxStore(opts.DS, globalLogger) streamRegistry = streams.NewRegistry(globalLogger, pipelineRunner) - workflowORM = workflowstore.NewDBStore(opts.DS, clockwork.NewRealClock()) + workflowORM = workflowstore.NewDBStore(opts.DS, globalLogger, clockwork.NewRealClock()) ) for _, chain := range legacyEVMChains.Slice() { diff --git a/core/services/chainlink/config.go b/core/services/chainlink/config.go index 200d4973ed..dc301c0967 100644 --- a/core/services/chainlink/config.go +++ b/core/services/chainlink/config.go @@ -42,6 +42,32 @@ type Config struct { Solana solcfg.TOMLConfigs `toml:",omitempty"` Starknet stkcfg.TOMLConfigs `toml:",omitempty"` + + Aptos RawConfigs `toml:",omitempty"` +} + +// RawConfigs is a list of RawConfig. +type RawConfigs []RawConfig + +// RawConfig is the config used for chains that are not embedded. +type RawConfig map[string]any + +// ValidateConfig returns an error if the Config is not valid for use, as-is. +func (c *RawConfig) ValidateConfig() (err error) { + if v, ok := (*c)["Enabled"]; ok { + if _, ok := v.(*bool); !ok { + err = multierr.Append(err, commonconfig.ErrInvalid{Name: "Enabled", Value: v, Msg: "expected *bool"}) + } + } + return err +} + +func (c *RawConfig) IsEnabled() bool { + if c == nil { + return false + } + + return (*c)["Enabled"] == nil || *(*c)["Enabled"].(*bool) } // TOMLString returns a TOML encoded string. diff --git a/core/services/chainlink/config_general.go b/core/services/chainlink/config_general.go index 5b4a6271d5..5b6b839fb5 100644 --- a/core/services/chainlink/config_general.go +++ b/core/services/chainlink/config_general.go @@ -345,6 +345,15 @@ func (g *generalConfig) StarkNetEnabled() bool { return false } +func (g *generalConfig) AptosEnabled() bool { + for _, c := range g.c.Aptos { + 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_test.go b/core/services/chainlink/config_test.go index 08c3a18aca..db3b3af3c1 100644 --- a/core/services/chainlink/config_test.go +++ b/core/services/chainlink/config_test.go @@ -101,8 +101,9 @@ var ( { ChainID: ubig.NewI(1), Chain: evmcfg.Chain{ - FinalityDepth: ptr[uint32](26), - FinalityTagEnabled: ptr[bool](false), + FinalityDepth: ptr[uint32](26), + FinalityTagEnabled: ptr[bool](false), + FinalizedBlockOffset: ptr[uint32](12), }, Nodes: []*evmcfg.Node{ { @@ -505,6 +506,7 @@ func TestConfig_Marshal(t *testing.T) { FinalityDepth: ptr[uint32](42), FinalityTagEnabled: ptr[bool](false), FlagsContractAddress: mustAddress("0xae4E781a6218A8031764928E88d457937A954fC3"), + FinalizedBlockOffset: ptr[uint32](16), GasEstimator: evmcfg.GasEstimator{ Mode: ptr("SuggestedPrice"), @@ -594,6 +596,8 @@ func TestConfig_Marshal(t *testing.T) { LeaseDuration: &zeroSeconds, NodeIsSyncingEnabled: ptr(true), FinalizedBlockPollInterval: &second, + EnforceRepeatableRead: ptr(true), + DeathDeclarationDelay: &minute, Errors: evmcfg.ClientErrors{ NonceTooLow: ptr[string]("(: |^)nonce too low"), NonceTooHigh: ptr[string]("(: |^)nonce too high"), @@ -992,6 +996,7 @@ NoNewHeadsThreshold = '1m0s' OperatorFactoryAddress = '0xa5B85635Be42F21f94F28034B7DA440EeFF0F418' RPCDefaultBatchSize = 17 RPCBlockQueryDelay = 10 +FinalizedBlockOffset = 16 [EVM.Transactions] ForwardersEnabled = true @@ -1062,6 +1067,8 @@ SyncThreshold = 13 LeaseDuration = '0s' NodeIsSyncingEnabled = true FinalizedBlockPollInterval = '1s' +EnforceRepeatableRead = true +DeathDeclarationDelay = '1m0s' [EVM.NodePool.Errors] NonceTooLow = '(: |^)nonce too low' @@ -1259,7 +1266,7 @@ func TestConfig_Validate(t *testing.T) { toml string exp string }{ - {name: "invalid", toml: invalidTOML, exp: `invalid configuration: 7 errors: + {name: "invalid", toml: invalidTOML, exp: `invalid configuration: 8 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: @@ -1289,10 +1296,11 @@ func TestConfig_Validate(t *testing.T) { - WSURL: missing: required for primary nodes - HTTPURL: missing: required for all nodes - 1.HTTPURL: missing: required for all nodes - - 1: 9 errors: + - 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, celo, gnosis, kroma, metis, optimismBedrock, scroll, wemix, xlayer, zkevm, zksync 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 - Transactions.AutoPurge.MinAttempts: missing: needs to be set if auto-purge feature is enabled for Foo @@ -1351,7 +1359,8 @@ func TestConfig_Validate(t *testing.T) { - 0.ChainID: missing: required for all chains - 1: 2 errors: - ChainID: missing: required for all chains - - Nodes: missing: must have at least one node`}, + - Nodes: missing: must have at least one node + - Aptos.0.Enabled: invalid value (1): expected *bool`}, } { t.Run(tt.name, func(t *testing.T) { var c Config diff --git a/core/services/chainlink/mocks/general_config.go b/core/services/chainlink/mocks/general_config.go index c7e224f4f2..6c11a1fe3a 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks @@ -46,6 +46,24 @@ func (_m *GeneralConfig) AppID() uuid.UUID { return r0 } +// AptosEnabled provides a mock function with given fields: +func (_m *GeneralConfig) AptosEnabled() bool { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for AptosEnabled") + } + + var r0 bool + if rf, ok := ret.Get(0).(func() bool); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + // AuditLogger provides a mock function with given fields: func (_m *GeneralConfig) AuditLogger() config.AuditLogger { ret := _m.Called() diff --git a/core/services/chainlink/relayer_chain_interoperators.go b/core/services/chainlink/relayer_chain_interoperators.go index 32bcc9f18a..60381c0d47 100644 --- a/core/services/chainlink/relayer_chain_interoperators.go +++ b/core/services/chainlink/relayer_chain_interoperators.go @@ -69,6 +69,10 @@ type ChainsNodesStatuser interface { var _ RelayerChainInteroperators = &CoreRelayerChainInteroperators{} +type DummyFactory interface { + NewDummy(config DummyFactoryConfig) (loop.Relayer, error) +} + // CoreRelayerChainInteroperators implements [RelayerChainInteroperators] // as needed for the core [chainlink.Application] type CoreRelayerChainInteroperators struct { @@ -76,6 +80,8 @@ type CoreRelayerChainInteroperators struct { loopRelayers map[types.RelayID]loop.Relayer legacyChains legacyChains + dummyFactory DummyFactory + // we keep an explicit list of services because the legacy implementations have more than // just the relayer service srvs []services.ServiceCtx @@ -98,6 +104,14 @@ func NewCoreRelayerChainInteroperators(initFuncs ...CoreRelayerChainInitFunc) (* // CoreRelayerChainInitFunc is a hook in the constructor to create relayers from a factory. type CoreRelayerChainInitFunc func(op *CoreRelayerChainInteroperators) error +// InitDummy instantiates a dummy relayer +func InitDummy(ctx context.Context, factory RelayerFactory) CoreRelayerChainInitFunc { + return func(op *CoreRelayerChainInteroperators) error { + op.dummyFactory = &factory + return nil + } +} + // InitEVM is a option for instantiating evm relayers func InitEVM(ctx context.Context, factory RelayerFactory, config EVMFactoryConfig) CoreRelayerChainInitFunc { return func(op *CoreRelayerChainInteroperators) (err error) { @@ -178,6 +192,16 @@ func (rs *CoreRelayerChainInteroperators) Get(id types.RelayID) (loop.Relayer, e defer rs.mu.Unlock() lr, exist := rs.loopRelayers[id] if !exist { + // lazily create dummy relayers + if id.Network == "dummy" { + var err error + lr, err = rs.dummyFactory.NewDummy(DummyFactoryConfig{id.ChainID}) + if err != nil { + return nil, err + } + rs.loopRelayers[id] = lr + return lr, nil + } return nil, fmt.Errorf("%w: %s", ErrNoSuchRelayer, id) } return lr, nil diff --git a/core/services/chainlink/relayer_chain_interoperators_test.go b/core/services/chainlink/relayer_chain_interoperators_test.go index c4fbd26861..aaf458c76c 100644 --- a/core/services/chainlink/relayer_chain_interoperators_test.go +++ b/core/services/chainlink/relayer_chain_interoperators_test.go @@ -198,6 +198,10 @@ func TestCoreRelayerChainInteroperators(t *testing.T) { expectedStarknetNodeCnt int expectedStarknetRelayerIds []types.RelayID + expectedDummyChainCnt int + expectedDummyNodeCnt int + expectedDummyRelayerIds []types.RelayID + expectedCosmosChainCnt int expectedCosmosNodeCnt int expectedCosmosRelayerIds []types.RelayID @@ -368,6 +372,8 @@ func TestCoreRelayerChainInteroperators(t *testing.T) { expectedChainCnt, expectedNodeCnt = tt.expectedSolanaChainCnt, tt.expectedSolanaNodeCnt case relay.NetworkStarkNet: expectedChainCnt, expectedNodeCnt = tt.expectedStarknetChainCnt, tt.expectedStarknetNodeCnt + case relay.NetworkDummy: + expectedChainCnt, expectedNodeCnt = tt.expectedDummyChainCnt, tt.expectedDummyNodeCnt case relay.NetworkAptos: t.Skip("aptos doesn't need a CoreRelayerChainInteroperator") diff --git a/core/services/chainlink/relayer_factory.go b/core/services/chainlink/relayer_factory.go index 99b0cecb0a..ae222f56c6 100644 --- a/core/services/chainlink/relayer_factory.go +++ b/core/services/chainlink/relayer_factory.go @@ -26,6 +26,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" corerelay "github.com/smartcontractkit/chainlink/v2/core/services/relay" + "github.com/smartcontractkit/chainlink/v2/core/services/relay/dummy" evmrelay "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/wsrpc" "github.com/smartcontractkit/chainlink/v2/plugins" @@ -39,6 +40,14 @@ type RelayerFactory struct { CapabilitiesRegistry *capabilities.Registry } +type DummyFactoryConfig struct { + ChainID string +} + +func (r *RelayerFactory) NewDummy(config DummyFactoryConfig) (loop.Relayer, error) { + return dummy.NewRelayer(r.Logger, config.ChainID), nil +} + type EVMFactoryConfig struct { legacyevm.ChainOpts evmrelay.CSAETHKeystore @@ -155,7 +164,7 @@ func (r *RelayerFactory) NewSolana(ks keystore.Solana, chainCfgs solcfg.TOMLConf if err != nil { return nil, err } - solanaRelayers[relayID] = relay.NewServerAdapter(solana.NewRelayer(lggr, chain), chain) + solanaRelayers[relayID] = relay.NewServerAdapter(solana.NewRelayer(lggr, chain, r.CapabilitiesRegistry), chain) } } return solanaRelayers, nil @@ -230,7 +239,7 @@ func (r *RelayerFactory) NewStarkNet(ks keystore.StarkNet, chainCfgs config.TOML return nil, err } - starknetRelayers[relayID] = relay.NewServerAdapter(pkgstarknet.NewRelayer(lggr, chain), chain) + starknetRelayers[relayID] = relay.NewServerAdapter(pkgstarknet.NewRelayer(lggr, chain, r.CapabilitiesRegistry), chain) } } return starknetRelayers, nil diff --git a/core/services/chainlink/testdata/config-full.toml b/core/services/chainlink/testdata/config-full.toml index 6a0bbe8688..1ea3051ca0 100644 --- a/core/services/chainlink/testdata/config-full.toml +++ b/core/services/chainlink/testdata/config-full.toml @@ -288,6 +288,7 @@ NoNewHeadsThreshold = '1m0s' OperatorFactoryAddress = '0xa5B85635Be42F21f94F28034B7DA440EeFF0F418' RPCDefaultBatchSize = 17 RPCBlockQueryDelay = 10 +FinalizedBlockOffset = 16 [EVM.Transactions] ForwardersEnabled = true @@ -358,6 +359,8 @@ SyncThreshold = 13 LeaseDuration = '0s' NodeIsSyncingEnabled = true FinalizedBlockPollInterval = '1s' +EnforceRepeatableRead = true +DeathDeclarationDelay = '1m0s' [EVM.NodePool.Errors] NonceTooLow = '(: |^)nonce too low' diff --git a/core/services/chainlink/testdata/config-invalid.toml b/core/services/chainlink/testdata/config-invalid.toml index 68feeeb045..ca22e68c22 100644 --- a/core/services/chainlink/testdata/config-invalid.toml +++ b/core/services/chainlink/testdata/config-invalid.toml @@ -53,6 +53,7 @@ SendOnly = true ChainID = '1' ChainType = 'Foo' FinalityDepth = 32 +FinalizedBlockOffset = 64 [EVM.Transactions.AutoPurge] Enabled = true @@ -158,6 +159,9 @@ APIKey = 'key' [[Starknet]] +[[Aptos]] +Enabled = 1 + [OCR2] Enabled = true diff --git a/core/services/chainlink/testdata/config-multi-chain-effective.toml b/core/services/chainlink/testdata/config-multi-chain-effective.toml index 7ca4311764..0990aa5027 100644 --- a/core/services/chainlink/testdata/config-multi-chain-effective.toml +++ b/core/services/chainlink/testdata/config-multi-chain-effective.toml @@ -275,6 +275,7 @@ NoNewHeadsThreshold = '3m0s' OperatorFactoryAddress = '0x3E64Cd889482443324F91bFA9c84fE72A511f48A' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 12 [EVM.Transactions] ForwardersEnabled = false @@ -329,6 +330,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [EVM.OCR] ContractConfirmations = 4 @@ -371,6 +374,7 @@ NoNewHeadsThreshold = '3m0s' OperatorFactoryAddress = '0x8007e24251b1D2Fc518Eb843A701d9cD21fe0aA3' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [EVM.Transactions] ForwardersEnabled = false @@ -425,6 +429,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [EVM.OCR] ContractConfirmations = 4 @@ -461,6 +467,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '30s' RPCDefaultBatchSize = 100 RPCBlockQueryDelay = 10 +FinalizedBlockOffset = 0 [EVM.Transactions] ForwardersEnabled = false @@ -515,6 +522,8 @@ SyncThreshold = 10 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [EVM.OCR] ContractConfirmations = 4 diff --git a/core/services/chainlink/testdata/config-multi-chain.toml b/core/services/chainlink/testdata/config-multi-chain.toml index e45255a437..5373e0e62d 100644 --- a/core/services/chainlink/testdata/config-multi-chain.toml +++ b/core/services/chainlink/testdata/config-multi-chain.toml @@ -39,6 +39,7 @@ CPUProfileRate = 7 ChainID = '1' FinalityDepth = 26 FinalityTagEnabled = false +FinalizedBlockOffset = 12 [[EVM.Nodes]] Name = 'primary' diff --git a/core/services/cron/cron.go b/core/services/cron/cron.go index 500192554f..aa4d1d782f 100644 --- a/core/services/cron/cron.go +++ b/core/services/cron/cron.go @@ -32,6 +32,9 @@ func NewCronFromJobSpec( "jobID", jobSpec.ID, "schedule", jobSpec.CronSpec.CronSchedule, ) + if id := jobSpec.CronSpec.EVMChainID; id != nil { + cronLogger = logger.With("evmChainID", id) + } return &Cron{ cronRunner: cronRunner(), @@ -48,7 +51,7 @@ func (cr *Cron) Start(context.Context) error { _, err := cr.cronRunner.AddFunc(cr.jobSpec.CronSpec.CronSchedule, cr.runPipeline) if err != nil { - cr.logger.Errorw(fmt.Sprintf("Error running cron job %d", cr.jobSpec.ID), "err", err, "schedule", cr.jobSpec.CronSpec.CronSchedule, "jobID", cr.jobSpec.ID) + cr.logger.Errorw(fmt.Sprintf("Error running cron job %d", cr.jobSpec.ID), "err", err) return err } cr.cronRunner.Start() @@ -67,12 +70,17 @@ func (cr *Cron) runPipeline() { ctx, cancel := cr.chStop.NewCtx() defer cancel() + jobSpec := map[string]interface{}{ + "databaseID": cr.jobSpec.ID, + "externalJobID": cr.jobSpec.ExternalJobID, + "name": cr.jobSpec.Name.ValueOrZero(), + } + if id := cr.jobSpec.CronSpec.EVMChainID; id != nil { + jobSpec["evmChainID"] = id.String() + } + vars := pipeline.NewVarsFrom(map[string]interface{}{ - "jobSpec": map[string]interface{}{ - "databaseID": cr.jobSpec.ID, - "externalJobID": cr.jobSpec.ExternalJobID, - "name": cr.jobSpec.Name.ValueOrZero(), - }, + "jobSpec": jobSpec, "jobRun": map[string]interface{}{ "meta": map[string]interface{}{}, }, diff --git a/core/services/feeds/mocks/connections_manager.go b/core/services/feeds/mocks/connections_manager.go index 06cb0eeb5a..daeb06f692 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/feeds/mocks/feeds_manager_client.go b/core/services/feeds/mocks/feeds_manager_client.go index 71c8786da3..e2b92c28a8 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/feeds/mocks/orm.go b/core/services/feeds/mocks/orm.go index 625a0b41d9..3fce89eb60 100644 --- a/core/services/feeds/mocks/orm.go +++ b/core/services/feeds/mocks/orm.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/feeds/mocks/service.go b/core/services/feeds/mocks/service.go index 664f39365a..425f38d69d 100644 --- a/core/services/feeds/mocks/service.go +++ b/core/services/feeds/mocks/service.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/fluxmonitorv2/mocks/contract_submitter.go b/core/services/fluxmonitorv2/mocks/contract_submitter.go index ac67c353cf..0e969294b9 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/fluxmonitorv2/mocks/flags.go b/core/services/fluxmonitorv2/mocks/flags.go index 0585611ed0..7b5e0330c8 100644 --- a/core/services/fluxmonitorv2/mocks/flags.go +++ b/core/services/fluxmonitorv2/mocks/flags.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/fluxmonitorv2/mocks/key_store_interface.go b/core/services/fluxmonitorv2/mocks/key_store_interface.go index f831c854ce..4c99cea1af 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/fluxmonitorv2/mocks/orm.go b/core/services/fluxmonitorv2/mocks/orm.go index 7755543b40..d43a9ea16e 100644 --- a/core/services/fluxmonitorv2/mocks/orm.go +++ b/core/services/fluxmonitorv2/mocks/orm.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/functions/mocks/bridge_accessor.go b/core/services/functions/mocks/bridge_accessor.go index 797007d788..3641e7dfa1 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. 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 cd402c1e6a..e7f7053a0b 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/functions/mocks/functions_listener.go b/core/services/functions/mocks/functions_listener.go index fed369fd65..ed0d269c6f 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/functions/mocks/offchain_transmitter.go b/core/services/functions/mocks/offchain_transmitter.go index 1e1422b8d4..b718222d09 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/functions/mocks/orm.go b/core/services/functions/mocks/orm.go index c921fda5c6..813abed7e2 100644 --- a/core/services/functions/mocks/orm.go +++ b/core/services/functions/mocks/orm.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. 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 0f37ee6040..41e3148d05 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/gateway/connector/mocks/gateway_connector_handler.go b/core/services/gateway/connector/mocks/gateway_connector_handler.go index c21528134f..a79a2d32fe 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/gateway/connector/mocks/signer.go b/core/services/gateway/connector/mocks/signer.go index bb4c364839..c60bd9d436 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. 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 7581414d00..6d7c6a2535 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/gateway/handlers/functions/allowlist/mocks/orm.go b/core/services/gateway/handlers/functions/allowlist/mocks/orm.go index c1d47169c5..9c293fb2f4 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. 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 93b476820a..9bc8fc3ccb 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/gateway/handlers/functions/subscriptions/mocks/orm.go b/core/services/gateway/handlers/functions/subscriptions/mocks/orm.go index 614ba83af1..759cf03c5d 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/gateway/handlers/mocks/don.go b/core/services/gateway/handlers/mocks/don.go index 91c162b2f5..ceaa81eea1 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/gateway/handlers/mocks/handler.go b/core/services/gateway/handlers/mocks/handler.go index e22c7f158f..e0bfe28f63 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/gateway/network/mocks/connection_acceptor.go b/core/services/gateway/network/mocks/connection_acceptor.go index 37ebc7b8c9..1af874b433 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/gateway/network/mocks/connection_initiator.go b/core/services/gateway/network/mocks/connection_initiator.go index 7efac288ba..0b63edf182 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. 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 0cbcf3eb6b..d2bba61f2f 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. 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 586677c604..8609803eaf 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/gateway/network/mocks/web_socket_server.go b/core/services/gateway/network/mocks/web_socket_server.go index fb880193c7..32483fe402 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/job/job_orm_test.go b/core/services/job/job_orm_test.go index 976847da45..d9a4ea16ab 100644 --- a/core/services/job/job_orm_test.go +++ b/core/services/job/job_orm_test.go @@ -997,6 +997,18 @@ func TestORM_ValidateKeyStoreMatch(t *testing.T) { require.NoError(t, err) }) + t.Run(("test Aptos key validation"), func(t *testing.T) { + ctx := testutils.Context(t) + jb.OCR2OracleSpec.Relay = relay.NetworkAptos + err := job.ValidateKeyStoreMatch(ctx, jb.OCR2OracleSpec, keyStore, "bad key") + require.EqualError(t, err, "no Aptos key matching: \"bad key\"") + + aptosKey, err := keyStore.Aptos().Create(ctx) + require.NoError(t, err) + err = job.ValidateKeyStoreMatch(ctx, jb.OCR2OracleSpec, keyStore, aptosKey.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/mocks/kv_store.go b/core/services/job/mocks/kv_store.go index 270a6c1d55..dba287da5e 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/job/mocks/orm.go b/core/services/job/mocks/orm.go index e8911b25af..1d2615b519 100644 --- a/core/services/job/mocks/orm.go +++ b/core/services/job/mocks/orm.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/job/mocks/service_ctx.go b/core/services/job/mocks/service_ctx.go index d01ef619be..2871f7fcf0 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/job/mocks/spawner.go b/core/services/job/mocks/spawner.go index 7127636cdb..24bfa36bec 100644 --- a/core/services/job/mocks/spawner.go +++ b/core/services/job/mocks/spawner.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/job/models.go b/core/services/job/models.go index 5457768141..a8c12cbece 100644 --- a/core/services/job/models.go +++ b/core/services/job/models.go @@ -490,6 +490,7 @@ type DirectRequestSpec struct { type CronSpec struct { ID int32 `toml:"-"` CronSchedule string `toml:"schedule"` + EVMChainID *big.Big `toml:"evmChainID"` CreatedAt time.Time `toml:"-"` UpdatedAt time.Time `toml:"-"` } diff --git a/core/services/job/orm.go b/core/services/job/orm.go index efcb088284..3f2d5c237d 100644 --- a/core/services/job/orm.go +++ b/core/services/job/orm.go @@ -502,8 +502,8 @@ func (o *orm) insertKeeperSpec(ctx context.Context, spec *KeeperSpec) (specID in } func (o *orm) insertCronSpec(ctx context.Context, spec *CronSpec) (specID int32, err error) { - return o.prepareQuerySpecID(ctx, `INSERT INTO cron_specs (cron_schedule, created_at, updated_at) - VALUES (:cron_schedule, NOW(), NOW()) + return o.prepareQuerySpecID(ctx, `INSERT INTO cron_specs (cron_schedule, evm_chain_id, created_at, updated_at) + VALUES (:cron_schedule, :evm_chain_id, NOW(), NOW()) RETURNING id;`, spec) } @@ -602,8 +602,10 @@ func validateKeyStoreMatchForRelay(ctx context.Context, network string, keyStore return errors.Errorf("no Starknet key matching: %q", key) } case relay.NetworkAptos: - // TODO BCI-2953 - return nil + _, err := keyStore.Aptos().Get(key) + if err != nil { + return errors.Errorf("no Aptos key matching: %q", key) + } } return nil } diff --git a/core/services/keystore/aptos.go b/core/services/keystore/aptos.go new file mode 100644 index 0000000000..4a40033ae0 --- /dev/null +++ b/core/services/keystore/aptos.go @@ -0,0 +1,182 @@ +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/aptoskey" +) + +//go:generate mockery --quiet --name Aptos --output ./mocks/ --case=underscore --filename aptos.go + +type Aptos interface { + Get(id string) (aptoskey.Key, error) + GetAll() ([]aptoskey.Key, error) + Create(ctx context.Context) (aptoskey.Key, error) + Add(ctx context.Context, key aptoskey.Key) error + Delete(ctx context.Context, id string) (aptoskey.Key, error) + Import(ctx context.Context, keyJSON []byte, password string) (aptoskey.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 aptos struct { + *keyManager +} + +var _ Aptos = &aptos{} + +func newAptosKeyStore(km *keyManager) *aptos { + return &aptos{ + km, + } +} + +func (ks *aptos) Get(id string) (aptoskey.Key, error) { + ks.lock.RLock() + defer ks.lock.RUnlock() + if ks.isLocked() { + return aptoskey.Key{}, ErrLocked + } + return ks.getByID(id) +} + +func (ks *aptos) GetAll() (keys []aptoskey.Key, _ error) { + ks.lock.RLock() + defer ks.lock.RUnlock() + if ks.isLocked() { + return nil, ErrLocked + } + for _, key := range ks.keyRing.Aptos { + keys = append(keys, key) + } + return keys, nil +} + +func (ks *aptos) Create(ctx context.Context) (aptoskey.Key, error) { + ks.lock.Lock() + defer ks.lock.Unlock() + if ks.isLocked() { + return aptoskey.Key{}, ErrLocked + } + key, err := aptoskey.New() + if err != nil { + return aptoskey.Key{}, err + } + return key, ks.safeAddKey(ctx, key) +} + +func (ks *aptos) Add(ctx context.Context, key aptoskey.Key) error { + ks.lock.Lock() + defer ks.lock.Unlock() + if ks.isLocked() { + return ErrLocked + } + if _, found := ks.keyRing.Aptos[key.ID()]; found { + return fmt.Errorf("key with ID %s already exists", key.ID()) + } + return ks.safeAddKey(ctx, key) +} + +func (ks *aptos) Delete(ctx context.Context, id string) (aptoskey.Key, error) { + ks.lock.Lock() + defer ks.lock.Unlock() + if ks.isLocked() { + return aptoskey.Key{}, ErrLocked + } + key, err := ks.getByID(id) + if err != nil { + return aptoskey.Key{}, err + } + err = ks.safeRemoveKey(ctx, key) + return key, err +} + +func (ks *aptos) Import(ctx context.Context, keyJSON []byte, password string) (aptoskey.Key, error) { + ks.lock.Lock() + defer ks.lock.Unlock() + if ks.isLocked() { + return aptoskey.Key{}, ErrLocked + } + key, err := aptoskey.FromEncryptedJSON(keyJSON, password) + if err != nil { + return aptoskey.Key{}, errors.Wrap(err, "AptosKeyStore#ImportKey failed to decrypt key") + } + if _, found := ks.keyRing.Aptos[key.ID()]; found { + return aptoskey.Key{}, fmt.Errorf("key with ID %s already exists", key.ID()) + } + return key, ks.keyManager.safeAddKey(ctx, key) +} + +func (ks *aptos) 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 *aptos) EnsureKey(ctx context.Context) error { + ks.lock.Lock() + defer ks.lock.Unlock() + if ks.isLocked() { + return ErrLocked + } + if len(ks.keyRing.Aptos) > 0 { + return nil + } + + key, err := aptoskey.New() + if err != nil { + return err + } + + ks.logger.Infof("Created Aptos key with ID %s", key.ID()) + + return ks.safeAddKey(ctx, key) +} + +func (ks *aptos) Sign(_ context.Context, id string, msg []byte) (signature []byte, err error) { + k, err := ks.Get(id) + if err != nil { + return nil, err + } + return k.Sign(msg) +} + +func (ks *aptos) getByID(id string) (aptoskey.Key, error) { + key, found := ks.keyRing.Aptos[id] + if !found { + return aptoskey.Key{}, KeyNotFoundError{ID: id, KeyType: "Aptos"} + } + return key, nil +} + +// AptosSigner implements [github.com/smartcontractkit/chainlink-common/pkg/loop.Keystore] interface and the requirements +// Handles signing for Apots Messages +type AptosLooppSigner struct { + Aptos +} + +var _ loop.Keystore = &AptosLooppSigner{} + +// Returns a list of Aptos Public Keys +func (s *AptosLooppSigner) Accounts(ctx context.Context) (accounts []string, err error) { + ks, err := s.GetAll() + if err != nil { + return nil, err + } + for _, k := range ks { + accounts = append(accounts, k.ID()) + } + return +} diff --git a/core/services/keystore/aptos_test.go b/core/services/keystore/aptos_test.go new file mode 100644 index 0000000000..9a2764adff --- /dev/null +++ b/core/services/keystore/aptos_test.go @@ -0,0 +1,138 @@ +package keystore_test + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-common/pkg/utils" + "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" + "github.com/smartcontractkit/chainlink/v2/core/services/keystore" + "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/aptoskey" +) + +func Test_AptosKeyStore_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.Aptos() + 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.Equal(t, 0, len(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) + assert.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) + assert.Error(t, err) + _, err = ks.Import(ctx, []byte(""), cltest.Password) + assert.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 := aptoskey.New() + require.NoError(t, err) + err = ks.Add(ctx, newKey) + require.NoError(t, err) + err = ks.Add(ctx, newKey) + assert.Error(t, err) + keys, err := ks.GetAll() + require.NoError(t, err) + require.Equal(t, 1, len(keys)) + _, err = ks.Delete(ctx, newKey.ID()) + require.NoError(t, err) + _, err = ks.Delete(ctx, newKey.ID()) + assert.Error(t, err) + keys, err = ks.GetAll() + require.NoError(t, err) + require.Equal(t, 0, len(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) + assert.NoError(t, err) + + err = ks.EnsureKey(ctx) + assert.NoError(t, err) + + keys, err := ks.GetAll() + require.NoError(t, err) + require.Equal(t, 1, len(keys)) + }) + + t.Run("sign tx", func(t *testing.T) { + defer reset() + ctx := testutils.Context(t) + newKey, err := aptoskey.New() + require.NoError(t, err) + require.NoError(t, ks.Add(ctx, newKey)) + + // sign unknown ID + _, err = ks.Sign(testutils.Context(t), "not-real", nil) + assert.Error(t, err) + + // sign known key + payload := []byte{1} + sig, err := ks.Sign(testutils.Context(t), newKey.ID(), payload) + require.NoError(t, err) + + directSig, err := newKey.Sign(payload) + require.NoError(t, err) + + // signatures should match using keystore sign or key sign + assert.Equal(t, directSig, sig) + }) +} diff --git a/core/services/keystore/chaintype/chaintype.go b/core/services/keystore/chaintype/chaintype.go index cd149e390e..8a12322ebf 100644 --- a/core/services/keystore/chaintype/chaintype.go +++ b/core/services/keystore/chaintype/chaintype.go @@ -19,6 +19,8 @@ const ( Solana ChainType = "solana" // StarkNet for the StarkNet chain StarkNet ChainType = "starknet" + // Aptos for the Aptos chain + Aptos ChainType = "aptos" ) type ChainTypes []ChainType @@ -35,7 +37,7 @@ func (c ChainTypes) String() (out string) { } // SupportedChainTypes contain all chains that are supported -var SupportedChainTypes = ChainTypes{EVM, Cosmos, Solana, StarkNet} +var SupportedChainTypes = ChainTypes{EVM, Cosmos, Solana, StarkNet, Aptos} // ErrInvalidChainType is an error to indicate an unsupported chain type var ErrInvalidChainType error diff --git a/core/services/keystore/keys/aptoskey/account.go b/core/services/keystore/keys/aptoskey/account.go new file mode 100644 index 0000000000..89f62d3301 --- /dev/null +++ b/core/services/keystore/keys/aptoskey/account.go @@ -0,0 +1,72 @@ +package aptoskey + +import ( + "encoding/hex" + "errors" + "fmt" + "strings" +) + +// AccountAddress is a 32 byte address on the Aptos blockchain +// It can represent an Object, an Account, and much more. +// +// AccountAddress is copied from the aptos sdk because: +// 1. There are still breaking changes in sdk and we don't want the dependency. +// 2. AccountAddress is just a wrapper and can be easily extracted out. +// +// https://github.com/aptos-labs/aptos-go-sdk/blob/main/internal/types/account.go +type AccountAddress [32]byte + +// IsSpecial Returns whether the address is a "special" address. Addresses are considered +// special if the first 63 characters of the hex string are zero. In other words, +// an address is special if the first 31 bytes are zero and the last byte is +// smaller than `0b10000` (16). In other words, special is defined as an address +// that matches the following regex: `^0x0{63}[0-9a-f]$`. In short form this means +// the addresses in the range from `0x0` to `0xf` (inclusive) are special. +// For more details see the v1 address standard defined as part of AIP-40: +// https://github.com/aptos-foundation/AIPs/blob/main/aips/aip-40.md +func (aa *AccountAddress) IsSpecial() bool { + for _, b := range aa[:31] { + if b != 0 { + return false + } + } + return aa[31] < 0x10 +} + +// String Returns the canonical string representation of the AccountAddress +func (aa *AccountAddress) String() string { + if aa.IsSpecial() { + return fmt.Sprintf("0x%x", aa[31]) + } + return BytesToHex(aa[:]) +} + +// ParseStringRelaxed parses a string into an AccountAddress +func (aa *AccountAddress) ParseStringRelaxed(x string) error { + x = strings.TrimPrefix(x, "0x") + if len(x) < 1 { + return ErrAddressTooShort + } + if len(x) > 64 { + return ErrAddressTooLong + } + if len(x)%2 != 0 { + x = "0" + x + } + bytes, err := hex.DecodeString(x) + if err != nil { + return err + } + // zero-prefix/right-align what bytes we got + copy((*aa)[32-len(bytes):], bytes) + + return nil +} + +var ErrAddressTooShort = errors.New("AccountAddress too short") +var ErrAddressTooLong = errors.New("AccountAddress too long") + +func BytesToHex(bytes []byte) string { + return "0x" + hex.EncodeToString(bytes) +} diff --git a/core/services/keystore/keys/aptoskey/account_test.go b/core/services/keystore/keys/aptoskey/account_test.go new file mode 100644 index 0000000000..b9ed4ea04a --- /dev/null +++ b/core/services/keystore/keys/aptoskey/account_test.go @@ -0,0 +1,141 @@ +package aptoskey + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +// Tests extracted from +// https://github.com/aptos-labs/aptos-go-sdk/blob/5ee5ac308e5881b508c0a5124f5e0b8df27a4d40/internal/types/account_test.go + +func TestStringOutput(t *testing.T) { + inputs := [][]byte{ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F}, + {0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}, + {0x02, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}, + {0x00, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}, + {0x00, 0x04, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}, + {0x00, 0x00, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}, + } + expected := []string{ + "0x0", + "0x1", + "0xf", + "0x1234123412341234123412341234123412341234123412340123456789abcdef", + "0x0234123412341234123412341234123412341234123412340123456789abcdef", + "0x0034123412341234123412341234123412341234123412340123456789abcdef", + "0x0004123412341234123412341234123412341234123412340123456789abcdef", + "0x0000123412341234123412341234123412341234123412340123456789abcdef", + } + + for i := 0; i < len(inputs); i++ { + addr := AccountAddress(inputs[i]) + assert.Equal(t, expected[i], addr.String()) + } +} + +func TestAccountAddress_ParseStringRelaxed_Error(t *testing.T) { + var owner AccountAddress + err := owner.ParseStringRelaxed("0x") + assert.Error(t, err) + err = owner.ParseStringRelaxed("0xF1234567812345678123456781234567812345678123456781234567812345678") + assert.Error(t, err) + err = owner.ParseStringRelaxed("NotHex") + assert.Error(t, err) +} + +func TestAccountAddress_String(t *testing.T) { + testCases := []struct { + name string + address AccountAddress + expected string + }{ + { + name: "Special address", + address: AccountAddress{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + expected: "0x1", + }, + { + name: "Non-special address", + address: AccountAddress{0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0}, + expected: "0x123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + assert.Equal(t, tc.expected, tc.address.String()) + }) + } +} + +func TestAccountAddress_IsSpecial(t *testing.T) { + testCases := []struct { + name string + address AccountAddress + expected bool + }{ + { + name: "Special address", + address: AccountAddress{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + expected: true, + }, + { + name: "Non-special address", + address: AccountAddress{0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0}, + expected: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + assert.Equal(t, tc.expected, tc.address.IsSpecial()) + }) + } +} + +func TestBytesToHex(t *testing.T) { + testCases := []struct { + name string + bytes []byte + expected string + }{ + { + name: "Empty bytes", + bytes: []byte{}, + expected: "0x", + }, + { + name: "Non-empty bytes", + bytes: []byte{0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0}, + expected: "0x123456789abcdef0", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + assert.Equal(t, tc.expected, BytesToHex(tc.bytes)) + }) + } +} + +func TestAccountSpecialString(t *testing.T) { + var aa AccountAddress + aa[31] = 3 + aas := aa.String() + if aas != "0x3" { + t.Errorf("wanted 0x3 got %s", aas) + } + + var aa2 AccountAddress + err := aa2.ParseStringRelaxed("0x3") + if err != nil { + t.Errorf("unexpected err %s", err) + } + if aa2 != aa { + t.Errorf("aa2 != aa") + } +} diff --git a/core/services/keystore/keys/aptoskey/export.go b/core/services/keystore/keys/aptoskey/export.go new file mode 100644 index 0000000000..23553ef9dc --- /dev/null +++ b/core/services/keystore/keys/aptoskey/export.go @@ -0,0 +1,48 @@ +package aptoskey + +import ( + "encoding/hex" + + "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 = "Aptos" + +// 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: hex.EncodeToString(key.pubKey), + Crypto: cryptoJSON, + } + }, + ) +} + +func adulteratedPassword(password string) string { + return "aptoskey" + password +} diff --git a/core/services/keystore/keys/aptoskey/export_test.go b/core/services/keystore/keys/aptoskey/export_test.go new file mode 100644 index 0000000000..a920e43229 --- /dev/null +++ b/core/services/keystore/keys/aptoskey/export_test.go @@ -0,0 +1,19 @@ +package aptoskey + +import ( + "testing" + + "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys" +) + +func TestAptosKeys_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/aptoskey/key.go b/core/services/keystore/keys/aptoskey/key.go new file mode 100644 index 0000000000..ec9b27a359 --- /dev/null +++ b/core/services/keystore/keys/aptoskey/key.go @@ -0,0 +1,103 @@ +package aptoskey + +import ( + "crypto" + "crypto/ed25519" + crypto_rand "crypto/rand" + "fmt" + "io" + + "github.com/mr-tron/base58" +) + +// Raw represents the Aptos private key +type Raw []byte + +// Key gets the Key +func (raw Raw) Key() Key { + privKey := ed25519.NewKeyFromSeed(raw) + pubKey := privKey.Public().(ed25519.PublicKey) + return Key{ + privkey: privKey, + pubKey: pubKey, + } +} + +// String returns description +func (raw Raw) String() string { + return "" +} + +// GoString wraps String() +func (raw Raw) GoString() string { + return raw.String() +} + +var _ fmt.GoStringer = &Key{} + +// Key represents Aptos key +type Key struct { + privkey ed25519.PrivateKey + pubKey ed25519.PublicKey +} + +// New creates new Key +func New() (Key, error) { + return newFrom(crypto_rand.Reader) +} + +// MustNewInsecure return Key if no error +func MustNewInsecure(reader io.Reader) Key { + key, err := newFrom(reader) + if err != nil { + panic(err) + } + return key +} + +// newFrom creates new Key from a provided random reader +func newFrom(reader io.Reader) (Key, error) { + pub, priv, err := ed25519.GenerateKey(reader) + if err != nil { + return Key{}, err + } + return Key{ + privkey: priv, + pubKey: pub, + }, nil +} + +// ID gets Key ID +func (key Key) ID() string { + return key.PublicKeyStr() +} + +// GetPublic get Key's public key +func (key Key) GetPublic() ed25519.PublicKey { + return key.pubKey +} + +// PublicKeyStr return base58 encoded public key +func (key Key) PublicKeyStr() string { + return base58.Encode(key.pubKey) +} + +// Raw returns the seed from private key +func (key Key) Raw() Raw { + return key.privkey.Seed() +} + +// String is the print-friendly format of the Key +func (key Key) String() string { + return fmt.Sprintf("AptosKey{PrivateKey: , Public Key: %s}", key.PublicKeyStr()) +} + +// 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 key.privkey.Sign(crypto_rand.Reader, msg, crypto.Hash(0)) // no specific hash function used +} diff --git a/core/services/keystore/keys/ocr2key/aptos_keyring.go b/core/services/keystore/keys/ocr2key/aptos_keyring.go new file mode 100644 index 0000000000..51e6c3a9c8 --- /dev/null +++ b/core/services/keystore/keys/ocr2key/aptos_keyring.go @@ -0,0 +1,118 @@ +package ocr2key + +import ( + "crypto/ed25519" + "encoding/binary" + "io" + + "github.com/hdevalence/ed25519consensus" + "github.com/pkg/errors" + "golang.org/x/crypto/blake2s" + + "github.com/smartcontractkit/chainlink/v2/core/utils" + + "github.com/smartcontractkit/libocr/offchainreporting2/types" + "github.com/smartcontractkit/libocr/offchainreporting2plus/chains/evmutil" + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" +) + +var _ ocrtypes.OnchainKeyring = &aptosKeyring{} + +type aptosKeyring struct { + privKey ed25519.PrivateKey + pubKey ed25519.PublicKey +} + +func newAptosKeyring(material io.Reader) (*aptosKeyring, error) { + pubKey, privKey, err := ed25519.GenerateKey(material) + if err != nil { + return nil, err + } + return &aptosKeyring{pubKey: pubKey, privKey: privKey}, nil +} + +func (akr *aptosKeyring) PublicKey() ocrtypes.OnchainPublicKey { + return []byte(akr.pubKey) +} + +func (akr *aptosKeyring) reportToSigData(reportCtx ocrtypes.ReportContext, report ocrtypes.Report) ([]byte, error) { + rawReportContext := evmutil.RawReportContext(reportCtx) + h, err := blake2s.New256(nil) + if err != nil { + return nil, err + } + reportLen := make([]byte, 4) + binary.BigEndian.PutUint32(reportLen[0:], uint32(len(report))) + h.Write(reportLen[:]) + h.Write(report) + h.Write(rawReportContext[0][:]) + h.Write(rawReportContext[1][:]) + h.Write(rawReportContext[2][:]) + return h.Sum(nil), nil +} + +func (akr *aptosKeyring) Sign(reportCtx ocrtypes.ReportContext, report ocrtypes.Report) ([]byte, error) { + sigData, err := akr.reportToSigData(reportCtx, report) + if err != nil { + return nil, err + } + return akr.signBlob(sigData) +} + +func (akr *aptosKeyring) Sign3(digest types.ConfigDigest, seqNr uint64, r ocrtypes.Report) (signature []byte, err error) { + return nil, errors.New("not implemented") +} + +func (akr *aptosKeyring) signBlob(b []byte) ([]byte, error) { + signedMsg := ed25519.Sign(akr.privKey, b) + // match on-chain parsing (first 32 bytes are for pubkey, remaining are for signature) + return utils.ConcatBytes(akr.PublicKey(), signedMsg), nil +} + +func (akr *aptosKeyring) Verify(publicKey ocrtypes.OnchainPublicKey, reportCtx ocrtypes.ReportContext, report ocrtypes.Report, signature []byte) bool { + hash, err := akr.reportToSigData(reportCtx, report) + if err != nil { + return false + } + return akr.verifyBlob(publicKey, hash, signature) +} + +func (akr *aptosKeyring) Verify3(publicKey ocrtypes.OnchainPublicKey, cd ocrtypes.ConfigDigest, seqNr uint64, r ocrtypes.Report, signature []byte) bool { + return false +} + +func (akr *aptosKeyring) verifyBlob(pubkey ocrtypes.OnchainPublicKey, b, sig []byte) bool { + // Ed25519 signatures are always 64 bytes and the + // public key (always prefixed, see Sign above) is always, + // 32 bytes, so we always require the max signature length. + if len(sig) != akr.MaxSignatureLength() { + return false + } + if len(pubkey) != ed25519.PublicKeySize { + return false + } + return ed25519consensus.Verify(ed25519.PublicKey(pubkey), b, sig[32:]) +} + +func (akr *aptosKeyring) MaxSignatureLength() int { + // Reference: https://pkg.go.dev/crypto/ed25519 + return ed25519.PublicKeySize + ed25519.SignatureSize // 32 + 64 +} + +func (akr *aptosKeyring) Marshal() ([]byte, error) { + return akr.privKey.Seed(), nil +} + +func (akr *aptosKeyring) Unmarshal(in []byte) error { + if len(in) != ed25519.SeedSize { + return errors.Errorf("unexpected seed size, got %d want %d", len(in), ed25519.SeedSize) + } + privKey := ed25519.NewKeyFromSeed(in) + akr.privKey = privKey + pubKey, ok := privKey.Public().(ed25519.PublicKey) + if !ok { + return errors.New("failed to cast public key to ed25519.PublicKey") + } + akr.pubKey = pubKey + return nil +} diff --git a/core/services/keystore/keys/ocr2key/aptos_keyring_test.go b/core/services/keystore/keys/ocr2key/aptos_keyring_test.go new file mode 100644 index 0000000000..2b88d61065 --- /dev/null +++ b/core/services/keystore/keys/ocr2key/aptos_keyring_test.go @@ -0,0 +1,58 @@ +package ocr2key + +import ( + "bytes" + cryptorand "crypto/rand" + "testing" + + "github.com/stretchr/testify/assert" + + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/stretchr/testify/require" +) + +func TestAptosKeyRing_Sign_Verify(t *testing.T) { + kr1, err := newAptosKeyring(cryptorand.Reader) + require.NoError(t, err) + kr2, err := newAptosKeyring(cryptorand.Reader) + require.NoError(t, err) + ctx := ocrtypes.ReportContext{} + + t.Run("can verify", func(t *testing.T) { + report := ocrtypes.Report{} + sig, err := kr1.Sign(ctx, report) + require.NoError(t, err) + t.Log(len(sig)) + result := kr2.Verify(kr1.PublicKey(), ctx, report, sig) + require.True(t, result) + }) + + t.Run("invalid sig", func(t *testing.T) { + report := ocrtypes.Report{} + result := kr2.Verify(kr1.PublicKey(), ctx, report, []byte{0x01}) + require.False(t, result) + }) + + t.Run("invalid pubkey", func(t *testing.T) { + report := ocrtypes.Report{} + sig, err := kr1.Sign(ctx, report) + require.NoError(t, err) + result := kr2.Verify([]byte{0x01}, ctx, report, sig) + require.False(t, result) + }) +} + +func TestAptosKeyRing_Marshalling(t *testing.T) { + kr1, err := newAptosKeyring(cryptorand.Reader) + require.NoError(t, err) + m, err := kr1.Marshal() + require.NoError(t, err) + kr2 := aptosKeyring{} + err = kr2.Unmarshal(m) + require.NoError(t, err) + assert.True(t, bytes.Equal(kr1.pubKey, kr2.pubKey)) + assert.True(t, bytes.Equal(kr1.privKey, kr2.privKey)) + + // Invalid seed size should error + require.Error(t, kr2.Unmarshal([]byte{0x01})) +} diff --git a/core/services/keystore/keys/ocr2key/cosmos_keyring.go b/core/services/keystore/keys/ocr2key/cosmos_keyring.go index 5f1b9b9819..1d1a477f2d 100644 --- a/core/services/keystore/keys/ocr2key/cosmos_keyring.go +++ b/core/services/keystore/keys/ocr2key/cosmos_keyring.go @@ -7,12 +7,13 @@ import ( "github.com/hdevalence/ed25519consensus" "github.com/pkg/errors" - "github.com/smartcontractkit/libocr/offchainreporting2/types" - "github.com/smartcontractkit/libocr/offchainreporting2plus/chains/evmutil" - ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "golang.org/x/crypto/blake2s" "github.com/smartcontractkit/chainlink/v2/core/utils" + + "github.com/smartcontractkit/libocr/offchainreporting2/types" + "github.com/smartcontractkit/libocr/offchainreporting2plus/chains/evmutil" + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" ) var _ ocrtypes.OnchainKeyring = &cosmosKeyring{} @@ -108,6 +109,10 @@ func (ckr *cosmosKeyring) Unmarshal(in []byte) error { } privKey := ed25519.NewKeyFromSeed(in) ckr.privKey = privKey - ckr.pubKey = privKey.Public().(ed25519.PublicKey) + pubKey, ok := privKey.Public().(ed25519.PublicKey) + if !ok { + return errors.New("failed to cast public key to ed25519.PublicKey") + } + ckr.pubKey = pubKey return nil } diff --git a/core/services/keystore/keys/ocr2key/export.go b/core/services/keystore/keys/ocr2key/export.go index 5487e31046..8fa5ffedfe 100644 --- a/core/services/keystore/keys/ocr2key/export.go +++ b/core/services/keystore/keys/ocr2key/export.go @@ -46,6 +46,8 @@ func FromEncryptedJSON(keyJSON []byte, password string) (KeyBundle, error) { kb = newKeyBundle(new(solanaKeyring)) case chaintype.StarkNet: kb = newKeyBundle(new(starkkey.OCR2Key)) + case chaintype.Aptos: + kb = newKeyBundle(new(aptosKeyring)) 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 dfa820eb2b..b0ffa2db00 100644 --- a/core/services/keystore/keys/ocr2key/export_test.go +++ b/core/services/keystore/keys/ocr2key/export_test.go @@ -18,6 +18,7 @@ func TestExport(t *testing.T) { {chain: chaintype.Cosmos}, {chain: chaintype.Solana}, {chain: chaintype.StarkNet}, + {chain: chaintype.Aptos}, } 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 2c3a4bebeb..2c25a159fe 100644 --- a/core/services/keystore/keys/ocr2key/key_bundle.go +++ b/core/services/keystore/keys/ocr2key/key_bundle.go @@ -43,6 +43,7 @@ var _ KeyBundle = &keyBundle[*evmKeyring]{} var _ KeyBundle = &keyBundle[*cosmosKeyring]{} var _ KeyBundle = &keyBundle[*solanaKeyring]{} var _ KeyBundle = &keyBundle[*starkkey.OCR2Key]{} +var _ KeyBundle = &keyBundle[*aptosKeyring]{} var curve = secp256k1.S256() @@ -57,6 +58,8 @@ func New(chainType chaintype.ChainType) (KeyBundle, error) { return newKeyBundleRand(chaintype.Solana, newSolanaKeyring) case chaintype.StarkNet: return newKeyBundleRand(chaintype.StarkNet, starkkey.NewOCR2Key) + case chaintype.Aptos: + return newKeyBundleRand(chaintype.Aptos, newAptosKeyring) } return nil, chaintype.NewErrInvalidChainType(chainType) } @@ -72,6 +75,8 @@ func MustNewInsecure(reader io.Reader, chainType chaintype.ChainType) KeyBundle return mustNewKeyBundleInsecure(chaintype.Solana, newSolanaKeyring, reader) case chaintype.StarkNet: return mustNewKeyBundleInsecure(chaintype.StarkNet, starkkey.NewOCR2Key, reader) + case chaintype.Aptos: + return mustNewKeyBundleInsecure(chaintype.Aptos, newAptosKeyring, reader) } panic(chaintype.NewErrInvalidChainType(chainType)) } @@ -121,6 +126,8 @@ func (raw Raw) Key() (kb KeyBundle) { kb = newKeyBundle(new(solanaKeyring)) case chaintype.StarkNet: kb = newKeyBundle(new(starkkey.OCR2Key)) + case chaintype.Aptos: + kb = newKeyBundle(new(aptosKeyring)) default: return nil } diff --git a/core/services/keystore/keys/solkey/key.go b/core/services/keystore/keys/solkey/key.go index bcec5e8940..c6cdd62d3b 100644 --- a/core/services/keystore/keys/solkey/key.go +++ b/core/services/keystore/keys/solkey/key.go @@ -11,7 +11,7 @@ import ( "github.com/mr-tron/base58" ) -// Raw represents the ETH private key +// Raw represents the Solana private key type Raw []byte // Key gets the Key diff --git a/core/services/keystore/keystoretest.go b/core/services/keystore/keystoretest.go index 91dc87ffe9..990f06c91a 100644 --- a/core/services/keystore/keystoretest.go +++ b/core/services/keystore/keystoretest.go @@ -73,6 +73,7 @@ func NewInMemory(ds sqlutil.DataSource, scryptParams utils.ScryptParams, lggr lo p2p: newP2PKeyStore(km), solana: newSolanaKeyStore(km), starknet: newStarkNetKeyStore(km), + aptos: newAptosKeyStore(km), vrf: newVRFKeyStore(km), dkgSign: newDKGSignKeyStore(km), dkgEncrypt: newDKGEncryptKeyStore(km), diff --git a/core/services/keystore/master.go b/core/services/keystore/master.go index bb5b21a96f..da42f5368c 100644 --- a/core/services/keystore/master.go +++ b/core/services/keystore/master.go @@ -11,6 +11,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/aptoskey" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/cosmoskey" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/csakey" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/dkgencryptkey" @@ -48,6 +49,7 @@ type Master interface { Solana() Solana Cosmos() Cosmos StarkNet() StarkNet + Aptos() Aptos VRF() VRF Unlock(ctx context.Context, password string) error IsEmpty(ctx context.Context) (bool, error) @@ -63,6 +65,7 @@ type master struct { p2p *p2p solana *solana starknet *starknet + aptos *aptos vrf *vrf dkgSign *dkgSign dkgEncrypt *dkgEncrypt @@ -92,6 +95,7 @@ func newMaster(ds sqlutil.DataSource, scryptParams utils.ScryptParams, lggr logg p2p: newP2PKeyStore(km), solana: newSolanaKeyStore(km), starknet: newStarkNetKeyStore(km), + aptos: newAptosKeyStore(km), vrf: newVRFKeyStore(km), dkgSign: newDKGSignKeyStore(km), dkgEncrypt: newDKGEncryptKeyStore(km), @@ -138,6 +142,10 @@ func (ks *master) StarkNet() StarkNet { return ks.starknet } +func (ks *master) Aptos() Aptos { + return ks.aptos +} + func (ks *master) VRF() VRF { return ks.vrf } @@ -273,6 +281,8 @@ func GetFieldNameForKey(unknownKey Key) (string, error) { return "Solana", nil case starkkey.Key: return "StarkNet", nil + case aptoskey.Key: + return "Aptos", nil case vrfkey.KeyV2: return "VRF", nil case dkgsignkey.Key: diff --git a/core/services/keystore/mocks/aptos.go b/core/services/keystore/mocks/aptos.go new file mode 100644 index 0000000000..476112149b --- /dev/null +++ b/core/services/keystore/mocks/aptos.go @@ -0,0 +1,268 @@ +// Code generated by mockery v2.43.2. DO NOT EDIT. + +package mocks + +import ( + context "context" + + aptoskey "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/aptoskey" + + mock "github.com/stretchr/testify/mock" +) + +// Aptos is an autogenerated mock type for the Aptos type +type Aptos struct { + mock.Mock +} + +// Add provides a mock function with given fields: ctx, key +func (_m *Aptos) Add(ctx context.Context, key aptoskey.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, aptoskey.Key) error); ok { + r0 = rf(ctx, key) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Create provides a mock function with given fields: ctx +func (_m *Aptos) Create(ctx context.Context) (aptoskey.Key, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for Create") + } + + var r0 aptoskey.Key + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (aptoskey.Key, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) aptoskey.Key); ok { + r0 = rf(ctx) + } else { + r0 = ret.Get(0).(aptoskey.Key) + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Delete provides a mock function with given fields: ctx, id +func (_m *Aptos) Delete(ctx context.Context, id string) (aptoskey.Key, error) { + ret := _m.Called(ctx, id) + + if len(ret) == 0 { + panic("no return value specified for Delete") + } + + var r0 aptoskey.Key + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string) (aptoskey.Key, error)); ok { + return rf(ctx, id) + } + if rf, ok := ret.Get(0).(func(context.Context, string) aptoskey.Key); ok { + r0 = rf(ctx, id) + } else { + r0 = ret.Get(0).(aptoskey.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 +} + +// EnsureKey provides a mock function with given fields: ctx +func (_m *Aptos) 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 +} + +// Export provides a mock function with given fields: id, password +func (_m *Aptos) 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 +} + +// Get provides a mock function with given fields: id +func (_m *Aptos) Get(id string) (aptoskey.Key, error) { + ret := _m.Called(id) + + if len(ret) == 0 { + panic("no return value specified for Get") + } + + var r0 aptoskey.Key + var r1 error + if rf, ok := ret.Get(0).(func(string) (aptoskey.Key, error)); ok { + return rf(id) + } + if rf, ok := ret.Get(0).(func(string) aptoskey.Key); ok { + r0 = rf(id) + } else { + r0 = ret.Get(0).(aptoskey.Key) + } + + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(id) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetAll provides a mock function with given fields: +func (_m *Aptos) GetAll() ([]aptoskey.Key, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for GetAll") + } + + var r0 []aptoskey.Key + var r1 error + if rf, ok := ret.Get(0).(func() ([]aptoskey.Key, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() []aptoskey.Key); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]aptoskey.Key) + } + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Import provides a mock function with given fields: ctx, keyJSON, password +func (_m *Aptos) Import(ctx context.Context, keyJSON []byte, password string) (aptoskey.Key, error) { + ret := _m.Called(ctx, keyJSON, password) + + if len(ret) == 0 { + panic("no return value specified for Import") + } + + var r0 aptoskey.Key + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []byte, string) (aptoskey.Key, error)); ok { + return rf(ctx, keyJSON, password) + } + if rf, ok := ret.Get(0).(func(context.Context, []byte, string) aptoskey.Key); ok { + r0 = rf(ctx, keyJSON, password) + } else { + r0 = ret.Get(0).(aptoskey.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 +} + +// Sign provides a mock function with given fields: ctx, id, msg +func (_m *Aptos) 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 +} + +// NewAptos creates a new instance of Aptos. 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 NewAptos(t interface { + mock.TestingT + Cleanup(func()) +}) *Aptos { + mock := &Aptos{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/core/services/keystore/mocks/cosmos.go b/core/services/keystore/mocks/cosmos.go index 5e16a1a3c3..db143e6e34 100644 --- a/core/services/keystore/mocks/cosmos.go +++ b/core/services/keystore/mocks/cosmos.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/keystore/mocks/csa.go b/core/services/keystore/mocks/csa.go index c99fef9de8..c3e76f9f69 100644 --- a/core/services/keystore/mocks/csa.go +++ b/core/services/keystore/mocks/csa.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/keystore/mocks/dkg_encrypt.go b/core/services/keystore/mocks/dkg_encrypt.go index 48f50b3cec..1a3fddf577 100644 --- a/core/services/keystore/mocks/dkg_encrypt.go +++ b/core/services/keystore/mocks/dkg_encrypt.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/keystore/mocks/dkg_sign.go b/core/services/keystore/mocks/dkg_sign.go index fd16f573a8..01e69f8f3f 100644 --- a/core/services/keystore/mocks/dkg_sign.go +++ b/core/services/keystore/mocks/dkg_sign.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/keystore/mocks/eth.go b/core/services/keystore/mocks/eth.go index f4a2bea1b0..352605efcd 100644 --- a/core/services/keystore/mocks/eth.go +++ b/core/services/keystore/mocks/eth.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/keystore/mocks/master.go b/core/services/keystore/mocks/master.go index 2739c4bbe3..0e70614170 100644 --- a/core/services/keystore/mocks/master.go +++ b/core/services/keystore/mocks/master.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks @@ -14,6 +14,26 @@ type Master struct { mock.Mock } +// Aptos provides a mock function with given fields: +func (_m *Master) Aptos() keystore.Aptos { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Aptos") + } + + var r0 keystore.Aptos + if rf, ok := ret.Get(0).(func() keystore.Aptos); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(keystore.Aptos) + } + } + + return r0 +} + // CSA provides a mock function with given fields: func (_m *Master) CSA() keystore.CSA { ret := _m.Called() diff --git a/core/services/keystore/mocks/ocr.go b/core/services/keystore/mocks/ocr.go index 76659d0d4d..7582295ee1 100644 --- a/core/services/keystore/mocks/ocr.go +++ b/core/services/keystore/mocks/ocr.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/keystore/mocks/ocr2.go b/core/services/keystore/mocks/ocr2.go index 74c8eed70c..0771f460db 100644 --- a/core/services/keystore/mocks/ocr2.go +++ b/core/services/keystore/mocks/ocr2.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/keystore/mocks/p2p.go b/core/services/keystore/mocks/p2p.go index cb5a9329e5..973fc81c95 100644 --- a/core/services/keystore/mocks/p2p.go +++ b/core/services/keystore/mocks/p2p.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/keystore/mocks/solana.go b/core/services/keystore/mocks/solana.go index 2f9514cc1d..4fde08fc26 100644 --- a/core/services/keystore/mocks/solana.go +++ b/core/services/keystore/mocks/solana.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/keystore/mocks/starknet.go b/core/services/keystore/mocks/starknet.go index a6cc0d5e5f..d12a8e9b8f 100644 --- a/core/services/keystore/mocks/starknet.go +++ b/core/services/keystore/mocks/starknet.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/keystore/mocks/vrf.go b/core/services/keystore/mocks/vrf.go index 87c6a724bd..bc88f04523 100644 --- a/core/services/keystore/mocks/vrf.go +++ b/core/services/keystore/mocks/vrf.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/keystore/models.go b/core/services/keystore/models.go index bb84efdd02..3c4efdf269 100644 --- a/core/services/keystore/models.go +++ b/core/services/keystore/models.go @@ -11,6 +11,7 @@ import ( "github.com/pkg/errors" "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/aptoskey" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/cosmoskey" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/csakey" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/dkgencryptkey" @@ -157,6 +158,7 @@ type keyRing struct { Cosmos map[string]cosmoskey.Key Solana map[string]solkey.Key StarkNet map[string]starkkey.Key + Aptos map[string]aptoskey.Key VRF map[string]vrfkey.KeyV2 DKGSign map[string]dkgsignkey.Key DKGEncrypt map[string]dkgencryptkey.Key @@ -173,6 +175,7 @@ func newKeyRing() *keyRing { Cosmos: make(map[string]cosmoskey.Key), Solana: make(map[string]solkey.Key), StarkNet: make(map[string]starkkey.Key), + Aptos: make(map[string]aptoskey.Key), VRF: make(map[string]vrfkey.KeyV2), DKGSign: make(map[string]dkgsignkey.Key), DKGEncrypt: make(map[string]dkgencryptkey.Key), @@ -233,6 +236,9 @@ func (kr *keyRing) raw() (rawKeys rawKeyRing) { for _, starkkey := range kr.StarkNet { rawKeys.StarkNet = append(rawKeys.StarkNet, starkkey.Raw()) } + for _, aptoskey := range kr.Aptos { + rawKeys.Aptos = append(rawKeys.Aptos, aptoskey.Raw()) + } for _, vrfKey := range kr.VRF { rawKeys.VRF = append(rawKeys.VRF, vrfKey.Raw()) } @@ -279,6 +285,10 @@ func (kr *keyRing) logPubKeys(lggr logger.Logger) { for _, starkkey := range kr.StarkNet { starknetIDs = append(starknetIDs, starkkey.ID()) } + var aptosIDs []string + for _, aptosKey := range kr.Aptos { + aptosIDs = append(aptosIDs, aptosKey.ID()) + } var vrfIDs []string for _, VRFKey := range kr.VRF { vrfIDs = append(vrfIDs, VRFKey.ID()) @@ -315,6 +325,9 @@ func (kr *keyRing) logPubKeys(lggr logger.Logger) { if len(starknetIDs) > 0 { lggr.Infow(fmt.Sprintf("Unlocked %d StarkNet keys", len(starknetIDs)), "keys", starknetIDs) } + if len(aptosIDs) > 0 { + lggr.Infow(fmt.Sprintf("Unlocked %d Aptos keys", len(aptosIDs)), "keys", aptosIDs) + } if len(vrfIDs) > 0 { lggr.Infow(fmt.Sprintf("Unlocked %d VRF keys", len(vrfIDs)), "keys", vrfIDs) } @@ -341,6 +354,7 @@ type rawKeyRing struct { Cosmos []cosmoskey.Raw Solana []solkey.Raw StarkNet []starkkey.Raw + Aptos []aptoskey.Raw VRF []vrfkey.Raw DKGSign []dkgsignkey.Raw DKGEncrypt []dkgencryptkey.Raw @@ -382,6 +396,10 @@ func (rawKeys rawKeyRing) keys() (*keyRing, error) { starkKey := rawStarkNetKey.Key() keyRing.StarkNet[starkKey.ID()] = starkKey } + for _, rawAptosKey := range rawKeys.Aptos { + aptosKey := rawAptosKey.Key() + keyRing.Aptos[aptosKey.ID()] = aptosKey + } for _, rawVRFKey := range rawKeys.VRF { vrfKey := rawVRFKey.Key() keyRing.VRF[vrfKey.ID()] = vrfKey diff --git a/core/services/keystore/solana.go b/core/services/keystore/solana.go index e95af37a7f..ba4031b64c 100644 --- a/core/services/keystore/solana.go +++ b/core/services/keystore/solana.go @@ -6,6 +6,7 @@ import ( "github.com/pkg/errors" + "github.com/smartcontractkit/chainlink-common/pkg/loop" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/solkey" ) @@ -28,6 +29,8 @@ type SolanaSigner struct { Solana } +var _ loop.Keystore = &SolanaSigner{} + func (s *SolanaSigner) Accounts(ctx context.Context) (accounts []string, err error) { ks, err := s.GetAll() if err != nil { diff --git a/core/services/llo/bm/dummy_transmitter.go b/core/services/llo/bm/dummy_transmitter.go index b998c19cb2..5573350b4e 100644 --- a/core/services/llo/bm/dummy_transmitter.go +++ b/core/services/llo/bm/dummy_transmitter.go @@ -2,8 +2,6 @@ package bm import ( "context" - "crypto/ed25519" - "fmt" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" @@ -37,10 +35,10 @@ type transmitter struct { fromAccount string } -func NewTransmitter(lggr logger.Logger, fromAccount ed25519.PublicKey) Transmitter { +func NewTransmitter(lggr logger.Logger, fromAccount string) Transmitter { return &transmitter{ lggr.Named("DummyTransmitter"), - fmt.Sprintf("%x", fromAccount), + fromAccount, } } diff --git a/core/services/llo/bm/dummy_transmitter_test.go b/core/services/llo/bm/dummy_transmitter_test.go index 055b150ad1..e6c59e2f9e 100644 --- a/core/services/llo/bm/dummy_transmitter_test.go +++ b/core/services/llo/bm/dummy_transmitter_test.go @@ -1,7 +1,6 @@ package bm import ( - "crypto/ed25519" "testing" "github.com/stretchr/testify/require" @@ -19,7 +18,7 @@ import ( func Test_DummyTransmitter(t *testing.T) { lggr, observedLogs := logger.TestLoggerObserved(t, zapcore.DebugLevel) - tr := NewTransmitter(lggr, ed25519.PublicKey("dummy")) + tr := NewTransmitter(lggr, "dummy") servicetest.Run(t, tr) diff --git a/core/services/llo/data_source.go b/core/services/llo/data_source.go index a9c3744f9e..30f646e1cb 100644 --- a/core/services/llo/data_source.go +++ b/core/services/llo/data_source.go @@ -61,6 +61,8 @@ func (d *dataSource) Observe(ctx context.Context, streamIDs map[llotypes.StreamI sv := make(llo.StreamValues) var mu sync.Mutex + d.lggr.Debugw("Observing streams", "streamIDs", streamIDs) + for streamID := range streamIDs { go func(streamID llotypes.StreamID) { defer wg.Done() @@ -99,5 +101,7 @@ func (d *dataSource) Observe(ctx context.Context, streamIDs map[llotypes.StreamI wg.Wait() + d.lggr.Debugw("Observed streams", "streamIDs", streamIDs, "values", sv) + return sv, nil } diff --git a/core/services/llo/onchain_config.go b/core/services/llo/onchain_config.go new file mode 100644 index 0000000000..7b5cfffaa9 --- /dev/null +++ b/core/services/llo/onchain_config.go @@ -0,0 +1,21 @@ +package llo + +type OnchainConfig struct{} + +type OnchainConfigCodec interface { + Encode(OnchainConfig) ([]byte, error) + Decode([]byte) (OnchainConfig, error) +} + +var _ OnchainConfigCodec = &JSONOnchainConfigCodec{} + +// TODO: Replace this with protobuf, if it is actually used for something +type JSONOnchainConfigCodec struct{} + +func (c *JSONOnchainConfigCodec) Encode(OnchainConfig) ([]byte, error) { + return nil, nil +} + +func (c *JSONOnchainConfigCodec) Decode([]byte) (OnchainConfig, error) { + return OnchainConfig{}, nil +} diff --git a/core/services/mocks/checker.go b/core/services/mocks/checker.go index 09be8c2345..71b5f301a5 100644 --- a/core/services/mocks/checker.go +++ b/core/services/mocks/checker.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/ocr/mocks/ocr_contract_tracker_db.go b/core/services/ocr/mocks/ocr_contract_tracker_db.go index d3dcce2641..8abfbba7f6 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/ocr2/delegate.go b/core/services/ocr2/delegate.go index 32b91c3d93..eca2cdc31c 100644 --- a/core/services/ocr2/delegate.go +++ b/core/services/ocr2/delegate.go @@ -832,7 +832,7 @@ func (d *Delegate) newServicesMercury( return nil, ErrJobSpecNoRelayer{Err: err, PluginName: "mercury"} } if rid.Network != relay.NetworkEVM { - return nil, fmt.Errorf("mercury services: expected EVM relayer got %s", rid.Network) + return nil, fmt.Errorf("mercury services: expected EVM relayer got %q", rid.Network) } relayer, err := d.RelayGetter.Get(rid) if err != nil { @@ -925,9 +925,6 @@ func (d *Delegate) newServicesLLO( if err != nil { return nil, ErrJobSpecNoRelayer{Err: err, PluginName: "streams"} } - if rid.Network != relay.NetworkEVM { - return nil, fmt.Errorf("streams services: expected EVM relayer got %s", rid.Network) - } relayer, err := d.RelayGetter.Get(rid) if err != nil { return nil, ErrRelayNotEnabled{Err: err, Relay: spec.Relay, PluginName: "streams"} @@ -1109,7 +1106,7 @@ func (d *Delegate) newServicesDKG( return nil, ErrJobSpecNoRelayer{Err: err, PluginName: "DKG"} } if rid.Network != relay.NetworkEVM { - return nil, fmt.Errorf("DKG services: expected EVM relayer got %s", rid.Network) + return nil, fmt.Errorf("DKG services: expected EVM relayer got %q", rid.Network) } chain, err2 := d.legacyChains.Get(rid.ChainID) @@ -1174,7 +1171,7 @@ func (d *Delegate) newServicesOCR2VRF( return nil, ErrJobSpecNoRelayer{Err: err, PluginName: "VRF"} } if rid.Network != relay.NetworkEVM { - return nil, fmt.Errorf("VRF services: expected EVM relayer got %s", rid.Network) + return nil, fmt.Errorf("VRF services: expected EVM relayer got %q", rid.Network) } chain, err2 := d.legacyChains.Get(rid.ChainID) if err2 != nil { @@ -1388,7 +1385,7 @@ func (d *Delegate) newServicesOCR2Keepers21( return nil, ErrJobSpecNoRelayer{Err: err, PluginName: "keeper2"} } if rid.Network != relay.NetworkEVM { - return nil, fmt.Errorf("keeper2 services: expected EVM relayer got %s", rid.Network) + return nil, fmt.Errorf("keeper2 services: expected EVM relayer got %q", rid.Network) } transmitterID := spec.TransmitterID.String @@ -1541,7 +1538,7 @@ func (d *Delegate) newServicesOCR2Keepers20( return nil, ErrJobSpecNoRelayer{Err: err, PluginName: "keepers2.0"} } if rid.Network != relay.NetworkEVM { - return nil, fmt.Errorf("keepers2.0 services: expected EVM relayer got %s", rid.Network) + return nil, fmt.Errorf("keepers2.0 services: expected EVM relayer got %q", rid.Network) } chain, err2 := d.legacyChains.Get(rid.ChainID) if err2 != nil { @@ -1669,7 +1666,7 @@ func (d *Delegate) newServicesOCR2Functions( return nil, ErrJobSpecNoRelayer{Err: err, PluginName: "functions"} } if rid.Network != relay.NetworkEVM { - return nil, fmt.Errorf("functions services: expected EVM relayer got %s", rid.Network) + return nil, fmt.Errorf("functions services: expected EVM relayer got %q", rid.Network) } chain, err := d.legacyChains.Get(rid.ChainID) if err != nil { diff --git a/core/services/ocr2/plugins/ccip/ccipcommit/factory.go b/core/services/ocr2/plugins/ccip/ccipcommit/factory.go index 95d6398f56..648f62a23a 100644 --- a/core/services/ocr2/plugins/ccip/ccipcommit/factory.go +++ b/core/services/ocr2/plugins/ccip/ccipcommit/factory.go @@ -9,6 +9,7 @@ import ( "github.com/smartcontractkit/libocr/offchainreporting2plus/types" cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipcommon" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipcalc" @@ -64,40 +65,60 @@ func (rf *CommitReportingPluginFactory) UpdateDynamicReaders(ctx context.Context return nil } -// NewReportingPlugin returns the ccip CommitReportingPlugin and satisfies the ReportingPluginFactory interface. +type reportingPluginAndInfo struct { + plugin types.ReportingPlugin + pluginInfo types.ReportingPluginInfo +} + +// NewReportingPlugin registers a new ReportingPlugin func (rf *CommitReportingPluginFactory) NewReportingPlugin(config types.ReportingPluginConfig) (types.ReportingPlugin, types.ReportingPluginInfo, error) { - ctx := context.Background() // todo: consider adding some timeout + initialRetryDelay := rf.config.newReportingPluginRetryConfig.InitialDelay + maxDelay := rf.config.newReportingPluginRetryConfig.MaxDelay - destPriceReg, err := rf.config.commitStore.ChangeConfig(ctx, config.OnchainConfig, config.OffchainConfig) + pluginAndInfo, err := ccipcommon.RetryUntilSuccess(rf.NewReportingPluginFn(config), initialRetryDelay, maxDelay) if err != nil { return nil, types.ReportingPluginInfo{}, err } + return pluginAndInfo.plugin, pluginAndInfo.pluginInfo, err +} - priceRegEvmAddr, err := ccipcalc.GenericAddrToEvm(destPriceReg) - if err != nil { - return nil, types.ReportingPluginInfo{}, err - } - if err = rf.UpdateDynamicReaders(ctx, priceRegEvmAddr); err != nil { - return nil, types.ReportingPluginInfo{}, err - } +// NewReportingPluginFn implements the NewReportingPlugin logic. It is defined as a function so that it can easily be +// 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(config types.ReportingPluginConfig) func() (reportingPluginAndInfo, error) { + return func() (reportingPluginAndInfo, error) { + ctx := context.Background() // todo: consider adding some timeout - pluginOffChainConfig, err := rf.config.commitStore.OffchainConfig(ctx) - if err != nil { - return nil, types.ReportingPluginInfo{}, err - } + destPriceReg, err := rf.config.commitStore.ChangeConfig(ctx, config.OnchainConfig, config.OffchainConfig) + if err != nil { + return reportingPluginAndInfo{}, err + } - gasPriceEstimator, err := rf.config.commitStore.GasPriceEstimator(ctx) - if err != nil { - return nil, types.ReportingPluginInfo{}, err - } + priceRegEvmAddr, err := ccipcalc.GenericAddrToEvm(destPriceReg) + if err != nil { + return reportingPluginAndInfo{}, err + } + if err = rf.UpdateDynamicReaders(ctx, priceRegEvmAddr); err != nil { + return reportingPluginAndInfo{}, err + } - err = rf.config.priceService.UpdateDynamicConfig(ctx, gasPriceEstimator, rf.destPriceRegReader) - if err != nil { - return nil, types.ReportingPluginInfo{}, err - } + pluginOffChainConfig, err := rf.config.commitStore.OffchainConfig(ctx) + if err != nil { + return reportingPluginAndInfo{}, err + } + + gasPriceEstimator, err := rf.config.commitStore.GasPriceEstimator(ctx) + if err != nil { + return reportingPluginAndInfo{}, err + } - lggr := rf.config.lggr.Named("CommitReportingPlugin") - return &CommitReportingPlugin{ + err = rf.config.priceService.UpdateDynamicConfig(ctx, gasPriceEstimator, rf.destPriceRegReader) + if err != nil { + return reportingPluginAndInfo{}, err + } + + lggr := rf.config.lggr.Named("CommitReportingPlugin") + plugin := &CommitReportingPlugin{ sourceChainSelector: rf.config.sourceChainSelector, sourceNative: rf.config.sourceNative, onRampReader: rf.config.onRampReader, @@ -112,8 +133,9 @@ func (rf *CommitReportingPluginFactory) NewReportingPlugin(config types.Reportin metricsCollector: rf.config.metricsCollector, chainHealthcheck: rf.config.chainHealthcheck, priceService: rf.config.priceService, - }, - types.ReportingPluginInfo{ + } + + pluginInfo := types.ReportingPluginInfo{ Name: "CCIPCommit", UniqueReports: false, // See comment in CommitStore constructor. Limits: types.ReportingPluginLimits{ @@ -121,5 +143,8 @@ func (rf *CommitReportingPluginFactory) NewReportingPlugin(config types.Reportin MaxObservationLength: ccip.MaxObservationLength, MaxReportLength: MaxCommitReportLength, }, - }, nil + } + + return reportingPluginAndInfo{plugin, pluginInfo}, nil + } } diff --git a/core/services/ocr2/plugins/ccip/ccipcommit/factory_test.go b/core/services/ocr2/plugins/ccip/ccipcommit/factory_test.go new file mode 100644 index 0000000000..825026bd17 --- /dev/null +++ b/core/services/ocr2/plugins/ccip/ccipcommit/factory_test.go @@ -0,0 +1,100 @@ +package ccipcommit + +import ( + "errors" + "testing" + "time" + + "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + + "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/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" + dbMocks "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdb/mocks" +) + +// Assert that NewReportingPlugin keeps retrying until it succeeds. +// +// NewReportingPlugin makes several calls (e.g. CommitStoreReader.ChangeConfig) that can fail. We use mocks to cause the +// first call to each of these functions to fail, then all subsequent calls succeed. We assert that NewReportingPlugin +// retries a sufficient number of times to get through the transient errors and eventually succeed. +func TestNewReportingPluginRetriesUntilSuccess(t *testing.T) { + commitConfig := CommitPluginStaticConfig{} + + // For this unit test, ensure that there is no delay between retries + commitConfig.newReportingPluginRetryConfig = ccipdata.RetryConfig{ + InitialDelay: 0 * time.Nanosecond, + MaxDelay: 0 * time.Nanosecond, + } + + // Set up the OffRampReader mock + mockCommitStore := new(mocks.CommitStoreReader) + + // The first call is set to return an error, the following calls return a nil error + mockCommitStore. + On("ChangeConfig", mock.Anything, mock.Anything, mock.Anything). + Return(ccip.Address(""), errors.New("")). + Once() + mockCommitStore. + On("ChangeConfig", mock.Anything, mock.Anything, mock.Anything). + Return(ccip.Address("0x7c6e4F0BDe29f83BC394B75a7f313B7E5DbD2d77"), nil). + Times(5) + + mockCommitStore. + On("OffchainConfig", mock.Anything). + Return(ccip.CommitOffchainConfig{}, errors.New("")). + Once() + mockCommitStore. + On("OffchainConfig", mock.Anything). + Return(ccip.CommitOffchainConfig{}, nil). + Times(3) + + mockCommitStore. + On("GasPriceEstimator", mock.Anything). + Return(nil, errors.New("")). + Once() + mockCommitStore. + On("GasPriceEstimator", mock.Anything). + Return(nil, nil). + Times(2) + + commitConfig.commitStore = mockCommitStore + + mockPriceService := new(dbMocks.PriceService) + + mockPriceService. + On("UpdateDynamicConfig", mock.Anything, mock.Anything, mock.Anything). + Return(errors.New("")). + Once() + mockPriceService. + On("UpdateDynamicConfig", mock.Anything, mock.Anything, mock.Anything). + Return(nil) + + commitConfig.priceService = mockPriceService + + priceRegistryProvider := new(ccipdataprovidermocks.PriceRegistry) + priceRegistryProvider. + On("NewPriceRegistryReader", mock.Anything, mock.Anything). + Return(nil, errors.New("")). + Once() + priceRegistryProvider. + On("NewPriceRegistryReader", mock.Anything, mock.Anything). + Return(nil, nil). + Once() + commitConfig.priceRegistryProvider = priceRegistryProvider + + commitConfig.lggr, _ = logger.NewLogger() + + factory := NewCommitReportingPluginFactory(commitConfig) + reportingConfig := types.ReportingPluginConfig{} + reportingConfig.OnchainConfig = []byte{1, 2, 3} + reportingConfig.OffchainConfig = []byte{1, 2, 3} + + // Assert that NewReportingPlugin succeeds despite many transient internal failures (mocked out above) + _, _, err := factory.NewReportingPlugin(reportingConfig) + assert.Equal(t, nil, err) +} diff --git a/core/services/ocr2/plugins/ccip/ccipcommit/initializers.go b/core/services/ocr2/plugins/ccip/ccipcommit/initializers.go index 0d8a5414dc..729d8ca5c1 100644 --- a/core/services/ocr2/plugins/ccip/ccipcommit/initializers.go +++ b/core/services/ocr2/plugins/ccip/ccipcommit/initializers.go @@ -6,6 +6,7 @@ import ( "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" @@ -43,6 +44,8 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" ) +var defaultNewReportingPluginRetryConfig = ccipdata.RetryConfig{InitialDelay: time.Second, MaxDelay: 5 * time.Minute} + 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) { spec := jb.OCR2OracleSpec @@ -171,17 +174,18 @@ func NewCommitServices(ctx context.Context, ds sqlutil.DataSource, srcProvider c ) wrappedPluginFactory := NewCommitReportingPluginFactory(CommitPluginStaticConfig{ - lggr: lggr, - onRampReader: onRampReader, - sourceChainSelector: staticConfig.SourceChainSelector, - sourceNative: sourceNative, - offRamp: offRampReader, - commitStore: commitStoreReader, - destChainSelector: staticConfig.ChainSelector, - priceRegistryProvider: ccip.NewChainAgnosticPriceRegistry(dstProvider), - metricsCollector: metricsCollector, - chainHealthcheck: chainHealthCheck, - priceService: priceService, + lggr: lggr, + newReportingPluginRetryConfig: defaultNewReportingPluginRetryConfig, + onRampReader: onRampReader, + sourceChainSelector: staticConfig.SourceChainSelector, + sourceNative: sourceNative, + offRamp: offRampReader, + commitStore: commitStoreReader, + destChainSelector: staticConfig.ChainSelector, + priceRegistryProvider: ccip.NewChainAgnosticPriceRegistry(dstProvider), + metricsCollector: metricsCollector, + chainHealthcheck: chainHealthCheck, + priceService: priceService, }) argsNoPlugin.ReportingPluginFactory = promwrapper.NewPromFactory(wrappedPluginFactory, "CCIPCommit", jb.OCR2OracleSpec.Relay, big.NewInt(0).SetInt64(destChainID)) argsNoPlugin.Logger = commonlogger.NewOCRWrapper(commitLggr, true, logError) diff --git a/core/services/ocr2/plugins/ccip/ccipcommit/ocr2.go b/core/services/ocr2/plugins/ccip/ccipcommit/ocr2.go index 3c96940b8d..2f0fc4e795 100644 --- a/core/services/ocr2/plugins/ccip/ccipcommit/ocr2.go +++ b/core/services/ocr2/plugins/ccip/ccipcommit/ocr2.go @@ -50,7 +50,8 @@ type update struct { } type CommitPluginStaticConfig struct { - lggr logger.Logger + lggr logger.Logger + newReportingPluginRetryConfig ccipdata.RetryConfig // Source onRampReader ccipdata.OnRampReader sourceChainSelector uint64 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 b46cf728c6..6276a28f6d 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks 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 06a29db4cb..5f56265cf3 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks 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 98a0673e9b..f733ad5ba8 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks 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 bf5e450f48..4e134b1f17 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 @@ -21,6 +21,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" gasmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas/mocks" rollupMocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas/rollups/mocks" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" "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/utils" @@ -149,7 +150,11 @@ func TestCommitStoreReaders(t *testing.T) { RpcBatchSize: 2, KeepFinalizedBlocksDepth: 1000, } - lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.SimulatedChainID, pgtest.NewSqlxDB(t), lggr), ec, lggr, lpOpts) + headTracker := headtracker.NewSimulatedHeadTracker(ec, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) + if lpOpts.PollPeriod == 0 { + lpOpts.PollPeriod = 1 * time.Hour + } + lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.SimulatedChainID, pgtest.NewSqlxDB(t), lggr), ec, lggr, headTracker, lpOpts) // Deploy 2 commit store versions onramp1 := utils.RandomAddress() 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 5f884e11de..cd56c47067 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks 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 3b91a9e898..fb5a6d6bbe 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks 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 837aa9b21d..7e7a081f67 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks 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 6863f9616f..e606797b4f 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks 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 039591088a..e37f0075cc 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks 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 3168dddf3b..22c6f3a135 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks 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 9b8f792a89..6f14fb8559 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 @@ -16,6 +16,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmclientmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" "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/utils" @@ -166,10 +167,15 @@ func setupOffRampReaderTH(t *testing.T, version string) offRampReaderTH { RpcBatchSize: 2, KeepFinalizedBlocksDepth: 1000, } + headTracker := headtracker.NewSimulatedHeadTracker(bc, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) + if lpOpts.PollPeriod == 0 { + lpOpts.PollPeriod = 1 * time.Hour + } lp := logpoller.NewLogPoller( orm, bc, log, + headTracker, lpOpts) assert.NoError(t, orm.InsertBlock(ctx, common.Hash{}, 1, time.Now(), 1)) // Setup offRamp. diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/onramp_reader_test.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/onramp_reader_test.go index 4ca856d564..9cfe3f628c 100644 --- a/core/services/ocr2/plugins/ccip/internal/ccipdata/onramp_reader_test.go +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/onramp_reader_test.go @@ -16,6 +16,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmclientmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" "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/utils" @@ -85,10 +86,15 @@ func setupOnRampReaderTH(t *testing.T, version string) onRampReaderTH { RpcBatchSize: 2, KeepFinalizedBlocksDepth: 1000, } + headTracker := headtracker.NewSimulatedHeadTracker(bc, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) + if lpOpts.PollPeriod == 0 { + lpOpts.PollPeriod = 1 * time.Hour + } lp := logpoller.NewLogPoller( orm, bc, log, + headTracker, lpOpts) // Setup onRamp. diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/price_registry_reader_test.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/price_registry_reader_test.go index 62e579e9ae..e17b885cff 100644 --- a/core/services/ocr2/plugins/ccip/internal/ccipdata/price_registry_reader_test.go +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/price_registry_reader_test.go @@ -19,6 +19,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmclientmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" "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/utils" @@ -78,8 +79,12 @@ func setupPriceRegistryReaderTH(t *testing.T) priceRegReaderTH { RpcBatchSize: 2, KeepFinalizedBlocksDepth: 1000, } + headTracker := headtracker.NewSimulatedHeadTracker(ec, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) + if lpOpts.PollPeriod == 0 { + lpOpts.PollPeriod = 1 * time.Hour + } // TODO: We should be able to use an in memory log poller ORM here to speed up the tests. - lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.SimulatedChainID, pgtest.NewSqlxDB(t), lggr), ec, lggr, lpOpts) + lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.SimulatedChainID, pgtest.NewSqlxDB(t), lggr), ec, lggr, headTracker, lpOpts) feeTokens := []common.Address{utils.RandomAddress(), utils.RandomAddress()} dest1 := uint64(10) diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/usdc_reader_internal_test.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/usdc_reader_internal_test.go index cbf4b6170e..ef3adfee7f 100644 --- a/core/services/ocr2/plugins/ccip/internal/ccipdata/usdc_reader_internal_test.go +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/usdc_reader_internal_test.go @@ -15,6 +15,7 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" lpmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" @@ -119,7 +120,11 @@ func TestFilters(t *testing.T) { RpcBatchSize: 1, KeepFinalizedBlocksDepth: 100, } - lp := logpoller.NewLogPoller(o, esc, lggr, lpOpts) + headTracker := headtracker.NewSimulatedHeadTracker(esc, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) + if lpOpts.PollPeriod == 0 { + lpOpts.PollPeriod = 1 * time.Hour + } + lp := logpoller.NewLogPoller(o, esc, lggr, headTracker, lpOpts) jobID1 := "job-1" jobID2 := "job-2" 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 ff9a95caea..a6e727c139 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/ocr2/plugins/ccip/internal/pricegetter/mock.go b/core/services/ocr2/plugins/ccip/internal/pricegetter/mock.go index ef1b437ca0..36d1a8a899 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package pricegetter 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 a95c935a64..373d3e9852 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. 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 598af50841..134dc8acd1 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. 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 dc1edf4595..c808c3718e 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. 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 0e67763341..7114ff33d6 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. 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 0102646669..fdef578672 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package tokendata diff --git a/core/services/ocr2/plugins/liquiditymanager/bridge/mocks/bridge_factory_mock.go b/core/services/ocr2/plugins/liquiditymanager/bridge/mocks/bridge_factory_mock.go index 53f1bf86cd..133ebe5666 100644 --- a/core/services/ocr2/plugins/liquiditymanager/bridge/mocks/bridge_factory_mock.go +++ b/core/services/ocr2/plugins/liquiditymanager/bridge/mocks/bridge_factory_mock.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/ocr2/plugins/liquiditymanager/bridge/mocks/bridge_mock.go b/core/services/ocr2/plugins/liquiditymanager/bridge/mocks/bridge_mock.go index 1207172a43..6ae8349a8f 100644 --- a/core/services/ocr2/plugins/liquiditymanager/bridge/mocks/bridge_mock.go +++ b/core/services/ocr2/plugins/liquiditymanager/bridge/mocks/bridge_mock.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/ocr2/plugins/liquiditymanager/chain/evm/mocks/liquidity_manager_mock.go b/core/services/ocr2/plugins/liquiditymanager/chain/evm/mocks/liquidity_manager_mock.go index 0473ea74fa..378d322c15 100644 --- a/core/services/ocr2/plugins/liquiditymanager/chain/evm/mocks/liquidity_manager_mock.go +++ b/core/services/ocr2/plugins/liquiditymanager/chain/evm/mocks/liquidity_manager_mock.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/ocr2/plugins/liquiditymanager/discoverer/mocks/discoverer_mock.go b/core/services/ocr2/plugins/liquiditymanager/discoverer/mocks/discoverer_mock.go index 1852e7eb6c..cf6e13bd6b 100644 --- a/core/services/ocr2/plugins/liquiditymanager/discoverer/mocks/discoverer_mock.go +++ b/core/services/ocr2/plugins/liquiditymanager/discoverer/mocks/discoverer_mock.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/ocr2/plugins/liquiditymanager/discoverer/mocks/factory_mock.go b/core/services/ocr2/plugins/liquiditymanager/discoverer/mocks/factory_mock.go index b24b7c4ebf..1747c69587 100644 --- a/core/services/ocr2/plugins/liquiditymanager/discoverer/mocks/factory_mock.go +++ b/core/services/ocr2/plugins/liquiditymanager/discoverer/mocks/factory_mock.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/ocr2/plugins/liquiditymanager/internal/integration_test.go b/core/services/ocr2/plugins/liquiditymanager/internal/integration_test.go index 2f2e5a3bd9..5701d29375 100644 --- a/core/services/ocr2/plugins/liquiditymanager/internal/integration_test.go +++ b/core/services/ocr2/plugins/liquiditymanager/internal/integration_test.go @@ -64,6 +64,7 @@ var ( ) func TestLiquidityManager_Integration(t *testing.T) { + t.Skip("flakey test") newTestUniverse(t, 2, false) } diff --git a/core/services/ocr2/plugins/liquiditymanager/mocks/lm_factory_mock.go b/core/services/ocr2/plugins/liquiditymanager/mocks/lm_factory_mock.go index aa11da08e4..ef51db806e 100644 --- a/core/services/ocr2/plugins/liquiditymanager/mocks/lm_factory_mock.go +++ b/core/services/ocr2/plugins/liquiditymanager/mocks/lm_factory_mock.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/ocr2/plugins/liquiditymanager/mocks/rebalancer_mock.go b/core/services/ocr2/plugins/liquiditymanager/mocks/rebalancer_mock.go index 31a011710a..bf07c48299 100644 --- a/core/services/ocr2/plugins/liquiditymanager/mocks/rebalancer_mock.go +++ b/core/services/ocr2/plugins/liquiditymanager/mocks/rebalancer_mock.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/ocr2/plugins/liquiditymanager/ocr3impls/multichain_config_digester.go b/core/services/ocr2/plugins/liquiditymanager/ocr3impls/multichain_config_digester.go index 5b8fe164e8..83a3ceedb3 100644 --- a/core/services/ocr2/plugins/liquiditymanager/ocr3impls/multichain_config_digester.go +++ b/core/services/ocr2/plugins/liquiditymanager/ocr3impls/multichain_config_digester.go @@ -8,6 +8,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/smartcontractkit/libocr/offchainreporting2/types" "github.com/smartcontractkit/libocr/offchainreporting2plus/chains/evmutil" + ocr2plustypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" @@ -78,5 +79,5 @@ func (d MultichainConfigDigester) ConfigDigest(cc types.ContractConfig) (types.C } func (d MultichainConfigDigester) ConfigDigestPrefix() (types.ConfigDigestPrefix, error) { - return types.ConfigDigestPrefixEVM, nil + return ocr2plustypes.ConfigDigestPrefixEVMSimple, nil } diff --git a/core/services/ocr2/plugins/liquiditymanager/ocr3impls/multichain_config_tracker_test.go b/core/services/ocr2/plugins/liquiditymanager/ocr3impls/multichain_config_tracker_test.go index dbfa2e2bb5..6b929398e8 100644 --- a/core/services/ocr2/plugins/liquiditymanager/ocr3impls/multichain_config_tracker_test.go +++ b/core/services/ocr2/plugins/liquiditymanager/ocr3impls/multichain_config_tracker_test.go @@ -15,6 +15,7 @@ import ( "github.com/stretchr/testify/require" commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/liquiditymanager/generated/no_op_ocr3" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest/heavyweight" @@ -43,7 +44,11 @@ func setupLogPoller[RI ocr3impls.MultichainMeta](t *testing.T, db *sqlx.DB, bs * RpcBatchSize: 100, KeepFinalizedBlocksDepth: 200, } - lp := logpoller.NewLogPoller(o, uni.simClient, lggr, lpOpts) + headTracker := headtracker.NewSimulatedHeadTracker(uni.simClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) + if lpOpts.PollPeriod == 0 { + lpOpts.PollPeriod = 1 * time.Hour + } + lp := logpoller.NewLogPoller(o, uni.simClient, lggr, headTracker, lpOpts) return lp, uni } diff --git a/core/services/ocr2/plugins/llo/config/config.go b/core/services/ocr2/plugins/llo/config/config.go index 1a6e528980..892229c46c 100644 --- a/core/services/ocr2/plugins/llo/config/config.go +++ b/core/services/ocr2/plugins/llo/config/config.go @@ -26,7 +26,7 @@ type PluginConfig struct { ChannelDefinitionsContractFromBlock int64 `json:"channelDefinitionsContractFromBlock" toml:"channelDefinitionsContractFromBlock"` // NOTE: ChannelDefinitions is an override. - // If Channe}lDefinitions is specified, values for + // If ChannelDefinitions is specified, values for // ChannelDefinitionsContractAddress and // ChannelDefinitionsContractFromBlock will be ignored ChannelDefinitions string `json:"channelDefinitions" toml:"channelDefinitions"` @@ -41,6 +41,10 @@ type PluginConfig struct { KeyBundleIDs map[string]string `json:"keyBundleIDs" toml:"keyBundleIDs"` } +func (p *PluginConfig) Unmarshal(data []byte) error { + return json.Unmarshal(data, p) +} + func (p PluginConfig) Validate() (merr error) { if p.RawServerURL == "" { merr = errors.New("llo: ServerURL must be specified") diff --git a/core/services/ocr2/plugins/llo/helpers_test.go b/core/services/ocr2/plugins/llo/helpers_test.go index 73f7fedc92..b993ec6dad 100644 --- a/core/services/ocr2/plugins/llo/helpers_test.go +++ b/core/services/ocr2/plugins/llo/helpers_test.go @@ -59,7 +59,7 @@ type request struct { } func (r request) TransmitterID() ocr2types.Account { - return ocr2types.Account(r.pk.String()) + return ocr2types.Account(fmt.Sprintf("%x", r.pk)) } type mercuryServer struct { @@ -114,7 +114,7 @@ func startMercuryServer(t *testing.T, srv *mercuryServer, pubKeys []ed25519.Publ t.Fatalf("[MAIN] failed to listen: %v", err) } serverURL = lis.Addr().String() - s := wsrpc.NewServer(wsrpc.WithCreds(srv.privKey, pubKeys)) + s := wsrpc.NewServer(wsrpc.Creds(srv.privKey, pubKeys)) // Register mercury implementation with the wsrpc server pb.RegisterMercuryServer(s, srv) @@ -130,6 +130,7 @@ type Node struct { App chainlink.Application ClientPubKey credentials.StaticSizedPublicKey KeyBundle ocr2key.KeyBundle + ObservedLogs *observer.ObservedLogs } func (node *Node) AddStreamJob(t *testing.T, spec string) { @@ -197,7 +198,11 @@ func setupNode( }) lggr, observedLogs := logger.TestLoggerObserved(t, zapcore.DebugLevel) - app = cltest.NewApplicationWithConfigV2OnSimulatedBlockchain(t, config, backend, p2pKey, ocr2kb, csaKey, lggr.Named(dbName)) + if backend != nil { + app = cltest.NewApplicationWithConfigV2OnSimulatedBlockchain(t, config, backend, p2pKey, ocr2kb, csaKey, lggr.Named(dbName)) + } else { + app = cltest.NewApplicationWithConfig(t, config, p2pKey, ocr2kb, csaKey, lggr.Named(dbName)) + } err := app.Start(testutils.Context(t)) require.NoError(t, err) @@ -236,74 +241,79 @@ observationSource = """ bridgeName, )) } -func addBootstrapJob(t *testing.T, bootstrapNode Node, chainID *big.Int, verifierAddress common.Address, name string) { + +func addBootstrapJob(t *testing.T, bootstrapNode Node, verifierAddress common.Address, name string, relayType, relayConfig string) { bootstrapNode.AddBootstrapJob(t, fmt.Sprintf(` type = "bootstrap" -relay = "evm" +relay = "%s" schemaVersion = 1 name = "boot-%s" contractID = "%s" contractConfigTrackerPollInterval = "1s" [relayConfig] -chainID = %s -providerType = "llo" - `, name, verifierAddress.Hex(), chainID.String())) +%s +providerType = "llo"`, relayType, name, verifierAddress.Hex(), relayConfig)) } func addLLOJob( t *testing.T, node Node, - verifierAddress, - configStoreAddress common.Address, + verifierAddress common.Address, bootstrapPeerID string, bootstrapNodePort int, - serverURL string, - serverPubKey, clientPubKey ed25519.PublicKey, jobName string, - chainID *big.Int, - fromBlock int, + pluginConfig, + relayType, + relayConfig string, ) { node.AddLLOJob(t, fmt.Sprintf(` type = "offchainreporting2" schemaVersion = 1 -name = "%[1]s" +name = "%s" forwardingAllowed = false maxTaskDuration = "1s" -contractID = "%[2]s" +contractID = "%s" contractConfigTrackerPollInterval = "1s" -ocrKeyBundleID = "%[3]s" +ocrKeyBundleID = "%s" p2pv2Bootstrappers = [ - "%[4]s" + "%s" ] -relay = "evm" +relay = "%s" pluginType = "llo" -transmitterID = "%[5]x" +transmitterID = "%x" [pluginConfig] -serverURL = "%[6]s" -serverPubKey = "%[7]x" -channelDefinitionsContractFromBlock = %[8]d -channelDefinitionsContractAddress = "%[9]s" +%s [relayConfig] -chainID = %[10]s -fromBlock = 1`, +%s`, jobName, verifierAddress.Hex(), node.KeyBundle.ID(), fmt.Sprintf("%s@127.0.0.1:%d", bootstrapPeerID, bootstrapNodePort), + relayType, clientPubKey, - serverURL, - serverPubKey, - fromBlock, - configStoreAddress.Hex(), - chainID.String(), + pluginConfig, + relayConfig, )) } -func addOCRJobs(t *testing.T, streams []Stream, serverPubKey ed25519.PublicKey, serverURL string, verifierAddress common.Address, bootstrapPeerID string, bootstrapNodePort int, nodes []Node, configStoreAddress common.Address, clientPubKeys []ed25519.PublicKey, chainID *big.Int, fromBlock int) { +func addOCRJobs( + t *testing.T, + streams []Stream, + serverPubKey ed25519.PublicKey, + serverURL string, + verifierAddress common.Address, + bootstrapPeerID string, + bootstrapNodePort int, + nodes []Node, + configStoreAddress common.Address, + clientPubKeys []ed25519.PublicKey, + pluginConfig, + relayType, + relayConfig string) { ctx := testutils.Context(t) createBridge := func(name string, i int, p *big.Int, borm bridges.ORM) (bridgeName string) { bridge := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { @@ -343,15 +353,13 @@ func addOCRJobs(t *testing.T, streams []Stream, serverPubKey ed25519.PublicKey, t, node, verifierAddress, - configStoreAddress, bootstrapPeerID, bootstrapNodePort, - serverURL, - serverPubKey, clientPubKeys[i], "feed-1", - chainID, - fromBlock, + pluginConfig, + relayType, + relayConfig, ) } } diff --git a/core/services/ocr2/plugins/llo/integration_test.go b/core/services/ocr2/plugins/llo/integration_test.go index fd955879f5..d80a422e68 100644 --- a/core/services/ocr2/plugins/llo/integration_test.go +++ b/core/services/ocr2/plugins/llo/integration_test.go @@ -3,6 +3,7 @@ package llo_test import ( "crypto/ed25519" "encoding/hex" + "encoding/json" "fmt" "math/big" "math/rand" @@ -20,6 +21,7 @@ import ( "github.com/stretchr/testify/require" chainselectors "github.com/smartcontractkit/chain-selectors" + "github.com/smartcontractkit/libocr/offchainreporting2/types" "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3confighelper" ocr2types "github.com/smartcontractkit/libocr/offchainreporting2plus/types" @@ -72,28 +74,31 @@ type Stream struct { baseBenchmarkPrice *big.Int } -func TestIntegration_LLO(t *testing.T) { - testStartTimeStamp := uint32(time.Now().Unix()) - - const fromBlock = 1 // cannot use zero, start from block 1 - - // streams - btcStream := Stream{ +var ( + btcStream = Stream{ id: 51, baseBenchmarkPrice: big.NewInt(20_000 * multiplier), } - ethStream := Stream{ + ethStream = Stream{ id: 52, baseBenchmarkPrice: big.NewInt(1_568 * multiplier), } - linkStream := Stream{ + linkStream = Stream{ id: 53, baseBenchmarkPrice: big.NewInt(7150 * multiplier / 1000), } - dogeStream := Stream{ + dogeStream = Stream{ id: 54, baseBenchmarkPrice: big.NewInt(2_020 * multiplier), } +) + +func TestIntegration_LLO(t *testing.T) { + testStartTimeStamp := uint32(time.Now().Unix()) + + const fromBlock = 1 // cannot use zero, start from block 1 + + // streams streams := []Stream{btcStream, ethStream, linkStream, dogeStream} streamMap := make(map[uint32]Stream) for _, strm := range streams { @@ -131,10 +136,10 @@ func TestIntegration_LLO(t *testing.T) { ) ports := freeport.GetN(t, nNodes) for i := 0; i < nNodes; i++ { - app, peerID, transmitter, kb, _ := setupNode(t, ports[i], fmt.Sprintf("oracle_streams_%d", i), backend, clientCSAKeys[i]) + app, peerID, transmitter, kb, observedLogs := setupNode(t, ports[i], fmt.Sprintf("oracle_streams_%d", i), backend, clientCSAKeys[i]) nodes = append(nodes, Node{ - app, transmitter, kb, + app, transmitter, kb, observedLogs, }) offchainPublicKey, _ := hex.DecodeString(strings.TrimPrefix(kb.OnChainPublicKey(), "0x")) oracles = append(oracles, confighelper.OracleIdentityExtra{ @@ -159,8 +164,16 @@ func TestIntegration_LLO(t *testing.T) { configDigest := setConfig(t, steve, backend, verifierContract, verifierAddress, nodes, oracles) channelDefinitions := setChannelDefinitions(t, steve, backend, configStoreContract, streams) - addBootstrapJob(t, bootstrapNode, chainID, verifierAddress, "job-1") - addOCRJobs(t, streams, serverPubKey, serverURL, verifierAddress, bootstrapPeerID, bootstrapNodePort, nodes, configStoreAddress, clientPubKeys, chainID, fromBlock) + relayType := "evm" + relayConfig := fmt.Sprintf(`chainID = %s +fromBlock = %d`, chainID.String(), fromBlock) + addBootstrapJob(t, bootstrapNode, verifierAddress, "job-1", relayType, relayConfig) + + pluginConfig := fmt.Sprintf(`serverURL = "%s" +serverPubKey = "%x" +channelDefinitionsContractFromBlock = %d +channelDefinitionsContractAddress = "%s"`, serverURL, serverPubKey, fromBlock, configStoreAddress.String()) + addOCRJobs(t, streams, serverPubKey, serverURL, verifierAddress, bootstrapPeerID, bootstrapNodePort, nodes, configStoreAddress, clientPubKeys, pluginConfig, relayType, relayConfig) t.Run("receives at least one report per feed from each oracle when EAs are at 100% reliability", func(t *testing.T) { // Expect at least one report per channel from each oracle (keyed by transmitter ID) seen := make(map[ocr2types.Account]map[llotypes.ChannelID]struct{}) @@ -254,17 +267,24 @@ func TestIntegration_LLO(t *testing.T) { // TODO: test verification } -func setConfig(t *testing.T, steve *bind.TransactOpts, backend *backends.SimulatedBackend, verifierContract *channel_verifier.ChannelVerifier, verifierAddress common.Address, nodes []Node, oracles []confighelper.OracleIdentityExtra) ocr2types.ConfigDigest { +func generateConfig(t *testing.T, nodes []Node, oracles []confighelper.OracleIdentityExtra) ( + signers []types.OnchainPublicKey, + transmitters []types.Account, + f uint8, + onchainConfig []byte, + offchainConfigVersion uint64, + offchainConfig []byte, +) { // Setup config on contract - rawOnchainConfig := datastreamsllo.OnchainConfig{} - onchainConfig, err := (&datastreamsllo.JSONOnchainConfigCodec{}).Encode(rawOnchainConfig) + rawOnchainConfig := llo.OnchainConfig{} + onchainConfig, err := (&llo.JSONOnchainConfigCodec{}).Encode(rawOnchainConfig) require.NoError(t, err) rawReportingPluginConfig := datastreamsllo.OffchainConfig{} reportingPluginConfig, err := rawReportingPluginConfig.Encode() require.NoError(t, err) - signers, _, _, _, offchainConfigVersion, offchainConfig, err := ocr3confighelper.ContractSetConfigArgsForTests( + signers, transmitters, f, onchainConfig, offchainConfigVersion, offchainConfig, err = ocr3confighelper.ContractSetConfigArgsForTests( 2*time.Second, // DeltaProgress 20*time.Second, // DeltaResend 400*time.Millisecond, // DeltaInitial @@ -285,24 +305,24 @@ func setConfig(t *testing.T, steve *bind.TransactOpts, backend *backends.Simulat ) require.NoError(t, err) + + return +} + +func setConfig(t *testing.T, steve *bind.TransactOpts, backend *backends.SimulatedBackend, verifierContract *channel_verifier.ChannelVerifier, verifierAddress common.Address, nodes []Node, oracles []confighelper.OracleIdentityExtra) ocr2types.ConfigDigest { + signers, _, _, _, offchainConfigVersion, offchainConfig := generateConfig(t, nodes, oracles) + signerAddresses, err := evm.OnchainPublicKeyToAddress(signers) require.NoError(t, err) - offchainTransmitters := make([][32]byte, nNodes) for i := 0; i < nNodes; i++ { offchainTransmitters[i] = nodes[i].ClientPubKey } - _, err = verifierContract.SetConfig(steve, signerAddresses, offchainTransmitters, fNodes, offchainConfig, offchainConfigVersion, offchainConfig, nil) require.NoError(t, err) backend.Commit() - accounts := make([]ocr2types.Account, len(offchainTransmitters)) - for i := range offchainTransmitters { - accounts[i] = ocr2types.Account(fmt.Sprintf("%x", offchainTransmitters[i])) - } - l, err := verifierContract.LatestConfigDigestAndEpoch(&bind.CallOpts{}) require.NoError(t, err) @@ -367,3 +387,136 @@ func setChannelDefinitions(t *testing.T, steve *bind.TransactOpts, backend *back return channelDefinitions } + +func TestIntegration_LLO_Dummy(t *testing.T) { + testStartTimeStamp := time.Now() + + streams := []Stream{btcStream, ethStream, linkStream, dogeStream} + streamMap := make(map[uint32]Stream) + for _, strm := range streams { + streamMap[strm.id] = strm + } + + clientCSAKeys := make([]csakey.KeyV2, nNodes) + clientPubKeys := make([]ed25519.PublicKey, nNodes) + for i := 0; i < nNodes; i++ { + k := big.NewInt(int64(i)) + key := csakey.MustNewV2XXXTestingOnly(k) + clientCSAKeys[i] = key + clientPubKeys[i] = key.PublicKey + } + + // Setup bootstrap + bootstrapCSAKey := csakey.MustNewV2XXXTestingOnly(big.NewInt(-1)) + bootstrapNodePort := freeport.GetOne(t) + appBootstrap, bootstrapPeerID, _, bootstrapKb, _ := setupNode(t, bootstrapNodePort, "bootstrap_mercury", nil, bootstrapCSAKey) + bootstrapNode := Node{App: appBootstrap, KeyBundle: bootstrapKb} + + t.Run("with at least one channel", func(t *testing.T) { + // Setup oracle nodes + var ( + oracles []confighelper.OracleIdentityExtra + nodes []Node + ) + ports := freeport.GetN(t, nNodes) + for i := 0; i < nNodes; i++ { + app, peerID, transmitter, kb, observedLogs := setupNode(t, ports[i], fmt.Sprintf("oracle_streams_%d", i), nil, clientCSAKeys[i]) + + nodes = append(nodes, Node{ + app, transmitter, kb, observedLogs, + }) + offchainPublicKey, _ := hex.DecodeString(strings.TrimPrefix(kb.OnChainPublicKey(), "0x")) + oracles = append(oracles, confighelper.OracleIdentityExtra{ + OracleIdentity: confighelper.OracleIdentity{ + OnchainPublicKey: offchainPublicKey, + TransmitAccount: ocr2types.Account(fmt.Sprintf("%x", transmitter[:])), + OffchainPublicKey: kb.OffchainPublicKey(), + PeerID: peerID, + }, + ConfigEncryptionPublicKey: kb.ConfigEncryptionPublicKey(), + }) + } + + verifierAddress := common.Address{} + chainID := "llo-dummy" + relayType := "dummy" + cd := "0x0102030405060708010203040506070801020304050607080102030405060708" + signers, transmitters, f, onchainConfig, offchainConfigVersion, offchainConfig := generateConfig(t, nodes, oracles) + var signersMarshalled, transmittersMarshalled []byte + { + var err error + signersHex := make([]string, len(signers)) + for i, signer := range signers { + signersHex[i] = fmt.Sprintf("0x%x", signer) + } + signersMarshalled, err = json.Marshal(signersHex) + require.NoError(t, err) + + transmittersMarshalled, err = json.Marshal(transmitters) + require.NoError(t, err) + } + + relayConfig := fmt.Sprintf(`chainID = "%s" +configTracker = { + configDigest = "%s", + configCoung = 1, + signers = %s, + transmitters = %s, + f = %d, + onchainConfig = "0x%x", + offchainConfigVersion = %d, + offchainConfig = "0x%x", + blockHeight = 10 +}`, chainID, cd, string(signersMarshalled), string(transmittersMarshalled), f, onchainConfig, offchainConfigVersion, offchainConfig) + addBootstrapJob(t, bootstrapNode, verifierAddress, "job-1", relayType, relayConfig) + + serverKey := csakey.MustNewV2XXXTestingOnly(big.NewInt(-1)) + serverPubKey := serverKey.PublicKey + serverURL := "foo" + configStoreAddress := common.Address{} + + // NOTE: Don't actually care about the chain ID, it just needs to be + // a valid chainSelector + chainSelector, err := chainselectors.SelectorFromChainId(testutils.SimulatedChainID.Uint64()) + require.NoError(t, err) + + channelDefinitions := fmt.Sprintf(`{ +"42": { + "reportFormat": %d, + "chainSelector": %d, + "streamIds": [51, 52] + } +}`, llotypes.ReportFormatJSON, chainSelector) + + pluginConfig := fmt.Sprintf(`serverURL = "foo" +serverPubKey = "%x" +channelDefinitions = %q`, serverPubKey, channelDefinitions) + addOCRJobs(t, streams, serverPubKey, serverURL, verifierAddress, bootstrapPeerID, bootstrapNodePort, nodes, configStoreAddress, clientPubKeys, pluginConfig, relayType, relayConfig) + + for _, node := range nodes { + le := testutils.WaitForLogMessage(t, node.ObservedLogs, "Transmit") + fields := le.ContextMap() + assert.Equal(t, cd[2:], fields["digest"]) + assert.Equal(t, llotypes.ReportInfo{LifeCycleStage: "production", ReportFormat: llotypes.ReportFormatJSON}, fields["report.Info"]) + + binaryReport := fields["report.Report"].(types.Report) + report, err := (datastreamsllo.JSONReportCodec{}).Decode(binaryReport) + require.NoError(t, err) + assert.Equal(t, datastreamsllo.Report{ + ConfigDigest: types.ConfigDigest{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8}, + ChainSelector: 0x2ee634951ef71b46, + SeqNr: fields["seqNr"].(uint64), + ChannelID: 0x2a, + ValidAfterSeconds: report.ValidAfterSeconds, // tested separately below + ValidUntilSeconds: report.ValidUntilSeconds, // tested separately below + Values: []*big.Int{big.NewInt(2000002000000), big.NewInt(156802000000)}, + Specimen: false, + }, report) + assert.GreaterOrEqual(t, report.ValidUntilSeconds, uint32(testStartTimeStamp.Unix())) + assert.GreaterOrEqual(t, report.ValidAfterSeconds, uint32(testStartTimeStamp.Unix())) + assert.GreaterOrEqual(t, report.ValidUntilSeconds, report.ValidAfterSeconds) + + assert.GreaterOrEqual(t, int(fields["seqNr"].(uint64)), 0) + } + }) +} diff --git a/core/services/ocr2/plugins/llo/onchain_channel_definition_cache_integration_test.go b/core/services/ocr2/plugins/llo/onchain_channel_definition_cache_integration_test.go index ea8f64c02f..8529ad8945 100644 --- a/core/services/ocr2/plugins/llo/onchain_channel_definition_cache_integration_test.go +++ b/core/services/ocr2/plugins/llo/onchain_channel_definition_cache_integration_test.go @@ -20,6 +20,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/llo-feeds/generated/channel_config_store" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" @@ -84,8 +85,9 @@ func Test_ChannelDefinitionCache_Integration(t *testing.T) { RpcBatchSize: 2, KeepFinalizedBlocksDepth: 1000, } + ht := headtracker.NewSimulatedHeadTracker(ethClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) lp := logpoller.NewLogPoller( - logpoller.NewORM(testutils.SimulatedChainID, db, lggr), ethClient, lggr, lpOpts) + logpoller.NewORM(testutils.SimulatedChainID, db, lggr), ethClient, lggr, ht, lpOpts) servicetest.Run(t, lp) cdc := llo.NewChannelDefinitionCache(lggr, orm, lp, configStoreAddress, 0) @@ -156,8 +158,9 @@ func Test_ChannelDefinitionCache_Integration(t *testing.T) { RpcBatchSize: 2, KeepFinalizedBlocksDepth: 1000, } + ht := headtracker.NewSimulatedHeadTracker(ethClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) lp := &mockLogPoller{ - LogPoller: logpoller.NewLogPoller(logpoller.NewORM(testutils.SimulatedChainID, db, lggr), ethClient, lggr, lpOpts), + LogPoller: logpoller.NewLogPoller(logpoller.NewORM(testutils.SimulatedChainID, db, lggr), ethClient, lggr, ht, lpOpts), LatestBlockFn: func(ctx context.Context) (int64, error) { return 0, nil }, @@ -198,7 +201,8 @@ func Test_ChannelDefinitionCache_Integration(t *testing.T) { RpcBatchSize: 2, KeepFinalizedBlocksDepth: 1000, } - lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.SimulatedChainID, db, lggr), ethClient, lggr, lpOpts) + ht := headtracker.NewSimulatedHeadTracker(ethClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) + lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.SimulatedChainID, db, lggr), ethClient, lggr, ht, lpOpts) servicetest.Run(t, lp) cdc := llo.NewChannelDefinitionCache(lggr, orm, lp, configStoreAddress, channel2Block.Number().Int64()+1) diff --git a/core/services/ocr2/plugins/mercury/helpers_test.go b/core/services/ocr2/plugins/mercury/helpers_test.go index fbb51557eb..43d709453b 100644 --- a/core/services/ocr2/plugins/mercury/helpers_test.go +++ b/core/services/ocr2/plugins/mercury/helpers_test.go @@ -103,7 +103,7 @@ func startMercuryServer(t *testing.T, srv *mercuryServer, pubKeys []ed25519.Publ t.Fatalf("[MAIN] failed to listen: %v", err) } serverURL = lis.Addr().String() - s := wsrpc.NewServer(wsrpc.WithCreds(srv.privKey, pubKeys)) + s := wsrpc.NewServer(wsrpc.Creds(srv.privKey, pubKeys)) // Register mercury implementation with the wsrpc server pb.RegisterMercuryServer(s, srv) diff --git a/core/services/ocr2/plugins/mercury/integration_plugin_test.go b/core/services/ocr2/plugins/mercury/integration_plugin_test.go index 4089cc485c..1dedaadcb5 100644 --- a/core/services/ocr2/plugins/mercury/integration_plugin_test.go +++ b/core/services/ocr2/plugins/mercury/integration_plugin_test.go @@ -6,23 +6,19 @@ import ( "testing" "github.com/smartcontractkit/chainlink/v2/core/config/env" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" ) func TestIntegration_MercuryV1_Plugin(t *testing.T) { t.Setenv(string(env.MercuryPlugin.Cmd), "chainlink-mercury") - integration_MercuryV1(t) } func TestIntegration_MercuryV2_Plugin(t *testing.T) { - testutils.SkipFlakey(t, "https://smartcontract-it.atlassian.net/browse/MERC-5697") t.Setenv(string(env.MercuryPlugin.Cmd), "chainlink-mercury") integration_MercuryV2(t) } func TestIntegration_MercuryV3_Plugin(t *testing.T) { t.Setenv(string(env.MercuryPlugin.Cmd), "chainlink-mercury") - integration_MercuryV3(t) } 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 0e2f87d082..ccc0feffae 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v20/registry.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v20/registry.go index ce7325a96b..0cddd43448 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v20/registry.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v20/registry.go @@ -594,16 +594,21 @@ func (r *EvmRegistry) checkUpkeeps(ctx context.Context, keys []ocr2keepers.Upkee return nil, err } + args := []interface{}{ + map[string]interface{}{ + "to": r.addr.Hex(), + "data": hexutil.Bytes(payload), + }, + } + + if opts.BlockNumber != nil { + args = append(args, hexutil.EncodeBig(opts.BlockNumber)) + } + var result string checkReqs[i] = rpc.BatchElem{ Method: "eth_call", - Args: []interface{}{ - map[string]interface{}{ - "to": r.addr.Hex(), - "data": hexutil.Bytes(payload), - }, - hexutil.EncodeBig(opts.BlockNumber), - }, + Args: args, Result: &result, } @@ -660,16 +665,21 @@ func (r *EvmRegistry) simulatePerformUpkeeps(ctx context.Context, checkResults [ return nil, err } + args := []interface{}{ + map[string]interface{}{ + "to": r.addr.Hex(), + "data": hexutil.Bytes(payload), + }, + } + + if opts.BlockNumber != nil { + args = append(args, hexutil.EncodeBig(opts.BlockNumber)) + } + var result string performReqs = append(performReqs, rpc.BatchElem{ Method: "eth_call", - Args: []interface{}{ - map[string]interface{}{ - "to": r.addr.Hex(), - "data": hexutil.Bytes(payload), - }, - hexutil.EncodeBig(opts.BlockNumber), - }, + Args: args, Result: &result, }) @@ -726,16 +736,21 @@ func (r *EvmRegistry) getUpkeepConfigs(ctx context.Context, ids []*big.Int) ([]a return nil, fmt.Errorf("failed to pack id with abi: %s", err) } + args := []interface{}{ + map[string]interface{}{ + "to": r.addr.Hex(), + "data": hexutil.Bytes(payload), + }, + } + + if opts.BlockNumber != nil { + args = append(args, hexutil.EncodeBig(opts.BlockNumber)) + } + var result string uReqs[i] = rpc.BatchElem{ Method: "eth_call", - Args: []interface{}{ - map[string]interface{}{ - "to": r.addr.Hex(), - "data": hexutil.Bytes(payload), - }, - hexutil.EncodeBig(opts.BlockNumber), - }, + Args: args, Result: &result, } 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 0e9badc2ac..89c1ec6fd1 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/integration_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/integration_test.go index ace17ca2db..cdd800071d 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/integration_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/integration_test.go @@ -6,8 +6,6 @@ import ( "testing" "time" - "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" - "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" "github.com/ethereum/go-ethereum/common" @@ -17,10 +15,13 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" + "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/log_upkeep_counter_wrapper" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" @@ -505,7 +506,8 @@ func setupDependencies(t *testing.T, db *sqlx.DB, backend *backends.SimulatedBac RpcBatchSize: 2, KeepFinalizedBlocksDepth: 1000, } - lp := logpoller.NewLogPoller(lorm, ethClient, pollerLggr, lpOpts) + ht := headtracker.NewSimulatedHeadTracker(ethClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) + lp := logpoller.NewLogPoller(lorm, ethClient, pollerLggr, ht, lpOpts) return lp, ethClient } 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 ab8e9f0494..f05cc8f75f 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. 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 c68e7094cc..240e4a7daa 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry_check_pipeline.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry_check_pipeline.go index f14c9865c3..6927f5ba4c 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry_check_pipeline.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry_check_pipeline.go @@ -233,17 +233,22 @@ func (r *EvmRegistry) checkUpkeeps(ctx context.Context, payloads []ocr2keepers.U indices[len(checkReqs)] = i results[i] = encoding.GetIneligibleCheckResultWithoutPerformData(p, encoding.UpkeepFailureReasonNone, encoding.NoPipelineError, false) + args := []interface{}{ + map[string]interface{}{ + "from": zeroAddress, + "to": r.addr.Hex(), + "data": hexutil.Bytes(payload), + }, + } + + if opts.BlockNumber != nil { + args = append(args, hexutil.EncodeBig(opts.BlockNumber)) + } + var result string checkReqs = append(checkReqs, rpc.BatchElem{ Method: "eth_call", - Args: []interface{}{ - map[string]interface{}{ - "from": zeroAddress, - "to": r.addr.Hex(), - "data": hexutil.Bytes(payload), - }, - hexutil.EncodeBig(opts.BlockNumber), - }, + Args: args, Result: &result, }) @@ -334,17 +339,23 @@ func (r *EvmRegistry) simulatePerformUpkeeps(ctx context.Context, checkResults [ } opts := r.buildCallOpts(ctx, block) + + args := []interface{}{ + map[string]interface{}{ + "from": zeroAddress, + "to": r.addr.Hex(), + "data": hexutil.Bytes(payload), + }, + } + + if opts.BlockNumber != nil { + args = append(args, hexutil.EncodeBig(opts.BlockNumber)) + } + var result string performReqs = append(performReqs, rpc.BatchElem{ Method: "eth_call", - Args: []interface{}{ - map[string]interface{}{ - "from": zeroAddress, - "to": r.addr.Hex(), - "data": hexutil.Bytes(payload), - }, - hexutil.EncodeBig(opts.BlockNumber), - }, + Args: args, Result: &result, }) diff --git a/core/services/ocr2/plugins/ocr2vrf/coordinator/mocks/vrf_beacon.go b/core/services/ocr2/plugins/ocr2vrf/coordinator/mocks/vrf_beacon.go index 1aeb0ea85c..79d1033377 100644 --- a/core/services/ocr2/plugins/ocr2vrf/coordinator/mocks/vrf_beacon.go +++ b/core/services/ocr2/plugins/ocr2vrf/coordinator/mocks/vrf_beacon.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/ocr2/plugins/ocr2vrf/coordinator/mocks/vrf_beacon_coordinator.go b/core/services/ocr2/plugins/ocr2vrf/coordinator/mocks/vrf_beacon_coordinator.go index d62a08f6a8..d12f69fde4 100644 --- a/core/services/ocr2/plugins/ocr2vrf/coordinator/mocks/vrf_beacon_coordinator.go +++ b/core/services/ocr2/plugins/ocr2vrf/coordinator/mocks/vrf_beacon_coordinator.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/ocr2/plugins/ocr2vrf/coordinator/mocks/vrf_coordinator.go b/core/services/ocr2/plugins/ocr2vrf/coordinator/mocks/vrf_coordinator.go index 268dff0523..205427f1a7 100644 --- a/core/services/ocr2/plugins/ocr2vrf/coordinator/mocks/vrf_coordinator.go +++ b/core/services/ocr2/plugins/ocr2vrf/coordinator/mocks/vrf_coordinator.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/ocr2/plugins/promwrapper/mocks/prometheus_backend.go b/core/services/ocr2/plugins/promwrapper/mocks/prometheus_backend.go index 4e4b912c75..7a480dfc8e 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/ocr2/plugins/threshold/mocks/decryptor.go b/core/services/ocr2/plugins/threshold/mocks/decryptor.go index daabd6d7de..b1afed995f 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/ocr3/plugins/ccip_integration_tests/integration_helpers.go b/core/services/ocr3/plugins/ccip_integration_tests/integration_helpers.go index b7e81cd2c9..9c84e53b38 100644 --- a/core/services/ocr3/plugins/ccip_integration_tests/integration_helpers.go +++ b/core/services/ocr3/plugins/ccip_integration_tests/integration_helpers.go @@ -15,6 +15,7 @@ import ( ccipreader "github.com/smartcontractkit/chainlink-ccip/pkg/reader" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/ccip_config" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/ocr3_config_encoder" kcr "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry" @@ -111,7 +112,11 @@ func NewTestUniverse(ctx context.Context, t *testing.T, lggr logger.Logger) Test KeepFinalizedBlocksDepth: 100000, } cl := client.NewSimulatedBackendClient(t, backend, big.NewInt(chainID)) - lp := logpoller.NewLogPoller(logpoller.NewORM(big.NewInt(chainID), db, lggr), cl, logger.NullLogger, lpOpts) + headTracker := headtracker.NewSimulatedHeadTracker(cl, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) + if lpOpts.PollPeriod == 0 { + lpOpts.PollPeriod = 1 * time.Hour + } + lp := logpoller.NewLogPoller(logpoller.NewORM(big.NewInt(chainID), db, lggr), cl, logger.NullLogger, headTracker, lpOpts) require.NoError(t, lp.Start(ctx)) t.Cleanup(func() { require.NoError(t, lp.Close()) }) diff --git a/core/services/ocr3/plugins/ccipevm/msghasher.go b/core/services/ocr3/plugins/ccipevm/msghasher.go index 948775a7be..13d0410ea3 100644 --- a/core/services/ocr3/plugins/ccipevm/msghasher.go +++ b/core/services/ocr3/plugins/ccipevm/msghasher.go @@ -12,7 +12,6 @@ import ( "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/ccip/generated/evm_2_evm_multi_onramp" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/message_hasher" ) @@ -59,23 +58,26 @@ func NewMessageHasherV1( ) */ func (h *MessageHasherV1) Hash(_ context.Context, msg cciptypes.CCIPMsg) (cciptypes.Bytes32, error) { - tokenAmounts := make([]evm_2_evm_multi_onramp.ClientEVMTokenAmount, len(msg.TokenAmounts)) + if len(msg.TokenAmounts) != len(msg.SourceTokenData) { + return [32]byte{}, fmt.Errorf("token amounts and source token data must have the same length") + } + + // TODO: this is not fully correct, but will be fixed in future PRs. + // CCIPMsg is missing the source pool address and the dest token address. + rampTokenAmounts := make([]message_hasher.InternalRampTokenAmount, len(msg.TokenAmounts)) for i, ta := range msg.TokenAmounts { - tokenAmounts[i] = evm_2_evm_multi_onramp.ClientEVMTokenAmount{ - Token: common.HexToAddress(string(ta.Token)), - Amount: ta.Amount, + rampTokenAmounts[i] = message_hasher.InternalRampTokenAmount{ + // SourcePoolAddress: , // TODO: fill this in future PRs. + // DestTokenAddress: , // TODO: fill this in future PRs. + ExtraData: msg.SourceTokenData[i], + Amount: ta.Amount, } } - encodedTokens, err := h.abiEncode("encodeTokenAmountsHashPreimage", tokenAmounts) + encodedRampTokenAmounts, err := h.abiEncode("encodeTokenAmountsHashPreimage", rampTokenAmounts) if err != nil { return [32]byte{}, fmt.Errorf("abi encode token amounts: %w", err) } - encodedSourceTokenData, err := h.abiEncode("encodeSourceTokenDataHashPreimage", msg.SourceTokenData) - if err != nil { - return [32]byte{}, fmt.Errorf("pack source token data: %w", err) - } - metaDataHashInput, err := h.abiEncode( "encodeMetadataHashPreimage", ANY_2_EVM_MESSAGE_HASH, @@ -129,8 +131,7 @@ func (h *MessageHasherV1) Hash(_ context.Context, msg cciptypes.CCIPMsg) (ccipty utils.Keccak256Fixed(metaDataHashInput), utils.Keccak256Fixed(fixedSizeFieldsEncoded), utils.Keccak256Fixed(msg.Data), - utils.Keccak256Fixed(encodedTokens), - utils.Keccak256Fixed(encodedSourceTokenData), + utils.Keccak256Fixed(encodedRampTokenAmounts), ) if err != nil { return [32]byte{}, fmt.Errorf("abi encode packed values: %w", err) diff --git a/core/services/ocr3/plugins/ccipevm/msghasher_test.go b/core/services/ocr3/plugins/ccipevm/msghasher_test.go index 0bdf2c04b9..a16815a0d7 100644 --- a/core/services/ocr3/plugins/ccipevm/msghasher_test.go +++ b/core/services/ocr3/plugins/ccipevm/msghasher_test.go @@ -43,11 +43,13 @@ func testHasher(ctx context.Context, t *testing.T, d *testSetupData) { onRampAddress := testutils.NewAddress().Bytes() ccipMsg := createCCIPMsg(t) - evmTokenAmounts := make([]message_hasher.ClientEVMTokenAmount, 0, len(ccipMsg.TokenAmounts)) - for _, ta := range ccipMsg.TokenAmounts { - evmTokenAmounts = append(evmTokenAmounts, message_hasher.ClientEVMTokenAmount{ - Token: common.HexToAddress(string(ta.Token)), - Amount: ta.Amount, + tokenAmounts := make([]message_hasher.InternalRampTokenAmount, 0, len(ccipMsg.TokenAmounts)) + for i := range ccipMsg.TokenAmounts { + tokenAmounts = append(tokenAmounts, message_hasher.InternalRampTokenAmount{ + // SourcePoolAddress: , // TODO: fill this in future PRs. + // DestTokenAddress: , // TODO: fill this in future PRs. + ExtraData: ccipMsg.SourceTokenData[i], + Amount: ccipMsg.TokenAmounts[i].Amount, }) } evmMsg := message_hasher.InternalAny2EVMRampMessage{ @@ -58,12 +60,11 @@ func testHasher(ctx context.Context, t *testing.T, d *testSetupData) { SequenceNumber: uint64(ccipMsg.SeqNum), Nonce: ccipMsg.Nonce, }, - Sender: common.HexToAddress(string(ccipMsg.Sender)).Bytes(), - Receiver: common.HexToAddress(string(ccipMsg.Receiver)), - GasLimit: ccipMsg.ChainFeeLimit.Int, - Data: ccipMsg.Data, - TokenAmounts: evmTokenAmounts, - SourceTokenData: ccipMsg.SourceTokenData, + Sender: common.HexToAddress(string(ccipMsg.Sender)).Bytes(), + Receiver: common.HexToAddress(string(ccipMsg.Receiver)), + GasLimit: ccipMsg.ChainFeeLimit.Int, + Data: ccipMsg.Data, + TokenAmounts: tokenAmounts, } expectedHash, err := d.contract.Hash(&bind.CallOpts{Context: ctx}, evmMsg, onRampAddress) @@ -94,19 +95,21 @@ func createCCIPMsg(t *testing.T) cciptypes.CCIPMsg { _, err = cryptorand.Read(messageData) require.NoError(t, err) - sourceTokenDatas := make([][]byte, rand.Intn(10)) - for i := range sourceTokenDatas { - sourceTokenDatas[i] = sourceTokenData + numTokens := rand.Intn(10) + var sourceTokenDatas [][]byte + for i := 0; i < numTokens; i++ { + sourceTokenDatas = append(sourceTokenDatas, sourceTokenData) } - numTokenAmounts := rand.Intn(50) - tokenAmounts := make([]cciptypes.TokenAmount, 0, numTokenAmounts) - for i := 0; i < numTokenAmounts; i++ { + var tokenAmounts []cciptypes.TokenAmount + for i := 0; i < len(sourceTokenDatas); i++ { tokenAmounts = append(tokenAmounts, cciptypes.TokenAmount{ - Token: types.Account(utils.RandomAddress().String()), + // Ignored by the hasher, will be removed in future PR. + // Token: types.Account(utils.RandomAddress().String()), Amount: big.NewInt(0).SetUint64(rand.Uint64()), }) } + return cciptypes.CCIPMsg{ CCIPMsgBaseDetails: cciptypes.CCIPMsgBaseDetails{ ID: hex.EncodeToString(messageID[:]), diff --git a/core/services/ocrcommon/peer_wrapper.go b/core/services/ocrcommon/peer_wrapper.go index 762a3d05aa..325311352f 100644 --- a/core/services/ocrcommon/peer_wrapper.go +++ b/core/services/ocrcommon/peer_wrapper.go @@ -6,6 +6,7 @@ import ( "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" + "github.com/smartcontractkit/libocr/networking/rageping" ocrnetworking "github.com/smartcontractkit/libocr/networking" ocr1types "github.com/smartcontractkit/libocr/offchainreporting/types" @@ -135,7 +136,8 @@ func (p *SingletonPeerWrapper) peerConfig() (ocrnetworking.PeerConfig, error) { IncomingMessageBufferSize: config.IncomingMessageBufferSize(), OutgoingMessageBufferSize: config.OutgoingMessageBufferSize(), }, - MetricsRegisterer: prometheus.DefaultRegisterer, + MetricsRegisterer: prometheus.DefaultRegisterer, + LatencyMetricsServiceConfigs: rageping.DefaultConfigs(), } return peerConfig, nil diff --git a/core/services/p2p/types/mocks/peer.go b/core/services/p2p/types/mocks/peer.go index 3a2e218c17..31fb4c10d0 100644 --- a/core/services/p2p/types/mocks/peer.go +++ b/core/services/p2p/types/mocks/peer.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/p2p/types/mocks/peer_wrapper.go b/core/services/p2p/types/mocks/peer_wrapper.go index cd20dc7ad9..170ab317f4 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/p2p/types/mocks/signer.go b/core/services/p2p/types/mocks/signer.go index baacbef07b..2e3f74a239 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/pipeline/mocks/config.go b/core/services/pipeline/mocks/config.go index 1c9213443f..91653b8d14 100644 --- a/core/services/pipeline/mocks/config.go +++ b/core/services/pipeline/mocks/config.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/pipeline/mocks/orm.go b/core/services/pipeline/mocks/orm.go index 9bd5ddfbde..387df0d41c 100644 --- a/core/services/pipeline/mocks/orm.go +++ b/core/services/pipeline/mocks/orm.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/pipeline/mocks/pipeline_param_unmarshaler.go b/core/services/pipeline/mocks/pipeline_param_unmarshaler.go index dcc15bf6fc..cbba896dda 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/pipeline/mocks/runner.go b/core/services/pipeline/mocks/runner.go index 88d71c76c3..cd93fbeab7 100644 --- a/core/services/pipeline/mocks/runner.go +++ b/core/services/pipeline/mocks/runner.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/promreporter/prom_reporter_test.go b/core/services/promreporter/prom_reporter_test.go index f17b4aafed..b61fa25bdc 100644 --- a/core/services/promreporter/prom_reporter_test.go +++ b/core/services/promreporter/prom_reporter_test.go @@ -13,6 +13,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" @@ -36,7 +37,8 @@ func newLegacyChainContainer(t *testing.T, db *sqlx.DB) legacyevm.LegacyChainCon config, dbConfig, evmConfig := txmgr.MakeTestConfigs(t) keyStore := cltest.NewKeyStore(t, db).Eth() ethClient := evmtest.NewEthClientMockWithDefaultChain(t) - estimator := gas.NewEstimator(logger.TestLogger(t), ethClient, config, evmConfig.GasEstimator()) + estimator, err := gas.NewEstimator(logger.TestLogger(t), ethClient, config, evmConfig.GasEstimator()) + require.NoError(t, err) lggr := logger.TestLogger(t) lpOpts := logpoller.Opts{ PollPeriod: 100 * time.Millisecond, @@ -45,7 +47,8 @@ func newLegacyChainContainer(t *testing.T, db *sqlx.DB) legacyevm.LegacyChainCon RpcBatchSize: 2, KeepFinalizedBlocksDepth: 1000, } - lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.FixtureChainID, db, lggr), ethClient, lggr, lpOpts) + ht := headtracker.NewSimulatedHeadTracker(ethClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) + lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.FixtureChainID, db, lggr), ethClient, lggr, ht, lpOpts) txm, err := txmgr.NewTxm( db, diff --git a/core/services/registrysyncer/syncer_test.go b/core/services/registrysyncer/syncer_test.go index 6804e4bec4..56818c81dc 100644 --- a/core/services/registrysyncer/syncer_test.go +++ b/core/services/registrysyncer/syncer_test.go @@ -18,7 +18,9 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-common/pkg/types" + evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" kcr "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" @@ -83,13 +85,16 @@ func newContractReaderFactory(t *testing.T, simulatedBackend *backends.Simulated testutils.SimulatedChainID, ) db := pgtest.NewSqlxDB(t) + const finalityDepth = 2 + ht := headtracker.NewSimulatedHeadTracker(client, false, finalityDepth) lp := logpoller.NewLogPoller( logpoller.NewORM(testutils.SimulatedChainID, db, lggr), client, lggr, + ht, logpoller.Opts{ PollPeriod: 100 * time.Millisecond, - FinalityDepth: 2, + FinalityDepth: finalityDepth, BackfillBatchSize: 3, RpcBatchSize: 2, KeepFinalizedBlocksDepth: 1000, diff --git a/core/services/relay/dummy/config_digester.go b/core/services/relay/dummy/config_digester.go new file mode 100644 index 0000000000..a74e007b0b --- /dev/null +++ b/core/services/relay/dummy/config_digester.go @@ -0,0 +1,26 @@ +package dummy + +import ( + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" +) + +// Stub config digester that uses a static config digest + +type configDigester struct { + configDigest ocrtypes.ConfigDigest +} + +func NewOffchainConfigDigester(cd ocrtypes.ConfigDigest) (ocrtypes.OffchainConfigDigester, error) { + return &configDigester{cd}, nil +} + +// Compute ConfigDigest for the given ContractConfig. The first two bytes of the +// ConfigDigest must be the big-endian encoding of ConfigDigestPrefix! +func (cd *configDigester) ConfigDigest(ocrtypes.ContractConfig) (ocrtypes.ConfigDigest, error) { + return cd.configDigest, nil +} + +// This should return the same constant value on every invocation +func (cd *configDigester) ConfigDigestPrefix() (ocrtypes.ConfigDigestPrefix, error) { + return ocrtypes.ConfigDigestPrefixFromConfigDigest(cd.configDigest), nil +} diff --git a/core/services/relay/dummy/config_provider.go b/core/services/relay/dummy/config_provider.go new file mode 100644 index 0000000000..10662ee296 --- /dev/null +++ b/core/services/relay/dummy/config_provider.go @@ -0,0 +1,97 @@ +package dummy + +import ( + "context" + + "github.com/ethereum/go-ethereum/common/hexutil" + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + + "github.com/smartcontractkit/chainlink/v2/core/logger" + + "github.com/smartcontractkit/chainlink-common/pkg/types" +) + +// Stub ConfigTracker that uses static config + +// interim struct used for unmarshalling from relay config +type ConfigTrackerCfg struct { + // OCR Config + ConfigDigest hexutil.Bytes + ConfigCount uint64 + Signers []hexutil.Bytes + Transmitters []string + F uint8 + OnchainConfig hexutil.Bytes + OffchainConfigVersion uint64 + OffchainConfig hexutil.Bytes + + // Tracker config + ChangedInBlock uint64 + BlockHeight uint64 +} + +func (cfg ConfigTrackerCfg) ToContractConfig() (ocrtypes.ContractConfig, error) { + cd, err := ocrtypes.BytesToConfigDigest(cfg.ConfigDigest) + if err != nil { + return ocrtypes.ContractConfig{}, err + } + signers := make([]ocrtypes.OnchainPublicKey, len(cfg.Signers)) + for i, s := range cfg.Signers { + signers[i] = ocrtypes.OnchainPublicKey(s) + } + transmitters := make([]ocrtypes.Account, len(cfg.Transmitters)) + for i, t := range cfg.Transmitters { + transmitters[i] = ocrtypes.Account(t) + } + return ocrtypes.ContractConfig{ + ConfigDigest: cd, + ConfigCount: cfg.ConfigCount, + Signers: signers, + Transmitters: transmitters, + F: cfg.F, + OnchainConfig: cfg.OnchainConfig, + OffchainConfigVersion: cfg.OffchainConfigVersion, + OffchainConfig: cfg.OffchainConfig, + }, nil +} + +type configProvider struct { + lggr logger.Logger + + digester ocrtypes.OffchainConfigDigester + tracker ocrtypes.ContractConfigTracker +} + +func NewConfigProvider(lggr logger.Logger, cfg RelayConfig) (types.ConfigProvider, error) { + cp := &configProvider{lggr: lggr.Named("DummyConfigProvider")} + + { + contractConfig, err := cfg.ConfigTracker.ToContractConfig() + if err != nil { + return nil, err + } + + cp.digester, err = NewOffchainConfigDigester(contractConfig.ConfigDigest) + if err != nil { + return nil, err + } + } + var err error + cp.tracker, err = NewContractConfigTracker(cp.lggr, cfg.ConfigTracker) + if err != nil { + return nil, err + } + return cp, nil +} + +func (cp *configProvider) OffchainConfigDigester() ocrtypes.OffchainConfigDigester { + return cp.digester +} +func (cp *configProvider) ContractConfigTracker() ocrtypes.ContractConfigTracker { return cp.tracker } +func (cp *configProvider) Name() string { return cp.lggr.Name() } +func (*configProvider) Start(context.Context) error { return nil } +func (*configProvider) Close() error { return nil } +func (*configProvider) Ready() error { return nil } +func (cp *configProvider) HealthReport() map[string]error { + return map[string]error{cp.lggr.Name(): nil} +} diff --git a/core/services/relay/dummy/config_tracker.go b/core/services/relay/dummy/config_tracker.go new file mode 100644 index 0000000000..0ae188361f --- /dev/null +++ b/core/services/relay/dummy/config_tracker.go @@ -0,0 +1,51 @@ +package dummy + +import ( + "context" + + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + + "github.com/smartcontractkit/chainlink/v2/core/logger" +) + +type configTracker struct { + lggr logger.Logger + cfg ocrtypes.ContractConfig + + changedInBlock uint64 + blockHeight uint64 +} + +func NewContractConfigTracker(lggr logger.Logger, cfg ConfigTrackerCfg) (ocrtypes.ContractConfigTracker, error) { + contractConfig, err := cfg.ToContractConfig() + if err != nil { + return nil, err + } + return &configTracker{lggr.Named("DummyConfigProvider"), contractConfig, cfg.ChangedInBlock, cfg.BlockHeight}, nil +} + +// Notify may optionally emit notification events when the contract's +// configuration changes. This is purely used as an optimization reducing +// the delay between a configuration change and its enactment. Implementors +// who don't care about this may simply return a nil channel. +// +// The returned channel should never be closed. +func (ct *configTracker) Notify() <-chan struct{} { + return nil +} + +// LatestConfigDetails returns information about the latest configuration, +// but not the configuration itself. +func (ct *configTracker) LatestConfigDetails(ctx context.Context) (changedInBlock uint64, configDigest ocrtypes.ConfigDigest, err error) { + return ct.changedInBlock, ct.cfg.ConfigDigest, nil +} + +// LatestConfig returns the latest configuration. +func (ct *configTracker) LatestConfig(ctx context.Context, changedInBlock uint64) (ocrtypes.ContractConfig, error) { + return ct.cfg, nil +} + +// LatestBlockHeight returns the height of the most recent block in the chain. +func (ct *configTracker) LatestBlockHeight(ctx context.Context) (blockHeight uint64, err error) { + return ct.blockHeight, nil // set LatestBlockHeight to a high number so that OCR considers it to be confirmed +} diff --git a/core/services/relay/dummy/llo_provider.go b/core/services/relay/dummy/llo_provider.go new file mode 100644 index 0000000000..4aeb21bed8 --- /dev/null +++ b/core/services/relay/dummy/llo_provider.go @@ -0,0 +1,86 @@ +package dummy + +import ( + "context" + "errors" + + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + + "github.com/smartcontractkit/chainlink-common/pkg/services" + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" + relaytypes "github.com/smartcontractkit/chainlink-common/pkg/types" + llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/llo" +) + +var _ commontypes.LLOProvider = (*lloProvider)(nil) + +type lloProvider struct { + cp commontypes.ConfigProvider + transmitter llo.Transmitter + logger logger.Logger + channelDefinitionCache llotypes.ChannelDefinitionCache + + ms services.MultiStart +} + +func NewLLOProvider( + lggr logger.Logger, + cp commontypes.ConfigProvider, + transmitter llo.Transmitter, + channelDefinitionCache llotypes.ChannelDefinitionCache, +) relaytypes.LLOProvider { + return &lloProvider{ + cp, + transmitter, + lggr.Named("LLOProvider"), + channelDefinitionCache, + services.MultiStart{}, + } +} + +func (p *lloProvider) Start(ctx context.Context) error { + err := p.ms.Start(ctx, p.cp, p.transmitter, p.channelDefinitionCache) + return err +} + +func (p *lloProvider) Close() error { + return p.ms.Close() +} + +func (p *lloProvider) Ready() error { + return errors.Join(p.cp.Ready(), p.transmitter.Ready(), p.channelDefinitionCache.Ready()) +} + +func (p *lloProvider) Name() string { + return p.logger.Name() +} + +func (p *lloProvider) HealthReport() map[string]error { + report := map[string]error{p.Name(): nil} + services.CopyHealth(report, p.cp.HealthReport()) + services.CopyHealth(report, p.transmitter.HealthReport()) + services.CopyHealth(report, p.channelDefinitionCache.HealthReport()) + return report +} + +func (p *lloProvider) ContractConfigTracker() ocrtypes.ContractConfigTracker { + return p.cp.ContractConfigTracker() +} + +func (p *lloProvider) OffchainConfigDigester() ocrtypes.OffchainConfigDigester { + return p.cp.OffchainConfigDigester() +} + +func (p *lloProvider) OnchainConfigCodec() llo.OnchainConfigCodec { + return &llo.JSONOnchainConfigCodec{} +} + +func (p *lloProvider) ContractTransmitter() llotypes.Transmitter { + return p.transmitter +} + +func (p *lloProvider) ChannelDefinitionCache() llotypes.ChannelDefinitionCache { + return p.channelDefinitionCache +} diff --git a/core/services/relay/dummy/relayer.go b/core/services/relay/dummy/relayer.go new file mode 100644 index 0000000000..9818eabb20 --- /dev/null +++ b/core/services/relay/dummy/relayer.go @@ -0,0 +1,78 @@ +package dummy + +import ( + "context" + "encoding/json" + "math/big" + + "github.com/smartcontractkit/chainlink-common/pkg/loop" + "github.com/smartcontractkit/chainlink-common/pkg/types" + + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/llo" + "github.com/smartcontractkit/chainlink/v2/core/services/llo/bm" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/llo/config" +) + +// The dummy relayer is a simple reference implementation that doesn't actually +// connect to any chain. It's useful for testing and as a reference for +// implementing a new relayer. + +type RelayConfig struct { + ConfigTracker ConfigTrackerCfg +} + +type relayer struct { + lggr logger.Logger + chainID string +} + +func NewRelayer(lggr logger.Logger, chainID string) loop.Relayer { + return &relayer{lggr, chainID} +} + +func (r *relayer) NewContractReader(ctx context.Context, contractReaderConfig []byte) (types.ContractReader, error) { + return nil, nil +} +func (r *relayer) NewConfigProvider(_ context.Context, rargs types.RelayArgs) (types.ConfigProvider, error) { + var cfg RelayConfig + if err := json.Unmarshal(rargs.RelayConfig, &cfg); err != nil { + return nil, err + } + return NewConfigProvider(r.lggr, cfg) +} +func (r *relayer) NewPluginProvider(context.Context, types.RelayArgs, types.PluginArgs) (types.PluginProvider, error) { + return nil, nil +} +func (r *relayer) NewLLOProvider(ctx context.Context, rargs types.RelayArgs, pargs types.PluginArgs) (types.LLOProvider, error) { + cp, err := r.NewConfigProvider(ctx, rargs) + if err != nil { + return nil, err + } + transmitter := bm.NewTransmitter(r.lggr, pargs.TransmitterID) + pluginCfg := new(config.PluginConfig) + if err = pluginCfg.Unmarshal(pargs.PluginConfig); err != nil { + return nil, err + } + cdc, err := llo.NewStaticChannelDefinitionCache(r.lggr, pluginCfg.ChannelDefinitions) + if err != nil { + return nil, err + } + return NewLLOProvider(r.lggr, cp, transmitter, cdc), nil +} +func (r *relayer) GetChainStatus(ctx context.Context) (types.ChainStatus, error) { + return types.ChainStatus{}, nil +} +func (r *relayer) ListNodeStatuses(ctx context.Context, pageSize int32, pageToken string) (stats []types.NodeStatus, nextPageToken string, total int, err error) { + return nil, "", 0, nil +} +func (r *relayer) Transact(ctx context.Context, from, to string, amount *big.Int, balanceCheck bool) error { + return nil +} +func (r *relayer) Name() string { return r.lggr.Name() } +func (r *relayer) Start(context.Context) error { return nil } +func (r *relayer) Close() error { return nil } +func (r *relayer) Ready() error { return nil } +func (r *relayer) HealthReport() map[string]error { + return map[string]error{r.lggr.Name(): nil} +} diff --git a/core/services/relay/evm/cap_encoder.go b/core/services/relay/evm/cap_encoder.go index ef78cb07b4..790114e4c0 100644 --- a/core/services/relay/evm/cap_encoder.go +++ b/core/services/relay/evm/cap_encoder.go @@ -104,9 +104,9 @@ func prependMetadataFields(meta consensustypes.Metadata, userPayload []byte) ([] result = append(result, tsBytes...) // 4. DON ID (4 bytes) - if result, err = decodeAndAppend(meta.DONID, 4, result, "DONID"); err != nil { - return nil, err - } + donIDBytes := make([]byte, 4) + binary.BigEndian.PutUint32(donIDBytes, meta.DONID) + result = append(result, donIDBytes...) // 5. DON config version (4 bytes) cfgVersionBytes := make([]byte, 4) diff --git a/core/services/relay/evm/cap_encoder_test.go b/core/services/relay/evm/cap_encoder_test.go index b56d3828c4..d290a7fd2b 100644 --- a/core/services/relay/evm/cap_encoder_test.go +++ b/core/services/relay/evm/cap_encoder_test.go @@ -20,7 +20,8 @@ var ( workflowID = "15c631d295ef5e32deb99a10ee6804bc4af1385568f9b3363f6552ac6dbb2cef" workflowName = "aabbccddeeaabbccddee" - donID = "00010203" + donID = uint32(2) + donIDHex = "00000002" executionID = "8d4e66421db647dd916d3ec28d56188c8d7dae5f808e03d03339ed2562f13bb0" workflowOwnerID = "0000000000000000000000000000000000000000" reportID = "9988" @@ -217,7 +218,7 @@ func TestEVMEncoder_InvalidIDs(t *testing.T) { } func getHexMetadata() string { - return "01" + executionID + timestampHex + donID + configVersionHex + workflowID + workflowName + workflowOwnerID + reportID + return "01" + executionID + timestampHex + donIDHex + configVersionHex + workflowID + workflowName + workflowOwnerID + reportID } func getMetadata(cid string) consensustypes.Metadata { diff --git a/core/services/relay/evm/chain_writer.go b/core/services/relay/evm/chain_writer.go index d0e76526dc..3f2d0da499 100644 --- a/core/services/relay/evm/chain_writer.go +++ b/core/services/relay/evm/chain_writer.go @@ -167,7 +167,7 @@ func (w *chainWriter) parseContracts() error { } func (w *chainWriter) GetTransactionStatus(ctx context.Context, transactionID string) (commontypes.TransactionStatus, error) { - return commontypes.Unknown, fmt.Errorf("not implemented") + return w.txm.GetTransactionStatus(ctx, transactionID) } // GetFeeComponents the execution and data availability (L1Oracle) fees for the chain. diff --git a/core/services/relay/evm/chain_writer_test.go b/core/services/relay/evm/chain_writer_test.go index 9344279da6..66c85bfc2c 100644 --- a/core/services/relay/evm/chain_writer_test.go +++ b/core/services/relay/evm/chain_writer_test.go @@ -5,11 +5,13 @@ import ( "math/big" "testing" + "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-common/pkg/types" + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" @@ -33,7 +35,6 @@ func TestChainWriter(t *testing.T) { chainWriterConfig := newBaseChainWriterConfig() cw, err := NewChainWriterService(lggr, client, txm, ge, chainWriterConfig) - require.NoError(t, err) t.Run("Initialization", func(t *testing.T) { @@ -60,6 +61,30 @@ func TestChainWriter(t *testing.T) { // TODO: implement }) + t.Run("GetTransactionStatus", func(t *testing.T) { + txs := []struct { + txid string + status commontypes.TransactionStatus + }{ + {uuid.NewString(), commontypes.Unknown}, + {uuid.NewString(), commontypes.Unconfirmed}, + {uuid.NewString(), commontypes.Finalized}, + {uuid.NewString(), commontypes.Failed}, + {uuid.NewString(), commontypes.Fatal}, + } + + for _, tx := range txs { + txm.On("GetTransactionStatus", mock.Anything, tx.txid).Return(tx.status, nil).Once() + } + + for _, tx := range txs { + var status commontypes.TransactionStatus + status, err = cw.GetTransactionStatus(ctx, tx.txid) + require.NoError(t, err) + require.Equal(t, tx.status, status) + } + }) + t.Run("GetFeeComponents", func(t *testing.T) { ge.On("GetFee", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(gas.EvmFee{ Legacy: assets.NewWei(big.NewInt(1000000001)), diff --git a/core/services/relay/evm/config_poller_test.go b/core/services/relay/evm/config_poller_test.go index 8c02c4e2e7..caf48caf49 100644 --- a/core/services/relay/evm/config_poller_test.go +++ b/core/services/relay/evm/config_poller_test.go @@ -32,6 +32,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmClientMocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks" evmutils "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" @@ -99,7 +100,8 @@ func TestConfigPoller(t *testing.T) { RpcBatchSize: 2, KeepFinalizedBlocksDepth: 1000, } - lp = logpoller.NewLogPoller(lorm, ethClient, lggr, lpOpts) + ht := headtracker.NewSimulatedHeadTracker(ethClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) + lp = logpoller.NewLogPoller(lorm, ethClient, lggr, ht, lpOpts) servicetest.Run(t, lp) } diff --git a/core/services/relay/evm/evm.go b/core/services/relay/evm/evm.go index cd2f4010b4..780bf4749b 100644 --- a/core/services/relay/evm/evm.go +++ b/core/services/relay/evm/evm.go @@ -566,7 +566,7 @@ func (r *Relayer) NewLLOProvider(rargs commontypes.RelayArgs, pargs commontypes. var transmitter llo.Transmitter if lloCfg.BenchmarkMode { r.lggr.Info("Benchmark mode enabled, using dummy transmitter. NOTE: THIS WILL NOT TRANSMIT ANYTHING") - transmitter = bm.NewTransmitter(r.lggr, privKey.PublicKey) + transmitter = bm.NewTransmitter(r.lggr, fmt.Sprintf("%x", privKey.PublicKey)) } else { var client wsrpc.Client client, err = r.mercuryPool.Checkout(context.Background(), privKey, lloCfg.ServerPubKey, lloCfg.ServerURL()) @@ -835,6 +835,15 @@ func newOnChainContractTransmitter(ctx context.Context, lggr logger.Logger, rarg ) } +func (r *Relayer) NewChainWriter(_ context.Context, config []byte) (commontypes.ChainWriter, error) { + var cfg types.ChainWriterConfig + if err := json.Unmarshal(config, &cfg); err != nil { + return nil, fmt.Errorf("failed to unmarshall chain writer config err: %s", err) + } + + return NewChainWriterService(r.lggr, r.chain.Client(), r.chain.TxManager(), r.chain.GasEstimator(), cfg) +} + func (r *Relayer) NewContractReader(chainReaderConfig []byte) (commontypes.ContractReader, error) { ctx := context.Background() cfg := &types.ChainReaderConfig{} diff --git a/core/services/relay/evm/evmtesting/chain_reader_interface_tester.go b/core/services/relay/evm/evmtesting/chain_reader_interface_tester.go index b9f141b2d0..9240780935 100644 --- a/core/services/relay/evm/evmtesting/chain_reader_interface_tester.go +++ b/core/services/relay/evm/evmtesting/chain_reader_interface_tester.go @@ -17,6 +17,7 @@ import ( . "github.com/smartcontractkit/chainlink-common/pkg/types/interfacetests" //nolint common practice to import test mods with . "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/chain_reader_tester" _ "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" // force binding for tx type @@ -199,7 +200,8 @@ func (it *EVMChainReaderInterfaceTester[T]) GetChainReader(t T) clcommontypes.Co RpcBatchSize: 1, KeepFinalizedBlocksDepth: 10000, } - lp := logpoller.NewLogPoller(logpoller.NewORM(it.Helper.ChainID(), db, lggr), it.client, lggr, lpOpts) + ht := headtracker.NewSimulatedHeadTracker(it.client, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) + lp := logpoller.NewLogPoller(logpoller.NewORM(it.Helper.ChainID(), db, lggr), it.client, lggr, ht, lpOpts) require.NoError(t, lp.Start(ctx)) // encode and decode the config to ensure the test covers type issues diff --git a/core/services/relay/evm/functions/config_poller_test.go b/core/services/relay/evm/functions/config_poller_test.go index 2d96b2fd15..c44d64c5ba 100644 --- a/core/services/relay/evm/functions/config_poller_test.go +++ b/core/services/relay/evm/functions/config_poller_test.go @@ -23,6 +23,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" evmutils "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/link_token_interface" @@ -88,7 +89,8 @@ func runTest(t *testing.T, pluginType functions.FunctionsPluginType, expectedDig RpcBatchSize: 2, KeepFinalizedBlocksDepth: 1000, } - lp := logpoller.NewLogPoller(lorm, ethClient, lggr, lpOpts) + ht := headtracker.NewSimulatedHeadTracker(ethClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) + lp := logpoller.NewLogPoller(lorm, ethClient, lggr, ht, lpOpts) servicetest.Run(t, lp) configPoller, err := functions.NewFunctionsConfigPoller(pluginType, lp, lggr) require.NoError(t, err) diff --git a/core/services/relay/evm/functions/offchain_config_digester.go b/core/services/relay/evm/functions/offchain_config_digester.go index c53d07e77c..fa8199ec0a 100644 --- a/core/services/relay/evm/functions/offchain_config_digester.go +++ b/core/services/relay/evm/functions/offchain_config_digester.go @@ -17,7 +17,7 @@ import ( var ( _ types.OffchainConfigDigester = &functionsOffchainConfigDigester{} _ evmRelayTypes.RouteUpdateSubscriber = &functionsOffchainConfigDigester{} - FunctionsDigestPrefix = types.ConfigDigestPrefixEVM + FunctionsDigestPrefix = types.ConfigDigestPrefixEVMSimple // In order to support multiple OCR plugins with a single jobspec & OCR2Base contract, each plugin must have a unique config digest. // This is accomplished by overriding the single config digest from the contract with a unique prefix for each plugin via this custom offchain digester & config poller. ThresholdDigestPrefix = types.ConfigDigestPrefix(7) diff --git a/core/services/relay/evm/llo_provider.go b/core/services/relay/evm/llo_provider.go index 0ab0773a16..b685565e6e 100644 --- a/core/services/relay/evm/llo_provider.go +++ b/core/services/relay/evm/llo_provider.go @@ -10,7 +10,6 @@ import ( commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" relaytypes "github.com/smartcontractkit/chainlink-common/pkg/types" llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" - datastreamsllo "github.com/smartcontractkit/chainlink-data-streams/llo" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/llo" @@ -75,10 +74,8 @@ func (p *lloProvider) OffchainConfigDigester() ocrtypes.OffchainConfigDigester { return p.cp.OffchainConfigDigester() } -func (p *lloProvider) OnchainConfigCodec() datastreamsllo.OnchainConfigCodec { - // TODO: This should probably be moved to core since its chain-specific - // https://smartcontract-it.atlassian.net/browse/MERC-3661 - return &datastreamsllo.JSONOnchainConfigCodec{} +func (p *lloProvider) OnchainConfigCodec() llo.OnchainConfigCodec { + return &llo.JSONOnchainConfigCodec{} } func (p *lloProvider) ContractTransmitter() llotypes.Transmitter { diff --git a/core/services/relay/evm/mercury/helpers_test.go b/core/services/relay/evm/mercury/helpers_test.go index f2923696bf..c7c59bf2e1 100644 --- a/core/services/relay/evm/mercury/helpers_test.go +++ b/core/services/relay/evm/mercury/helpers_test.go @@ -20,6 +20,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/llo-feeds/generated/verifier" @@ -174,7 +175,8 @@ func SetupTH(t *testing.T, feedID common.Hash) TestHarness { RpcBatchSize: 2, KeepFinalizedBlocksDepth: 1000, } - lp := logpoller.NewLogPoller(lorm, ethClient, lggr, lpOpts) + ht := headtracker.NewSimulatedHeadTracker(ethClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) + lp := logpoller.NewLogPoller(lorm, ethClient, lggr, ht, lpOpts) servicetest.Run(t, lp) configPoller, err := NewConfigPoller(testutils.Context(t), lggr, lp, verifierAddress, feedID) diff --git a/core/services/relay/evm/mercury/mocks/async_deleter.go b/core/services/relay/evm/mercury/mocks/async_deleter.go index 6a43c1a056..181b9080fd 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/relay/evm/mocks/loop_relay_adapter.go b/core/services/relay/evm/mocks/loop_relay_adapter.go index 273589709c..b4eb32729d 100644 --- a/core/services/relay/evm/mocks/loop_relay_adapter.go +++ b/core/services/relay/evm/mocks/loop_relay_adapter.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/relay/evm/mocks/request_round_db.go b/core/services/relay/evm/mocks/request_round_db.go index d1aa15a3b5..07d02af533 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. 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 46a0b08d0c..4febb45369 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/relay/evm/write_target.go b/core/services/relay/evm/write_target.go index 46f4f83b05..dc1a363533 100644 --- a/core/services/relay/evm/write_target.go +++ b/core/services/relay/evm/write_target.go @@ -69,9 +69,14 @@ func NewWriteTarget(ctx context.Context, relayer *Relayer, chain legacyevm.Chain }, }, } - chainWriterConfig.MaxGasPrice = chain.Config().EVM().GasEstimator().PriceMax() - cw, err := NewChainWriterService(lggr.Named("ChainWriter"), chain.Client(), chain.TxManager(), chain.GasEstimator(), chainWriterConfig) + + encodedWriterConfig, err := json.Marshal(chainWriterConfig) + if err != nil { + return nil, fmt.Errorf("failed to marshal chainwriter config: %w", err) + } + + cw, err := relayer.NewChainWriter(ctx, encodedWriterConfig) if err != nil { return nil, err } diff --git a/core/services/relay/relay.go b/core/services/relay/relay.go index de3f80a5e7..a2681418ce 100644 --- a/core/services/relay/relay.go +++ b/core/services/relay/relay.go @@ -6,6 +6,8 @@ const ( NetworkSolana = "solana" NetworkStarkNet = "starknet" NetworkAptos = "aptos" + + NetworkDummy = "dummy" ) var SupportedNetworks = map[string]struct{}{ @@ -14,4 +16,6 @@ var SupportedNetworks = map[string]struct{}{ NetworkSolana: {}, NetworkStarkNet: {}, NetworkAptos: {}, + + NetworkDummy: {}, } diff --git a/core/services/s4/mocks/orm.go b/core/services/s4/mocks/orm.go index f616623eb8..9b627d033e 100644 --- a/core/services/s4/mocks/orm.go +++ b/core/services/s4/mocks/orm.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/s4/mocks/storage.go b/core/services/s4/mocks/storage.go index 5971b9f1ec..29344d9ebf 100644 --- a/core/services/s4/mocks/storage.go +++ b/core/services/s4/mocks/storage.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/synchronization/mocks/telem_client.go b/core/services/synchronization/mocks/telem_client.go index 3ed4247dde..3c19dd07bc 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/synchronization/mocks/telemetry_service.go b/core/services/synchronization/mocks/telemetry_service.go index b705d49aec..8c6677e4be 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/vrf/mocks/aggregator_v3_interface.go b/core/services/vrf/mocks/aggregator_v3_interface.go index 65351354d7..65bf4f1126 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/vrf/mocks/config.go b/core/services/vrf/mocks/config.go index 3685e17b0f..75cbd82d78 100644 --- a/core/services/vrf/mocks/config.go +++ b/core/services/vrf/mocks/config.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/vrf/mocks/fee_config.go b/core/services/vrf/mocks/fee_config.go index 0d62898862..df9d5ce1ae 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/vrf/mocks/vrf_coordinator_v2.go b/core/services/vrf/mocks/vrf_coordinator_v2.go index e8c1467e44..79b389af23 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks 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 5b827a5291..0865245504 100644 --- a/core/services/vrf/v2/listener_v2_log_listener_test.go +++ b/core/services/vrf/v2/listener_v2_log_listener_test.go @@ -21,6 +21,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" @@ -102,7 +103,8 @@ func setupVRFLogPollerListenerTH(t *testing.T, RpcBatchSize: rpcBatchSize, KeepFinalizedBlocksDepth: keepFinalizedBlocksDepth, } - lp := logpoller.NewLogPoller(o, esc, lggr, lpOpts) + ht := headtracker.NewSimulatedHeadTracker(esc, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) + lp := logpoller.NewLogPoller(o, esc, lggr, ht, lpOpts) emitterAddress1, _, emitter1, err := log_emitter.DeployLogEmitter(owner, ec) require.NoError(t, err) diff --git a/core/services/webhook/mocks/external_initiator_manager.go b/core/services/webhook/mocks/external_initiator_manager.go index 7a3ee29f62..7e0b334f09 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/webhook/mocks/http_client.go b/core/services/webhook/mocks/http_client.go index 27167f235c..4bc69d5e7e 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/services/workflows/engine.go b/core/services/workflows/engine.go index 28f37cd7c4..54d79697d8 100644 --- a/core/services/workflows/engine.go +++ b/core/services/workflows/engine.go @@ -83,7 +83,7 @@ func (e *Engine) resolveWorkflowCapabilities(ctx context.Context) error { for _, t := range e.workflow.triggers { tg, err := e.registry.GetTrigger(ctx, t.ID) if err != nil { - e.logger.Errorf("failed to get trigger capability: %s", err) + e.logger.With(cIDKey, t.ID).Errorf("failed to get trigger capability: %s", err) // we don't immediately return here, since we want to retry all triggers // to notify the user of all errors at once. triggersInitialized = false @@ -92,7 +92,9 @@ func (e *Engine) resolveWorkflowCapabilities(ctx context.Context) error { } } if !triggersInitialized { - return fmt.Errorf("failed to resolve triggers") + return &workflowError{reason: "failed to resolve triggers", labels: map[string]string{ + wIDKey: e.workflow.id, + }} } // Step 2. Walk the graph and register each step's capability to this workflow @@ -111,7 +113,12 @@ func (e *Engine) resolveWorkflowCapabilities(ctx context.Context) error { err := e.initializeCapability(ctx, s) if err != nil { - return fmt.Errorf("failed to initialize capability for step %s: %w", s.Ref, err) + return &workflowError{err: err, reason: "failed to initialize capability for step", + labels: map[string]string{ + wIDKey: e.workflow.id, + sIDKey: s.ID, + sRKey: s.Ref, + }} } return nil @@ -121,6 +128,20 @@ func (e *Engine) resolveWorkflowCapabilities(ctx context.Context) error { } func (e *Engine) initializeCapability(ctx context.Context, step *step) error { + // We use varadic err here so that err can be optional, but we assume that + // its length is either 0 or 1 + newCPErr := func(reason string, errs ...error) *workflowError { + var err error + if len(errs) > 0 { + err = errs[0] + } + + return &workflowError{reason: reason, err: err, labels: map[string]string{ + wIDKey: e.workflow.id, + sIDKey: step.ID, + }} + } + // If the capability already exists, that means we've already registered it if step.capability != nil { return nil @@ -128,20 +149,22 @@ func (e *Engine) initializeCapability(ctx context.Context, step *step) error { cp, err := e.registry.Get(ctx, step.ID) if err != nil { - return fmt.Errorf("failed to get capability with ref %s: %s", step.ID, err) + return newCPErr("failed to get capability", err) } info, err := cp.Info(ctx) if err != nil { - return fmt.Errorf("failed to get info of capability with id %s: %w", step.ID, err) + return newCPErr("failed to get capability info", err) } // Special treatment for local targets - wrap into a transmission capability // If the DON is nil, this is a local target. if info.CapabilityType == capabilities.CapabilityTypeTarget && info.DON == nil { - e.logger.Debugf("wrapping capability %s in local transmission protocol", info.ID) + l := e.logger.With("capabilityID", step.ID) + l.Debug("wrapping capability in local transmission protocol") cp = transmission.NewLocalTargetCapability( e.logger, + step.ID, e.localNode, cp.(capabilities.TargetCapability), ) @@ -151,13 +174,13 @@ func (e *Engine) initializeCapability(ctx context.Context, step *step) error { // they all satisfy the `CallbackCapability` interface cc, ok := cp.(capabilities.CallbackCapability) if !ok { - return fmt.Errorf("could not coerce capability %s to CallbackCapability", step.ID) + return newCPErr("capability does not satisfy CallbackCapability") } if step.config == nil { configMap, newMapErr := values.NewMap(step.Config) if newMapErr != nil { - return fmt.Errorf("failed to convert config to values.Map: %s", newMapErr) + return newCPErr("failed to convert config to values.Map", newMapErr) } step.config = configMap } @@ -171,7 +194,7 @@ func (e *Engine) initializeCapability(ctx context.Context, step *step) error { err = cc.RegisterToWorkflow(ctx, registrationRequest) if err != nil { - return fmt.Errorf("failed to register to workflow (%+v): %w", registrationRequest, err) + return newCPErr(fmt.Sprintf("failed to register capability to workflow (%+v)", registrationRequest), err) } step.capability = cc @@ -200,7 +223,10 @@ func (e *Engine) init(ctx context.Context) { err = e.resolveWorkflowCapabilities(ctx) if err != nil { - return fmt.Errorf("failed to resolve workflow: %s", err) + return &workflowError{err: err, reason: "failed to resolve workflow capabilities", + labels: map[string]string{ + wIDKey: e.workflow.id, + }} } return nil }) @@ -214,14 +240,14 @@ func (e *Engine) init(ctx context.Context) { e.logger.Debug("capabilities resolved, resuming in-progress workflows") err := e.resumeInProgressExecutions(ctx) if err != nil { - e.logger.Errorf("failed to resume workflows: %v", err) + e.logger.Errorf("failed to resume in-progress workflows: %v", err) } e.logger.Debug("registering triggers") for idx, t := range e.workflow.triggers { err := e.registerTrigger(ctx, t, idx) if err != nil { - e.logger.Errorf("failed to register trigger: %s", err) + e.logger.With(cIDKey, t.ID).Errorf("failed to register trigger: %s", err) } } @@ -241,7 +267,7 @@ func (e *Engine) resumeInProgressExecutions(ctx context.Context) error { // TODO: paginate properly if len(wipExecutions) >= defaultLimit { - e.logger.Warnf("possible execution overflow during resumption") + e.logger.Warnf("possible execution overflow during resumption, work in progress executions: %d >= %d", len(wipExecutions), defaultLimit) } // Cache the dependents associated with a step. @@ -284,9 +310,10 @@ func generateTriggerId(workflowID string, triggerIdx int) string { // registerTrigger is used during the initialization phase to bind a trigger to this workflow func (e *Engine) registerTrigger(ctx context.Context, t *triggerCapability, triggerIdx int) error { + triggerID := generateTriggerId(e.workflow.id, triggerIdx) triggerInputs, err := values.NewMap( map[string]any{ - "triggerId": generateTriggerId(e.workflow.id, triggerIdx), + "triggerId": triggerID, }, ) if err != nil { @@ -302,22 +329,52 @@ func (e *Engine) registerTrigger(ctx context.Context, t *triggerCapability, trig triggerRegRequest := capabilities.CapabilityRequest{ Metadata: capabilities.RequestMetadata{ - WorkflowID: e.workflow.id, - WorkflowDonID: e.localNode.WorkflowDON.ID, - WorkflowName: e.workflow.name, - WorkflowOwner: e.workflow.owner, + WorkflowID: e.workflow.id, + WorkflowDonID: e.localNode.WorkflowDON.ID, + WorkflowDonConfigVersion: e.localNode.WorkflowDON.ConfigVersion, + WorkflowName: e.workflow.name, + WorkflowOwner: e.workflow.owner, }, Config: tc, Inputs: triggerInputs, } eventsCh, err := t.trigger.RegisterTrigger(ctx, triggerRegRequest) if err != nil { - return fmt.Errorf("failed to instantiate trigger %s, %s", t.ID, err) - } - + // It's confusing that t.ID is different from triggerID, but + // t.ID is the capability ID, and triggerID is the trigger ID. + // + // The capability ID is globally scoped, whereas the trigger ID + // is scoped to this workflow. + // + // For example, t.ID might be "streams-trigger:network=mainnet@1.0.0" + // and triggerID might be "wf_123_trigger_0" + return &workflowError{err: err, reason: fmt.Sprintf("failed to register trigger: %+v", triggerRegRequest), + labels: map[string]string{ + wIDKey: e.workflow.id, + cIDKey: t.ID, + tIDKey: triggerID, + }} + } + + e.wg.Add(1) go func() { - for event := range eventsCh { - e.triggerEvents <- event + defer e.wg.Done() + + for { + select { + case <-e.stopCh: + return + case event, isOpen := <-eventsCh: + if !isOpen { + return + } + + select { + case <-e.stopCh: + return + case e.triggerEvents <- event: + } + } } }() @@ -344,11 +401,11 @@ func (e *Engine) loop(ctx context.Context) { for { select { case <-ctx.Done(): - e.logger.Debugw("shutting down loop") + e.logger.Debug("shutting down loop") return case resp, isOpen := <-e.triggerEvents: if !isOpen { - e.logger.Errorf("trigger events channel is no longer open, skipping") + e.logger.Error("trigger events channel is no longer open, skipping") continue } @@ -366,13 +423,13 @@ func (e *Engine) loop(ctx context.Context) { executionID, err := generateExecutionID(e.workflow.id, te.ID) if err != nil { - e.logger.Errorf("could not generate execution ID; error %v", resp.Err) + e.logger.With(tIDKey, te.ID).Errorf("could not generate execution ID: %v", err) continue } err = e.startExecution(ctx, executionID, resp.Value) if err != nil { - e.logger.Errorf("failed to start execution: %v", err) + e.logger.With(eIDKey, executionID).Errorf("failed to start execution: %v", err) } case pendingStepRequest := <-e.pendingStepRequests: // Wait for a new worker to be available before dispatching a new one. @@ -384,7 +441,8 @@ func (e *Engine) loop(ctx context.Context) { e.wg.Add(1) go e.workerForStepRequest(ctx, pendingStepRequest) case <-t.Chan(): - e.logger.Errorf("timed out when spinning off worker for pending step request %+v", pendingStepRequest) + e.logger.With(eIDKey, pendingStepRequest.state.ExecutionID, sRKey, pendingStepRequest.stepRef). + Errorf("timed out when spinning off worker for pending step request %+v", pendingStepRequest) e.pendingStepRequests <- pendingStepRequest } t.Stop() @@ -392,7 +450,8 @@ func (e *Engine) loop(ctx context.Context) { // Executed synchronously to ensure we correctly schedule subsequent tasks. err := e.handleStepUpdate(ctx, stepUpdate) if err != nil { - e.logger.Errorf("failed to update step state: %+v, %s", stepUpdate, err) + e.logger.With(eIDKey, stepUpdate.ExecutionID, sRKey, stepUpdate.Ref). + Errorf("failed to update step state: %+v, %s", stepUpdate, err) } } } @@ -415,7 +474,7 @@ func generateExecutionID(workflowID, eventID string) (string, error) { // startExecution kicks off a new workflow execution when a trigger event is received. func (e *Engine) startExecution(ctx context.Context, executionID string, event values.Value) error { - e.logger.Debugw("executing on a trigger event", "event", event, "executionID", executionID) + e.logger.With("event", event, eIDKey, executionID).Debug("executing on a trigger event") ec := &store.WorkflowExecution{ Steps: map[string]*store.WorkflowExecutionStep{ workflows.KeywordTrigger: { @@ -457,6 +516,7 @@ func (e *Engine) handleStepUpdate(ctx context.Context, stepUpdate store.Workflow if err != nil { return err } + l := e.logger.With(eIDKey, state.ExecutionID, sRKey, stepUpdate.Ref) switch stepUpdate.Status { case store.StatusCompleted: @@ -499,7 +559,7 @@ func (e *Engine) handleStepUpdate(ctx context.Context, stepUpdate store.Workflow // We haven't completed the workflow, but should we continue? // If we've been executing for too long, let's time the workflow out and stop here. if state.CreatedAt != nil && e.clock.Since(*state.CreatedAt) > e.maxExecutionDuration { - e.logger.Infow("execution timed out", "executionID", state.ExecutionID) + l.Info("execution timed out") return e.finishExecution(ctx, state.ExecutionID, store.StatusTimeout) } @@ -509,7 +569,7 @@ func (e *Engine) handleStepUpdate(ctx context.Context, stepUpdate store.Workflow e.queueIfReady(state, sd) } case store.StatusCompletedEarlyExit: - e.logger.Infow("execution terminated early", "executionID", state.ExecutionID) + l.Info("execution terminated early") // NOTE: even though this marks the workflow as completed, any branches of the DAG // that don't depend on the step that signaled for an early exit will still complete. // This is to ensure that any side effects are executed consistently, since otherwise @@ -519,7 +579,7 @@ func (e *Engine) handleStepUpdate(ctx context.Context, stepUpdate store.Workflow return err } case store.StatusErrored: - e.logger.Infow("execution errored", "executionID", state.ExecutionID) + l.Info("execution errored") err := e.finishExecution(ctx, state.ExecutionID, store.StatusErrored) if err != nil { return err @@ -551,7 +611,8 @@ func (e *Engine) queueIfReady(state store.WorkflowExecution, step *step) { // If all dependencies are completed, enqueue the step. if !waitingOnDependencies { - e.logger.Debugw("step request enqueued", "ref", step.Ref, "state", copyState(state)) + e.logger.With(sRKey, step.Ref, eIDKey, state.ExecutionID, "state", copyState(state)). + Debug("step request enqueued") e.pendingStepRequests <- stepRequest{ state: copyState(state), stepRef: step.Ref, @@ -560,7 +621,7 @@ func (e *Engine) queueIfReady(state store.WorkflowExecution, step *step) { } func (e *Engine) finishExecution(ctx context.Context, executionID string, status string) error { - e.logger.Infow("finishing execution", "executionID", executionID, "status", status) + e.logger.With(eIDKey, executionID, "status", status).Info("finishing execution") err := e.executionStates.UpdateStatus(ctx, executionID, status) if err != nil { return err @@ -576,26 +637,26 @@ func (e *Engine) workerForStepRequest(ctx context.Context, msg stepRequest) { // Instantiate a child logger; in addition to the WorkflowID field the workflow // logger will already have, this adds the `stepRef` and `executionID` - l := e.logger.With("stepRef", msg.stepRef, "executionID", msg.state.ExecutionID) + l := e.logger.With(sRKey, msg.stepRef, eIDKey, msg.state.ExecutionID) - l.Debugw("executing on a step event") + l.Debug("executing on a step event") stepState := &store.WorkflowExecutionStep{ Outputs: store.StepOutput{}, ExecutionID: msg.state.ExecutionID, Ref: msg.stepRef, } - inputs, outputs, err := e.executeStep(ctx, l, msg) + inputs, outputs, err := e.executeStep(ctx, msg) var stepStatus string switch { case errors.Is(capabilities.ErrStopExecution, err): - l.Infow("step executed successfully with a termination") + l.Info("step executed successfully with a termination") stepStatus = store.StatusCompletedEarlyExit case err != nil: l.Errorf("error executing step request: %s", err) stepStatus = store.StatusErrored default: - l.Infow("step executed successfully", "outputs", outputs) + l.With("outputs", outputs).Info("step executed successfully") stepStatus = store.StatusCompleted } @@ -618,40 +679,48 @@ func (e *Engine) workerForStepRequest(ctx context.Context, msg stepRequest) { } // executeStep executes the referenced capability within a step and returns the result. -func (e *Engine) executeStep(ctx context.Context, l logger.Logger, msg stepRequest) (*values.Map, values.Value, error) { +func (e *Engine) executeStep(ctx context.Context, msg stepRequest) (*values.Map, values.Value, error) { step, err := e.workflow.Vertex(msg.stepRef) if err != nil { return nil, nil, err } - i, err := findAndInterpolateAllKeys(step.Inputs, msg.state) + var inputs any + if step.Inputs.OutputRef != "" { + inputs = step.Inputs.OutputRef + } else { + inputs = step.Inputs.Mapping + } + + i, err := findAndInterpolateAllKeys(inputs, msg.state) if err != nil { return nil, nil, err } - inputs, err := values.NewMap(i.(map[string]any)) + inputsMap, err := values.NewMap(i.(map[string]any)) if err != nil { return nil, nil, err } tr := capabilities.CapabilityRequest{ - Inputs: inputs, + Inputs: inputsMap, Config: step.config, Metadata: capabilities.RequestMetadata{ - WorkflowID: msg.state.WorkflowID, - WorkflowExecutionID: msg.state.ExecutionID, - WorkflowOwner: e.workflow.owner, - WorkflowName: e.workflow.name, - WorkflowDonID: e.localNode.WorkflowDON.ID, + WorkflowID: msg.state.WorkflowID, + WorkflowExecutionID: msg.state.ExecutionID, + WorkflowOwner: e.workflow.owner, + WorkflowName: e.workflow.name, + WorkflowDonID: e.localNode.WorkflowDON.ID, + WorkflowDonConfigVersion: e.localNode.WorkflowDON.ConfigVersion, }, } output, err := executeSyncAndUnwrapSingleValue(ctx, step.capability, tr) if err != nil { - return inputs, nil, err + return inputsMap, nil, err } - return inputs, output, err + return inputsMap, output, err } func (e *Engine) deregisterTrigger(ctx context.Context, t *triggerCapability, triggerIdx int) error { @@ -665,10 +734,11 @@ func (e *Engine) deregisterTrigger(ctx context.Context, t *triggerCapability, tr } deregRequest := capabilities.CapabilityRequest{ Metadata: capabilities.RequestMetadata{ - WorkflowID: e.workflow.id, - WorkflowDonID: e.localNode.WorkflowDON.ID, - WorkflowName: e.workflow.name, - WorkflowOwner: e.workflow.owner, + WorkflowID: e.workflow.id, + WorkflowDonID: e.localNode.WorkflowDON.ID, + WorkflowDonConfigVersion: e.localNode.WorkflowDON.ConfigVersion, + WorkflowName: e.workflow.name, + WorkflowOwner: e.workflow.owner, }, Inputs: triggerInputs, Config: t.config, @@ -723,7 +793,13 @@ func (e *Engine) Close() error { innerErr := s.capability.UnregisterFromWorkflow(ctx, reg) if innerErr != nil { - return fmt.Errorf("failed to unregister from workflow: %+v", reg) + return &workflowError{err: innerErr, + reason: fmt.Sprintf("failed to unregister capability from workflow: %+v", reg), + labels: map[string]string{ + wIDKey: e.workflow.id, + sIDKey: s.ID, + sRKey: s.Ref, + }} } return nil @@ -767,7 +843,11 @@ const ( func NewEngine(cfg Config) (engine *Engine, err error) { if cfg.Store == nil { - return nil, errors.New("must provide store") + return nil, &workflowError{reason: "store is nil", + labels: map[string]string{ + wIDKey: cfg.WorkflowID, + }, + } } if cfg.MaxWorkerLimit == 0 { @@ -871,3 +951,48 @@ func executeSyncAndUnwrapSingleValue(ctx context.Context, cap capabilities.Callb return l.Underlying[0], nil } + +// Logging keys +const ( + cIDKey = "capabilityID" + tIDKey = "triggerID" + wIDKey = "workflowID" + eIDKey = "executionID" + sIDKey = "stepID" + sRKey = "stepRef" +) + +type workflowError struct { + labels map[string]string + // err is the underlying error that caused this error + err error + // reason is a human-readable string that describes the error + reason string +} + +func (e *workflowError) Error() string { + // declare in reverse order so that the error message is ordered correctly + orderedLabels := []string{sRKey, sIDKey, tIDKey, cIDKey, eIDKey, wIDKey} + + errStr := "" + if e.err != nil { + if e.reason != "" { + errStr = fmt.Sprintf("%s: %v", e.reason, e.err) + } else { + errStr = e.err.Error() + } + } else { + errStr = e.reason + } + + // prefix the error with the labels + for _, label := range orderedLabels { + // This will silently ignore any labels that are not present in the map + // are we ok with this? + if value, ok := e.labels[label]; ok { + errStr = fmt.Sprintf("%s %s: %s", label, value, errStr) + } + } + + return errStr +} diff --git a/core/services/workflows/engine_test.go b/core/services/workflows/engine_test.go index 09af6d6358..2c4129dbaf 100644 --- a/core/services/workflows/engine_test.go +++ b/core/services/workflows/engine_test.go @@ -20,10 +20,12 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/job" p2ptypes "github.com/smartcontractkit/chainlink/v2/core/services/p2p/types" "github.com/smartcontractkit/chainlink/v2/core/services/workflows/store" ) +const testWorkflowId = "" const hardcodedWorkflow = ` triggers: - id: "mercury-trigger@1.0.0" @@ -78,6 +80,25 @@ type testHooks struct { executionFinished chan string } +func newTestDBStore(t *testing.T, clock clockwork.Clock) store.Store { + // Taken from https://github.com/smartcontractkit/chainlink/blob/d736d9e0838983a021677bc608556b3994f46690/core/services/job/orm.go#L412 + // We need to insert this row so that we dont get foreign key constraint errors + // based on the workflow_id + db := pgtest.NewSqlxDB(t) + sql := `INSERT INTO workflow_specs (workflow, workflow_id, workflow_owner, workflow_name, created_at, updated_at) + VALUES (:workflow, :workflow_id, :workflow_owner, :workflow_name, NOW(), NOW()) + RETURNING id;` + var wfSpec job.WorkflowSpec + wfSpec.Workflow = simpleWorkflow + wfSpec.WorkflowID = testWorkflowId + wfSpec.WorkflowOwner = "testowner" + wfSpec.WorkflowName = "testworkflow" + _, err := db.NamedExec(sql, wfSpec) + require.NoError(t, err) + + return store.NewDBStore(db, logger.TestLogger(t), clock) +} + // newTestEngine creates a new engine with some test defaults. func newTestEngine(t *testing.T, reg *coreCap.Registry, spec string, opts ...func(c *Config)) (*Engine, *testHooks) { peerID := p2ptypes.PeerID{} @@ -86,13 +107,14 @@ func newTestEngine(t *testing.T, reg *coreCap.Registry, spec string, opts ...fun executionFinished := make(chan string, 100) clock := clockwork.NewFakeClock() cfg := Config{ - Lggr: logger.TestLogger(t), - Registry: reg, - Spec: spec, + WorkflowID: testWorkflowId, + Lggr: logger.TestLogger(t), + Registry: reg, + Spec: spec, GetLocalNode: func(ctx context.Context) (capabilities.Node, error) { return capabilities.Node{ WorkflowDON: capabilities.DON{ - ID: "00010203", + ID: 1, }, PeerID: &peerID, }, nil @@ -110,11 +132,14 @@ func newTestEngine(t *testing.T, reg *coreCap.Registry, spec string, opts ...fun executionFinished <- weid }, clock: clock, - Store: store.NewDBStore(pgtest.NewSqlxDB(t), clock), } for _, o := range opts { o(&cfg) } + // We use the cfg clock incase they override it + if cfg.Store == nil { + cfg.Store = newTestDBStore(t, cfg.clock) + } eng, err := NewEngine(cfg) require.NoError(t, err) return eng, &testHooks{initSuccessful: initSuccessful, initFailed: initFailed, executionFinished: executionFinished} @@ -193,7 +218,6 @@ func (m *mockTriggerCapability) UnregisterTrigger(ctx context.Context, req capab } func TestEngineWithHardcodedWorkflow(t *testing.T) { - dbstore := store.NewDBStore(pgtest.NewSqlxDB(t), clockwork.NewFakeClock()) ctx := testutils.Context(t) reg := coreCap.NewRegistry(logger.TestLogger(t)) @@ -223,7 +247,6 @@ func TestEngineWithHardcodedWorkflow(t *testing.T) { t, reg, hardcodedWorkflow, - func(c *Config) { c.Store = dbstore }, ) servicetest.Run(t, eng) @@ -483,8 +506,9 @@ targets: ` ) -func mockAction() (*mockCapability, values.Value) { - outputs := values.NewString("output") +func mockAction(t *testing.T) (*mockCapability, values.Value) { + outputs, err := values.NewMap(map[string]any{"output": "foo"}) + require.NoError(t, err) return newMockCapability( capabilities.MustNewCapabilityInfo( "read_chain_action@1.0.0", @@ -510,7 +534,7 @@ func TestEngine_MultiStepDependencies(t *testing.T) { require.NoError(t, reg.Add(ctx, mockConsensus())) require.NoError(t, reg.Add(ctx, mockTarget())) - action, out := mockAction() + action, out := mockAction(t) require.NoError(t, reg.Add(ctx, action)) eng, hooks := newTestEngine(t, reg, multiStepWorkflow) @@ -557,10 +581,9 @@ func TestEngine_ResumesPendingExecutions(t *testing.T) { require.NoError(t, reg.Add(ctx, mockConsensus())) require.NoError(t, reg.Add(ctx, mockTarget())) - action, _ := mockAction() + action, _ := mockAction(t) require.NoError(t, reg.Add(ctx, action)) - - dbstore := store.NewDBStore(pgtest.NewSqlxDB(t), clockwork.NewFakeClock()) + dbstore := newTestDBStore(t, clockwork.NewFakeClock()) ec := &store.WorkflowExecution{ Steps: map[string]*store.WorkflowExecutionStep{ workflows.KeywordTrigger: { @@ -572,7 +595,7 @@ func TestEngine_ResumesPendingExecutions(t *testing.T) { Ref: workflows.KeywordTrigger, }, }, - WorkflowID: "", + WorkflowID: testWorkflowId, ExecutionID: "", Status: store.StatusStarted, } @@ -610,11 +633,11 @@ func TestEngine_TimesOutOldExecutions(t *testing.T) { require.NoError(t, reg.Add(ctx, mockConsensus())) require.NoError(t, reg.Add(ctx, mockTarget())) - action, _ := mockAction() + action, _ := mockAction(t) require.NoError(t, reg.Add(ctx, action)) clock := clockwork.NewFakeClock() - dbstore := store.NewDBStore(pgtest.NewSqlxDB(t), clock) + dbstore := newTestDBStore(t, clock) ec := &store.WorkflowExecution{ Steps: map[string]*store.WorkflowExecutionStep{ workflows.KeywordTrigger: { @@ -626,7 +649,7 @@ func TestEngine_TimesOutOldExecutions(t *testing.T) { Ref: workflows.KeywordTrigger, }, }, - WorkflowID: "", + WorkflowID: testWorkflowId, ExecutionID: "", Status: store.StatusStarted, } @@ -708,7 +731,7 @@ func TestEngine_WrapsTargets(t *testing.T) { require.NoError(t, reg.Add(ctx, mockTarget())) clock := clockwork.NewFakeClock() - dbstore := store.NewDBStore(pgtest.NewSqlxDB(t), clock) + dbstore := newTestDBStore(t, clock) eng, hooks := newTestEngine( t, @@ -754,13 +777,13 @@ func TestEngine_GetsNodeInfoDuringInitialization(t *testing.T) { require.NoError(t, reg.Add(ctx, mockTarget())) clock := clockwork.NewFakeClock() - dbstore := store.NewDBStore(pgtest.NewSqlxDB(t), clock) + dbstore := newTestDBStore(t, clock) var peerID p2ptypes.PeerID node := capabilities.Node{ PeerID: &peerID, WorkflowDON: capabilities.DON{ - ID: "1", + ID: 1, }, } retryCount := 0 @@ -791,3 +814,158 @@ func TestEngine_GetsNodeInfoDuringInitialization(t *testing.T) { assert.Equal(t, node, eng.localNode) } + +const passthroughInterpolationWorkflow = ` +triggers: + - id: "mercury-trigger@1.0.0" + config: + feedIds: + - "0x1111111111111111111100000000000000000000000000000000000000000000" + - "0x2222222222222222222200000000000000000000000000000000000000000000" + - "0x3333333333333333333300000000000000000000000000000000000000000000" + +consensus: + - id: "offchain_reporting@1.0.0" + ref: "evm_median" + inputs: + observations: + - "$(trigger.outputs)" + config: + aggregation_method: "data_feeds_2_0" + aggregation_config: + "0x1111111111111111111100000000000000000000000000000000000000000000": + deviation: "0.001" + heartbeat: 3600 + "0x2222222222222222222200000000000000000000000000000000000000000000": + deviation: "0.001" + heartbeat: 3600 + "0x3333333333333333333300000000000000000000000000000000000000000000": + deviation: "0.001" + heartbeat: 3600 + encoder: "EVM" + encoder_config: + abi: "mercury_reports bytes[]" + +targets: + - id: "write_ethereum-testnet-sepolia@1.0.0" + inputs: "$(evm_median.outputs)" + config: + address: "0x54e220867af6683aE6DcBF535B4f952cB5116510" + params: ["$(report)"] + abi: "receive(report bytes)" +` + +func TestEngine_PassthroughInterpolation(t *testing.T) { + ctx := testutils.Context(t) + reg := coreCap.NewRegistry(logger.TestLogger(t)) + + trigger, _ := mockTrigger(t) + + require.NoError(t, reg.Add(ctx, trigger)) + require.NoError(t, reg.Add(ctx, mockConsensus())) + writeID := "write_ethereum-testnet-sepolia@1.0.0" + target := newMockCapability( + capabilities.MustNewCapabilityInfo( + writeID, + capabilities.CapabilityTypeTarget, + "a write capability targeting ethereum sepolia testnet", + ), + func(req capabilities.CapabilityRequest) (capabilities.CapabilityResponse, error) { + return capabilities.CapabilityResponse{ + Value: req.Inputs, + }, nil + }, + ) + require.NoError(t, reg.Add(ctx, target)) + + eng, testHooks := newTestEngine( + t, + reg, + passthroughInterpolationWorkflow, + ) + + servicetest.Run(t, eng) + + eid := getExecutionId(t, eng, testHooks) + + state, err := eng.executionStates.Get(ctx, eid) + require.NoError(t, err) + + assert.Equal(t, state.Status, store.StatusCompleted) + + // There is passthrough interpolation between the consensus and target steps, + // so the input of one should be the output of the other, exactly. + gotInputs, err := values.Unwrap(state.Steps[writeID].Inputs) + require.NoError(t, err) + + gotOutputs, err := values.Unwrap(state.Steps["evm_median"].Outputs.Value) + require.NoError(t, err) + assert.Equal(t, gotInputs, gotOutputs) +} + +func TestEngine_Error(t *testing.T) { + err := errors.New("some error") + tests := []struct { + name string + labels map[string]string + err error + reason string + want string + }{ + { + name: "Error with error and reason", + labels: map[string]string{wIDKey: "my-workflow-id"}, + err: err, + reason: "some reason", + want: "workflowID my-workflow-id: some reason: some error", + }, + { + name: "Error with error and no reason", + labels: map[string]string{eIDKey: "dd3708ac7d8dd6fa4fae0fb87b73f318a4da2526c123e159b72435e3b2fe8751"}, + err: err, + want: "executionID dd3708ac7d8dd6fa4fae0fb87b73f318a4da2526c123e159b72435e3b2fe8751: some error", + }, + { + name: "Error with no error and reason", + labels: map[string]string{cIDKey: "streams-trigger:network_eth@1.0.0"}, + reason: "some reason", + want: "capabilityID streams-trigger:network_eth@1.0.0: some reason", + }, + { + name: "Error with no error and no reason", + labels: map[string]string{tIDKey: "wf_123_trigger_456"}, + want: "triggerID wf_123_trigger_456: ", + }, + { + name: "Error with no labels", + labels: map[string]string{}, + err: err, + reason: "some reason", + want: "some reason: some error", + }, + { + name: "Multiple labels", + labels: map[string]string{ + wIDKey: "my-workflow-id", + eIDKey: "dd3708ac7d8dd6fa4fae0fb87b73f318a4da2526c123e159b72435e3b2fe8751", + cIDKey: "streams-trigger:network_eth@1.0.0", + }, + err: err, + reason: "some reason", + want: "workflowID my-workflow-id: executionID dd3708ac7d8dd6fa4fae0fb87b73f318a4da2526c123e159b72435e3b2fe8751: capabilityID streams-trigger:network_eth@1.0.0: some reason: some error", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + e := &workflowError{ + labels: tt.labels, + err: tt.err, + reason: tt.reason, + } + if got := e.Error(); got != tt.want { + t.Errorf("err string mismatch\ngot = %v\nwant = %v", got, tt.want) + } + }) + } +} diff --git a/core/services/workflows/models_test.go b/core/services/workflows/models_test.go index 4b4747c486..a28aeb9df0 100644 --- a/core/services/workflows/models_test.go +++ b/core/services/workflows/models_test.go @@ -214,7 +214,7 @@ targets: }, }, { - name: "non-trigger step with no dependent refs", + name: "invalid refs", yaml: ` triggers: - id: "a-trigger@1.0.0" @@ -241,7 +241,7 @@ targets: inputs: consensus_output: $(a-consensus.outputs) `, - errMsg: "all non-trigger steps must have a dependent ref", + errMsg: "invalid refs", }, { name: "duplicate edge declarations", diff --git a/core/services/workflows/store/store_db.go b/core/services/workflows/store/store_db.go index e9204efd7b..e1d0862905 100644 --- a/core/services/workflows/store/store_db.go +++ b/core/services/workflows/store/store_db.go @@ -14,11 +14,13 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" "github.com/smartcontractkit/chainlink-common/pkg/values" valuespb "github.com/smartcontractkit/chainlink-common/pkg/values/pb" + "github.com/smartcontractkit/chainlink/v2/core/logger" ) // `DBStore` is a postgres-backed // data store that persists workflow progress. type DBStore struct { + lggr logger.Logger db sqlutil.DataSource clock clockwork.Clock } @@ -197,16 +199,20 @@ func stateToStep(state *WorkflowExecutionStep) (workflowStepRow, error) { // `Add` creates the relevant workflow_execution and workflow_step entries // to persist the passed in ExecutionState. func (d *DBStore) Add(ctx context.Context, state *WorkflowExecution) error { + l := d.lggr.With("executionID", state.ExecutionID, "workflowID", state.WorkflowID, "status", state.Status) return d.transact(ctx, func(db *DBStore) error { var wid *string if state.WorkflowID != "" { wid = &state.WorkflowID } + wex := &workflowExecutionRow{ ID: state.ExecutionID, WorkflowID: wid, Status: state.Status, } + l.Debug("Adding workflow execution") + err := db.insertWorkflowExecution(ctx, wex) if err != nil { return fmt.Errorf("could not insert workflow execution %s: %w", state.ExecutionID, err) @@ -218,6 +224,7 @@ func (d *DBStore) Add(ctx context.Context, state *WorkflowExecution) error { if err != nil { return err } + l.With("stepRef", step.Ref).Debug("Adding workflow step") ws = append(ws, step) } if len(ws) > 0 { @@ -368,6 +375,6 @@ func (d *DBStore) GetUnfinished(ctx context.Context, offset, limit int) ([]Workf return states, nil } -func NewDBStore(ds sqlutil.DataSource, clock clockwork.Clock) *DBStore { - return &DBStore{db: ds, clock: clock} +func NewDBStore(ds sqlutil.DataSource, lggr logger.Logger, clock clockwork.Clock) *DBStore { + return &DBStore{db: ds, lggr: lggr.Named("WorkflowDBStore"), clock: clock} } diff --git a/core/services/workflows/store/store_db_test.go b/core/services/workflows/store/store_db_test.go index 7ae139b943..9a98db3056 100644 --- a/core/services/workflows/store/store_db_test.go +++ b/core/services/workflows/store/store_db_test.go @@ -13,6 +13,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" "github.com/smartcontractkit/chainlink-common/pkg/values" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" + "github.com/smartcontractkit/chainlink/v2/core/logger" ) func randomID() string { @@ -24,9 +25,13 @@ func randomID() string { return hex.EncodeToString(b) } -func Test_StoreDB(t *testing.T) { +func newTestDBStore(t *testing.T) *DBStore { db := pgtest.NewSqlxDB(t) - store := &DBStore{db: db, clock: clockwork.NewFakeClock()} + return &DBStore{db: db, lggr: logger.TestLogger(t), clock: clockwork.NewFakeClock()} +} + +func Test_StoreDB(t *testing.T) { + store := newTestDBStore(t) id := randomID() es := WorkflowExecution{ @@ -58,8 +63,7 @@ func Test_StoreDB(t *testing.T) { } func Test_StoreDB_DuplicateEntry(t *testing.T) { - db := pgtest.NewSqlxDB(t) - store := &DBStore{db: db, clock: clockwork.NewFakeClock()} + store := newTestDBStore(t) id := randomID() es := WorkflowExecution{ @@ -87,8 +91,7 @@ func Test_StoreDB_DuplicateEntry(t *testing.T) { } func Test_StoreDB_UpdateStatus(t *testing.T) { - db := pgtest.NewSqlxDB(t) - store := &DBStore{db: db, clock: clockwork.NewFakeClock()} + store := newTestDBStore(t) id := randomID() es := WorkflowExecution{ @@ -122,8 +125,7 @@ func Test_StoreDB_UpdateStatus(t *testing.T) { } func Test_StoreDB_UpdateStep(t *testing.T) { - db := pgtest.NewSqlxDB(t) - store := &DBStore{db: db, clock: clockwork.NewFakeClock()} + store := newTestDBStore(t) id := randomID() stepOne := &WorkflowExecutionStep{ @@ -170,8 +172,7 @@ func Test_StoreDB_UpdateStep(t *testing.T) { } func Test_StoreDB_WorkflowStatus(t *testing.T) { - db := pgtest.NewSqlxDB(t) - store := &DBStore{db: db, clock: clockwork.NewFakeClock()} + store := newTestDBStore(t) for s := range ValidStatuses { id := randomID() @@ -200,8 +201,7 @@ func Test_StoreDB_WorkflowStatus(t *testing.T) { } func Test_StoreDB_WorkflowStepStatus(t *testing.T) { - db := pgtest.NewSqlxDB(t) - store := &DBStore{db: db, clock: clockwork.NewFakeClock()} + store := newTestDBStore(t) id := randomID() stepOne := &WorkflowExecutionStep{ @@ -234,8 +234,7 @@ func Test_StoreDB_WorkflowStepStatus(t *testing.T) { } func Test_StoreDB_GetUnfinishedSteps(t *testing.T) { - db := pgtest.NewSqlxDB(t) - store := &DBStore{db: db, clock: clockwork.NewFakeClock()} + store := newTestDBStore(t) id := randomID() stepOne := &WorkflowExecutionStep{ diff --git a/core/sessions/ldapauth/mocks/ldap_client.go b/core/sessions/ldapauth/mocks/ldap_client.go index d8fe4dbadf..f902730d02 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/sessions/ldapauth/mocks/ldap_conn.go b/core/sessions/ldapauth/mocks/ldap_conn.go index 3116320cd9..48783feaf5 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/sessions/mocks/authentication_provider.go b/core/sessions/mocks/authentication_provider.go index 3031a7c980..69777f3e96 100644 --- a/core/sessions/mocks/authentication_provider.go +++ b/core/sessions/mocks/authentication_provider.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. 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 b8376267c2..800f7b595a 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.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/store/migrate/migrations/0246_cron_spec_evm_chain_id.sql b/core/store/migrate/migrations/0246_cron_spec_evm_chain_id.sql new file mode 100644 index 0000000000..d2c0b1582e --- /dev/null +++ b/core/store/migrate/migrations/0246_cron_spec_evm_chain_id.sql @@ -0,0 +1,5 @@ +-- +goose Up +ALTER TABLE cron_specs ADD COLUMN evm_chain_id numeric(78,0); + +-- +goose Down +ALTER TABLE cron_specs DROP COLUMN evm_chain_id; diff --git a/core/testdata/testspecs/v2_specs.go b/core/testdata/testspecs/v2_specs.go index 949515b335..3a1798ab5a 100644 --- a/core/testdata/testspecs/v2_specs.go +++ b/core/testdata/testspecs/v2_specs.go @@ -24,7 +24,7 @@ var ( type = "cron" schemaVersion = 1 schedule = "CRON_TZ=UTC * 0 0 1 1 *" -externalJobID = "%s" +externalJobID = "%s" observationSource = """ ds [type=http method=GET url="https://chain.link/ETH-USD"]; ds_parse [type=jsonparse path="data,price"]; @@ -36,7 +36,20 @@ ds -> ds_parse -> ds_multiply; type = "cron" schemaVersion = 1 schedule = "CRON_TZ=UTC * 0 0 1 1 *" -externalJobID = "%s" +externalJobID = "%s" +observationSource = """ +ds [type=http method=GET url="https://chain.link/ETH-USD"]; +ds_parse [type=jsonparse path="data.price" separator="."]; +ds_multiply [type=multiply times=100]; +ds -> ds_parse -> ds_multiply; +""" +` + CronSpecEVMChainIDTemplate = ` +type = "cron" +schemaVersion = 1 +schedule = "CRON_TZ=UTC * 0 0 1 1 *" +externalJobID = "%s" +evmChainID = "42" observationSource = """ ds [type=http method=GET url="https://chain.link/ETH-USD"]; ds_parse [type=jsonparse path="data.price" separator="."]; @@ -62,7 +75,7 @@ type = "directrequest" schemaVersion = 1 name = "%s" contractAddress = "0x613a38AC1659769640aaE063C651F48E0250454C" -externalJobID = "%s" +externalJobID = "%s" evmChainID = "0" observationSource = """ ds1 [type=http method=GET url="http://example.com" allowunrestrictednetworkaccess="true"]; diff --git a/core/web/aptos_keys_controller.go b/core/web/aptos_keys_controller.go new file mode 100644 index 0000000000..5be4b81577 --- /dev/null +++ b/core/web/aptos_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/aptoskey" + "github.com/smartcontractkit/chainlink/v2/core/web/presenters" +) + +func NewAptosKeysController(app chainlink.Application) KeysController { + return NewKeysController[aptoskey.Key, presenters.AptosKeyResource](app.GetKeyStore().Aptos(), app.GetLogger(), app.GetAuditLogger(), + "aptosKey", presenters.NewAptosKeyResource, presenters.NewAptosKeyResources) +} diff --git a/core/web/aptos_keys_controller_test.go b/core/web/aptos_keys_controller_test.go new file mode 100644 index 0000000000..c5de75a510 --- /dev/null +++ b/core/web/aptos_keys_controller_test.go @@ -0,0 +1,109 @@ +package web_test + +import ( + "fmt" + "net/http" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-common/pkg/utils" + "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" + "github.com/smartcontractkit/chainlink/v2/core/services/keystore" + "github.com/smartcontractkit/chainlink/v2/core/web" + "github.com/smartcontractkit/chainlink/v2/core/web/presenters" +) + +func TestAptosKeysController_Index_HappyPath(t *testing.T) { + t.Parallel() + + client, keyStore := setupAptosKeysControllerTests(t) + keys, _ := keyStore.Aptos().GetAll() + + response, cleanup := client.Get("/v2/keys/aptos") + t.Cleanup(cleanup) + cltest.AssertServerResponse(t, response, http.StatusOK) + + resources := []presenters.AptosKeyResource{} + err := web.ParseJSONAPIResponse(cltest.ParseResponseBody(t, response), &resources) + assert.NoError(t, err) + + require.Len(t, resources, len(keys)) + + assert.Equal(t, keys[0].ID(), resources[0].ID) + assert.Equal(t, keys[0].PublicKeyStr(), resources[0].PubKey) +} + +func TestAptosKeysController_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/aptos", nil) + t.Cleanup(cleanup) + cltest.AssertServerResponse(t, response, http.StatusOK) + + keys, _ := keyStore.Aptos().GetAll() + require.Len(t, keys, 1) + + resource := presenters.AptosKeyResource{} + err := web.ParseJSONAPIResponse(cltest.ParseResponseBody(t, response), &resource) + assert.NoError(t, err) + + assert.Equal(t, keys[0].ID(), resource.ID) + assert.Equal(t, keys[0].PublicKeyStr(), resource.PubKey) + + _, err = keyStore.Aptos().Get(resource.ID) + require.NoError(t, err) +} + +func TestAptosKeysController_Delete_NonExistentAptosKeyID(t *testing.T) { + t.Parallel() + + client, _ := setupAptosKeysControllerTests(t) + + nonExistentAptosKeyID := "foobar" + response, cleanup := client.Delete("/v2/keys/aptos/" + nonExistentAptosKeyID) + t.Cleanup(cleanup) + assert.Equal(t, http.StatusNotFound, response.StatusCode) +} + +func TestAptosKeysController_Delete_HappyPath(t *testing.T) { + t.Parallel() + ctx := testutils.Context(t) + + client, keyStore := setupAptosKeysControllerTests(t) + + keys, _ := keyStore.Aptos().GetAll() + initialLength := len(keys) + key, _ := keyStore.Aptos().Create(ctx) + + response, cleanup := client.Delete(fmt.Sprintf("/v2/keys/aptos/%s", key.ID())) + t.Cleanup(cleanup) + assert.Equal(t, http.StatusOK, response.StatusCode) + assert.Error(t, utils.JustError(keyStore.Aptos().Get(key.ID()))) + + keys, _ = keyStore.Aptos().GetAll() + assert.Equal(t, initialLength, len(keys)) +} + +func setupAptosKeysControllerTests(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)) + aptosKeyStore := app.GetKeyStore().Aptos() + require.NotNil(t, aptosKeyStore) + require.NoError(t, aptosKeyStore.Add(ctx, cltest.DefaultAptosKey)) + + client := app.NewHTTPClient(nil) + + return client, app.GetKeyStore() +} diff --git a/core/web/assets/index.html b/core/web/assets/index.html index eb81b6d994..e50dfc0709 100644 --- a/core/web/assets/index.html +++ b/core/web/assets/index.html @@ -1 +1 @@ -Operator UIChainlink
\ No newline at end of file +Operator UIChainlink
\ No newline at end of file diff --git a/core/web/assets/index.html.gz b/core/web/assets/index.html.gz index 009ac43692..f32bc10dbc 100644 Binary files a/core/web/assets/index.html.gz and b/core/web/assets/index.html.gz differ diff --git a/core/web/assets/main.026013f04da39527a75d.js b/core/web/assets/main.6f07a88cfc748f57e21d.js similarity index 90% rename from core/web/assets/main.026013f04da39527a75d.js rename to core/web/assets/main.6f07a88cfc748f57e21d.js index e3e27950a6..bf6a280a99 100644 --- a/core/web/assets/main.026013f04da39527a75d.js +++ b/core/web/assets/main.6f07a88cfc748f57e21d.js @@ -171,4 +171,4 @@ object-assign */ Object.defineProperty(t,"__esModule",{value:!0}),"undefined"==typeof window||"function"!=typeof MessageChannel){var n,r,i,a,o,s=null,u=null,c=function(){if(null!==s)try{var e=t.unstable_now();s(!0,e),s=null}catch(n){throw setTimeout(c,0),n}},l=Date.now();t.unstable_now=function(){return Date.now()-l},n=function(e){null!==s?setTimeout(n,0,e):(s=e,setTimeout(c,0))},r=function(e,t){u=setTimeout(e,t)},i=function(){clearTimeout(u)},a=function(){return!1},o=t.unstable_forceFrameRate=function(){}}else{var f=window.performance,d=window.Date,h=window.setTimeout,p=window.clearTimeout;if("undefined"!=typeof console){var b=window.cancelAnimationFrame;"function"!=typeof window.requestAnimationFrame&&console.error("This browser doesn't support requestAnimationFrame. Make sure that you load a polyfill in older browsers. https://fb.me/react-polyfills"),"function"!=typeof b&&console.error("This browser doesn't support cancelAnimationFrame. Make sure that you load a polyfill in older browsers. https://fb.me/react-polyfills")}if("object"==typeof f&&"function"==typeof f.now)t.unstable_now=function(){return f.now()};else{var m=d.now();t.unstable_now=function(){return d.now()-m}}var g=!1,v=null,y=-1,w=5,_=0;a=function(){return t.unstable_now()>=_},o=function(){},t.unstable_forceFrameRate=function(e){0>e||125M(o,n))void 0!==u&&0>M(u,o)?(e[r]=u,e[s]=n,r=s):(e[r]=o,e[a]=n,r=a);else if(void 0!==u&&0>M(u,n))e[r]=u,e[s]=n,r=s;else break a}}return t}return null}function M(e,t){var n=e.sortIndex-t.sortIndex;return 0!==n?n:e.id-t.id}var O=[],A=[],L=1,C=null,I=3,D=!1,N=!1,P=!1;function R(e){for(var t=x(A);null!==t;){if(null===t.callback)T(A);else if(t.startTime<=e)T(A),t.sortIndex=t.expirationTime,k(O,t);else break;t=x(A)}}function j(e){if(P=!1,R(e),!N){if(null!==x(O))N=!0,n(F);else{var t=x(A);null!==t&&r(j,t.startTime-e)}}}function F(e,n){N=!1,P&&(P=!1,i()),D=!0;var o=I;try{for(R(n),C=x(O);null!==C&&(!(C.expirationTime>n)||e&&!a());){var s=C.callback;if(null!==s){C.callback=null,I=C.priorityLevel;var u=s(C.expirationTime<=n);n=t.unstable_now(),"function"==typeof u?C.callback=u:C===x(O)&&T(O),R(n)}else T(O);C=x(O)}if(null!==C)var c=!0;else{var l=x(A);null!==l&&r(j,l.startTime-n),c=!1}return c}finally{C=null,I=o,D=!1}}function Y(e){switch(e){case 1:return -1;case 2:return 250;case 5:return 1073741823;case 4:return 1e4;default:return 5e3}}var B=o;t.unstable_ImmediatePriority=1,t.unstable_UserBlockingPriority=2,t.unstable_NormalPriority=3,t.unstable_IdlePriority=5,t.unstable_LowPriority=4,t.unstable_runWithPriority=function(e,t){switch(e){case 1:case 2:case 3:case 4:case 5:break;default:e=3}var n=I;I=e;try{return t()}finally{I=n}},t.unstable_next=function(e){switch(I){case 1:case 2:case 3:var t=3;break;default:t=I}var n=I;I=t;try{return e()}finally{I=n}},t.unstable_scheduleCallback=function(e,a,o){var s=t.unstable_now();if("object"==typeof o&&null!==o){var u=o.delay;u="number"==typeof u&&0s?(e.sortIndex=u,k(A,e),null===x(O)&&e===x(A)&&(P?i():P=!0,r(j,u-s))):(e.sortIndex=o,k(O,e),N||D||(N=!0,n(F))),e},t.unstable_cancelCallback=function(e){e.callback=null},t.unstable_wrapCallback=function(e){var t=I;return function(){var n=I;I=t;try{return e.apply(this,arguments)}finally{I=n}}},t.unstable_getCurrentPriorityLevel=function(){return I},t.unstable_shouldYield=function(){var e=t.unstable_now();R(e);var n=x(O);return n!==C&&null!==C&&null!==n&&null!==n.callback&&n.startTime<=e&&n.expirationTime>5==6?2:e>>4==14?3:e>>3==30?4:e>>6==2?-1:-2}function c(e,t,n){var r=t.length-1;if(r=0?(i>0&&(e.lastNeed=i-1),i):--r=0?(i>0&&(e.lastNeed=i-2),i):--r=0?(i>0&&(2===i?i=0:e.lastNeed=i-3),i):0}function l(e,t,n){if((192&t[0])!=128)return e.lastNeed=0,"�";if(e.lastNeed>1&&t.length>1){if((192&t[1])!=128)return e.lastNeed=1,"�";if(e.lastNeed>2&&t.length>2&&(192&t[2])!=128)return e.lastNeed=2,"�"}}function f(e){var t=this.lastTotal-this.lastNeed,n=l(this,e,t);return void 0!==n?n:this.lastNeed<=e.length?(e.copy(this.lastChar,t,0,this.lastNeed),this.lastChar.toString(this.encoding,0,this.lastTotal)):void(e.copy(this.lastChar,t,0,e.length),this.lastNeed-=e.length)}function d(e,t){var n=c(this,e,t);if(!this.lastNeed)return e.toString("utf8",t);this.lastTotal=n;var r=e.length-(n-this.lastNeed);return e.copy(this.lastChar,0,r),e.toString("utf8",t,r)}function h(e){var t=e&&e.length?this.write(e):"";return this.lastNeed?t+"�":t}function p(e,t){if((e.length-t)%2==0){var n=e.toString("utf16le",t);if(n){var r=n.charCodeAt(n.length-1);if(r>=55296&&r<=56319)return this.lastNeed=2,this.lastTotal=4,this.lastChar[0]=e[e.length-2],this.lastChar[1]=e[e.length-1],n.slice(0,-1)}return n}return this.lastNeed=1,this.lastTotal=2,this.lastChar[0]=e[e.length-1],e.toString("utf16le",t,e.length-1)}function b(e){var t=e&&e.length?this.write(e):"";if(this.lastNeed){var n=this.lastTotal-this.lastNeed;return t+this.lastChar.toString("utf16le",0,n)}return t}function m(e,t){var n=(e.length-t)%3;return 0===n?e.toString("base64",t):(this.lastNeed=3-n,this.lastTotal=3,1===n?this.lastChar[0]=e[e.length-1]:(this.lastChar[0]=e[e.length-2],this.lastChar[1]=e[e.length-1]),e.toString("base64",t,e.length-n))}function g(e){var t=e&&e.length?this.write(e):"";return this.lastNeed?t+this.lastChar.toString("base64",0,3-this.lastNeed):t}function v(e){return e.toString(this.encoding)}function y(e){return e&&e.length?this.write(e):""}t.s=s,s.prototype.write=function(e){var t,n;if(0===e.length)return"";if(this.lastNeed){if(void 0===(t=this.fillLast(e)))return"";n=this.lastNeed,this.lastNeed=0}else n=0;return 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;nOW});var r,i,a,o,s,u,c,l=n(67294),f=n.t(l,2),d=n(39814),h=n(5977),p=n(57209),b=n(32316),m=n(95880),g=n(17051),v=n(71381),y=n(81701),w=n(3022),_=n(60323),E=n(87591),S=n(25649),k=n(28902),x=n(71426),T=n(48884),M=n(94184),O=n.n(M),A=n(37703),L=n(73935),C=function(){if("undefined"!=typeof Map)return Map;function e(e,t){var n=-1;return e.some(function(e,r){return e[0]===t&&(n=r,!0)}),n}return function(){function t(){this.__entries__=[]}return Object.defineProperty(t.prototype,"size",{get:function(){return this.__entries__.length},enumerable:!0,configurable:!0}),t.prototype.get=function(t){var n=e(this.__entries__,t),r=this.__entries__[n];return r&&r[1]},t.prototype.set=function(t,n){var r=e(this.__entries__,t);~r?this.__entries__[r][1]=n:this.__entries__.push([t,n])},t.prototype.delete=function(t){var n=this.__entries__,r=e(n,t);~r&&n.splice(r,1)},t.prototype.has=function(t){return!!~e(this.__entries__,t)},t.prototype.clear=function(){this.__entries__.splice(0)},t.prototype.forEach=function(e,t){void 0===t&&(t=null);for(var n=0,r=this.__entries__;n0},e.prototype.connect_=function(){I&&!this.connected_&&(document.addEventListener("transitionend",this.onTransitionEnd_),window.addEventListener("resize",this.refresh),Y?(this.mutationsObserver_=new MutationObserver(this.refresh),this.mutationsObserver_.observe(document,{attributes:!0,childList:!0,characterData:!0,subtree:!0})):(document.addEventListener("DOMSubtreeModified",this.refresh),this.mutationEventsAdded_=!0),this.connected_=!0)},e.prototype.disconnect_=function(){I&&this.connected_&&(document.removeEventListener("transitionend",this.onTransitionEnd_),window.removeEventListener("resize",this.refresh),this.mutationsObserver_&&this.mutationsObserver_.disconnect(),this.mutationEventsAdded_&&document.removeEventListener("DOMSubtreeModified",this.refresh),this.mutationsObserver_=null,this.mutationEventsAdded_=!1,this.connected_=!1)},e.prototype.onTransitionEnd_=function(e){var t=e.propertyName,n=void 0===t?"":t;F.some(function(e){return!!~n.indexOf(e)})&&this.refresh()},e.getInstance=function(){return this.instance_||(this.instance_=new e),this.instance_},e.instance_=null,e}(),U=function(e,t){for(var n=0,r=Object.keys(t);n0},e}(),er="undefined"!=typeof WeakMap?new WeakMap:new C,ei=function(){function e(t){if(!(this instanceof e))throw TypeError("Cannot call a class as a function.");if(!arguments.length)throw TypeError("1 argument required, but only 0 present.");var n=B.getInstance(),r=new en(t,n,this);er.set(this,r)}return e}();["observe","unobserve","disconnect"].forEach(function(e){ei.prototype[e]=function(){var t;return(t=er.get(this))[e].apply(t,arguments)}});var ea=void 0!==D.ResizeObserver?D.ResizeObserver:ei;let eo=ea;var es=function(e){var t=[],n=null,r=function(){for(var r=arguments.length,i=Array(r),a=0;a=t||n<0||f&&r>=a}function g(){var e=eb();if(m(e))return v(e);s=setTimeout(g,b(e))}function v(e){return(s=void 0,d&&r)?h(e):(r=i=void 0,o)}function y(){void 0!==s&&clearTimeout(s),c=0,r=u=i=s=void 0}function w(){return void 0===s?o:v(eb())}function _(){var e=eb(),n=m(e);if(r=arguments,i=this,u=e,n){if(void 0===s)return p(u);if(f)return clearTimeout(s),s=setTimeout(g,t),h(u)}return void 0===s&&(s=setTimeout(g,t)),o}return t=ez(t)||0,ed(n)&&(l=!!n.leading,a=(f="maxWait"in n)?eW(ez(n.maxWait)||0,t):a,d="trailing"in n?!!n.trailing:d),_.cancel=y,_.flush=w,_}let eq=eV;var eZ="Expected a function";function eX(e,t,n){var r=!0,i=!0;if("function"!=typeof e)throw TypeError(eZ);return ed(n)&&(r="leading"in n?!!n.leading:r,i="trailing"in n?!!n.trailing:i),eq(e,t,{leading:r,maxWait:t,trailing:i})}let eJ=eX;var eQ={debounce:eq,throttle:eJ},e1=function(e){return eQ[e]},e0=function(e){return"function"==typeof e},e2=function(){return"undefined"==typeof window},e3=function(e){return e instanceof Element||e instanceof HTMLDocument};function e4(e){return(e4="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function e6(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}function e5(e,t){for(var n=0;ne.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&l.createElement(tG.Z,{variant:"indeterminate",classes:r}))};tK.propTypes={fetchCount:el().number.isRequired};let tV=(0,b.withStyles)(tW)(tK);var tq=n(5536);let tZ=n.p+"ba8bbf16ebf8e1d05bef.svg";function tX(){return(tX=Object.assign||function(e){for(var t=1;t120){for(var d=Math.floor(u/80),h=u%80,p=[],b=0;b0},name:{enumerable:!1},nodes:{enumerable:!1},source:{enumerable:!1},positions:{enumerable:!1},originalError:{enumerable:!1}}),null!=s&&s.stack)?(Object.defineProperty(nf(b),"stack",{value:s.stack,writable:!0,configurable:!0}),nl(b)):(Error.captureStackTrace?Error.captureStackTrace(nf(b),n):Object.defineProperty(nf(b),"stack",{value:Error().stack,writable:!0,configurable:!0}),b)}return ns(n,[{key:"toString",value:function(){return nw(this)}},{key:t4.YF,get:function(){return"Object"}}]),n}(nd(Error));function ny(e){return void 0===e||0===e.length?void 0:e}function nw(e){var t=e.message;if(e.nodes)for(var n=0,r=e.nodes;n",EOF:"",BANG:"!",DOLLAR:"$",AMP:"&",PAREN_L:"(",PAREN_R:")",SPREAD:"...",COLON:":",EQUALS:"=",AT:"@",BRACKET_L:"[",BRACKET_R:"]",BRACE_L:"{",PIPE:"|",BRACE_R:"}",NAME:"Name",INT:"Int",FLOAT:"Float",STRING:"String",BLOCK_STRING:"BlockString",COMMENT:"Comment"}),nx=n(10143),nT=Object.freeze({QUERY:"QUERY",MUTATION:"MUTATION",SUBSCRIPTION:"SUBSCRIPTION",FIELD:"FIELD",FRAGMENT_DEFINITION:"FRAGMENT_DEFINITION",FRAGMENT_SPREAD:"FRAGMENT_SPREAD",INLINE_FRAGMENT:"INLINE_FRAGMENT",VARIABLE_DEFINITION:"VARIABLE_DEFINITION",SCHEMA:"SCHEMA",SCALAR:"SCALAR",OBJECT:"OBJECT",FIELD_DEFINITION:"FIELD_DEFINITION",ARGUMENT_DEFINITION:"ARGUMENT_DEFINITION",INTERFACE:"INTERFACE",UNION:"UNION",ENUM:"ENUM",ENUM_VALUE:"ENUM_VALUE",INPUT_OBJECT:"INPUT_OBJECT",INPUT_FIELD_DEFINITION:"INPUT_FIELD_DEFINITION"}),nM=n(87392),nO=function(){function e(e){var t=new nS.WU(nk.SOF,0,0,0,0,null);this.source=e,this.lastToken=t,this.token=t,this.line=1,this.lineStart=0}var t=e.prototype;return t.advance=function(){return this.lastToken=this.token,this.token=this.lookahead()},t.lookahead=function(){var e,t=this.token;if(t.kind!==nk.EOF)do t=null!==(e=t.next)&&void 0!==e?e:t.next=nC(this,t);while(t.kind===nk.COMMENT)return t},e}();function nA(e){return e===nk.BANG||e===nk.DOLLAR||e===nk.AMP||e===nk.PAREN_L||e===nk.PAREN_R||e===nk.SPREAD||e===nk.COLON||e===nk.EQUALS||e===nk.AT||e===nk.BRACKET_L||e===nk.BRACKET_R||e===nk.BRACE_L||e===nk.PIPE||e===nk.BRACE_R}function nL(e){return isNaN(e)?nk.EOF:e<127?JSON.stringify(String.fromCharCode(e)):'"\\u'.concat(("00"+e.toString(16).toUpperCase()).slice(-4),'"')}function nC(e,t){for(var n=e.source,r=n.body,i=r.length,a=t.end;a31||9===a))return new nS.WU(nk.COMMENT,t,s,n,r,i,o.slice(t+1,s))}function nN(e,t,n,r,i,a){var o=e.body,s=n,u=t,c=!1;if(45===s&&(s=o.charCodeAt(++u)),48===s){if((s=o.charCodeAt(++u))>=48&&s<=57)throw n_(e,u,"Invalid number, unexpected digit after 0: ".concat(nL(s),"."))}else u=nP(e,u,s),s=o.charCodeAt(u);if(46===s&&(c=!0,s=o.charCodeAt(++u),u=nP(e,u,s),s=o.charCodeAt(u)),(69===s||101===s)&&(c=!0,(43===(s=o.charCodeAt(++u))||45===s)&&(s=o.charCodeAt(++u)),u=nP(e,u,s),s=o.charCodeAt(u)),46===s||nU(s))throw n_(e,u,"Invalid number, expected digit but got: ".concat(nL(s),"."));return new nS.WU(c?nk.FLOAT:nk.INT,t,u,r,i,a,o.slice(t,u))}function nP(e,t,n){var r=e.body,i=t,a=n;if(a>=48&&a<=57){do a=r.charCodeAt(++i);while(a>=48&&a<=57)return i}throw n_(e,i,"Invalid number, expected digit but got: ".concat(nL(a),"."))}function nR(e,t,n,r,i){for(var a=e.body,o=t+1,s=o,u=0,c="";o=48&&e<=57?e-48:e>=65&&e<=70?e-55:e>=97&&e<=102?e-87:-1}function nB(e,t,n,r,i){for(var a=e.body,o=a.length,s=t+1,u=0;s!==o&&!isNaN(u=a.charCodeAt(s))&&(95===u||u>=48&&u<=57||u>=65&&u<=90||u>=97&&u<=122);)++s;return new nS.WU(nk.NAME,t,s,n,r,i,a.slice(t,s))}function nU(e){return 95===e||e>=65&&e<=90||e>=97&&e<=122}function nH(e,t){return new n$(e,t).parseDocument()}var n$=function(){function e(e,t){var n=(0,nx.T)(e)?e:new nx.H(e);this._lexer=new nO(n),this._options=t}var t=e.prototype;return t.parseName=function(){var e=this.expectToken(nk.NAME);return{kind:nE.h.NAME,value:e.value,loc:this.loc(e)}},t.parseDocument=function(){var e=this._lexer.token;return{kind:nE.h.DOCUMENT,definitions:this.many(nk.SOF,this.parseDefinition,nk.EOF),loc:this.loc(e)}},t.parseDefinition=function(){if(this.peek(nk.NAME))switch(this._lexer.token.value){case"query":case"mutation":case"subscription":return this.parseOperationDefinition();case"fragment":return this.parseFragmentDefinition();case"schema":case"scalar":case"type":case"interface":case"union":case"enum":case"input":case"directive":return this.parseTypeSystemDefinition();case"extend":return this.parseTypeSystemExtension()}else if(this.peek(nk.BRACE_L))return this.parseOperationDefinition();else if(this.peekDescription())return this.parseTypeSystemDefinition();throw this.unexpected()},t.parseOperationDefinition=function(){var e,t=this._lexer.token;if(this.peek(nk.BRACE_L))return{kind:nE.h.OPERATION_DEFINITION,operation:"query",name:void 0,variableDefinitions:[],directives:[],selectionSet:this.parseSelectionSet(),loc:this.loc(t)};var n=this.parseOperationType();return this.peek(nk.NAME)&&(e=this.parseName()),{kind:nE.h.OPERATION_DEFINITION,operation:n,name:e,variableDefinitions:this.parseVariableDefinitions(),directives:this.parseDirectives(!1),selectionSet:this.parseSelectionSet(),loc:this.loc(t)}},t.parseOperationType=function(){var e=this.expectToken(nk.NAME);switch(e.value){case"query":return"query";case"mutation":return"mutation";case"subscription":return"subscription"}throw this.unexpected(e)},t.parseVariableDefinitions=function(){return this.optionalMany(nk.PAREN_L,this.parseVariableDefinition,nk.PAREN_R)},t.parseVariableDefinition=function(){var e=this._lexer.token;return{kind:nE.h.VARIABLE_DEFINITION,variable:this.parseVariable(),type:(this.expectToken(nk.COLON),this.parseTypeReference()),defaultValue:this.expectOptionalToken(nk.EQUALS)?this.parseValueLiteral(!0):void 0,directives:this.parseDirectives(!0),loc:this.loc(e)}},t.parseVariable=function(){var e=this._lexer.token;return this.expectToken(nk.DOLLAR),{kind:nE.h.VARIABLE,name:this.parseName(),loc:this.loc(e)}},t.parseSelectionSet=function(){var e=this._lexer.token;return{kind:nE.h.SELECTION_SET,selections:this.many(nk.BRACE_L,this.parseSelection,nk.BRACE_R),loc:this.loc(e)}},t.parseSelection=function(){return this.peek(nk.SPREAD)?this.parseFragment():this.parseField()},t.parseField=function(){var e,t,n=this._lexer.token,r=this.parseName();return this.expectOptionalToken(nk.COLON)?(e=r,t=this.parseName()):t=r,{kind:nE.h.FIELD,alias:e,name:t,arguments:this.parseArguments(!1),directives:this.parseDirectives(!1),selectionSet:this.peek(nk.BRACE_L)?this.parseSelectionSet():void 0,loc:this.loc(n)}},t.parseArguments=function(e){var t=e?this.parseConstArgument:this.parseArgument;return this.optionalMany(nk.PAREN_L,t,nk.PAREN_R)},t.parseArgument=function(){var e=this._lexer.token,t=this.parseName();return this.expectToken(nk.COLON),{kind:nE.h.ARGUMENT,name:t,value:this.parseValueLiteral(!1),loc:this.loc(e)}},t.parseConstArgument=function(){var e=this._lexer.token;return{kind:nE.h.ARGUMENT,name:this.parseName(),value:(this.expectToken(nk.COLON),this.parseValueLiteral(!0)),loc:this.loc(e)}},t.parseFragment=function(){var e=this._lexer.token;this.expectToken(nk.SPREAD);var t=this.expectOptionalKeyword("on");return!t&&this.peek(nk.NAME)?{kind:nE.h.FRAGMENT_SPREAD,name:this.parseFragmentName(),directives:this.parseDirectives(!1),loc:this.loc(e)}:{kind:nE.h.INLINE_FRAGMENT,typeCondition:t?this.parseNamedType():void 0,directives:this.parseDirectives(!1),selectionSet:this.parseSelectionSet(),loc:this.loc(e)}},t.parseFragmentDefinition=function(){var e,t=this._lexer.token;return(this.expectKeyword("fragment"),(null===(e=this._options)||void 0===e?void 0:e.experimentalFragmentVariables)===!0)?{kind:nE.h.FRAGMENT_DEFINITION,name:this.parseFragmentName(),variableDefinitions:this.parseVariableDefinitions(),typeCondition:(this.expectKeyword("on"),this.parseNamedType()),directives:this.parseDirectives(!1),selectionSet:this.parseSelectionSet(),loc:this.loc(t)}:{kind:nE.h.FRAGMENT_DEFINITION,name:this.parseFragmentName(),typeCondition:(this.expectKeyword("on"),this.parseNamedType()),directives:this.parseDirectives(!1),selectionSet:this.parseSelectionSet(),loc:this.loc(t)}},t.parseFragmentName=function(){if("on"===this._lexer.token.value)throw this.unexpected();return this.parseName()},t.parseValueLiteral=function(e){var t=this._lexer.token;switch(t.kind){case nk.BRACKET_L:return this.parseList(e);case nk.BRACE_L:return this.parseObject(e);case nk.INT:return this._lexer.advance(),{kind:nE.h.INT,value:t.value,loc:this.loc(t)};case nk.FLOAT:return this._lexer.advance(),{kind:nE.h.FLOAT,value:t.value,loc:this.loc(t)};case nk.STRING:case nk.BLOCK_STRING:return this.parseStringLiteral();case nk.NAME:switch(this._lexer.advance(),t.value){case"true":return{kind:nE.h.BOOLEAN,value:!0,loc:this.loc(t)};case"false":return{kind:nE.h.BOOLEAN,value:!1,loc:this.loc(t)};case"null":return{kind:nE.h.NULL,loc:this.loc(t)};default:return{kind:nE.h.ENUM,value:t.value,loc:this.loc(t)}}case nk.DOLLAR:if(!e)return this.parseVariable()}throw this.unexpected()},t.parseStringLiteral=function(){var e=this._lexer.token;return this._lexer.advance(),{kind:nE.h.STRING,value:e.value,block:e.kind===nk.BLOCK_STRING,loc:this.loc(e)}},t.parseList=function(e){var t=this,n=this._lexer.token,r=function(){return t.parseValueLiteral(e)};return{kind:nE.h.LIST,values:this.any(nk.BRACKET_L,r,nk.BRACKET_R),loc:this.loc(n)}},t.parseObject=function(e){var t=this,n=this._lexer.token,r=function(){return t.parseObjectField(e)};return{kind:nE.h.OBJECT,fields:this.any(nk.BRACE_L,r,nk.BRACE_R),loc:this.loc(n)}},t.parseObjectField=function(e){var t=this._lexer.token,n=this.parseName();return this.expectToken(nk.COLON),{kind:nE.h.OBJECT_FIELD,name:n,value:this.parseValueLiteral(e),loc:this.loc(t)}},t.parseDirectives=function(e){for(var t=[];this.peek(nk.AT);)t.push(this.parseDirective(e));return t},t.parseDirective=function(e){var t=this._lexer.token;return this.expectToken(nk.AT),{kind:nE.h.DIRECTIVE,name:this.parseName(),arguments:this.parseArguments(e),loc:this.loc(t)}},t.parseTypeReference=function(){var e,t=this._lexer.token;return(this.expectOptionalToken(nk.BRACKET_L)?(e=this.parseTypeReference(),this.expectToken(nk.BRACKET_R),e={kind:nE.h.LIST_TYPE,type:e,loc:this.loc(t)}):e=this.parseNamedType(),this.expectOptionalToken(nk.BANG))?{kind:nE.h.NON_NULL_TYPE,type:e,loc:this.loc(t)}:e},t.parseNamedType=function(){var e=this._lexer.token;return{kind:nE.h.NAMED_TYPE,name:this.parseName(),loc:this.loc(e)}},t.parseTypeSystemDefinition=function(){var e=this.peekDescription()?this._lexer.lookahead():this._lexer.token;if(e.kind===nk.NAME)switch(e.value){case"schema":return this.parseSchemaDefinition();case"scalar":return this.parseScalarTypeDefinition();case"type":return this.parseObjectTypeDefinition();case"interface":return this.parseInterfaceTypeDefinition();case"union":return this.parseUnionTypeDefinition();case"enum":return this.parseEnumTypeDefinition();case"input":return this.parseInputObjectTypeDefinition();case"directive":return this.parseDirectiveDefinition()}throw this.unexpected(e)},t.peekDescription=function(){return this.peek(nk.STRING)||this.peek(nk.BLOCK_STRING)},t.parseDescription=function(){if(this.peekDescription())return this.parseStringLiteral()},t.parseSchemaDefinition=function(){var e=this._lexer.token,t=this.parseDescription();this.expectKeyword("schema");var n=this.parseDirectives(!0),r=this.many(nk.BRACE_L,this.parseOperationTypeDefinition,nk.BRACE_R);return{kind:nE.h.SCHEMA_DEFINITION,description:t,directives:n,operationTypes:r,loc:this.loc(e)}},t.parseOperationTypeDefinition=function(){var e=this._lexer.token,t=this.parseOperationType();this.expectToken(nk.COLON);var n=this.parseNamedType();return{kind:nE.h.OPERATION_TYPE_DEFINITION,operation:t,type:n,loc:this.loc(e)}},t.parseScalarTypeDefinition=function(){var e=this._lexer.token,t=this.parseDescription();this.expectKeyword("scalar");var n=this.parseName(),r=this.parseDirectives(!0);return{kind:nE.h.SCALAR_TYPE_DEFINITION,description:t,name:n,directives:r,loc:this.loc(e)}},t.parseObjectTypeDefinition=function(){var e=this._lexer.token,t=this.parseDescription();this.expectKeyword("type");var n=this.parseName(),r=this.parseImplementsInterfaces(),i=this.parseDirectives(!0),a=this.parseFieldsDefinition();return{kind:nE.h.OBJECT_TYPE_DEFINITION,description:t,name:n,interfaces:r,directives:i,fields:a,loc:this.loc(e)}},t.parseImplementsInterfaces=function(){var e;if(!this.expectOptionalKeyword("implements"))return[];if((null===(e=this._options)||void 0===e?void 0:e.allowLegacySDLImplementsInterfaces)===!0){var t=[];this.expectOptionalToken(nk.AMP);do t.push(this.parseNamedType());while(this.expectOptionalToken(nk.AMP)||this.peek(nk.NAME))return t}return this.delimitedMany(nk.AMP,this.parseNamedType)},t.parseFieldsDefinition=function(){var e;return(null===(e=this._options)||void 0===e?void 0:e.allowLegacySDLEmptyFields)===!0&&this.peek(nk.BRACE_L)&&this._lexer.lookahead().kind===nk.BRACE_R?(this._lexer.advance(),this._lexer.advance(),[]):this.optionalMany(nk.BRACE_L,this.parseFieldDefinition,nk.BRACE_R)},t.parseFieldDefinition=function(){var e=this._lexer.token,t=this.parseDescription(),n=this.parseName(),r=this.parseArgumentDefs();this.expectToken(nk.COLON);var i=this.parseTypeReference(),a=this.parseDirectives(!0);return{kind:nE.h.FIELD_DEFINITION,description:t,name:n,arguments:r,type:i,directives:a,loc:this.loc(e)}},t.parseArgumentDefs=function(){return this.optionalMany(nk.PAREN_L,this.parseInputValueDef,nk.PAREN_R)},t.parseInputValueDef=function(){var e,t=this._lexer.token,n=this.parseDescription(),r=this.parseName();this.expectToken(nk.COLON);var i=this.parseTypeReference();this.expectOptionalToken(nk.EQUALS)&&(e=this.parseValueLiteral(!0));var a=this.parseDirectives(!0);return{kind:nE.h.INPUT_VALUE_DEFINITION,description:n,name:r,type:i,defaultValue:e,directives:a,loc:this.loc(t)}},t.parseInterfaceTypeDefinition=function(){var e=this._lexer.token,t=this.parseDescription();this.expectKeyword("interface");var n=this.parseName(),r=this.parseImplementsInterfaces(),i=this.parseDirectives(!0),a=this.parseFieldsDefinition();return{kind:nE.h.INTERFACE_TYPE_DEFINITION,description:t,name:n,interfaces:r,directives:i,fields:a,loc:this.loc(e)}},t.parseUnionTypeDefinition=function(){var e=this._lexer.token,t=this.parseDescription();this.expectKeyword("union");var n=this.parseName(),r=this.parseDirectives(!0),i=this.parseUnionMemberTypes();return{kind:nE.h.UNION_TYPE_DEFINITION,description:t,name:n,directives:r,types:i,loc:this.loc(e)}},t.parseUnionMemberTypes=function(){return this.expectOptionalToken(nk.EQUALS)?this.delimitedMany(nk.PIPE,this.parseNamedType):[]},t.parseEnumTypeDefinition=function(){var e=this._lexer.token,t=this.parseDescription();this.expectKeyword("enum");var n=this.parseName(),r=this.parseDirectives(!0),i=this.parseEnumValuesDefinition();return{kind:nE.h.ENUM_TYPE_DEFINITION,description:t,name:n,directives:r,values:i,loc:this.loc(e)}},t.parseEnumValuesDefinition=function(){return this.optionalMany(nk.BRACE_L,this.parseEnumValueDefinition,nk.BRACE_R)},t.parseEnumValueDefinition=function(){var e=this._lexer.token,t=this.parseDescription(),n=this.parseName(),r=this.parseDirectives(!0);return{kind:nE.h.ENUM_VALUE_DEFINITION,description:t,name:n,directives:r,loc:this.loc(e)}},t.parseInputObjectTypeDefinition=function(){var e=this._lexer.token,t=this.parseDescription();this.expectKeyword("input");var n=this.parseName(),r=this.parseDirectives(!0),i=this.parseInputFieldsDefinition();return{kind:nE.h.INPUT_OBJECT_TYPE_DEFINITION,description:t,name:n,directives:r,fields:i,loc:this.loc(e)}},t.parseInputFieldsDefinition=function(){return this.optionalMany(nk.BRACE_L,this.parseInputValueDef,nk.BRACE_R)},t.parseTypeSystemExtension=function(){var e=this._lexer.lookahead();if(e.kind===nk.NAME)switch(e.value){case"schema":return this.parseSchemaExtension();case"scalar":return this.parseScalarTypeExtension();case"type":return this.parseObjectTypeExtension();case"interface":return this.parseInterfaceTypeExtension();case"union":return this.parseUnionTypeExtension();case"enum":return this.parseEnumTypeExtension();case"input":return this.parseInputObjectTypeExtension()}throw this.unexpected(e)},t.parseSchemaExtension=function(){var e=this._lexer.token;this.expectKeyword("extend"),this.expectKeyword("schema");var t=this.parseDirectives(!0),n=this.optionalMany(nk.BRACE_L,this.parseOperationTypeDefinition,nk.BRACE_R);if(0===t.length&&0===n.length)throw this.unexpected();return{kind:nE.h.SCHEMA_EXTENSION,directives:t,operationTypes:n,loc:this.loc(e)}},t.parseScalarTypeExtension=function(){var e=this._lexer.token;this.expectKeyword("extend"),this.expectKeyword("scalar");var t=this.parseName(),n=this.parseDirectives(!0);if(0===n.length)throw this.unexpected();return{kind:nE.h.SCALAR_TYPE_EXTENSION,name:t,directives:n,loc:this.loc(e)}},t.parseObjectTypeExtension=function(){var e=this._lexer.token;this.expectKeyword("extend"),this.expectKeyword("type");var t=this.parseName(),n=this.parseImplementsInterfaces(),r=this.parseDirectives(!0),i=this.parseFieldsDefinition();if(0===n.length&&0===r.length&&0===i.length)throw this.unexpected();return{kind:nE.h.OBJECT_TYPE_EXTENSION,name:t,interfaces:n,directives:r,fields:i,loc:this.loc(e)}},t.parseInterfaceTypeExtension=function(){var e=this._lexer.token;this.expectKeyword("extend"),this.expectKeyword("interface");var t=this.parseName(),n=this.parseImplementsInterfaces(),r=this.parseDirectives(!0),i=this.parseFieldsDefinition();if(0===n.length&&0===r.length&&0===i.length)throw this.unexpected();return{kind:nE.h.INTERFACE_TYPE_EXTENSION,name:t,interfaces:n,directives:r,fields:i,loc:this.loc(e)}},t.parseUnionTypeExtension=function(){var e=this._lexer.token;this.expectKeyword("extend"),this.expectKeyword("union");var t=this.parseName(),n=this.parseDirectives(!0),r=this.parseUnionMemberTypes();if(0===n.length&&0===r.length)throw this.unexpected();return{kind:nE.h.UNION_TYPE_EXTENSION,name:t,directives:n,types:r,loc:this.loc(e)}},t.parseEnumTypeExtension=function(){var e=this._lexer.token;this.expectKeyword("extend"),this.expectKeyword("enum");var t=this.parseName(),n=this.parseDirectives(!0),r=this.parseEnumValuesDefinition();if(0===n.length&&0===r.length)throw this.unexpected();return{kind:nE.h.ENUM_TYPE_EXTENSION,name:t,directives:n,values:r,loc:this.loc(e)}},t.parseInputObjectTypeExtension=function(){var e=this._lexer.token;this.expectKeyword("extend"),this.expectKeyword("input");var t=this.parseName(),n=this.parseDirectives(!0),r=this.parseInputFieldsDefinition();if(0===n.length&&0===r.length)throw this.unexpected();return{kind:nE.h.INPUT_OBJECT_TYPE_EXTENSION,name:t,directives:n,fields:r,loc:this.loc(e)}},t.parseDirectiveDefinition=function(){var e=this._lexer.token,t=this.parseDescription();this.expectKeyword("directive"),this.expectToken(nk.AT);var n=this.parseName(),r=this.parseArgumentDefs(),i=this.expectOptionalKeyword("repeatable");this.expectKeyword("on");var a=this.parseDirectiveLocations();return{kind:nE.h.DIRECTIVE_DEFINITION,description:t,name:n,arguments:r,repeatable:i,locations:a,loc:this.loc(e)}},t.parseDirectiveLocations=function(){return this.delimitedMany(nk.PIPE,this.parseDirectiveLocation)},t.parseDirectiveLocation=function(){var e=this._lexer.token,t=this.parseName();if(void 0!==nT[t.value])return t;throw this.unexpected(e)},t.loc=function(e){var t;if((null===(t=this._options)||void 0===t?void 0:t.noLocation)!==!0)return new nS.Ye(e,this._lexer.lastToken,this._lexer.source)},t.peek=function(e){return this._lexer.token.kind===e},t.expectToken=function(e){var t=this._lexer.token;if(t.kind===e)return this._lexer.advance(),t;throw n_(this._lexer.source,t.start,"Expected ".concat(nG(e),", found ").concat(nz(t),"."))},t.expectOptionalToken=function(e){var t=this._lexer.token;if(t.kind===e)return this._lexer.advance(),t},t.expectKeyword=function(e){var t=this._lexer.token;if(t.kind===nk.NAME&&t.value===e)this._lexer.advance();else throw n_(this._lexer.source,t.start,'Expected "'.concat(e,'", found ').concat(nz(t),"."))},t.expectOptionalKeyword=function(e){var t=this._lexer.token;return t.kind===nk.NAME&&t.value===e&&(this._lexer.advance(),!0)},t.unexpected=function(e){var t=null!=e?e:this._lexer.token;return n_(this._lexer.source,t.start,"Unexpected ".concat(nz(t),"."))},t.any=function(e,t,n){this.expectToken(e);for(var r=[];!this.expectOptionalToken(n);)r.push(t.call(this));return r},t.optionalMany=function(e,t,n){if(this.expectOptionalToken(e)){var r=[];do r.push(t.call(this));while(!this.expectOptionalToken(n))return r}return[]},t.many=function(e,t,n){this.expectToken(e);var r=[];do r.push(t.call(this));while(!this.expectOptionalToken(n))return r},t.delimitedMany=function(e,t){this.expectOptionalToken(e);var n=[];do n.push(t.call(this));while(this.expectOptionalToken(e))return n},e}();function nz(e){var t=e.value;return nG(e.kind)+(null!=t?' "'.concat(t,'"'):"")}function nG(e){return nA(e)?'"'.concat(e,'"'):e}var nW=new Map,nK=new Map,nV=!0,nq=!1;function nZ(e){return e.replace(/[\s,]+/g," ").trim()}function nX(e){return nZ(e.source.body.substring(e.start,e.end))}function nJ(e){var t=new Set,n=[];return e.definitions.forEach(function(e){if("FragmentDefinition"===e.kind){var r=e.name.value,i=nX(e.loc),a=nK.get(r);a&&!a.has(i)?nV&&console.warn("Warning: fragment with name "+r+" already exists.\ngraphql-tag enforces all fragment names across your application to be unique; read more about\nthis in the docs: http://dev.apollodata.com/core/fragments.html#unique-names"):a||nK.set(r,a=new Set),a.add(i),t.has(i)||(t.add(i),n.push(e))}else n.push(e)}),(0,t0.pi)((0,t0.pi)({},e),{definitions:n})}function nQ(e){var t=new Set(e.definitions);t.forEach(function(e){e.loc&&delete e.loc,Object.keys(e).forEach(function(n){var r=e[n];r&&"object"==typeof r&&t.add(r)})});var n=e.loc;return n&&(delete n.startToken,delete n.endToken),e}function n1(e){var t=nZ(e);if(!nW.has(t)){var n=nH(e,{experimentalFragmentVariables:nq,allowLegacyFragmentVariables:nq});if(!n||"Document"!==n.kind)throw Error("Not a valid GraphQL document.");nW.set(t,nQ(nJ(n)))}return nW.get(t)}function n0(e){for(var t=[],n=1;n, or pass an ApolloClient instance in via options.'):(0,n9.kG)(!!n,32),n}var rp=n(10542),rb=n(53712),rm=n(21436),rg=Object.prototype.hasOwnProperty;function rv(e,t){return void 0===t&&(t=Object.create(null)),ry(rh(t.client),e).useQuery(t)}function ry(e,t){var n=(0,l.useRef)();n.current&&e===n.current.client&&t===n.current.query||(n.current=new rw(e,t,n.current));var r=n.current,i=(0,l.useState)(0),a=(i[0],i[1]);return r.forceUpdate=function(){a(function(e){return e+1})},r}var rw=function(){function e(e,t,n){this.client=e,this.query=t,this.ssrDisabledResult=(0,rp.J)({loading:!0,data:void 0,error:void 0,networkStatus:ru.I.loading}),this.skipStandbyResult=(0,rp.J)({loading:!1,data:void 0,error:void 0,networkStatus:ru.I.ready}),this.toQueryResultCache=new(n7.mr?WeakMap:Map),rd(t,r.Query);var i=n&&n.result,a=i&&i.data;a&&(this.previousData=a)}return e.prototype.forceUpdate=function(){__DEV__&&n9.kG.warn("Calling default no-op implementation of InternalState#forceUpdate")},e.prototype.executeQuery=function(e){var t,n=this;e.query&&Object.assign(this,{query:e.query}),this.watchQueryOptions=this.createWatchQueryOptions(this.queryHookOptions=e);var r=this.observable.reobserveAsConcast(this.getObsQueryOptions());return this.previousData=(null===(t=this.result)||void 0===t?void 0:t.data)||this.previousData,this.result=void 0,this.forceUpdate(),new Promise(function(e){var t;r.subscribe({next:function(e){t=e},error:function(){e(n.toQueryResult(n.observable.getCurrentResult()))},complete:function(){e(n.toQueryResult(t))}})})},e.prototype.useQuery=function(e){var t=this;this.renderPromises=(0,l.useContext)((0,ro.K)()).renderPromises,this.useOptions(e);var n=this.useObservableQuery(),r=rt((0,l.useCallback)(function(){if(t.renderPromises)return function(){};var e=function(){var e=t.result,r=n.getCurrentResult();!(e&&e.loading===r.loading&&e.networkStatus===r.networkStatus&&(0,ri.D)(e.data,r.data))&&t.setResult(r)},r=function(a){var o=n.last;i.unsubscribe();try{n.resetLastResults(),i=n.subscribe(e,r)}finally{n.last=o}if(!rg.call(a,"graphQLErrors"))throw a;var s=t.result;(!s||s&&s.loading||!(0,ri.D)(a,s.error))&&t.setResult({data:s&&s.data,error:a,loading:!1,networkStatus:ru.I.error})},i=n.subscribe(e,r);return function(){return setTimeout(function(){return i.unsubscribe()})}},[n,this.renderPromises,this.client.disableNetworkFetches,]),function(){return t.getCurrentResult()},function(){return t.getCurrentResult()});return this.unsafeHandlePartialRefetch(r),this.toQueryResult(r)},e.prototype.useOptions=function(t){var n,r=this.createWatchQueryOptions(this.queryHookOptions=t),i=this.watchQueryOptions;!(0,ri.D)(r,i)&&(this.watchQueryOptions=r,i&&this.observable&&(this.observable.reobserve(this.getObsQueryOptions()),this.previousData=(null===(n=this.result)||void 0===n?void 0:n.data)||this.previousData,this.result=void 0)),this.onCompleted=t.onCompleted||e.prototype.onCompleted,this.onError=t.onError||e.prototype.onError,(this.renderPromises||this.client.disableNetworkFetches)&&!1===this.queryHookOptions.ssr&&!this.queryHookOptions.skip?this.result=this.ssrDisabledResult:this.queryHookOptions.skip||"standby"===this.watchQueryOptions.fetchPolicy?this.result=this.skipStandbyResult:(this.result===this.ssrDisabledResult||this.result===this.skipStandbyResult)&&(this.result=void 0)},e.prototype.getObsQueryOptions=function(){var e=[],t=this.client.defaultOptions.watchQuery;return t&&e.push(t),this.queryHookOptions.defaultOptions&&e.push(this.queryHookOptions.defaultOptions),e.push((0,rb.o)(this.observable&&this.observable.options,this.watchQueryOptions)),e.reduce(ra.J)},e.prototype.createWatchQueryOptions=function(e){void 0===e&&(e={});var t,n=e.skip,r=Object.assign((e.ssr,e.onCompleted,e.onError,e.defaultOptions,(0,t0._T)(e,["skip","ssr","onCompleted","onError","defaultOptions"])),{query:this.query});if(this.renderPromises&&("network-only"===r.fetchPolicy||"cache-and-network"===r.fetchPolicy)&&(r.fetchPolicy="cache-first"),r.variables||(r.variables={}),n){var i=r.fetchPolicy,a=void 0===i?this.getDefaultFetchPolicy():i,o=r.initialFetchPolicy;Object.assign(r,{initialFetchPolicy:void 0===o?a:o,fetchPolicy:"standby"})}else r.fetchPolicy||(r.fetchPolicy=(null===(t=this.observable)||void 0===t?void 0:t.options.initialFetchPolicy)||this.getDefaultFetchPolicy());return r},e.prototype.getDefaultFetchPolicy=function(){var e,t;return(null===(e=this.queryHookOptions.defaultOptions)||void 0===e?void 0:e.fetchPolicy)||(null===(t=this.client.defaultOptions.watchQuery)||void 0===t?void 0:t.fetchPolicy)||"cache-first"},e.prototype.onCompleted=function(e){},e.prototype.onError=function(e){},e.prototype.useObservableQuery=function(){var e=this.observable=this.renderPromises&&this.renderPromises.getSSRObservable(this.watchQueryOptions)||this.observable||this.client.watchQuery(this.getObsQueryOptions());this.obsQueryFields=(0,l.useMemo)(function(){return{refetch:e.refetch.bind(e),reobserve:e.reobserve.bind(e),fetchMore:e.fetchMore.bind(e),updateQuery:e.updateQuery.bind(e),startPolling:e.startPolling.bind(e),stopPolling:e.stopPolling.bind(e),subscribeToMore:e.subscribeToMore.bind(e)}},[e]);var t=!(!1===this.queryHookOptions.ssr||this.queryHookOptions.skip);return this.renderPromises&&t&&(this.renderPromises.registerSSRObservable(e),e.getCurrentResult().loading&&this.renderPromises.addObservableQueryPromise(e)),e},e.prototype.setResult=function(e){var t=this.result;t&&t.data&&(this.previousData=t.data),this.result=e,this.forceUpdate(),this.handleErrorOrCompleted(e)},e.prototype.handleErrorOrCompleted=function(e){var t=this;if(!e.loading){var n=this.toApolloError(e);Promise.resolve().then(function(){n?t.onError(n):e.data&&t.onCompleted(e.data)}).catch(function(e){__DEV__&&n9.kG.warn(e)})}},e.prototype.toApolloError=function(e){return(0,rm.O)(e.errors)?new rs.cA({graphQLErrors:e.errors}):e.error},e.prototype.getCurrentResult=function(){return this.result||this.handleErrorOrCompleted(this.result=this.observable.getCurrentResult()),this.result},e.prototype.toQueryResult=function(e){var t=this.toQueryResultCache.get(e);if(t)return t;var n=e.data,r=(e.partial,(0,t0._T)(e,["data","partial"]));return this.toQueryResultCache.set(e,t=(0,t0.pi)((0,t0.pi)((0,t0.pi)({data:n},r),this.obsQueryFields),{client:this.client,observable:this.observable,variables:this.observable.variables,called:!this.queryHookOptions.skip,previousData:this.previousData})),!t.error&&(0,rm.O)(e.errors)&&(t.error=new rs.cA({graphQLErrors:e.errors})),t},e.prototype.unsafeHandlePartialRefetch=function(e){e.partial&&this.queryHookOptions.partialRefetch&&!e.loading&&(!e.data||0===Object.keys(e.data).length)&&"cache-only"!==this.observable.options.fetchPolicy&&(Object.assign(e,{loading:!0,networkStatus:ru.I.refetch}),this.observable.refetch())},e}();function r_(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&void 0!==arguments[0]?arguments[0]:{};return rv(iH,e)},iz=function(){var e=ij(),t=parseInt(e.get("page")||"1",10),n=parseInt(e.get("per")||"50",10),r=i$({variables:{offset:(t-1)*n,limit:n},fetchPolicy:"network-only"}),i=r.data,a=r.loading,o=r.error;return a?l.createElement(iR,null):o?l.createElement(iD,{error:o}):i?l.createElement(iI,{chains:i.chains.results,page:t,pageSize:n,total:i.chains.metadata.total}):null},iG=n(67932),iW=n(8126),iK="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e};function iV(e){if(iq())return Intl.DateTimeFormat.supportedLocalesOf(e)[0]}function iq(){return("undefined"==typeof Intl?"undefined":iK(Intl))==="object"&&"function"==typeof Intl.DateTimeFormat}var iZ="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},iX=function(){function e(e,t){for(var n=0;n=i.length)break;s=i[o++]}else{if((o=i.next()).done)break;s=o.value}var s,u=s;if((void 0===e?"undefined":iZ(e))!=="object")return;e=e[u]}return e}},{key:"put",value:function(){for(var e=arguments.length,t=Array(e),n=0;n=o.length)break;c=o[u++]}else{if((u=o.next()).done)break;c=u.value}var c,l=c;"object"!==iZ(a[l])&&(a[l]={}),a=a[l]}return a[i]=r}}]),e}();let i1=iQ;var i0=new i1;function i2(e,t){if(!iq())return function(e){return e.toString()};var n=i4(e),r=JSON.stringify(t),i=i0.get(String(n),r)||i0.put(String(n),r,new Intl.DateTimeFormat(n,t));return function(e){return i.format(e)}}var i3={};function i4(e){var t=e.toString();return i3[t]?i3[t]:i3[t]=iV(e)}var i6="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e};function i5(e){return i8(e)?e:new Date(e)}function i8(e){return e instanceof Date||i9(e)}function i9(e){return(void 0===e?"undefined":i6(e))==="object"&&"function"==typeof e.getTime}var i7=n(54087),ae=n.n(i7);function at(e,t){if(0===e.length)return 0;for(var n=0,r=e.length-1,i=void 0;n<=r;){var a=t(e[i=Math.floor((r+n)/2)]);if(0===a)return i;if(a<0){if((n=i+1)>r)return n}else if((r=i-1)=t.nextUpdateTime)aa(t,this.instances);else break}},scheduleNextTick:function(){var e=this;this.scheduledTick=ae()(function(){e.tick(),e.scheduleNextTick()})},start:function(){this.scheduleNextTick()},stop:function(){ae().cancel(this.scheduledTick)}};function ai(e){var t=an(e.getNextValue(),2),n=t[0],r=t[1];e.setValue(n),e.nextUpdateTime=r}function aa(e,t){ai(e),as(t,e),ao(t,e)}function ao(e,t){var n=au(e,t);e.splice(n,0,t)}function as(e,t){var n=e.indexOf(t);e.splice(n,1)}function au(e,t){var n=t.nextUpdateTime;return at(e,function(e){return e.nextUpdateTime===n?0:e.nextUpdateTime>n?1:-1})}var ac=(0,ec.oneOfType)([(0,ec.shape)({minTime:ec.number,formatAs:ec.string.isRequired}),(0,ec.shape)({test:ec.func,formatAs:ec.string.isRequired}),(0,ec.shape)({minTime:ec.number,format:ec.func.isRequired}),(0,ec.shape)({test:ec.func,format:ec.func.isRequired})]),al=(0,ec.oneOfType)([ec.string,(0,ec.shape)({steps:(0,ec.arrayOf)(ac).isRequired,labels:(0,ec.oneOfType)([ec.string,(0,ec.arrayOf)(ec.string)]).isRequired,round:ec.string})]),af=Object.assign||function(e){for(var t=1;t=0)&&Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]);return n}function ap(e){var t=e.date,n=e.future,r=e.timeStyle,i=e.round,a=e.minTimeLeft,o=e.tooltip,s=e.component,u=e.container,c=e.wrapperComponent,f=e.wrapperProps,d=e.locale,h=e.locales,p=e.formatVerboseDate,b=e.verboseDateFormat,m=e.updateInterval,g=e.tick,v=ah(e,["date","future","timeStyle","round","minTimeLeft","tooltip","component","container","wrapperComponent","wrapperProps","locale","locales","formatVerboseDate","verboseDateFormat","updateInterval","tick"]),y=(0,l.useMemo)(function(){return d&&(h=[d]),h.concat(iW.Z.getDefaultLocale())},[d,h]),w=(0,l.useMemo)(function(){return new iW.Z(y)},[y]);t=(0,l.useMemo)(function(){return i5(t)},[t]);var _=(0,l.useCallback)(function(){var e=Date.now(),o=void 0;if(n&&e>=t.getTime()&&(e=t.getTime(),o=!0),void 0!==a){var s=t.getTime()-1e3*a;e>s&&(e=s,o=!0)}var u=w.format(t,r,{getTimeToNextUpdate:!0,now:e,future:n,round:i}),c=ad(u,2),l=c[0],f=c[1];return f=o?ag:m||f||6e4,[l,e+f]},[t,n,r,m,i,a,w]),E=(0,l.useRef)();E.current=_;var S=(0,l.useMemo)(_,[]),k=ad(S,2),x=k[0],T=k[1],M=(0,l.useState)(x),O=ad(M,2),A=O[0],L=O[1],C=ad((0,l.useState)(),2),I=C[0],D=C[1],N=(0,l.useRef)();(0,l.useEffect)(function(){if(g)return N.current=ar.add({getNextValue:function(){return E.current()},setValue:L,nextUpdateTime:T}),function(){return N.current.stop()}},[g]),(0,l.useEffect)(function(){if(N.current)N.current.forceUpdate();else{var e=_(),t=ad(e,1)[0];L(t)}},[_]),(0,l.useEffect)(function(){D(!0)},[]);var P=(0,l.useMemo)(function(){if("undefined"!=typeof window)return i2(y,b)},[y,b]),R=(0,l.useMemo)(function(){if("undefined"!=typeof window)return p?p(t):P(t)},[t,p,P]),j=l.createElement(s,af({date:t,verboseDate:I?R:void 0,tooltip:o},v),A),F=c||u;return F?l.createElement(F,af({},f,{verboseDate:I?R:void 0}),j):j}ap.propTypes={date:el().oneOfType([el().instanceOf(Date),el().number]).isRequired,locale:el().string,locales:el().arrayOf(el().string),future:el().bool,timeStyle:al,round:el().string,minTimeLeft:el().number,component:el().elementType.isRequired,tooltip:el().bool.isRequired,formatVerboseDate:el().func,verboseDateFormat:el().object,updateInterval:el().oneOfType([el().number,el().arrayOf(el().shape({threshold:el().number,interval:el().number.isRequired}))]),tick:el().bool,wrapperComponent:el().func,wrapperProps:el().object},ap.defaultProps={locales:[],component:av,tooltip:!0,verboseDateFormat:{weekday:"long",day:"numeric",month:"long",year:"numeric",hour:"numeric",minute:"2-digit",second:"2-digit"},tick:!0},ap=l.memo(ap);let ab=ap;var am,ag=31536e9;function av(e){var t=e.date,n=e.verboseDate,r=e.tooltip,i=e.children,a=ah(e,["date","verboseDate","tooltip","children"]),o=(0,l.useMemo)(function(){return t.toISOString()},[t]);return l.createElement("time",af({},a,{dateTime:o,title:r?n:void 0}),i)}av.propTypes={date:el().instanceOf(Date).isRequired,verboseDate:el().string,tooltip:el().bool.isRequired,children:el().string.isRequired};var ay=n(30381),aw=n.n(ay),a_=n(31657);function aE(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function aS(e){for(var t=1;te.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0?new rs.cA({graphQLErrors:i}):void 0;if(u===s.current.mutationId&&!c.ignoreResults){var f={called:!0,loading:!1,data:r,error:l,client:a};s.current.isMounted&&!(0,ri.D)(s.current.result,f)&&o(s.current.result=f)}var d=e.onCompleted||(null===(n=s.current.options)||void 0===n?void 0:n.onCompleted);return null==d||d(t.data,c),t}).catch(function(t){if(u===s.current.mutationId&&s.current.isMounted){var n,r={loading:!1,error:t,data:void 0,called:!0,client:a};(0,ri.D)(s.current.result,r)||o(s.current.result=r)}var i=e.onError||(null===(n=s.current.options)||void 0===n?void 0:n.onError);if(i)return i(t,c),{data:void 0,errors:t};throw t})},[]),c=(0,l.useCallback)(function(){s.current.isMounted&&o({called:!1,loading:!1,client:n})},[]);return(0,l.useEffect)(function(){return s.current.isMounted=!0,function(){s.current.isMounted=!1}},[]),[u,(0,t0.pi)({reset:c},a)]}var os=n(59067),ou=n(28428),oc=n(11186),ol=n(78513);function of(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}var od=function(e){return(0,b.createStyles)({paper:{display:"flex",margin:"".concat(2.5*e.spacing.unit,"px 0"),padding:"".concat(3*e.spacing.unit,"px ").concat(3.5*e.spacing.unit,"px")},content:{flex:1,width:"100%"},actions:of({marginTop:-(1.5*e.spacing.unit),marginLeft:-(4*e.spacing.unit)},e.breakpoints.up("sm"),{marginLeft:0,marginRight:-(1.5*e.spacing.unit)}),itemBlock:{border:"1px solid rgba(224, 224, 224, 1)",borderRadius:e.shape.borderRadius,padding:2*e.spacing.unit,marginTop:e.spacing.unit},itemBlockText:{overflowWrap:"anywhere"}})},oh=(0,b.withStyles)(od)(function(e){var t=e.actions,n=e.children,r=e.classes;return l.createElement(ii.default,{className:r.paper},l.createElement("div",{className:r.content},n),t&&l.createElement("div",{className:r.actions},t))}),op=function(e){var t=e.title;return l.createElement(x.default,{variant:"subtitle2",gutterBottom:!0},t)},ob=function(e){var t=e.children,n=e.value;return l.createElement(x.default,{variant:"body1",noWrap:!0},t||n)},om=(0,b.withStyles)(od)(function(e){var t=e.children,n=e.classes,r=e.value;return l.createElement("div",{className:n.itemBlock},l.createElement(x.default,{variant:"body1",className:n.itemBlockText},t||r))});function og(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]-1}let sq=sV;function sZ(e,t){var n=this.__data__,r=sH(n,e);return r<0?(++this.size,n.push([e,t])):n[r][1]=t,this}let sX=sZ;function sJ(e){var t=-1,n=null==e?0:e.length;for(this.clear();++t-1&&e%1==0&&e-1&&e%1==0&&e<=cC}let cD=cI;var cN="[object Arguments]",cP="[object Array]",cR="[object Boolean]",cj="[object Date]",cF="[object Error]",cY="[object Function]",cB="[object Map]",cU="[object Number]",cH="[object Object]",c$="[object RegExp]",cz="[object Set]",cG="[object String]",cW="[object WeakMap]",cK="[object ArrayBuffer]",cV="[object DataView]",cq="[object Float64Array]",cZ="[object Int8Array]",cX="[object Int16Array]",cJ="[object Int32Array]",cQ="[object Uint8Array]",c1="[object Uint8ClampedArray]",c0="[object Uint16Array]",c2="[object Uint32Array]",c3={};function c4(e){return eD(e)&&cD(e.length)&&!!c3[eC(e)]}c3["[object Float32Array]"]=c3[cq]=c3[cZ]=c3[cX]=c3[cJ]=c3[cQ]=c3[c1]=c3[c0]=c3[c2]=!0,c3[cN]=c3[cP]=c3[cK]=c3[cR]=c3[cV]=c3[cj]=c3[cF]=c3[cY]=c3[cB]=c3[cU]=c3[cH]=c3[c$]=c3[cz]=c3[cG]=c3[cW]=!1;let c6=c4;function c5(e){return function(t){return e(t)}}let c8=c5;var c9=n(79730),c7=c9.Z&&c9.Z.isTypedArray,le=c7?c8(c7):c6;let lt=le;var ln=Object.prototype.hasOwnProperty;function lr(e,t){var n=cx(e),r=!n&&cS(e),i=!n&&!r&&(0,cT.Z)(e),a=!n&&!r&&!i&<(e),o=n||r||i||a,s=o?cb(e.length,String):[],u=s.length;for(var c in e)(t||ln.call(e,c))&&!(o&&("length"==c||i&&("offset"==c||"parent"==c)||a&&("buffer"==c||"byteLength"==c||"byteOffset"==c)||cL(c,u)))&&s.push(c);return s}let li=lr;var la=Object.prototype;function lo(e){var t=e&&e.constructor;return e===("function"==typeof t&&t.prototype||la)}let ls=lo;var lu=sT(Object.keys,Object);let lc=lu;var ll=Object.prototype.hasOwnProperty;function lf(e){if(!ls(e))return lc(e);var t=[];for(var n in Object(e))ll.call(e,n)&&"constructor"!=n&&t.push(n);return t}let ld=lf;function lh(e){return null!=e&&cD(e.length)&&!ur(e)}let lp=lh;function lb(e){return lp(e)?li(e):ld(e)}let lm=lb;function lg(e,t){return e&&ch(t,lm(t),e)}let lv=lg;function ly(e){var t=[];if(null!=e)for(var n in Object(e))t.push(n);return t}let lw=ly;var l_=Object.prototype.hasOwnProperty;function lE(e){if(!ed(e))return lw(e);var t=ls(e),n=[];for(var r in e)"constructor"==r&&(t||!l_.call(e,r))||n.push(r);return n}let lS=lE;function lk(e){return lp(e)?li(e,!0):lS(e)}let lx=lk;function lT(e,t){return e&&ch(t,lx(t),e)}let lM=lT;var lO=n(42896);function lA(e,t){var n=-1,r=e.length;for(t||(t=Array(r));++n=0||(i[n]=e[n]);return i}function hu(e){if(void 0===e)throw ReferenceError("this hasn't been initialised - super() hasn't been called");return e}var hc=function(e){return Array.isArray(e)&&0===e.length},hl=function(e){return"function"==typeof e},hf=function(e){return null!==e&&"object"==typeof e},hd=function(e){return String(Math.floor(Number(e)))===e},hh=function(e){return"[object String]"===Object.prototype.toString.call(e)},hp=function(e){return 0===l.Children.count(e)},hb=function(e){return hf(e)&&hl(e.then)};function hm(e,t,n,r){void 0===r&&(r=0);for(var i=d8(t);e&&r=0?[]:{}}}return(0===a?e:i)[o[a]]===n?e:(void 0===n?delete i[o[a]]:i[o[a]]=n,0===a&&void 0===n&&delete r[o[a]],r)}function hv(e,t,n,r){void 0===n&&(n=new WeakMap),void 0===r&&(r={});for(var i=0,a=Object.keys(e);i0?t.map(function(t){return x(t,hm(e,t))}):[Promise.resolve("DO_NOT_DELETE_YOU_WILL_BE_FIRED")]).then(function(e){return e.reduce(function(e,n,r){return"DO_NOT_DELETE_YOU_WILL_BE_FIRED"===n||n&&(e=hg(e,t[r],n)),e},{})})},[x]),M=(0,l.useCallback)(function(e){return Promise.all([T(e),h.validationSchema?k(e):{},h.validate?S(e):{}]).then(function(e){var t=e[0],n=e[1],r=e[2];return sk.all([t,n,r],{arrayMerge:hL})})},[h.validate,h.validationSchema,T,S,k]),O=hN(function(e){return void 0===e&&(e=_.values),E({type:"SET_ISVALIDATING",payload:!0}),M(e).then(function(e){return v.current&&(E({type:"SET_ISVALIDATING",payload:!1}),sd()(_.errors,e)||E({type:"SET_ERRORS",payload:e})),e})});(0,l.useEffect)(function(){o&&!0===v.current&&sd()(p.current,h.initialValues)&&O(p.current)},[o,O]);var A=(0,l.useCallback)(function(e){var t=e&&e.values?e.values:p.current,n=e&&e.errors?e.errors:b.current?b.current:h.initialErrors||{},r=e&&e.touched?e.touched:m.current?m.current:h.initialTouched||{},i=e&&e.status?e.status:g.current?g.current:h.initialStatus;p.current=t,b.current=n,m.current=r,g.current=i;var a=function(){E({type:"RESET_FORM",payload:{isSubmitting:!!e&&!!e.isSubmitting,errors:n,touched:r,status:i,values:t,isValidating:!!e&&!!e.isValidating,submitCount:e&&e.submitCount&&"number"==typeof e.submitCount?e.submitCount:0}})};if(h.onReset){var o=h.onReset(_.values,V);hb(o)?o.then(a):a()}else a()},[h.initialErrors,h.initialStatus,h.initialTouched]);(0,l.useEffect)(function(){!0===v.current&&!sd()(p.current,h.initialValues)&&(c&&(p.current=h.initialValues,A()),o&&O(p.current))},[c,h.initialValues,A,o,O]),(0,l.useEffect)(function(){c&&!0===v.current&&!sd()(b.current,h.initialErrors)&&(b.current=h.initialErrors||hS,E({type:"SET_ERRORS",payload:h.initialErrors||hS}))},[c,h.initialErrors]),(0,l.useEffect)(function(){c&&!0===v.current&&!sd()(m.current,h.initialTouched)&&(m.current=h.initialTouched||hk,E({type:"SET_TOUCHED",payload:h.initialTouched||hk}))},[c,h.initialTouched]),(0,l.useEffect)(function(){c&&!0===v.current&&!sd()(g.current,h.initialStatus)&&(g.current=h.initialStatus,E({type:"SET_STATUS",payload:h.initialStatus}))},[c,h.initialStatus,h.initialTouched]);var L=hN(function(e){if(y.current[e]&&hl(y.current[e].validate)){var t=hm(_.values,e),n=y.current[e].validate(t);return hb(n)?(E({type:"SET_ISVALIDATING",payload:!0}),n.then(function(e){return e}).then(function(t){E({type:"SET_FIELD_ERROR",payload:{field:e,value:t}}),E({type:"SET_ISVALIDATING",payload:!1})})):(E({type:"SET_FIELD_ERROR",payload:{field:e,value:n}}),Promise.resolve(n))}return h.validationSchema?(E({type:"SET_ISVALIDATING",payload:!0}),k(_.values,e).then(function(e){return e}).then(function(t){E({type:"SET_FIELD_ERROR",payload:{field:e,value:t[e]}}),E({type:"SET_ISVALIDATING",payload:!1})})):Promise.resolve()}),C=(0,l.useCallback)(function(e,t){var n=t.validate;y.current[e]={validate:n}},[]),I=(0,l.useCallback)(function(e){delete y.current[e]},[]),D=hN(function(e,t){return E({type:"SET_TOUCHED",payload:e}),(void 0===t?i:t)?O(_.values):Promise.resolve()}),N=(0,l.useCallback)(function(e){E({type:"SET_ERRORS",payload:e})},[]),P=hN(function(e,t){var r=hl(e)?e(_.values):e;return E({type:"SET_VALUES",payload:r}),(void 0===t?n:t)?O(r):Promise.resolve()}),R=(0,l.useCallback)(function(e,t){E({type:"SET_FIELD_ERROR",payload:{field:e,value:t}})},[]),j=hN(function(e,t,r){return E({type:"SET_FIELD_VALUE",payload:{field:e,value:t}}),(void 0===r?n:r)?O(hg(_.values,e,t)):Promise.resolve()}),F=(0,l.useCallback)(function(e,t){var n,r=t,i=e;if(!hh(e)){e.persist&&e.persist();var a=e.target?e.target:e.currentTarget,o=a.type,s=a.name,u=a.id,c=a.value,l=a.checked,f=(a.outerHTML,a.options),d=a.multiple;r=t||s||u,i=/number|range/.test(o)?(n=parseFloat(c),isNaN(n)?"":n):/checkbox/.test(o)?hI(hm(_.values,r),l,c):d?hC(f):c}r&&j(r,i)},[j,_.values]),Y=hN(function(e){if(hh(e))return function(t){return F(t,e)};F(e)}),B=hN(function(e,t,n){return void 0===t&&(t=!0),E({type:"SET_FIELD_TOUCHED",payload:{field:e,value:t}}),(void 0===n?i:n)?O(_.values):Promise.resolve()}),U=(0,l.useCallback)(function(e,t){e.persist&&e.persist();var n,r=e.target,i=r.name,a=r.id;r.outerHTML,B(t||i||a,!0)},[B]),H=hN(function(e){if(hh(e))return function(t){return U(t,e)};U(e)}),$=(0,l.useCallback)(function(e){hl(e)?E({type:"SET_FORMIK_STATE",payload:e}):E({type:"SET_FORMIK_STATE",payload:function(){return e}})},[]),z=(0,l.useCallback)(function(e){E({type:"SET_STATUS",payload:e})},[]),G=(0,l.useCallback)(function(e){E({type:"SET_ISSUBMITTING",payload:e})},[]),W=hN(function(){return E({type:"SUBMIT_ATTEMPT"}),O().then(function(e){var t,n=e instanceof Error;if(!n&&0===Object.keys(e).length){try{if(void 0===(t=q()))return}catch(r){throw r}return Promise.resolve(t).then(function(e){return v.current&&E({type:"SUBMIT_SUCCESS"}),e}).catch(function(e){if(v.current)throw E({type:"SUBMIT_FAILURE"}),e})}if(v.current&&(E({type:"SUBMIT_FAILURE"}),n))throw e})}),K=hN(function(e){e&&e.preventDefault&&hl(e.preventDefault)&&e.preventDefault(),e&&e.stopPropagation&&hl(e.stopPropagation)&&e.stopPropagation(),W().catch(function(e){console.warn("Warning: An unhandled error was caught from submitForm()",e)})}),V={resetForm:A,validateForm:O,validateField:L,setErrors:N,setFieldError:R,setFieldTouched:B,setFieldValue:j,setStatus:z,setSubmitting:G,setTouched:D,setValues:P,setFormikState:$,submitForm:W},q=hN(function(){return f(_.values,V)}),Z=hN(function(e){e&&e.preventDefault&&hl(e.preventDefault)&&e.preventDefault(),e&&e.stopPropagation&&hl(e.stopPropagation)&&e.stopPropagation(),A()}),X=(0,l.useCallback)(function(e){return{value:hm(_.values,e),error:hm(_.errors,e),touched:!!hm(_.touched,e),initialValue:hm(p.current,e),initialTouched:!!hm(m.current,e),initialError:hm(b.current,e)}},[_.errors,_.touched,_.values]),J=(0,l.useCallback)(function(e){return{setValue:function(t,n){return j(e,t,n)},setTouched:function(t,n){return B(e,t,n)},setError:function(t){return R(e,t)}}},[j,B,R]),Q=(0,l.useCallback)(function(e){var t=hf(e),n=t?e.name:e,r=hm(_.values,n),i={name:n,value:r,onChange:Y,onBlur:H};if(t){var a=e.type,o=e.value,s=e.as,u=e.multiple;"checkbox"===a?void 0===o?i.checked=!!r:(i.checked=!!(Array.isArray(r)&&~r.indexOf(o)),i.value=o):"radio"===a?(i.checked=r===o,i.value=o):"select"===s&&u&&(i.value=i.value||[],i.multiple=!0)}return i},[H,Y,_.values]),ee=(0,l.useMemo)(function(){return!sd()(p.current,_.values)},[p.current,_.values]),et=(0,l.useMemo)(function(){return void 0!==s?ee?_.errors&&0===Object.keys(_.errors).length:!1!==s&&hl(s)?s(h):s:_.errors&&0===Object.keys(_.errors).length},[s,ee,_.errors,h]);return ha({},_,{initialValues:p.current,initialErrors:b.current,initialTouched:m.current,initialStatus:g.current,handleBlur:H,handleChange:Y,handleReset:Z,handleSubmit:K,resetForm:A,setErrors:N,setFormikState:$,setFieldTouched:B,setFieldValue:j,setFieldError:R,setStatus:z,setSubmitting:G,setTouched:D,setValues:P,submitForm:W,validateForm:O,validateField:L,isValid:et,dirty:ee,unregisterField:I,registerField:C,getFieldProps:Q,getFieldMeta:X,getFieldHelpers:J,validateOnBlur:i,validateOnChange:n,validateOnMount:o})}function hT(e){var t=hx(e),n=e.component,r=e.children,i=e.render,a=e.innerRef;return(0,l.useImperativeHandle)(a,function(){return t}),(0,l.createElement)(hw,{value:t},n?(0,l.createElement)(n,t):i?i(t):r?hl(r)?r(t):hp(r)?null:l.Children.only(r):null)}function hM(e){var t={};if(e.inner){if(0===e.inner.length)return hg(t,e.path,e.message);for(var n=e.inner,r=Array.isArray(n),i=0,n=r?n:n[Symbol.iterator]();;){if(r){if(i>=n.length)break;a=n[i++]}else{if((i=n.next()).done)break;a=i.value}var a,o=a;hm(t,o.path)||(t=hg(t,o.path,o.message))}}return t}function hO(e,t,n,r){void 0===n&&(n=!1),void 0===r&&(r={});var i=hA(e);return t[n?"validateSync":"validate"](i,{abortEarly:!1,context:r})}function hA(e){var t=Array.isArray(e)?[]:{};for(var n in e)if(Object.prototype.hasOwnProperty.call(e,n)){var r=String(n);!0===Array.isArray(e[r])?t[r]=e[r].map(function(e){return!0===Array.isArray(e)||sR(e)?hA(e):""!==e?e:void 0}):sR(e[r])?t[r]=hA(e[r]):t[r]=""!==e[r]?e[r]:void 0}return t}function hL(e,t,n){var r=e.slice();return t.forEach(function(t,i){if(void 0===r[i]){var a=!1!==n.clone&&n.isMergeableObject(t);r[i]=a?sk(Array.isArray(t)?[]:{},t,n):t}else n.isMergeableObject(t)?r[i]=sk(e[i],t,n):-1===e.indexOf(t)&&r.push(t)}),r}function hC(e){return Array.from(e).filter(function(e){return e.selected}).map(function(e){return e.value})}function hI(e,t,n){if("boolean"==typeof e)return Boolean(t);var r=[],i=!1,a=-1;if(Array.isArray(e))r=e,i=(a=e.indexOf(n))>=0;else if(!n||"true"==n||"false"==n)return Boolean(t);return t&&n&&!i?r.concat(n):i?r.slice(0,a).concat(r.slice(a+1)):r}var hD="undefined"!=typeof window&&void 0!==window.document&&void 0!==window.document.createElement?l.useLayoutEffect:l.useEffect;function hN(e){var t=(0,l.useRef)(e);return hD(function(){t.current=e}),(0,l.useCallback)(function(){for(var e=arguments.length,n=Array(e),r=0;re?t:e},0);return Array.from(ha({},e,{length:t+1}))};(function(e){function t(t){var n;return(n=e.call(this,t)||this).updateArrayField=function(e,t,r){var i=n.props,a=i.name;(0,i.formik.setFormikState)(function(n){var i="function"==typeof r?r:e,o="function"==typeof t?t:e,s=hg(n.values,a,e(hm(n.values,a))),u=r?i(hm(n.errors,a)):void 0,c=t?o(hm(n.touched,a)):void 0;return hc(u)&&(u=void 0),hc(c)&&(c=void 0),ha({},n,{values:s,errors:r?hg(n.errors,a,u):n.errors,touched:t?hg(n.touched,a,c):n.touched})})},n.push=function(e){return n.updateArrayField(function(t){return[].concat(hU(t),[hi(e)])},!1,!1)},n.handlePush=function(e){return function(){return n.push(e)}},n.swap=function(e,t){return n.updateArrayField(function(n){return hF(n,e,t)},!0,!0)},n.handleSwap=function(e,t){return function(){return n.swap(e,t)}},n.move=function(e,t){return n.updateArrayField(function(n){return hj(n,e,t)},!0,!0)},n.handleMove=function(e,t){return function(){return n.move(e,t)}},n.insert=function(e,t){return n.updateArrayField(function(n){return hY(n,e,t)},function(t){return hY(t,e,null)},function(t){return hY(t,e,null)})},n.handleInsert=function(e,t){return function(){return n.insert(e,t)}},n.replace=function(e,t){return n.updateArrayField(function(n){return hB(n,e,t)},!1,!1)},n.handleReplace=function(e,t){return function(){return n.replace(e,t)}},n.unshift=function(e){var t=-1;return n.updateArrayField(function(n){var r=n?[e].concat(n):[e];return t<0&&(t=r.length),r},function(e){var n=e?[null].concat(e):[null];return t<0&&(t=n.length),n},function(e){var n=e?[null].concat(e):[null];return t<0&&(t=n.length),n}),t},n.handleUnshift=function(e){return function(){return n.unshift(e)}},n.handleRemove=function(e){return function(){return n.remove(e)}},n.handlePop=function(){return function(){return n.pop()}},n.remove=n.remove.bind(hu(n)),n.pop=n.pop.bind(hu(n)),n}ho(t,e);var n=t.prototype;return n.componentDidUpdate=function(e){this.props.validateOnChange&&this.props.formik.validateOnChange&&!sd()(hm(e.formik.values,e.name),hm(this.props.formik.values,this.props.name))&&this.props.formik.validateForm(this.props.formik.values)},n.remove=function(e){var t;return this.updateArrayField(function(n){var r=n?hU(n):[];return t||(t=r[e]),hl(r.splice)&&r.splice(e,1),r},!0,!0),t},n.pop=function(){var e;return this.updateArrayField(function(t){var n=t;return e||(e=n&&n.pop&&n.pop()),n},!0,!0),e},n.render=function(){var e={push:this.push,pop:this.pop,swap:this.swap,move:this.move,insert:this.insert,replace:this.replace,unshift:this.unshift,remove:this.remove,handlePush:this.handlePush,handlePop:this.handlePop,handleSwap:this.handleSwap,handleMove:this.handleMove,handleInsert:this.handleInsert,handleReplace:this.handleReplace,handleUnshift:this.handleUnshift,handleRemove:this.handleRemove},t=this.props,n=t.component,r=t.render,i=t.children,a=t.name,o=hs(t.formik,["validate","validationSchema"]),s=ha({},e,{form:o,name:a});return n?(0,l.createElement)(n,s):r?r(s):i?"function"==typeof i?i(s):hp(i)?null:l.Children.only(i):null},t})(l.Component).defaultProps={validateOnChange:!0},l.Component,l.Component;var hH=n(24802),h$=n(71209),hz=n(91750),hG=n(11970),hW=n(4689),hK=n(67598),hV=function(){return(hV=Object.assign||function(e){for(var t,n=1,r=arguments.length;nt.indexOf(r)&&(n[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var i=0,r=Object.getOwnPropertySymbols(e);it.indexOf(r[i])&&(n[r[i]]=e[r[i]]);return n}function hZ(e){var t=e.disabled,n=e.field,r=n.onBlur,i=hq(n,["onBlur"]),a=e.form,o=a.isSubmitting,s=a.touched,u=a.errors,c=e.onBlur,l=e.helperText,f=hq(e,["disabled","field","form","onBlur","helperText"]),d=hm(u,i.name),h=hm(s,i.name)&&!!d;return hV(hV({variant:f.variant,error:h,helperText:h?d:l,disabled:null!=t?t:o,onBlur:null!=c?c:function(e){r(null!=e?e:i.name)}},i),f)}function hX(e){var t=e.children,n=hq(e,["children"]);return(0,l.createElement)(iw.Z,hV({},hZ(n)),t)}function hJ(e){var t=e.disabled,n=e.field,r=n.onBlur,i=hq(n,["onBlur"]),a=e.form.isSubmitting,o=(e.type,e.onBlur),s=hq(e,["disabled","field","form","type","onBlur"]);return hV(hV({disabled:null!=t?t:a,onBlur:null!=o?o:function(e){r(null!=e?e:i.name)}},i),s)}function hQ(e){return(0,l.createElement)(hH.Z,hV({},hJ(e)))}function h1(e){var t,n=e.disabled,r=e.field,i=r.onBlur,a=hq(r,["onBlur"]),o=e.form.isSubmitting,s=(e.type,e.onBlur),u=hq(e,["disabled","field","form","type","onBlur"]);return hV(hV({disabled:null!=n?n:o,indeterminate:!Array.isArray(a.value)&&null==a.value,onBlur:null!=s?s:function(e){i(null!=e?e:a.name)}},a),u)}function h0(e){return(0,l.createElement)(h$.Z,hV({},h1(e)))}function h2(e){var t=e.Label,n=hq(e,["Label"]);return(0,l.createElement)(hz.Z,hV({control:(0,l.createElement)(h$.Z,hV({},h1(n)))},t))}function h3(e){var t=e.disabled,n=e.field,r=n.onBlur,i=hq(n,["onBlur"]),a=e.form.isSubmitting,o=e.onBlur,s=hq(e,["disabled","field","form","onBlur"]);return hV(hV({disabled:null!=t?t:a,onBlur:null!=o?o:function(e){r(null!=e?e:i.name)}},i),s)}function h4(e){return(0,l.createElement)(hG.default,hV({},h3(e)))}function h6(e){var t=e.field,n=t.onBlur,r=hq(t,["onBlur"]),i=(e.form,e.onBlur),a=hq(e,["field","form","onBlur"]);return hV(hV({onBlur:null!=i?i:function(e){n(null!=e?e:r.name)}},r),a)}function h5(e){return(0,l.createElement)(hW.Z,hV({},h6(e)))}function h8(e){var t=e.disabled,n=e.field,r=n.onBlur,i=hq(n,["onBlur"]),a=e.form.isSubmitting,o=e.onBlur,s=hq(e,["disabled","field","form","onBlur"]);return hV(hV({disabled:null!=t?t:a,onBlur:null!=o?o:function(e){r(null!=e?e:i.name)}},i),s)}function h9(e){return(0,l.createElement)(hK.default,hV({},h8(e)))}hX.displayName="FormikMaterialUITextField",hQ.displayName="FormikMaterialUISwitch",h0.displayName="FormikMaterialUICheckbox",h2.displayName="FormikMaterialUICheckboxWithLabel",h4.displayName="FormikMaterialUISelect",h5.displayName="FormikMaterialUIRadioGroup",h9.displayName="FormikMaterialUIInputBase";try{a=Map}catch(h7){}try{o=Set}catch(pe){}function pt(e,t,n){if(!e||"object"!=typeof e||"function"==typeof e)return e;if(e.nodeType&&"cloneNode"in e)return e.cloneNode(!0);if(e instanceof Date)return new Date(e.getTime());if(e instanceof RegExp)return RegExp(e);if(Array.isArray(e))return e.map(pn);if(a&&e instanceof a)return new Map(Array.from(e.entries()));if(o&&e instanceof o)return new Set(Array.from(e.values()));if(e instanceof Object){t.push(e);var r=Object.create(e);for(var i in n.push(r),e){var s=t.findIndex(function(t){return t===e[i]});r[i]=s>-1?n[s]:pt(e[i],t,n)}return r}return e}function pn(e){return pt(e,[],[])}let pr=Object.prototype.toString,pi=Error.prototype.toString,pa=RegExp.prototype.toString,po="undefined"!=typeof Symbol?Symbol.prototype.toString:()=>"",ps=/^Symbol\((.*)\)(.*)$/;function pu(e){if(e!=+e)return"NaN";let t=0===e&&1/e<0;return t?"-0":""+e}function pc(e,t=!1){if(null==e||!0===e||!1===e)return""+e;let n=typeof e;if("number"===n)return pu(e);if("string"===n)return t?`"${e}"`:e;if("function"===n)return"[Function "+(e.name||"anonymous")+"]";if("symbol"===n)return po.call(e).replace(ps,"Symbol($1)");let r=pr.call(e).slice(8,-1);return"Date"===r?isNaN(e.getTime())?""+e:e.toISOString(e):"Error"===r||e instanceof Error?"["+pi.call(e)+"]":"RegExp"===r?pa.call(e):null}function pl(e,t){let n=pc(e,t);return null!==n?n:JSON.stringify(e,function(e,n){let r=pc(this[e],t);return null!==r?r:n},2)}let pf={default:"${path} is invalid",required:"${path} is a required field",oneOf:"${path} must be one of the following values: ${values}",notOneOf:"${path} must not be one of the following values: ${values}",notType({path:e,type:t,value:n,originalValue:r}){let i=null!=r&&r!==n,a=`${e} must be a \`${t}\` type, but the final value was: \`${pl(n,!0)}\``+(i?` (cast from the value \`${pl(r,!0)}\`).`:".");return null===n&&(a+='\n If "null" is intended as an empty value be sure to mark the schema as `.nullable()`'),a},defined:"${path} must be defined"},pd={length:"${path} must be exactly ${length} characters",min:"${path} must be at least ${min} characters",max:"${path} must be at most ${max} characters",matches:'${path} must match the following: "${regex}"',email:"${path} must be a valid email",url:"${path} must be a valid URL",uuid:"${path} must be a valid UUID",trim:"${path} must be a trimmed string",lowercase:"${path} must be a lowercase string",uppercase:"${path} must be a upper case string"},ph={min:"${path} must be greater than or equal to ${min}",max:"${path} must be less than or equal to ${max}",lessThan:"${path} must be less than ${less}",moreThan:"${path} must be greater than ${more}",positive:"${path} must be a positive number",negative:"${path} must be a negative number",integer:"${path} must be an integer"},pp={min:"${path} field must be later than ${min}",max:"${path} field must be at earlier than ${max}"},pb={isValue:"${path} field must be ${value}"},pm={noUnknown:"${path} field has unspecified keys: ${unknown}"},pg={min:"${path} field must have at least ${min} items",max:"${path} field must have less than or equal to ${max} items",length:"${path} must be have ${length} items"};Object.assign(Object.create(null),{mixed:pf,string:pd,number:ph,date:pp,object:pm,array:pg,boolean:pb});var pv=n(18721),py=n.n(pv);let pw=e=>e&&e.__isYupSchema__;class p_{constructor(e,t){if(this.refs=e,this.refs=e,"function"==typeof t){this.fn=t;return}if(!py()(t,"is"))throw TypeError("`is:` is required for `when()` conditions");if(!t.then&&!t.otherwise)throw TypeError("either `then:` or `otherwise:` is required for `when()` conditions");let{is:n,then:r,otherwise:i}=t,a="function"==typeof n?n:(...e)=>e.every(e=>e===n);this.fn=function(...e){let t=e.pop(),n=e.pop(),o=a(...e)?r:i;if(o)return"function"==typeof o?o(n):n.concat(o.resolve(t))}}resolve(e,t){let n=this.refs.map(e=>e.getValue(null==t?void 0:t.value,null==t?void 0:t.parent,null==t?void 0:t.context)),r=this.fn.apply(e,n.concat(e,t));if(void 0===r||r===e)return e;if(!pw(r))throw TypeError("conditions must return a schema object");return r.resolve(t)}}let pE=p_;function pS(e){return null==e?[]:[].concat(e)}function pk(){return(pk=Object.assign||function(e){for(var t=1;tpl(t[n])):"function"==typeof e?e(t):e}static isError(e){return e&&"ValidationError"===e.name}constructor(e,t,n,r){super(),this.name="ValidationError",this.value=t,this.path=n,this.type=r,this.errors=[],this.inner=[],pS(e).forEach(e=>{pT.isError(e)?(this.errors.push(...e.errors),this.inner=this.inner.concat(e.inner.length?e.inner:e)):this.errors.push(e)}),this.message=this.errors.length>1?`${this.errors.length} errors occurred`:this.errors[0],Error.captureStackTrace&&Error.captureStackTrace(this,pT)}}let pM=e=>{let t=!1;return(...n)=>{t||(t=!0,e(...n))}};function pO(e,t){let{endEarly:n,tests:r,args:i,value:a,errors:o,sort:s,path:u}=e,c=pM(t),l=r.length,f=[];if(o=o||[],!l)return o.length?c(new pT(o,a,u)):c(null,a);for(let d=0;d=0||(i[n]=e[n]);return i}function pR(e){function t(t,n){let{value:r,path:i="",label:a,options:o,originalValue:s,sync:u}=t,c=pP(t,["value","path","label","options","originalValue","sync"]),{name:l,test:f,params:d,message:h}=e,{parent:p,context:b}=o;function m(e){return pD.isRef(e)?e.getValue(r,p,b):e}function g(e={}){let t=pL()(pN({value:r,originalValue:s,label:a,path:e.path||i},d,e.params),m),n=new pT(pT.formatError(e.message||h,t),r,t.path,e.type||l);return n.params=t,n}let v=pN({path:i,parent:p,type:l,createError:g,resolve:m,options:o,originalValue:s},c);if(!u){try{Promise.resolve(f.call(v,r,v)).then(e=>{pT.isError(e)?n(e):e?n(null,e):n(g())})}catch(y){n(y)}return}let w;try{var _;if(w=f.call(v,r,v),"function"==typeof(null==(_=w)?void 0:_.then))throw Error(`Validation test of type: "${v.type}" returned a Promise during a synchronous validate. This test will finish after the validate call has returned`)}catch(E){n(E);return}pT.isError(w)?n(w):w?n(null,w):n(g())}return t.OPTIONS=e,t}pD.prototype.__isYupRef=!0;let pj=e=>e.substr(0,e.length-1).substr(1);function pF(e,t,n,r=n){let i,a,o;return t?((0,pC.forEach)(t,(s,u,c)=>{let l=u?pj(s):s;if((e=e.resolve({context:r,parent:i,value:n})).innerType){let f=c?parseInt(l,10):0;if(n&&f>=n.length)throw Error(`Yup.reach cannot resolve an array item at index: ${s}, in the path: ${t}. because there is no value at that index. `);i=n,n=n&&n[f],e=e.innerType}if(!c){if(!e.fields||!e.fields[l])throw Error(`The schema does not contain the path: ${t}. (failed at: ${o} which is a type: "${e._type}")`);i=n,n=n&&n[l],e=e.fields[l]}a=l,o=u?"["+s+"]":"."+s}),{schema:e,parent:i,parentPath:a}):{parent:i,parentPath:t,schema:e}}class pY{constructor(){this.list=new Set,this.refs=new Map}get size(){return this.list.size+this.refs.size}describe(){let e=[];for(let t of this.list)e.push(t);for(let[,n]of this.refs)e.push(n.describe());return e}toArray(){return Array.from(this.list).concat(Array.from(this.refs.values()))}add(e){pD.isRef(e)?this.refs.set(e.key,e):this.list.add(e)}delete(e){pD.isRef(e)?this.refs.delete(e.key):this.list.delete(e)}has(e,t){if(this.list.has(e))return!0;let n,r=this.refs.values();for(;!(n=r.next()).done;)if(t(n.value)===e)return!0;return!1}clone(){let e=new pY;return e.list=new Set(this.list),e.refs=new Map(this.refs),e}merge(e,t){let n=this.clone();return e.list.forEach(e=>n.add(e)),e.refs.forEach(e=>n.add(e)),t.list.forEach(e=>n.delete(e)),t.refs.forEach(e=>n.delete(e)),n}}function pB(){return(pB=Object.assign||function(e){for(var t=1;t{this.typeError(pf.notType)}),this.type=(null==e?void 0:e.type)||"mixed",this.spec=pB({strip:!1,strict:!1,abortEarly:!0,recursive:!0,nullable:!1,presence:"optional"},null==e?void 0:e.spec)}get _type(){return this.type}_typeCheck(e){return!0}clone(e){if(this._mutate)return e&&Object.assign(this.spec,e),this;let t=Object.create(Object.getPrototypeOf(this));return t.type=this.type,t._typeError=this._typeError,t._whitelistError=this._whitelistError,t._blacklistError=this._blacklistError,t._whitelist=this._whitelist.clone(),t._blacklist=this._blacklist.clone(),t.exclusiveTests=pB({},this.exclusiveTests),t.deps=[...this.deps],t.conditions=[...this.conditions],t.tests=[...this.tests],t.transforms=[...this.transforms],t.spec=pn(pB({},this.spec,e)),t}label(e){var t=this.clone();return t.spec.label=e,t}meta(...e){if(0===e.length)return this.spec.meta;let t=this.clone();return t.spec.meta=Object.assign(t.spec.meta||{},e[0]),t}withMutation(e){let t=this._mutate;this._mutate=!0;let n=e(this);return this._mutate=t,n}concat(e){if(!e||e===this)return this;if(e.type!==this.type&&"mixed"!==this.type)throw TypeError(`You cannot \`concat()\` schema's of different types: ${this.type} and ${e.type}`);let t=this,n=e.clone(),r=pB({},t.spec,n.spec);return n.spec=r,n._typeError||(n._typeError=t._typeError),n._whitelistError||(n._whitelistError=t._whitelistError),n._blacklistError||(n._blacklistError=t._blacklistError),n._whitelist=t._whitelist.merge(e._whitelist,e._blacklist),n._blacklist=t._blacklist.merge(e._blacklist,e._whitelist),n.tests=t.tests,n.exclusiveTests=t.exclusiveTests,n.withMutation(t=>{e.tests.forEach(e=>{t.test(e.OPTIONS)})}),n}isType(e){return!!this.spec.nullable&&null===e||this._typeCheck(e)}resolve(e){let t=this;if(t.conditions.length){let n=t.conditions;(t=t.clone()).conditions=[],t=(t=n.reduce((t,n)=>n.resolve(t,e),t)).resolve(e)}return t}cast(e,t={}){let n=this.resolve(pB({value:e},t)),r=n._cast(e,t);if(void 0!==e&&!1!==t.assert&&!0!==n.isType(r)){let i=pl(e),a=pl(r);throw TypeError(`The value of ${t.path||"field"} could not be cast to a value that satisfies the schema type: "${n._type}". attempted value: ${i} -`+(a!==i?`result of cast: ${a}`:""))}return r}_cast(e,t){let n=void 0===e?e:this.transforms.reduce((t,n)=>n.call(this,t,e,this),e);return void 0===n&&(n=this.getDefault()),n}_validate(e,t={},n){let{sync:r,path:i,from:a=[],originalValue:o=e,strict:s=this.spec.strict,abortEarly:u=this.spec.abortEarly}=t,c=e;s||(c=this._cast(c,pB({assert:!1},t)));let l={value:c,path:i,options:t,originalValue:o,schema:this,label:this.spec.label,sync:r,from:a},f=[];this._typeError&&f.push(this._typeError),this._whitelistError&&f.push(this._whitelistError),this._blacklistError&&f.push(this._blacklistError),pO({args:l,value:c,path:i,sync:r,tests:f,endEarly:u},e=>{if(e)return void n(e,c);pO({tests:this.tests,args:l,path:i,sync:r,value:c,endEarly:u},n)})}validate(e,t,n){let r=this.resolve(pB({},t,{value:e}));return"function"==typeof n?r._validate(e,t,n):new Promise((n,i)=>r._validate(e,t,(e,t)=>{e?i(e):n(t)}))}validateSync(e,t){let n;return this.resolve(pB({},t,{value:e}))._validate(e,pB({},t,{sync:!0}),(e,t)=>{if(e)throw e;n=t}),n}isValid(e,t){return this.validate(e,t).then(()=>!0,e=>{if(pT.isError(e))return!1;throw e})}isValidSync(e,t){try{return this.validateSync(e,t),!0}catch(n){if(pT.isError(n))return!1;throw n}}_getDefault(){let e=this.spec.default;return null==e?e:"function"==typeof e?e.call(this):pn(e)}getDefault(e){return this.resolve(e||{})._getDefault()}default(e){return 0===arguments.length?this._getDefault():this.clone({default:e})}strict(e=!0){var t=this.clone();return t.spec.strict=e,t}_isPresent(e){return null!=e}defined(e=pf.defined){return this.test({message:e,name:"defined",exclusive:!0,test:e=>void 0!==e})}required(e=pf.required){return this.clone({presence:"required"}).withMutation(t=>t.test({message:e,name:"required",exclusive:!0,test(e){return this.schema._isPresent(e)}}))}notRequired(){var e=this.clone({presence:"optional"});return e.tests=e.tests.filter(e=>"required"!==e.OPTIONS.name),e}nullable(e=!0){return this.clone({nullable:!1!==e})}transform(e){var t=this.clone();return t.transforms.push(e),t}test(...e){let t;if(void 0===(t=1===e.length?"function"==typeof e[0]?{test:e[0]}:e[0]:2===e.length?{name:e[0],test:e[1]}:{name:e[0],message:e[1],test:e[2]}).message&&(t.message=pf.default),"function"!=typeof t.test)throw TypeError("`test` is a required parameters");let n=this.clone(),r=pR(t),i=t.exclusive||t.name&&!0===n.exclusiveTests[t.name];if(t.exclusive&&!t.name)throw TypeError("Exclusive tests must provide a unique `name` identifying the test");return t.name&&(n.exclusiveTests[t.name]=!!t.exclusive),n.tests=n.tests.filter(e=>e.OPTIONS.name!==t.name||!i&&e.OPTIONS.test!==r.OPTIONS.test),n.tests.push(r),n}when(e,t){Array.isArray(e)||"string"==typeof e||(t=e,e=".");let n=this.clone(),r=pS(e).map(e=>new pD(e));return r.forEach(e=>{e.isSibling&&n.deps.push(e.key)}),n.conditions.push(new pE(r,t)),n}typeError(e){var t=this.clone();return t._typeError=pR({message:e,name:"typeError",test(e){return!!(void 0===e||this.schema.isType(e))||this.createError({params:{type:this.schema._type}})}}),t}oneOf(e,t=pf.oneOf){var n=this.clone();return e.forEach(e=>{n._whitelist.add(e),n._blacklist.delete(e)}),n._whitelistError=pR({message:t,name:"oneOf",test(e){if(void 0===e)return!0;let t=this.schema._whitelist;return!!t.has(e,this.resolve)||this.createError({params:{values:t.toArray().join(", ")}})}}),n}notOneOf(e,t=pf.notOneOf){var n=this.clone();return e.forEach(e=>{n._blacklist.add(e),n._whitelist.delete(e)}),n._blacklistError=pR({message:t,name:"notOneOf",test(e){let t=this.schema._blacklist;return!t.has(e,this.resolve)||this.createError({params:{values:t.toArray().join(", ")}})}}),n}strip(e=!0){let t=this.clone();return t.spec.strip=e,t}describe(){let e=this.clone(),{label:t,meta:n}=e.spec,r={meta:n,label:t,type:e.type,oneOf:e._whitelist.describe(),notOneOf:e._blacklist.describe(),tests:e.tests.map(e=>({name:e.OPTIONS.name,params:e.OPTIONS.params})).filter((e,t,n)=>n.findIndex(t=>t.name===e.name)===t)};return r}}for(let pH of(pU.prototype.__isYupSchema__=!0,["validate","validateSync"]))pU.prototype[`${pH}At`]=function(e,t,n={}){let{parent:r,parentPath:i,schema:a}=pF(this,e,t,n.context);return a[pH](r&&r[i],pB({},n,{parent:r,path:e}))};for(let p$ of["equals","is"])pU.prototype[p$]=pU.prototype.oneOf;for(let pz of["not","nope"])pU.prototype[pz]=pU.prototype.notOneOf;pU.prototype.optional=pU.prototype.notRequired;let pG=pU;function pW(){return new pG}pW.prototype=pG.prototype;let pK=e=>null==e;function pV(){return new pq}class pq extends pU{constructor(){super({type:"boolean"}),this.withMutation(()=>{this.transform(function(e){if(!this.isType(e)){if(/^(true|1)$/i.test(String(e)))return!0;if(/^(false|0)$/i.test(String(e)))return!1}return e})})}_typeCheck(e){return e instanceof Boolean&&(e=e.valueOf()),"boolean"==typeof e}isTrue(e=pb.isValue){return this.test({message:e,name:"is-value",exclusive:!0,params:{value:"true"},test:e=>pK(e)||!0===e})}isFalse(e=pb.isValue){return this.test({message:e,name:"is-value",exclusive:!0,params:{value:"false"},test:e=>pK(e)||!1===e})}}pV.prototype=pq.prototype;let pZ=/^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i,pX=/^((https?|ftp):)?\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i,pJ=/^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i,pQ=e=>pK(e)||e===e.trim(),p1=({}).toString();function p0(){return new p2}class p2 extends pU{constructor(){super({type:"string"}),this.withMutation(()=>{this.transform(function(e){if(this.isType(e)||Array.isArray(e))return e;let t=null!=e&&e.toString?e.toString():e;return t===p1?e:t})})}_typeCheck(e){return e instanceof String&&(e=e.valueOf()),"string"==typeof e}_isPresent(e){return super._isPresent(e)&&!!e.length}length(e,t=pd.length){return this.test({message:t,name:"length",exclusive:!0,params:{length:e},test(t){return pK(t)||t.length===this.resolve(e)}})}min(e,t=pd.min){return this.test({message:t,name:"min",exclusive:!0,params:{min:e},test(t){return pK(t)||t.length>=this.resolve(e)}})}max(e,t=pd.max){return this.test({name:"max",exclusive:!0,message:t,params:{max:e},test(t){return pK(t)||t.length<=this.resolve(e)}})}matches(e,t){let n=!1,r,i;return t&&("object"==typeof t?{excludeEmptyString:n=!1,message:r,name:i}=t:r=t),this.test({name:i||"matches",message:r||pd.matches,params:{regex:e},test:t=>pK(t)||""===t&&n||-1!==t.search(e)})}email(e=pd.email){return this.matches(pZ,{name:"email",message:e,excludeEmptyString:!0})}url(e=pd.url){return this.matches(pX,{name:"url",message:e,excludeEmptyString:!0})}uuid(e=pd.uuid){return this.matches(pJ,{name:"uuid",message:e,excludeEmptyString:!1})}ensure(){return this.default("").transform(e=>null===e?"":e)}trim(e=pd.trim){return this.transform(e=>null!=e?e.trim():e).test({message:e,name:"trim",test:pQ})}lowercase(e=pd.lowercase){return this.transform(e=>pK(e)?e:e.toLowerCase()).test({message:e,name:"string_case",exclusive:!0,test:e=>pK(e)||e===e.toLowerCase()})}uppercase(e=pd.uppercase){return this.transform(e=>pK(e)?e:e.toUpperCase()).test({message:e,name:"string_case",exclusive:!0,test:e=>pK(e)||e===e.toUpperCase()})}}p0.prototype=p2.prototype;let p3=e=>e!=+e;function p4(){return new p6}class p6 extends pU{constructor(){super({type:"number"}),this.withMutation(()=>{this.transform(function(e){let t=e;if("string"==typeof t){if(""===(t=t.replace(/\s/g,"")))return NaN;t=+t}return this.isType(t)?t:parseFloat(t)})})}_typeCheck(e){return e instanceof Number&&(e=e.valueOf()),"number"==typeof e&&!p3(e)}min(e,t=ph.min){return this.test({message:t,name:"min",exclusive:!0,params:{min:e},test(t){return pK(t)||t>=this.resolve(e)}})}max(e,t=ph.max){return this.test({message:t,name:"max",exclusive:!0,params:{max:e},test(t){return pK(t)||t<=this.resolve(e)}})}lessThan(e,t=ph.lessThan){return this.test({message:t,name:"max",exclusive:!0,params:{less:e},test(t){return pK(t)||tthis.resolve(e)}})}positive(e=ph.positive){return this.moreThan(0,e)}negative(e=ph.negative){return this.lessThan(0,e)}integer(e=ph.integer){return this.test({name:"integer",message:e,test:e=>pK(e)||Number.isInteger(e)})}truncate(){return this.transform(e=>pK(e)?e:0|e)}round(e){var t,n=["ceil","floor","round","trunc"];if("trunc"===(e=(null==(t=e)?void 0:t.toLowerCase())||"round"))return this.truncate();if(-1===n.indexOf(e.toLowerCase()))throw TypeError("Only valid options for round() are: "+n.join(", "));return this.transform(t=>pK(t)?t:Math[e](t))}}p4.prototype=p6.prototype;var p5=/^(\d{4}|[+\-]\d{6})(?:-?(\d{2})(?:-?(\d{2}))?)?(?:[ T]?(\d{2}):?(\d{2})(?::?(\d{2})(?:[,\.](\d{1,}))?)?(?:(Z)|([+\-])(\d{2})(?::?(\d{2}))?)?)?$/;function p8(e){var t,n,r=[1,4,5,6,7,10,11],i=0;if(n=p5.exec(e)){for(var a,o=0;a=r[o];++o)n[a]=+n[a]||0;n[2]=(+n[2]||1)-1,n[3]=+n[3]||1,n[7]=n[7]?String(n[7]).substr(0,3):0,(void 0===n[8]||""===n[8])&&(void 0===n[9]||""===n[9])?t=+new Date(n[1],n[2],n[3],n[4],n[5],n[6],n[7]):("Z"!==n[8]&&void 0!==n[9]&&(i=60*n[10]+n[11],"+"===n[9]&&(i=0-i)),t=Date.UTC(n[1],n[2],n[3],n[4],n[5]+i,n[6],n[7]))}else t=Date.parse?Date.parse(e):NaN;return t}let p9=new Date(""),p7=e=>"[object Date]"===Object.prototype.toString.call(e);function be(){return new bt}class bt extends pU{constructor(){super({type:"date"}),this.withMutation(()=>{this.transform(function(e){return this.isType(e)?e:(e=p8(e),isNaN(e)?p9:new Date(e))})})}_typeCheck(e){return p7(e)&&!isNaN(e.getTime())}prepareParam(e,t){let n;if(pD.isRef(e))n=e;else{let r=this.cast(e);if(!this._typeCheck(r))throw TypeError(`\`${t}\` must be a Date or a value that can be \`cast()\` to a Date`);n=r}return n}min(e,t=pp.min){let n=this.prepareParam(e,"min");return this.test({message:t,name:"min",exclusive:!0,params:{min:e},test(e){return pK(e)||e>=this.resolve(n)}})}max(e,t=pp.max){var n=this.prepareParam(e,"max");return this.test({message:t,name:"max",exclusive:!0,params:{max:e},test(e){return pK(e)||e<=this.resolve(n)}})}}bt.INVALID_DATE=p9,be.prototype=bt.prototype,be.INVALID_DATE=p9;var bn=n(11865),br=n.n(bn),bi=n(68929),ba=n.n(bi),bo=n(67523),bs=n.n(bo),bu=n(94633),bc=n.n(bu);function bl(e,t=[]){let n=[],r=[];function i(e,i){var a=(0,pC.split)(e)[0];~r.indexOf(a)||r.push(a),~t.indexOf(`${i}-${a}`)||n.push([i,a])}for(let a in e)if(py()(e,a)){let o=e[a];~r.indexOf(a)||r.push(a),pD.isRef(o)&&o.isSibling?i(o.path,a):pw(o)&&"deps"in o&&o.deps.forEach(e=>i(e,a))}return bc().array(r,n).reverse()}function bf(e,t){let n=1/0;return e.some((e,r)=>{var i;if((null==(i=t.path)?void 0:i.indexOf(e))!==-1)return n=r,!0}),n}function bd(e){return(t,n)=>bf(e,t)-bf(e,n)}function bh(){return(bh=Object.assign||function(e){for(var t=1;t"[object Object]"===Object.prototype.toString.call(e);function bb(e,t){let n=Object.keys(e.fields);return Object.keys(t).filter(e=>-1===n.indexOf(e))}let bm=bd([]);class bg extends pU{constructor(e){super({type:"object"}),this.fields=Object.create(null),this._sortErrors=bm,this._nodes=[],this._excludedEdges=[],this.withMutation(()=>{this.transform(function(e){if("string"==typeof e)try{e=JSON.parse(e)}catch(t){e=null}return this.isType(e)?e:null}),e&&this.shape(e)})}_typeCheck(e){return bp(e)||"function"==typeof e}_cast(e,t={}){var n;let r=super._cast(e,t);if(void 0===r)return this.getDefault();if(!this._typeCheck(r))return r;let i=this.fields,a=null!=(n=t.stripUnknown)?n:this.spec.noUnknown,o=this._nodes.concat(Object.keys(r).filter(e=>-1===this._nodes.indexOf(e))),s={},u=bh({},t,{parent:s,__validating:t.__validating||!1}),c=!1;for(let l of o){let f=i[l],d=py()(r,l);if(f){let h,p=r[l];u.path=(t.path?`${t.path}.`:"")+l;let b="spec"in(f=f.resolve({value:p,context:t.context,parent:s}))?f.spec:void 0,m=null==b?void 0:b.strict;if(null==b?void 0:b.strip){c=c||l in r;continue}void 0!==(h=t.__validating&&m?r[l]:f.cast(r[l],u))&&(s[l]=h)}else d&&!a&&(s[l]=r[l]);s[l]!==r[l]&&(c=!0)}return c?s:r}_validate(e,t={},n){let r=[],{sync:i,from:a=[],originalValue:o=e,abortEarly:s=this.spec.abortEarly,recursive:u=this.spec.recursive}=t;a=[{schema:this,value:o},...a],t.__validating=!0,t.originalValue=o,t.from=a,super._validate(e,t,(e,c)=>{if(e){if(!pT.isError(e)||s)return void n(e,c);r.push(e)}if(!u||!bp(c)){n(r[0]||null,c);return}o=o||c;let l=this._nodes.map(e=>(n,r)=>{let i=-1===e.indexOf(".")?(t.path?`${t.path}.`:"")+e:`${t.path||""}["${e}"]`,s=this.fields[e];if(s&&"validate"in s){s.validate(c[e],bh({},t,{path:i,from:a,strict:!0,parent:c,originalValue:o[e]}),r);return}r(null)});pO({sync:i,tests:l,value:c,errors:r,endEarly:s,sort:this._sortErrors,path:t.path},n)})}clone(e){let t=super.clone(e);return t.fields=bh({},this.fields),t._nodes=this._nodes,t._excludedEdges=this._excludedEdges,t._sortErrors=this._sortErrors,t}concat(e){let t=super.concat(e),n=t.fields;for(let[r,i]of Object.entries(this.fields)){let a=n[r];void 0===a?n[r]=i:a instanceof pU&&i instanceof pU&&(n[r]=i.concat(a))}return t.withMutation(()=>t.shape(n))}getDefaultFromShape(){let e={};return this._nodes.forEach(t=>{let n=this.fields[t];e[t]="default"in n?n.getDefault():void 0}),e}_getDefault(){return"default"in this.spec?super._getDefault():this._nodes.length?this.getDefaultFromShape():void 0}shape(e,t=[]){let n=this.clone(),r=Object.assign(n.fields,e);if(n.fields=r,n._sortErrors=bd(Object.keys(r)),t.length){Array.isArray(t[0])||(t=[t]);let i=t.map(([e,t])=>`${e}-${t}`);n._excludedEdges=n._excludedEdges.concat(i)}return n._nodes=bl(r,n._excludedEdges),n}pick(e){let t={};for(let n of e)this.fields[n]&&(t[n]=this.fields[n]);return this.clone().withMutation(e=>(e.fields={},e.shape(t)))}omit(e){let t=this.clone(),n=t.fields;for(let r of(t.fields={},e))delete n[r];return t.withMutation(()=>t.shape(n))}from(e,t,n){let r=(0,pC.getter)(e,!0);return this.transform(i=>{if(null==i)return i;let a=i;return py()(i,e)&&(a=bh({},i),n||delete a[e],a[t]=r(i)),a})}noUnknown(e=!0,t=pm.noUnknown){"string"==typeof e&&(t=e,e=!0);let n=this.test({name:"noUnknown",exclusive:!0,message:t,test(t){if(null==t)return!0;let n=bb(this.schema,t);return!e||0===n.length||this.createError({params:{unknown:n.join(", ")}})}});return n.spec.noUnknown=e,n}unknown(e=!0,t=pm.noUnknown){return this.noUnknown(!e,t)}transformKeys(e){return this.transform(t=>t&&bs()(t,(t,n)=>e(n)))}camelCase(){return this.transformKeys(ba())}snakeCase(){return this.transformKeys(br())}constantCase(){return this.transformKeys(e=>br()(e).toUpperCase())}describe(){let e=super.describe();return e.fields=pL()(this.fields,e=>e.describe()),e}}function bv(e){return new bg(e)}function by(){return(by=Object.assign||function(e){for(var t=1;t{this.transform(function(e){if("string"==typeof e)try{e=JSON.parse(e)}catch(t){e=null}return this.isType(e)?e:null})})}_typeCheck(e){return Array.isArray(e)}get _subType(){return this.innerType}_cast(e,t){let n=super._cast(e,t);if(!this._typeCheck(n)||!this.innerType)return n;let r=!1,i=n.map((e,n)=>{let i=this.innerType.cast(e,by({},t,{path:`${t.path||""}[${n}]`}));return i!==e&&(r=!0),i});return r?i:n}_validate(e,t={},n){var r,i;let a=[],o=t.sync,s=t.path,u=this.innerType,c=null!=(r=t.abortEarly)?r:this.spec.abortEarly,l=null!=(i=t.recursive)?i:this.spec.recursive,f=null!=t.originalValue?t.originalValue:e;super._validate(e,t,(e,r)=>{if(e){if(!pT.isError(e)||c)return void n(e,r);a.push(e)}if(!l||!u||!this._typeCheck(r)){n(a[0]||null,r);return}f=f||r;let i=Array(r.length);for(let d=0;du.validate(h,b,t)}pO({sync:o,path:s,value:r,errors:a,endEarly:c,tests:i},n)})}clone(e){let t=super.clone(e);return t.innerType=this.innerType,t}concat(e){let t=super.concat(e);return t.innerType=this.innerType,e.innerType&&(t.innerType=t.innerType?t.innerType.concat(e.innerType):e.innerType),t}of(e){let t=this.clone();if(!pw(e))throw TypeError("`array.of()` sub-schema must be a valid yup schema not: "+pl(e));return t.innerType=e,t}length(e,t=pg.length){return this.test({message:t,name:"length",exclusive:!0,params:{length:e},test(t){return pK(t)||t.length===this.resolve(e)}})}min(e,t){return t=t||pg.min,this.test({message:t,name:"min",exclusive:!0,params:{min:e},test(t){return pK(t)||t.length>=this.resolve(e)}})}max(e,t){return t=t||pg.max,this.test({message:t,name:"max",exclusive:!0,params:{max:e},test(t){return pK(t)||t.length<=this.resolve(e)}})}ensure(){return this.default(()=>[]).transform((e,t)=>this._typeCheck(e)?e:null==t?[]:[].concat(t))}compact(e){let t=e?(t,n,r)=>!e(t,n,r):e=>!!e;return this.transform(e=>null!=e?e.filter(t):e)}describe(){let e=super.describe();return this.innerType&&(e.innerType=this.innerType.describe()),e}nullable(e=!0){return super.nullable(e)}defined(){return super.defined()}required(e){return super.required(e)}}bw.prototype=b_.prototype;var bE=bv().shape({name:p0().required("Required"),url:p0().required("Required")}),bS=function(e){var t=e.initialValues,n=e.onSubmit,r=e.submitButtonText,i=e.nameDisabled,a=void 0!==i&&i;return l.createElement(hT,{initialValues:t,validationSchema:bE,onSubmit:n},function(e){var t=e.isSubmitting;return l.createElement(l.Fragment,null,l.createElement(hR,{"data-testid":"bridge-form",noValidate:!0},l.createElement(d.Z,{container:!0,spacing:16},l.createElement(d.Z,{item:!0,xs:12,md:7},l.createElement(hP,{component:hX,id:"name",name:"name",label:"Name",disabled:a,required:!0,fullWidth:!0,FormHelperTextProps:{"data-testid":"name-helper-text"}})),l.createElement(d.Z,{item:!0,xs:12,md:7},l.createElement(hP,{component:hX,id:"url",name:"url",label:"Bridge URL",placeholder:"https://",required:!0,fullWidth:!0,FormHelperTextProps:{"data-testid":"url-helper-text"}})),l.createElement(d.Z,{item:!0,xs:12,md:7},l.createElement(d.Z,{container:!0,spacing:16},l.createElement(d.Z,{item:!0,xs:7},l.createElement(hP,{component:hX,id:"minimumContractPayment",name:"minimumContractPayment",label:"Minimum Contract Payment",placeholder:"0",fullWidth:!0,inputProps:{min:0},FormHelperTextProps:{"data-testid":"minimumContractPayment-helper-text"}})),l.createElement(d.Z,{item:!0,xs:7},l.createElement(hP,{component:hX,id:"confirmations",name:"confirmations",label:"Confirmations",placeholder:"0",type:"number",fullWidth:!0,inputProps:{min:0},FormHelperTextProps:{"data-testid":"confirmations-helper-text"}})))),l.createElement(d.Z,{item:!0,xs:12,md:7},l.createElement(ok.default,{variant:"contained",color:"primary",type:"submit",disabled:t,size:"large"},r)))))})},bk=function(e){var t=e.bridge,n=e.onSubmit,r={name:t.name,url:t.url,minimumContractPayment:t.minimumContractPayment,confirmations:t.confirmations};return l.createElement(ig,null,l.createElement(d.Z,{container:!0,spacing:40},l.createElement(d.Z,{item:!0,xs:12,md:11,lg:9},l.createElement(r5.Z,null,l.createElement(sl.Z,{title:"Edit Bridge",action:l.createElement(aA.Z,{component:tz,href:"/bridges/".concat(t.id)},"Cancel")}),l.createElement(aW.Z,null,l.createElement(bS,{nameDisabled:!0,initialValues:r,onSubmit:n,submitButtonText:"Save Bridge"}))))))};function bx(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&void 0!==arguments[0]&&arguments[0],t=e?function(){return l.createElement(x.default,{variant:"body1"},"Loading...")}:function(){return null};return{isLoading:e,LoadingPlaceholder:t}},mc=n(76023);function ml(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]=0)&&Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}function mB(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n=4?[e[0],e[1],e[2],e[3],"".concat(e[0],".").concat(e[1]),"".concat(e[0],".").concat(e[2]),"".concat(e[0],".").concat(e[3]),"".concat(e[1],".").concat(e[0]),"".concat(e[1],".").concat(e[2]),"".concat(e[1],".").concat(e[3]),"".concat(e[2],".").concat(e[0]),"".concat(e[2],".").concat(e[1]),"".concat(e[2],".").concat(e[3]),"".concat(e[3],".").concat(e[0]),"".concat(e[3],".").concat(e[1]),"".concat(e[3],".").concat(e[2]),"".concat(e[0],".").concat(e[1],".").concat(e[2]),"".concat(e[0],".").concat(e[1],".").concat(e[3]),"".concat(e[0],".").concat(e[2],".").concat(e[1]),"".concat(e[0],".").concat(e[2],".").concat(e[3]),"".concat(e[0],".").concat(e[3],".").concat(e[1]),"".concat(e[0],".").concat(e[3],".").concat(e[2]),"".concat(e[1],".").concat(e[0],".").concat(e[2]),"".concat(e[1],".").concat(e[0],".").concat(e[3]),"".concat(e[1],".").concat(e[2],".").concat(e[0]),"".concat(e[1],".").concat(e[2],".").concat(e[3]),"".concat(e[1],".").concat(e[3],".").concat(e[0]),"".concat(e[1],".").concat(e[3],".").concat(e[2]),"".concat(e[2],".").concat(e[0],".").concat(e[1]),"".concat(e[2],".").concat(e[0],".").concat(e[3]),"".concat(e[2],".").concat(e[1],".").concat(e[0]),"".concat(e[2],".").concat(e[1],".").concat(e[3]),"".concat(e[2],".").concat(e[3],".").concat(e[0]),"".concat(e[2],".").concat(e[3],".").concat(e[1]),"".concat(e[3],".").concat(e[0],".").concat(e[1]),"".concat(e[3],".").concat(e[0],".").concat(e[2]),"".concat(e[3],".").concat(e[1],".").concat(e[0]),"".concat(e[3],".").concat(e[1],".").concat(e[2]),"".concat(e[3],".").concat(e[2],".").concat(e[0]),"".concat(e[3],".").concat(e[2],".").concat(e[1]),"".concat(e[0],".").concat(e[1],".").concat(e[2],".").concat(e[3]),"".concat(e[0],".").concat(e[1],".").concat(e[3],".").concat(e[2]),"".concat(e[0],".").concat(e[2],".").concat(e[1],".").concat(e[3]),"".concat(e[0],".").concat(e[2],".").concat(e[3],".").concat(e[1]),"".concat(e[0],".").concat(e[3],".").concat(e[1],".").concat(e[2]),"".concat(e[0],".").concat(e[3],".").concat(e[2],".").concat(e[1]),"".concat(e[1],".").concat(e[0],".").concat(e[2],".").concat(e[3]),"".concat(e[1],".").concat(e[0],".").concat(e[3],".").concat(e[2]),"".concat(e[1],".").concat(e[2],".").concat(e[0],".").concat(e[3]),"".concat(e[1],".").concat(e[2],".").concat(e[3],".").concat(e[0]),"".concat(e[1],".").concat(e[3],".").concat(e[0],".").concat(e[2]),"".concat(e[1],".").concat(e[3],".").concat(e[2],".").concat(e[0]),"".concat(e[2],".").concat(e[0],".").concat(e[1],".").concat(e[3]),"".concat(e[2],".").concat(e[0],".").concat(e[3],".").concat(e[1]),"".concat(e[2],".").concat(e[1],".").concat(e[0],".").concat(e[3]),"".concat(e[2],".").concat(e[1],".").concat(e[3],".").concat(e[0]),"".concat(e[2],".").concat(e[3],".").concat(e[0],".").concat(e[1]),"".concat(e[2],".").concat(e[3],".").concat(e[1],".").concat(e[0]),"".concat(e[3],".").concat(e[0],".").concat(e[1],".").concat(e[2]),"".concat(e[3],".").concat(e[0],".").concat(e[2],".").concat(e[1]),"".concat(e[3],".").concat(e[1],".").concat(e[0],".").concat(e[2]),"".concat(e[3],".").concat(e[1],".").concat(e[2],".").concat(e[0]),"".concat(e[3],".").concat(e[2],".").concat(e[0],".").concat(e[1]),"".concat(e[3],".").concat(e[2],".").concat(e[1],".").concat(e[0])]:void 0}var mZ={};function mX(e){if(0===e.length||1===e.length)return e;var t=e.join(".");return mZ[t]||(mZ[t]=mq(e)),mZ[t]}function mJ(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=arguments.length>2?arguments[2]:void 0;return mX(e.filter(function(e){return"token"!==e})).reduce(function(e,t){return mK({},e,n[t])},t)}function mQ(e){return e.join(" ")}function m1(e,t){var n=0;return function(r){return n+=1,r.map(function(r,i){return m0({node:r,stylesheet:e,useInlineStyles:t,key:"code-segment-".concat(n,"-").concat(i)})})}}function m0(e){var t=e.node,n=e.stylesheet,r=e.style,i=void 0===r?{}:r,a=e.useInlineStyles,o=e.key,s=t.properties,u=t.type,c=t.tagName,f=t.value;if("text"===u)return f;if(c){var d,h=m1(n,a);if(a){var p=Object.keys(n).reduce(function(e,t){return t.split(".").forEach(function(t){e.includes(t)||e.push(t)}),e},[]),b=s.className&&s.className.includes("token")?["token"]:[],m=s.className&&b.concat(s.className.filter(function(e){return!p.includes(e)}));d=mK({},s,{className:mQ(m)||void 0,style:mJ(s.className,Object.assign({},s.style,i),n)})}else d=mK({},s,{className:mQ(s.className)});var g=h(t.children);return l.createElement(c,(0,mV.Z)({key:o},d),g)}}let m2=function(e,t){return -1!==e.listLanguages().indexOf(t)};var m3=/\n/g;function m4(e){return e.match(m3)}function m6(e){var t=e.lines,n=e.startingLineNumber,r=e.style;return t.map(function(e,t){var i=t+n;return l.createElement("span",{key:"line-".concat(t),className:"react-syntax-highlighter-line-number",style:"function"==typeof r?r(i):r},"".concat(i,"\n"))})}function m5(e){var t=e.codeString,n=e.codeStyle,r=e.containerStyle,i=void 0===r?{float:"left",paddingRight:"10px"}:r,a=e.numberStyle,o=void 0===a?{}:a,s=e.startingLineNumber;return l.createElement("code",{style:Object.assign({},n,i)},m6({lines:t.replace(/\n$/,"").split("\n"),style:o,startingLineNumber:s}))}function m8(e){return"".concat(e.toString().length,".25em")}function m9(e,t){return{type:"element",tagName:"span",properties:{key:"line-number--".concat(e),className:["comment","linenumber","react-syntax-highlighter-line-number"],style:t},children:[{type:"text",value:e}]}}function m7(e,t,n){var r,i={display:"inline-block",minWidth:m8(n),paddingRight:"1em",textAlign:"right",userSelect:"none"};return mK({},i,"function"==typeof e?e(t):e)}function ge(e){var t=e.children,n=e.lineNumber,r=e.lineNumberStyle,i=e.largestLineNumber,a=e.showInlineLineNumbers,o=e.lineProps,s=void 0===o?{}:o,u=e.className,c=void 0===u?[]:u,l=e.showLineNumbers,f=e.wrapLongLines,d="function"==typeof s?s(n):s;if(d.className=c,n&&a){var h=m7(r,n,i);t.unshift(m9(n,h))}return f&l&&(d.style=mK({},d.style,{display:"flex"})),{type:"element",tagName:"span",properties:d,children:t}}function gt(e){for(var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[],n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:[],r=0;r2&&void 0!==arguments[2]?arguments[2]:[];return ge({children:e,lineNumber:t,lineNumberStyle:s,largestLineNumber:o,showInlineLineNumbers:i,lineProps:n,className:a,showLineNumbers:r,wrapLongLines:u})}function b(e,t){if(r&&t&&i){var n=m7(s,t,o);e.unshift(m9(t,n))}return e}function m(e,n){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:[];return t||r.length>0?p(e,n,r):b(e,n)}for(var g=function(){var e=l[h],t=e.children[0].value;if(m4(t)){var n=t.split("\n");n.forEach(function(t,i){var o=r&&f.length+a,s={type:"text",value:"".concat(t,"\n")};if(0===i){var u=l.slice(d+1,h).concat(ge({children:[s],className:e.properties.className})),c=m(u,o);f.push(c)}else if(i===n.length-1){if(l[h+1]&&l[h+1].children&&l[h+1].children[0]){var p={type:"text",value:"".concat(t)},b=ge({children:[p],className:e.properties.className});l.splice(h+1,0,b)}else{var g=[s],v=m(g,o,e.properties.className);f.push(v)}}else{var y=[s],w=m(y,o,e.properties.className);f.push(w)}}),d=h}h++};h code[class*="language-"]':{background:"#f5f2f0",padding:".1em",borderRadius:".3em",whiteSpace:"normal"},comment:{color:"slategray"},prolog:{color:"slategray"},doctype:{color:"slategray"},cdata:{color:"slategray"},punctuation:{color:"#999"},namespace:{Opacity:".7"},property:{color:"#905"},tag:{color:"#905"},boolean:{color:"#905"},number:{color:"#905"},constant:{color:"#905"},symbol:{color:"#905"},deleted:{color:"#905"},selector:{color:"#690"},"attr-name":{color:"#690"},string:{color:"#690"},char:{color:"#690"},builtin:{color:"#690"},inserted:{color:"#690"},operator:{color:"#9a6e3a",background:"hsla(0, 0%, 100%, .5)"},entity:{color:"#9a6e3a",background:"hsla(0, 0%, 100%, .5)",cursor:"help"},url:{color:"#9a6e3a",background:"hsla(0, 0%, 100%, .5)"},".language-css .token.string":{color:"#9a6e3a",background:"hsla(0, 0%, 100%, .5)"},".style .token.string":{color:"#9a6e3a",background:"hsla(0, 0%, 100%, .5)"},atrule:{color:"#07a"},"attr-value":{color:"#07a"},keyword:{color:"#07a"},function:{color:"#DD4A68"},"class-name":{color:"#DD4A68"},regex:{color:"#e90"},important:{color:"#e90",fontWeight:"bold"},variable:{color:"#e90"},bold:{fontWeight:"bold"},italic:{fontStyle:"italic"}};var gu=n(98695),gc=n.n(gu);let gl=["abap","abnf","actionscript","ada","agda","al","antlr4","apacheconf","apl","applescript","aql","arduino","arff","asciidoc","asm6502","aspnet","autohotkey","autoit","bash","basic","batch","bbcode","birb","bison","bnf","brainfuck","brightscript","bro","bsl","c","cil","clike","clojure","cmake","coffeescript","concurnas","cpp","crystal","csharp","csp","css-extras","css","cypher","d","dart","dax","dhall","diff","django","dns-zone-file","docker","ebnf","editorconfig","eiffel","ejs","elixir","elm","erb","erlang","etlua","excel-formula","factor","firestore-security-rules","flow","fortran","fsharp","ftl","gcode","gdscript","gedcom","gherkin","git","glsl","gml","go","graphql","groovy","haml","handlebars","haskell","haxe","hcl","hlsl","hpkp","hsts","http","ichigojam","icon","iecst","ignore","inform7","ini","io","j","java","javadoc","javadoclike","javascript","javastacktrace","jolie","jq","js-extras","js-templates","jsdoc","json","json5","jsonp","jsstacktrace","jsx","julia","keyman","kotlin","latex","latte","less","lilypond","liquid","lisp","livescript","llvm","lolcode","lua","makefile","markdown","markup-templating","markup","matlab","mel","mizar","mongodb","monkey","moonscript","n1ql","n4js","nand2tetris-hdl","naniscript","nasm","neon","nginx","nim","nix","nsis","objectivec","ocaml","opencl","oz","parigp","parser","pascal","pascaligo","pcaxis","peoplecode","perl","php-extras","php","phpdoc","plsql","powerquery","powershell","processing","prolog","properties","protobuf","pug","puppet","pure","purebasic","purescript","python","q","qml","qore","r","racket","reason","regex","renpy","rest","rip","roboconf","robotframework","ruby","rust","sas","sass","scala","scheme","scss","shell-session","smali","smalltalk","smarty","sml","solidity","solution-file","soy","sparql","splunk-spl","sqf","sql","stan","stylus","swift","t4-cs","t4-templating","t4-vb","tap","tcl","textile","toml","tsx","tt2","turtle","twig","typescript","typoscript","unrealscript","vala","vbnet","velocity","verilog","vhdl","vim","visual-basic","warpscript","wasm","wiki","xeora","xml-doc","xojo","xquery","yaml","yang","zig"];var gf=go(gc(),gs);gf.supportedLanguages=gl;let gd=gf;var gh=n(64566);function gp(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function gb(){var e=gp(["\n query FetchConfigV2 {\n configv2 {\n user\n effective\n }\n }\n"]);return gb=function(){return e},e}var gm=n0(gb()),gg=function(e){var t=e.children;return l.createElement(ir.Z,null,l.createElement(r7.default,{component:"th",scope:"row",colSpan:3},t))},gv=function(){return l.createElement(gg,null,"...")},gy=function(e){var t=e.children;return l.createElement(gg,null,t)},gw=function(e){var t=e.loading,n=e.toml,r=e.error,i=void 0===r?"":r,a=e.title,o=e.expanded;if(i)return l.createElement(gy,null,i);if(t)return l.createElement(gv,null);a||(a="TOML");var s={display:"block"};return l.createElement(x.default,null,l.createElement(mP.Z,{defaultExpanded:o},l.createElement(mR.Z,{expandIcon:l.createElement(gh.Z,null)},a),l.createElement(mj.Z,{style:s},l.createElement(gd,{language:"toml",style:gs},n))))},g_=function(){var e=rv(gm,{fetchPolicy:"cache-and-network"}),t=e.data,n=e.loading,r=e.error;return(null==t?void 0:t.configv2.effective)=="N/A"?l.createElement(l.Fragment,null,l.createElement(d.Z,{item:!0,xs:12},l.createElement(r5.Z,null,l.createElement(sl.Z,{title:"TOML Configuration"}),l.createElement(gw,{title:"V2 config dump:",error:null==r?void 0:r.message,loading:n,toml:null==t?void 0:t.configv2.user,showHead:!0})))):l.createElement(l.Fragment,null,l.createElement(d.Z,{container:!0},l.createElement(d.Z,{item:!0,xs:12},l.createElement(r5.Z,null,l.createElement(sl.Z,{title:"TOML Configuration"}),l.createElement(gw,{title:"User specified:",error:null==r?void 0:r.message,loading:n,toml:null==t?void 0:t.configv2.user,showHead:!0,expanded:!0}),l.createElement(gw,{title:"Effective (with defaults):",error:null==r?void 0:r.message,loading:n,toml:null==t?void 0:t.configv2.effective,showHead:!0})))))},gE=n(34823),gS=function(e){return(0,b.createStyles)({cell:{paddingTop:1.5*e.spacing.unit,paddingBottom:1.5*e.spacing.unit}})},gk=(0,b.withStyles)(gS)(function(e){var t=e.classes,n=(0,A.I0)();(0,l.useEffect)(function(){n((0,ty.DQ)())});var r=(0,A.v9)(gE.N,A.wU);return l.createElement(r5.Z,null,l.createElement(sl.Z,{title:"Node"}),l.createElement(r8.Z,null,l.createElement(r9.Z,null,l.createElement(ir.Z,null,l.createElement(r7.default,{className:t.cell},l.createElement(x.default,null,"Version"),l.createElement(x.default,{variant:"subtitle1",color:"textSecondary"},r.version))),l.createElement(ir.Z,null,l.createElement(r7.default,{className:t.cell},l.createElement(x.default,null,"SHA"),l.createElement(x.default,{variant:"subtitle1",color:"textSecondary"},r.commitSHA))))))}),gx=function(){return l.createElement(ig,null,l.createElement(d.Z,{container:!0},l.createElement(d.Z,{item:!0,sm:12,md:8},l.createElement(d.Z,{container:!0},l.createElement(g_,null))),l.createElement(d.Z,{item:!0,sm:12,md:4},l.createElement(d.Z,{container:!0},l.createElement(d.Z,{item:!0,xs:12},l.createElement(gk,null)),l.createElement(d.Z,{item:!0,xs:12},l.createElement(mN,null)),l.createElement(d.Z,{item:!0,xs:12},l.createElement(mE,null))))))},gT=function(){return l.createElement(gx,null)},gM=function(){return l.createElement(gT,null)},gO=n(44431),gA=1e18,gL=function(e){return new gO.BigNumber(e).dividedBy(gA).toFixed(8)},gC=function(e){var t=e.keys,n=e.chainID,r=e.hideHeaderTitle;return l.createElement(l.Fragment,null,l.createElement(sl.Z,{title:!r&&"Account Balances",subheader:"Chain ID "+n}),l.createElement(aW.Z,null,l.createElement(w.default,{dense:!1,disablePadding:!0},t&&t.map(function(e,r){return l.createElement(l.Fragment,null,l.createElement(_.default,{disableGutters:!0,key:["acc-balance",n.toString(),r.toString()].join("-")},l.createElement(E.Z,{primary:l.createElement(l.Fragment,null,l.createElement(d.Z,{container:!0,spacing:16},l.createElement(d.Z,{item:!0,xs:12},l.createElement(op,{title:"Address"}),l.createElement(ob,{value:e.address})),l.createElement(d.Z,{item:!0,xs:6},l.createElement(op,{title:"Native Token Balance"}),l.createElement(ob,{value:e.ethBalance||"--"})),l.createElement(d.Z,{item:!0,xs:6},l.createElement(op,{title:"LINK Balance"}),l.createElement(ob,{value:e.linkBalance?gL(e.linkBalance):"--"}))))})),r+1s&&l.createElement(gB.Z,null,l.createElement(ir.Z,null,l.createElement(r7.default,{className:r.footer},l.createElement(aA.Z,{href:"/runs",component:tz},"View More"))))))});function vt(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function vn(){var e=vt(["\n ","\n query FetchRecentJobRuns($offset: Int, $limit: Int) {\n jobRuns(offset: $offset, limit: $limit) {\n results {\n ...RecentJobRunsPayload_ResultsFields\n }\n metadata {\n total\n }\n }\n }\n"]);return vn=function(){return e},e}var vr=5,vi=n0(vn(),g9),va=function(){var e=rv(vi,{variables:{offset:0,limit:vr},fetchPolicy:"cache-and-network"}),t=e.data,n=e.loading,r=e.error;return l.createElement(ve,{data:t,errorMsg:null==r?void 0:r.message,loading:n,maxRunsSize:vr})},vo=function(e){return(0,b.createStyles)({style:{textAlign:"center",padding:2.5*e.spacing.unit,position:"fixed",left:"0",bottom:"0",width:"100%",borderRadius:0},bareAnchor:{color:e.palette.common.black,textDecoration:"none"}})},vs=(0,b.withStyles)(vo)(function(e){var t=e.classes,n=(0,A.v9)(gE.N,A.wU),r=(0,A.I0)();return(0,l.useEffect)(function(){r((0,ty.DQ)())}),l.createElement(ii.default,{className:t.style},l.createElement(x.default,null,"Chainlink Node ",n.version," at commit"," ",l.createElement("a",{target:"_blank",rel:"noopener noreferrer",href:"https://github.com/smartcontractkit/chainlink/commit/".concat(n.commitSHA),className:t.bareAnchor},n.commitSHA)))}),vu=function(e){return(0,b.createStyles)({cell:{borderColor:e.palette.divider,borderTop:"1px solid",borderBottom:"none",paddingTop:2*e.spacing.unit,paddingBottom:2*e.spacing.unit,paddingLeft:2*e.spacing.unit},block:{display:"block"},overflowEllipsis:{textOverflow:"ellipsis",overflow:"hidden"}})},vc=(0,b.withStyles)(vu)(function(e){var t=e.classes,n=e.job;return l.createElement(ir.Z,null,l.createElement(r7.default,{scope:"row",className:t.cell},l.createElement(d.Z,{container:!0,spacing:0},l.createElement(d.Z,{item:!0,xs:12},l.createElement(ih,{href:"/jobs/".concat(n.id),classes:{linkContent:t.block}},l.createElement(x.default,{className:t.overflowEllipsis,variant:"body1",component:"span",color:"primary"},n.name||n.id))),l.createElement(d.Z,{item:!0,xs:12},l.createElement(x.default,{variant:"body1",color:"textSecondary"},"Created ",l.createElement(aO,{tooltip:!0},n.createdAt))))))});function vl(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function vf(){var e=vl(["\n fragment RecentJobsPayload_ResultsFields on Job {\n id\n name\n createdAt\n }\n"]);return vf=function(){return e},e}var vd=n0(vf()),vh=function(){return(0,b.createStyles)({cardHeader:{borderBottom:0},table:{tableLayout:"fixed"}})},vp=(0,b.withStyles)(vh)(function(e){var t,n,r=e.classes,i=e.data,a=e.errorMsg,o=e.loading;return l.createElement(r5.Z,null,l.createElement(sl.Z,{title:"Recent Jobs",className:r.cardHeader}),l.createElement(r8.Z,{className:r.table},l.createElement(r9.Z,null,l.createElement(g$,{visible:o}),l.createElement(gz,{visible:(null===(t=null==i?void 0:i.jobs.results)||void 0===t?void 0:t.length)===0},"No recently created jobs"),l.createElement(gU,{msg:a}),null===(n=null==i?void 0:i.jobs.results)||void 0===n?void 0:n.map(function(e,t){return l.createElement(vc,{job:e,key:t})}))))});function vb(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function vm(){var e=vb(["\n ","\n query FetchRecentJobs($offset: Int, $limit: Int) {\n jobs(offset: $offset, limit: $limit) {\n results {\n ...RecentJobsPayload_ResultsFields\n }\n }\n }\n"]);return vm=function(){return e},e}var vg=5,vv=n0(vm(),vd),vy=function(){var e=rv(vv,{variables:{offset:0,limit:vg},fetchPolicy:"cache-and-network"}),t=e.data,n=e.loading,r=e.error;return l.createElement(vp,{data:t,errorMsg:null==r?void 0:r.message,loading:n})},vw=function(){return l.createElement(ig,null,l.createElement(d.Z,{container:!0},l.createElement(d.Z,{item:!0,xs:8},l.createElement(va,null)),l.createElement(d.Z,{item:!0,xs:4},l.createElement(d.Z,{container:!0},l.createElement(d.Z,{item:!0,xs:12},l.createElement(gY,null)),l.createElement(d.Z,{item:!0,xs:12},l.createElement(vy,null))))),l.createElement(vs,null))},v_=function(){return l.createElement(vw,null)},vE=function(){return l.createElement(v_,null)},vS=n(87239),vk=function(e){switch(e){case"DirectRequestSpec":return"Direct Request";case"FluxMonitorSpec":return"Flux Monitor";default:return e.replace(/Spec$/,"")}},vx=n(5022),vT=n(78718),vM=n.n(vT);function vO(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n1?t-1:0),r=1;r1?t-1:0),r=1;re.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&n.map(function(e){return l.createElement(ir.Z,{key:e.id,style:{cursor:"pointer"},onClick:function(){return r.push("/runs/".concat(e.id))}},l.createElement(r7.default,{className:t.idCell,scope:"row"},l.createElement("div",{className:t.runDetails},l.createElement(x.default,{variant:"h5",color:"primary",component:"span"},e.id))),l.createElement(r7.default,{className:t.stampCell},l.createElement(x.default,{variant:"body1",color:"textSecondary",className:t.stamp},"Created ",l.createElement(aO,{tooltip:!0},e.createdAt))),l.createElement(r7.default,{className:t.statusCell,scope:"row"},l.createElement(x.default,{variant:"body1",className:O()(t.status,yh(t,e.status))},e.status.toLowerCase())))})))}),yb=n(16839),ym=n.n(yb);function yg(e){var t=e.replace(/\w+\s*=\s*<([^>]|[\r\n])*>/g,""),n=ym().read(t),r=n.edges();return n.nodes().map(function(e){var t={id:e,parentIds:r.filter(function(t){return t.w===e}).map(function(e){return e.v})};return Object.keys(n.node(e)).length>0&&(t.attributes=n.node(e)),t})}var yv=n(94164),yy=function(e){var t=e.data,n=[];return(null==t?void 0:t.attributes)&&Object.keys(t.attributes).forEach(function(e){var r;n.push(l.createElement("div",{key:e},l.createElement(x.default,{variant:"body1",color:"textSecondary",component:"div"},l.createElement("b",null,e,":")," ",null===(r=t.attributes)||void 0===r?void 0:r[e])))}),l.createElement("div",null,t&&l.createElement(x.default,{variant:"body1",color:"textPrimary"},l.createElement("b",null,t.id)),n)},yw=n(73343),y_=n(3379),yE=n.n(y_);function yS(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);nwindow.innerWidth?u-r.getBoundingClientRect().width-a:u+a,n=c+r.getBoundingClientRect().height+i>window.innerHeight?c-r.getBoundingClientRect().height-a:c+a,r.style.opacity=String(1),r.style.top="".concat(n,"px"),r.style.left="".concat(t,"px"),r.style.zIndex=String(1)}},h=function(e){var t=document.getElementById("tooltip-d3-chart-".concat(e));t&&(t.style.opacity=String(0),t.style.zIndex=String(-1))};return l.createElement("div",{style:{fontFamily:"sans-serif",fontWeight:"normal"}},l.createElement(yv.kJ,{id:"task-list-graph-d3",data:i,config:s,onMouseOverNode:d,onMouseOutNode:h},"D3 chart"),n.map(function(e){return l.createElement("div",{key:"d3-tooltip-key-".concat(e.id),id:"tooltip-d3-chart-".concat(e.id),style:{position:"absolute",opacity:"0",border:"1px solid rgba(0, 0, 0, 0.1)",padding:yw.r.spacing.unit,background:"white",borderRadius:5,zIndex:-1,inlineSize:"min-content"}},l.createElement(yy,{data:e}))}))};function yL(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);nyY&&l.createElement("div",{className:t.runDetails},l.createElement(aA.Z,{href:"/jobs/".concat(n.id,"/runs"),component:tz},"View more")))),l.createElement(d.Z,{item:!0,xs:12,sm:6},l.createElement(yF,{observationSource:n.observationSource})))});function yH(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&void 0!==arguments[0]?arguments[0]:"";try{return vx.parse(e),!0}catch(t){return!1}})}),wW=function(e){var t=e.initialValues,n=e.onSubmit,r=e.onTOMLChange;return l.createElement(hT,{initialValues:t,validationSchema:wG,onSubmit:n},function(e){var t=e.isSubmitting,n=e.values;return r&&r(n.toml),l.createElement(hR,{"data-testid":"job-form",noValidate:!0},l.createElement(d.Z,{container:!0,spacing:16},l.createElement(d.Z,{item:!0,xs:12},l.createElement(hP,{component:hX,id:"toml",name:"toml",label:"Job Spec (TOML)",required:!0,fullWidth:!0,multiline:!0,rows:10,rowsMax:25,variant:"outlined",autoComplete:"off",FormHelperTextProps:{"data-testid":"toml-helper-text"}})),l.createElement(d.Z,{item:!0,xs:12,md:7},l.createElement(ok.default,{variant:"contained",color:"primary",type:"submit",disabled:t,size:"large"},"Create Job"))))})},wK=n(50109),wV="persistSpec";function wq(e){var t=e.query,n=new URLSearchParams(t).get("definition");return n?(wK.t8(wV,n),{toml:n}):{toml:wK.U2(wV)||""}}var wZ=function(e){var t=e.onSubmit,n=e.onTOMLChange,r=wq({query:(0,h.TH)().search}),i=function(e){var t=e.replace(/[\u200B-\u200D\uFEFF]/g,"");wK.t8("".concat(wV),t),n&&n(t)};return l.createElement(r5.Z,null,l.createElement(sl.Z,{title:"New Job"}),l.createElement(aW.Z,null,l.createElement(wW,{initialValues:r,onSubmit:t,onTOMLChange:i})))};function wX(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n1&&void 0!==arguments[1]?arguments[1]:{},n=t.start,r=void 0===n?6:n,i=t.end,a=void 0===i?4:i;return e.substring(0,r)+"..."+e.substring(e.length-a)}function _M(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0&&void 0!==arguments[0]?arguments[0]:{};return rv(_W,e)},_V=function(){var e=_K({fetchPolicy:"cache-and-network"}),t=e.data,n=e.loading,r=e.error,i=e.refetch;return l.createElement(_U,{loading:n,data:t,errorMsg:null==r?void 0:r.message,refetch:i})},_q=function(e){var t=e.csaKey;return l.createElement(ir.Z,{hover:!0},l.createElement(r7.default,null,l.createElement(x.default,{variant:"body1"},t.publicKey," ",l.createElement(_x,{data:t.publicKey}))))};function _Z(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function _X(){var e=_Z(["\n fragment CSAKeysPayload_ResultsFields on CSAKey {\n id\n publicKey\n }\n"]);return _X=function(){return e},e}var _J=n0(_X()),_Q=function(e){var t,n,r,i=e.data,a=e.errorMsg,o=e.loading,s=e.onCreate;return l.createElement(r5.Z,null,l.createElement(sl.Z,{action:(null===(t=null==i?void 0:i.csaKeys.results)||void 0===t?void 0:t.length)===0&&l.createElement(ok.default,{variant:"outlined",color:"primary",onClick:s},"New CSA Key"),title:"CSA Key",subheader:"Manage your CSA Key"}),l.createElement(r8.Z,null,l.createElement(ie.Z,null,l.createElement(ir.Z,null,l.createElement(r7.default,null,"Public Key"))),l.createElement(r9.Z,null,l.createElement(g$,{visible:o}),l.createElement(gz,{visible:(null===(n=null==i?void 0:i.csaKeys.results)||void 0===n?void 0:n.length)===0}),l.createElement(gU,{msg:a}),null===(r=null==i?void 0:i.csaKeys.results)||void 0===r?void 0:r.map(function(e,t){return l.createElement(_q,{csaKey:e,key:t})}))))};function _1(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0&&void 0!==arguments[0]?arguments[0]:{};return rv(EM,e)};function EA(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0&&void 0!==arguments[0]?arguments[0]:{};return rv(EJ,e)},E3=function(){return oo(EQ)},E4=function(){return oo(E1)},E6=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return rv(E0,e)};function E5(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0&&void 0!==arguments[0]?arguments[0]:{};return rv(SK,e)};function Sq(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n=0)&&Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}function kV(e,t){if(null==e)return{};var n,r,i={},a=Object.keys(e);for(r=0;r=0||(i[n]=e[n]);return i}var kq=function(e){var t=e.run,n=l.useMemo(function(){var e=t.inputs,n=t.outputs,r=t.taskRuns,i=kK(t,["inputs","outputs","taskRuns"]),a={};try{a=JSON.parse(e)}catch(o){a={}}return kW(kz({},i),{inputs:a,outputs:n,taskRuns:r})},[t]);return l.createElement(r5.Z,null,l.createElement(aW.Z,null,l.createElement(kH,{object:n})))};function kZ(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function kX(e){for(var t=1;t0&&l.createElement(kr,{errors:t.allErrors})),l.createElement(d.Z,{item:!0,xs:12},l.createElement(h.rs,null,l.createElement(h.AW,{path:"".concat(n,"/json")},l.createElement(kq,{run:t})),l.createElement(h.AW,{path:n},t.taskRuns.length>0&&l.createElement(kN,{taskRuns:t.taskRuns,observationSource:t.job.observationSource}))))))))};function k5(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function k8(){var e=k5(["\n ","\n query FetchJobRun($id: ID!) {\n jobRun(id: $id) {\n __typename\n ... on JobRun {\n ...JobRunPayload_Fields\n }\n ... on NotFoundError {\n message\n }\n }\n }\n"]);return k8=function(){return e},e}var k9=n0(k8(),k4),k7=function(){var e=rv(k9,{variables:{id:(0,h.UO)().id}}),t=e.data,n=e.loading,r=e.error;if(n)return l.createElement(iR,null);if(r)return l.createElement(iD,{error:r});var i=null==t?void 0:t.jobRun;switch(null==i?void 0:i.__typename){case"JobRun":return l.createElement(k6,{run:i});case"NotFoundError":return l.createElement(oa,null);default:return null}};function xe(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function xt(){var e=xe(["\n fragment JobRunsPayload_ResultsFields on JobRun {\n id\n allErrors\n createdAt\n finishedAt\n status\n job {\n id\n }\n }\n"]);return xt=function(){return e},e}var xn=n0(xt()),xr=function(e){var t=e.loading,n=e.data,r=e.page,i=e.pageSize,a=(0,h.k6)(),o=l.useMemo(function(){return null==n?void 0:n.jobRuns.results.map(function(e){var t,n=e.allErrors,r=e.id,i=e.createdAt;return{id:r,createdAt:i,errors:n,finishedAt:e.finishedAt,status:e.status}})},[n]);return l.createElement(ig,null,l.createElement(d.Z,{container:!0,spacing:32},l.createElement(d.Z,{item:!0,xs:12},l.createElement(iy,null,"Job Runs")),t&&l.createElement(iR,null),n&&o&&l.createElement(d.Z,{item:!0,xs:12},l.createElement(r5.Z,null,l.createElement(yp,{runs:o}),l.createElement(it.Z,{component:"div",count:n.jobRuns.metadata.total,rowsPerPage:i,rowsPerPageOptions:[i],page:r-1,onChangePage:function(e,t){a.push("/runs?page=".concat(t+1,"&per=").concat(i))},onChangeRowsPerPage:function(){},backIconButtonProps:{"aria-label":"prev-page"},nextIconButtonProps:{"aria-label":"next-page"}})))))};function xi(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function xa(){var e=xi(["\n ","\n query FetchJobRuns($offset: Int, $limit: Int) {\n jobRuns(offset: $offset, limit: $limit) {\n results {\n ...JobRunsPayload_ResultsFields\n }\n metadata {\n total\n }\n }\n }\n"]);return xa=function(){return e},e}var xo=n0(xa(),xn),xs=function(){var e=ij(),t=parseInt(e.get("page")||"1",10),n=parseInt(e.get("per")||"25",10),r=rv(xo,{variables:{offset:(t-1)*n,limit:n},fetchPolicy:"cache-and-network"}),i=r.data,a=r.loading,o=r.error;return o?l.createElement(iD,{error:o}):l.createElement(xr,{loading:a,data:i,page:t,pageSize:n})},xu=function(){var e=(0,h.$B)().path;return l.createElement(h.rs,null,l.createElement(h.AW,{exact:!0,path:e},l.createElement(xs,null)),l.createElement(h.AW,{path:"".concat(e,"/:id")},l.createElement(k7,null)))},xc=bv().shape({name:p0().required("Required"),uri:p0().required("Required"),publicKey:p0().required("Required")}),xl=function(e){var t=e.initialValues,n=e.onSubmit;return l.createElement(hT,{initialValues:t,validationSchema:xc,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 Feeds Manager 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 Feeds Manager 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"))))})},xf=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 Feeds Manager"}),l.createElement(aW.Z,null,l.createElement(xl,{initialValues:r,onSubmit:n})))))};function xd(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function xh(){var e=xd(["\n query FetchFeedsManagers {\n feedsManagers {\n results {\n __typename\n id\n name\n uri\n publicKey\n isConnectionActive\n createdAt\n }\n }\n }\n"]);return xh=function(){return e},e}var xp=n0(xh()),xb=function(){return rv(xp)};function xm(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 xF(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 xY(e,t){return xD(e)||xP(e,t)||xB(e,t)||xR()}function xB(e,t){if(e){if("string"==typeof e)return xI(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 xI(e,t)}}var xU=function(e){return"SN_MAIN"===e||"SN_SEPOLIA"===e},xH=bv().shape({chainID:p0().required("Required"),chainType:p0().required("Required"),accountAddr:p0().required("Required"),accountAddrPubKey:p0().nullable(),adminAddr:p0().required("Required"),ocr1Multiaddr:p0().when(["ocr1Enabled","ocr1IsBootstrap"],{is:function(e,t){return e&&t},then:p0().required("Required").nullable()}).nullable(),ocr1P2PPeerID:p0().when(["ocr1Enabled","ocr1IsBootstrap"],{is:function(e,t){return e&&!t},then:p0().required("Required").nullable()}).nullable(),ocr1KeyBundleID:p0().when(["ocr1Enabled","ocr1IsBootstrap"],{is:function(e,t){return e&&!t},then:p0().required("Required").nullable()}).nullable(),ocr2Multiaddr:p0().when(["ocr2Enabled","ocr2IsBootstrap"],{is:function(e,t){return e&&t},then:p0().required("Required").nullable()}).nullable(),ocr2P2PPeerID:p0().when(["ocr2Enabled","ocr2IsBootstrap"],{is:function(e,t){return e&&!t},then:p0().required("Required").nullable()}).nullable(),ocr2KeyBundleID:p0().when(["ocr2Enabled","ocr2IsBootstrap"],{is:function(e,t){return e&&!t},then:p0().required("Required").nullable()}).nullable(),ocr2CommitPluginEnabled:pV().required("Required"),ocr2ExecutePluginEnabled:pV().required("Required"),ocr2MedianPluginEnabled:pV().required("Required"),ocr2MercuryPluginEnabled:pV().required("Required"),ocr2ForwarderAddress:p0().nullable()}),x$=function(e){return(0,b.createStyles)({supportedJobOptionsPaper:{padding:2*e.spacing.unit}})},xz=function(e){var t=e.chainAccounts,n=xj(e,["chainAccounts"]),r=h_(),i=r.values,a=i.chainID,o=i.accountAddr,s=r.setFieldValue,u=xY(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,!xU(a)&&l.createElement(hP,xN({},n,{select:!0,value:c?"custom":o,onChange:h}),t.map(function(e){return l.createElement(tE.default,{key:e.address,value:e.address},e.address)})),xU(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}),xU(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"}})))},xG=(0,b.withStyles)(x$)(function(e){var t=e.classes,n=e.editing,r=void 0!==n&&n,i=e.innerRef,a=e.initialValues,o=e.onSubmit,s=e.chainIDs,u=void 0===s?[]:s,c=e.accounts,f=void 0===c?[]:c,h=e.p2pKeys,p=void 0===h?[]:h,b=e.ocrKeys,m=void 0===b?[]:b,g=e.ocr2Keys,v=void 0===g?[]:g,y=e.showSubmit,w=void 0!==y&&y;return l.createElement(hT,{innerRef:i,initialValues:a,validationSchema:xH,onSubmit:o},function(e){var n=e.values,i=f.filter(function(e){return e.chain.id==n.chainID&&!e.isDisabled});return l.createElement(hR,{"data-testid":"feeds-manager-form",id:"chain-configuration-form",noValidate:!0},l.createElement(d.Z,{container:!0,spacing:16},l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(hP,{component:hX,id:"chainType",name:"chainType",label:"Chain Type",select:!0,required:!0,fullWidth:!0,disabled:!0},l.createElement(tE.default,{key:"EVM",value:"EVM"},"EVM"))),l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(hP,{component:hX,id:"chainID",name:"chainID",label:"Chain ID",required:!0,fullWidth:!0,select:!0,disabled:r,inputProps:{"data-testid":"chainID-input"},FormHelperTextProps:{"data-testid":"chainID-helper-text"}},u.map(function(e){return l.createElement(tE.default,{key:e,value:e},e)}))),l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(xz,{component:hX,id:"accountAddr",name:"accountAddr",label:"Account Address",inputProps:{"data-testid":"accountAddr-input"},required:!0,fullWidth:!0,select:!0,helperText:"The account address used for this chain",chainAccounts:i,FormHelperTextProps:{"data-testid":"accountAddr-helper-text"}})),l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(hP,{component:hX,id:"adminAddr",name:"adminAddr",label:"Admin Address",required:!0,fullWidth:!0,helperText:"The address used for LINK payments",FormHelperTextProps:{"data-testid":"adminAddr-helper-text"}})),l.createElement(d.Z,{item:!0,xs:12},l.createElement(x.default,null,"Supported Job Types")),l.createElement(d.Z,{item:!0,xs:12},l.createElement(hP,{component:h2,name:"fluxMonitorEnabled",type:"checkbox",Label:{label:"Flux Monitor"}})),l.createElement(d.Z,{item:!0,xs:12},l.createElement(hP,{component:h2,name:"ocr1Enabled",type:"checkbox",Label:{label:"OCR"}}),n.ocr1Enabled&&l.createElement(ii.default,{className:t.supportedJobOptionsPaper},l.createElement(d.Z,{container:!0,spacing:8},l.createElement(l.Fragment,null,l.createElement(d.Z,{item:!0,xs:12},l.createElement(hP,{component:h2,name:"ocr1IsBootstrap",type:"checkbox",Label:{label:"Is this node running as a bootstrap peer?"}})),n.ocr1IsBootstrap?l.createElement(d.Z,{item:!0,xs:12},l.createElement(hP,{component:hX,id:"ocr1Multiaddr",name:"ocr1Multiaddr",label:"Multiaddr",required:!0,fullWidth:!0,helperText:"The OCR Multiaddr which oracles use to query for network information",FormHelperTextProps:{"data-testid":"ocr1Multiaddr-helper-text"}})):l.createElement(l.Fragment,null,l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(hP,{component:hX,id:"ocr1P2PPeerID",name:"ocr1P2PPeerID",label:"Peer ID",select:!0,required:!0,fullWidth:!0,helperText:"The Peer ID used for this chain",FormHelperTextProps:{"data-testid":"ocr1P2PPeerID-helper-text"}},p.map(function(e){return l.createElement(tE.default,{key:e.peerID,value:e.peerID},e.peerID)}))),l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(hP,{component:hX,id:"ocr1KeyBundleID",name:"ocr1KeyBundleID",label:"Key Bundle ID",select:!0,required:!0,fullWidth:!0,helperText:"The OCR Key Bundle ID used for this chain",FormHelperTextProps:{"data-testid":"ocr1KeyBundleID-helper-text"}},m.map(function(e){return l.createElement(tE.default,{key:e.id,value:e.id},e.id)})))))))),l.createElement(d.Z,{item:!0,xs:12},l.createElement(hP,{component:h2,name:"ocr2Enabled",type:"checkbox",Label:{label:"OCR2"}}),n.ocr2Enabled&&l.createElement(ii.default,{className:t.supportedJobOptionsPaper},l.createElement(d.Z,{container:!0,spacing:8},l.createElement(l.Fragment,null,l.createElement(d.Z,{item:!0,xs:12},l.createElement(hP,{component:h2,name:"ocr2IsBootstrap",type:"checkbox",Label:{label:"Is this node running as a bootstrap peer?"}})),n.ocr2IsBootstrap?l.createElement(d.Z,{item:!0,xs:12},l.createElement(hP,{component:hX,id:"ocr2Multiaddr",name:"ocr2Multiaddr",label:"Multiaddr",required:!0,fullWidth:!0,helperText:"The OCR2 Multiaddr which oracles use to query for network information",FormHelperTextProps:{"data-testid":"ocr2Multiaddr-helper-text"}})):l.createElement(l.Fragment,null,l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(hP,{component:hX,id:"ocr2P2PPeerID",name:"ocr2P2PPeerID",label:"Peer ID",select:!0,required:!0,fullWidth:!0,helperText:"The Peer ID used for this chain",FormHelperTextProps:{"data-testid":"ocr2P2PPeerID-helper-text"}},p.map(function(e){return l.createElement(tE.default,{key:e.peerID,value:e.peerID},e.peerID)}))),l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(hP,{component:hX,id:"ocr2KeyBundleID",name:"ocr2KeyBundleID",label:"Key Bundle ID",select:!0,required:!0,fullWidth:!0,helperText:"The OCR2 Key Bundle ID used for this chain",FormHelperTextProps:{"data-testid":"ocr2KeyBundleID-helper-text"}},v.filter(function(e){return n.chainType===e.chainType}).map(function(e){return l.createElement(tE.default,{key:e.id,value:e.id},e.id)}))),l.createElement(d.Z,{item:!0,xs:12},l.createElement(x.default,null,"Supported Plugins")),l.createElement(d.Z,{item:!0,xs:6},l.createElement(hP,{component:h2,name:"ocr2CommitPluginEnabled",type:"checkbox",Label:{label:"Commit"}})),l.createElement(d.Z,{item:!0,xs:6},l.createElement(hP,{component:h2,name:"ocr2ExecutePluginEnabled",type:"checkbox",Label:{label:"Execute"}})),l.createElement(d.Z,{item:!0,xs:6},l.createElement(hP,{component:h2,name:"ocr2RebalancerPluginEnabled",type:"checkbox",Label:{label:"Rebalancer"}})),l.createElement(d.Z,{item:!0,xs:6},l.createElement(hP,{component:h2,name:"ocr2MedianPluginEnabled",type:"checkbox",Label:{label:"Median"}})),l.createElement(d.Z,{item:!0,xs:6},l.createElement(hP,{component:h2,name:"ocr2MercuryPluginEnabled",type:"checkbox",Label:{label:"Mercury"}})),l.createElement(d.Z,{item:!0,xs:12,md:12},l.createElement(hP,{component:hX,id:"ocr2ForwarderAddress",name:"ocr2ForwarderAddress",label:"Forwarder Address (optional)",fullWidth:!0,helperText:"The forwarder address from the Operator Forwarder Contract",FormHelperTextProps:{"data-testid":"ocr2ForwarderAddress-helper-text"}}))))))),w&&l.createElement(d.Z,{item:!0,xs:12,md:7},l.createElement(ok.default,{variant:"contained",color:"primary",type:"submit",size:"large"},"Submit"))))})}),xW=function(e){var t=e.onClose,n=e.open,r=e.onSubmit,i=l.useRef(),a=i$({fetchPolicy:"network-only"}).data,o=_K({fetchPolicy:"cache-and-network"}).data,s=SV({fetchPolicy:"cache-and-network"}).data,u=EO({fetchPolicy:"cache-and-network"}).data,c=E2({fetchPolicy:"cache-and-network"}).data,f={chainID:"",chainType:"EVM",accountAddr:"",adminAddr:"",accountAddrPubKey:"",fluxMonitorEnabled:!1,ocr1Enabled:!1,ocr1IsBootstrap:!1,ocr1Multiaddr:"",ocr1P2PPeerID:"",ocr1KeyBundleID:"",ocr2Enabled:!1,ocr2IsBootstrap:!1,ocr2Multiaddr:"",ocr2P2PPeerID:"",ocr2KeyBundleID:"",ocr2CommitPluginEnabled:!1,ocr2ExecutePluginEnabled:!1,ocr2MedianPluginEnabled:!1,ocr2MercuryPluginEnabled:!1,ocr2RebalancerPluginEnabled:!1,ocr2ForwarderAddress:""},d=a?a.chains.results.map(function(e){return e.id}):[],h=o?o.ethKeys.results:[],p=s?s.p2pKeys.results:[],b=u?u.ocrKeyBundles.results:[],m=c?c.ocr2KeyBundles.results:[];return l.createElement(aD.Z,{onClose:t,open:n,disableBackdropClick:!0},l.createElement(oO.Z,{disableTypography:!0},l.createElement(x.default,{variant:"body2"},"New Supported Chain")),l.createElement(oT.Z,null,l.createElement(xG,{innerRef:i,initialValues:f,onSubmit:r,chainIDs:d,accounts:h,p2pKeys:p,ocrKeys:b,ocr2Keys:m})),l.createElement(ox.Z,null,l.createElement(ok.default,{onClick:t},"Cancel"),l.createElement(ok.default,{color:"primary",type:"submit",form:"chain-configuration-form",variant:"contained"},"Submit")))},xK=function(e){var t=e.cfg,n=e.onClose,r=e.open,i=e.onSubmit,a=l.useRef(),o=i$({fetchPolicy:"network-only"}).data,s=_K({fetchPolicy:"cache-and-network"}).data,u=SV({fetchPolicy:"cache-and-network"}).data,c=EO({fetchPolicy:"cache-and-network"}).data,f=E2({fetchPolicy:"cache-and-network"}).data;if(!t)return null;var d={chainID:t.chainID,chainType:"EVM",accountAddr:t.accountAddr,adminAddr:t.adminAddr,accountAddrPubKey:t.accountAddrPubKey,fluxMonitorEnabled:t.fluxMonitorJobConfig.enabled,ocr1Enabled:t.ocr1JobConfig.enabled,ocr1IsBootstrap:t.ocr1JobConfig.isBootstrap,ocr1Multiaddr:t.ocr1JobConfig.multiaddr,ocr1P2PPeerID:t.ocr1JobConfig.p2pPeerID,ocr1KeyBundleID:t.ocr1JobConfig.keyBundleID,ocr2Enabled:t.ocr2JobConfig.enabled,ocr2IsBootstrap:t.ocr2JobConfig.isBootstrap,ocr2Multiaddr:t.ocr2JobConfig.multiaddr,ocr2P2PPeerID:t.ocr2JobConfig.p2pPeerID,ocr2KeyBundleID:t.ocr2JobConfig.keyBundleID,ocr2CommitPluginEnabled:t.ocr2JobConfig.plugins.commit,ocr2ExecutePluginEnabled:t.ocr2JobConfig.plugins.execute,ocr2MedianPluginEnabled:t.ocr2JobConfig.plugins.median,ocr2MercuryPluginEnabled:t.ocr2JobConfig.plugins.mercury,ocr2RebalancerPluginEnabled:t.ocr2JobConfig.plugins.rebalancer,ocr2ForwarderAddress:t.ocr2JobConfig.forwarderAddress},h=o?o.chains.results.map(function(e){return e.id}):[],p=s?s.ethKeys.results:[],b=u?u.p2pKeys.results:[],m=c?c.ocrKeyBundles.results:[],g=f?f.ocr2KeyBundles.results:[];return l.createElement(aD.Z,{onClose:n,open:r,disableBackdropClick:!0},l.createElement(oO.Z,{disableTypography:!0},l.createElement(x.default,{variant:"body2"},"Edit Supported Chain")),l.createElement(oT.Z,null,l.createElement(xG,{innerRef:a,initialValues:d,onSubmit:i,chainIDs:h,accounts:p,p2pKeys:b,ocrKeys:m,ocr2Keys:g,editing:!0})),l.createElement(ox.Z,null,l.createElement(ok.default,{onClick:n},"Cancel"),l.createElement(ok.default,{color:"primary",type:"submit",form:"chain-configuration-form",variant:"contained"},"Submit")))};function xV(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function xq(){var e=xV(["\n fragment FeedsManager_ChainConfigFields on FeedsManagerChainConfig {\n id\n chainID\n chainType\n accountAddr\n adminAddr\n accountAddrPubKey\n fluxMonitorJobConfig {\n enabled\n }\n ocr1JobConfig {\n enabled\n isBootstrap\n multiaddr\n p2pPeerID\n keyBundleID\n }\n ocr2JobConfig {\n enabled\n isBootstrap\n multiaddr\n forwarderAddress\n p2pPeerID\n keyBundleID\n plugins {\n commit\n execute\n median\n mercury\n rebalancer\n }\n }\n }\n"]);return xq=function(){return e},e}function xZ(){var e=xV(["\n ","\n fragment FeedsManagerFields on FeedsManager {\n id\n name\n uri\n publicKey\n isConnectionActive\n chainConfigs {\n ...FeedsManager_ChainConfigFields\n }\n }\n"]);return xZ=function(){return e},e}function xX(){var e=xV(["\n fragment FeedsManager_JobProposalsFields on JobProposal {\n id\n name\n externalJobID\n remoteUUID\n status\n pendingUpdate\n latestSpec {\n createdAt\n version\n }\n }\n"]);return xX=function(){return e},e}function xJ(){var e=xV(["\n ","\n ","\n fragment FeedsManagerPayload_ResultsFields on FeedsManager {\n ...FeedsManagerFields\n jobProposals {\n ...FeedsManager_JobProposalsFields\n }\n }\n"]);return xJ=function(){return e},e}function xQ(){var e=xV(["\n ","\n query FetchFeedManagersWithProposals {\n feedsManagers {\n results {\n ...FeedsManagerPayload_ResultsFields\n }\n }\n }\n"]);return xQ=function(){return e},e}var x1=n0(xq()),x0=n0(xZ(),x1),x2=n0(xX()),x3=n0(xJ(),x0,x2),x4=n0(xQ(),x3),x6=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return rv(x4,e)};function x5(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0?n.feedsManagers.results[0]:void 0;return n&&a?l.createElement(TZ,{manager:a}):l.createElement(h.l_,{to:{pathname:"/feeds_manager/new",state:{from:e}}})},TJ={name:"Chainlink Feeds Manager",uri:"",publicKey:""},TQ=function(e){var t=e.onSubmit;return l.createElement(d.Z,{container:!0},l.createElement(d.Z,{item:!0,xs:12,md:11,lg:9},l.createElement(r5.Z,null,l.createElement(sl.Z,{title:"Register Feeds Manager"}),l.createElement(aW.Z,null,l.createElement(xl,{initialValues:TJ,onSubmit:t})))))};function T1(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);nt.version?e:t})},[o]),g=l.useMemo(function(){return ME(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?MM[c.action].title:"",body:c?MM[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(Md,{open:h,onClose:function(){return p(!1)},initialValues:{definition:m.definition,id:m.id},onSubmit:a}))});function MA(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function ML(){var e=MA(["\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 ML=function(){return e},e}var MC=n0(ML(),Mx),MI=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(Mo,{proposal:a}),l.createElement(d.Z,{container:!0,spacing:32},l.createElement(d.Z,{item:!0,xs:9},l.createElement(Tq,null,"Specs"))),l.createElement(d.Z,{container:!0,spacing:32},l.createElement(d.Z,{item:!0,xs:12},l.createElement(MO,{proposal:a,specs:a.specs,onReject:r,onApprove:t,onCancel:n,onUpdateSpec:i}))))};function MD(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);nU,tA:()=>$,KL:()=>H,Iw:()=>V,DQ:()=>W,cB:()=>T,LO:()=>M,t5:()=>k,qt:()=>x,Jc:()=>C,L7:()=>Y,EO:()=>B});var r,i,a=n(66289),o=n(41800),s=n.n(o),u=n(67932);(i=r||(r={})).IN_PROGRESS="in_progress",i.PENDING_INCOMING_CONFIRMATIONS="pending_incoming_confirmations",i.PENDING_CONNECTION="pending_connection",i.PENDING_BRIDGE="pending_bridge",i.PENDING_SLEEP="pending_sleep",i.ERRORED="errored",i.COMPLETED="completed";var c=n(87013),l=n(19084),f=n(34823);function d(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]j,v2:()=>F});var r=n(66289);function i(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var a="/sessions",o="/sessions",s=function e(t){var n=this;i(this,e),this.api=t,this.createSession=function(e){return n.create(e)},this.destroySession=function(){return n.destroy()},this.create=this.api.createResource(a),this.destroy=this.api.deleteResource(o)};function u(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var c="/v2/bulk_delete_runs",l=function e(t){var n=this;u(this,e),this.api=t,this.bulkDeleteJobRuns=function(e){return n.destroy(e)},this.destroy=this.api.deleteResource(c)};function f(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var d="/v2/chains/evm",h="".concat(d,"/:id"),p=function e(t){var n=this;f(this,e),this.api=t,this.getChains=function(){return n.index()},this.createChain=function(e){return n.create(e)},this.destroyChain=function(e){return n.destroy(void 0,{id:e})},this.updateChain=function(e,t){return n.update(t,{id:e})},this.index=this.api.fetchResource(d),this.create=this.api.createResource(d),this.destroy=this.api.deleteResource(h),this.update=this.api.updateResource(h)};function b(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var m="/v2/keys/evm/chain",g=function e(t){var n=this;b(this,e),this.api=t,this.chain=function(e){var t=new URLSearchParams;t.append("address",e.address),t.append("evmChainID",e.evmChainID),null!==e.nextNonce&&t.append("nextNonce",e.nextNonce),null!==e.abandon&&t.append("abandon",String(e.abandon)),null!==e.enabled&&t.append("enabled",String(e.enabled));var r=m+"?"+t.toString();return n.api.createResource(r)()}};function v(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var y="/v2/jobs",w="".concat(y,"/:specId/runs"),_=function e(t){var n=this;v(this,e),this.api=t,this.createJobRunV2=function(e,t){return n.post(t,{specId:e})},this.post=this.api.createResource(w,!0)};function E(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var S="/v2/log",k=function e(t){var n=this;E(this,e),this.api=t,this.getLogConfig=function(){return n.show()},this.updateLogConfig=function(e){return n.update(e)},this.show=this.api.fetchResource(S),this.update=this.api.updateResource(S)};function x(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var T="/v2/nodes",M=function e(t){var n=this;x(this,e),this.api=t,this.getNodes=function(){return n.index()},this.createNode=function(e){return n.create(e)},this.index=this.api.fetchResource(T),this.create=this.api.createResource(T)};function O(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var A="/v2/enroll_webauthn",L=function e(t){var n=this;O(this,e),this.api=t,this.beginKeyRegistration=function(e){return n.create(e)},this.finishKeyRegistration=function(e){return n.put(e)},this.create=this.api.fetchResource(A),this.put=this.api.createResource(A)};function C(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var I="/v2/build_info",D=function e(t){var n=this;C(this,e),this.api=t,this.show=function(){return n.api.GET(I)()}};function N(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var P=function e(t){N(this,e),this.api=t,this.buildInfo=new D(this.api),this.bulkDeleteRuns=new l(this.api),this.chains=new p(this.api),this.logConfig=new k(this.api),this.nodes=new M(this.api),this.jobs=new _(this.api),this.webauthn=new L(this.api),this.evmKeys=new g(this.api)},R=new r.V0({base:void 0}),j=new s(R),F=new P(R)},1398(e,t,n){"use strict";n.d(t,{Z:()=>d});var r=n(67294),i=n(32316),a=n(83638),o=n(94184),s=n.n(o);function u(){return(u=Object.assign||function(e){for(var t=1;tc});var r=n(67294),i=n(32316);function a(){return(a=Object.assign||function(e){for(var t=1;tx,jK:()=>v});var r=n(67294),i=n(37703),a=n(45697),o=n.n(a),s=n(82204),u=n(71426),c=n(94184),l=n.n(c),f=n(32316),d=function(e){var t=e.palette.success||{},n=e.palette.warning||{};return{base:{paddingLeft:5*e.spacing.unit,paddingRight:5*e.spacing.unit},success:{backgroundColor:t.main,color:t.contrastText},error:{backgroundColor:e.palette.error.dark,color:e.palette.error.contrastText},warning:{backgroundColor:n.contrastText,color:n.main}}},h=function(e){var t,n=e.success,r=e.error,i=e.warning,a=e.classes,o=e.className;return n?t=a.success:r?t=a.error:i&&(t=a.warning),l()(a.base,o,t)},p=function(e){return r.createElement(s.Z,{className:h(e),square:!0},r.createElement(u.default,{variant:"body2",color:"inherit",component:"div"},e.children))};p.defaultProps={success:!1,error:!1,warning:!1},p.propTypes={success:o().bool,error:o().bool,warning:o().bool};let b=(0,f.withStyles)(d)(p);var m=function(){return r.createElement(r.Fragment,null,"Unhandled error. Please help us by opening a"," ",r.createElement("a",{href:"https://github.com/smartcontractkit/chainlink/issues/new"},"bug report"))};let g=m;function v(e){return"string"==typeof e?e:e.component?e.component(e.props):r.createElement(g,null)}function y(e,t){var n;return n="string"==typeof e?e:e.component?e.component(e.props):r.createElement(g,null),r.createElement("p",{key:t},n)}var w=function(e){var t=e.notifications;return r.createElement(b,{error:!0},t.map(y))},_=function(e){var t=e.notifications;return r.createElement(b,{success:!0},t.map(y))},E=function(e){var t=e.errors,n=e.successes;return r.createElement("div",null,(null==t?void 0:t.length)>0&&r.createElement(w,{notifications:t}),n.length>0&&r.createElement(_,{notifications:n}))},S=function(e){return{errors:e.notifications.errors,successes:e.notifications.successes}},k=(0,i.$j)(S)(E);let x=k},9409(e,t,n){"use strict";n.d(t,{ZP:()=>j});var r=n(67294),i=n(37703),a=n(5977),o=n(32316),s=n(1398),u=n(82204),c=n(30060),l=n(71426),f=n(60520),d=n(39814),h=n(57209),p=n(26842),b=n(3950),m=n(5536),g=n(45697),v=n.n(g);let y=n.p+"9f6d832ef97e8493764e.svg";function w(){return(w=Object.assign||function(e){for(var t=1;te.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&_.map(function(e,t){return r.createElement(d.Z,{item:!0,xs:12,key:t},r.createElement(u.Z,{raised:!1,className:v.error},r.createElement(c.Z,null,r.createElement(l.default,{variant:"body1",className:v.errorText},(0,b.jK)(e)))))}),r.createElement(d.Z,{item:!0,xs:12},r.createElement(f.Z,{id:"email",label:"Email",margin:"normal",value:n,onChange:m("email"),error:_.length>0,variant:"outlined",fullWidth:!0})),r.createElement(d.Z,{item:!0,xs:12},r.createElement(f.Z,{id:"password",label:"Password",type:"password",autoComplete:"password",margin:"normal",value:h,onChange:m("password"),error:_.length>0,variant:"outlined",fullWidth:!0})),r.createElement(d.Z,{item:!0,xs:12},r.createElement(d.Z,{container:!0,spacing:0,justify:"center"},r.createElement(d.Z,{item:!0},r.createElement(s.Z,{type:"submit",variant:"primary"},"Access Account")))),y&&r.createElement(l.default,{variant:"body1",color:"textSecondary"},"Signing in...")))))))},P=function(e){return{fetching:e.authentication.fetching,authenticated:e.authentication.allowed,errors:e.notifications.errors}},R=(0,i.$j)(P,x({submitSignIn:p.L7}))(N);let j=(0,h.wU)(e)((0,o.withStyles)(D)(R))},16353(e,t,n){"use strict";n.d(t,{ZP:()=>H,rH:()=>U});var r,i=n(37703),a=n(97779),o=n(9541),s=n(19084);function u(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function c(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:h,t=arguments.length>1?arguments[1]:void 0;switch(t.type){case s.Mk.RECEIVE_SIGNOUT_SUCCESS:case s.Mk.RECEIVE_SIGNIN_SUCCESS:var n={allowed:t.authenticated};return o.Ks(n),f(c({},e,n),{errors:[]});case s.Mk.RECEIVE_SIGNIN_FAIL:var r={allowed:!1};return o.Ks(r),f(c({},e,r),{errors:[]});case s.Mk.RECEIVE_SIGNIN_ERROR:case s.Mk.RECEIVE_SIGNOUT_ERROR:var i={allowed:!1};return o.Ks(i),f(c({},e,i),{errors:t.errors||[]});default:return e}};let b=p;function m(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function g(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:_,t=arguments.length>1?arguments[1]:void 0;return t.type?t.type.startsWith(r.REQUEST)?y(g({},e),{count:e.count+1}):t.type.startsWith(r.RECEIVE)?y(g({},e),{count:Math.max(e.count-1,0)}):t.type.startsWith(r.RESPONSE)?y(g({},e),{count:Math.max(e.count-1,0)}):t.type===s.di.REDIRECT?y(g({},e),{count:0}):e:e};let S=E;function k(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function x(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:O,t=arguments.length>1?arguments[1]:void 0;switch(t.type){case s.di.MATCH_ROUTE:return M(x({},O),{currentUrl:t.pathname});case s.Ih.NOTIFY_SUCCESS:var n={component:t.component,props:t.props};return M(x({},e),{successes:[n],errors:[]});case s.Ih.NOTIFY_SUCCESS_MSG:return M(x({},e),{successes:[t.msg],errors:[]});case s.Ih.NOTIFY_ERROR:var r=t.error.errors,i=null==r?void 0:r.map(function(e){return L(t,e)});return M(x({},e),{successes:[],errors:i});case s.Ih.NOTIFY_ERROR_MSG:return M(x({},e),{successes:[],errors:[t.msg]});case s.Mk.RECEIVE_SIGNIN_FAIL:return M(x({},e),{successes:[],errors:["Your email or password is incorrect. Please try again"]});default:return e}};function L(e,t){return{component:e.component,props:{msg:t.detail}}}let C=A;function I(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function D(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:R,t=arguments.length>1?arguments[1]:void 0;switch(t.type){case s.di.REDIRECT:return P(D({},e),{to:t.to});case s.di.MATCH_ROUTE:return P(D({},e),{to:void 0});default:return e}};let F=j;var Y=n(87013),B=(0,a.UY)({authentication:b,fetching:S,notifications:C,redirect:F,buildInfo:Y.Z});B(void 0,{type:"INITIAL_STATE"});var U=i.v9;let H=B},19084(e,t,n){"use strict";var r,i,a,o,s,u,c,l,f,d;n.d(t,{Ih:()=>i,Mk:()=>a,Y0:()=>s,di:()=>r,jp:()=>o}),n(67294),(u=r||(r={})).REDIRECT="REDIRECT",u.MATCH_ROUTE="MATCH_ROUTE",(c=i||(i={})).NOTIFY_SUCCESS="NOTIFY_SUCCESS",c.NOTIFY_SUCCESS_MSG="NOTIFY_SUCCESS_MSG",c.NOTIFY_ERROR="NOTIFY_ERROR",c.NOTIFY_ERROR_MSG="NOTIFY_ERROR_MSG",(l=a||(a={})).REQUEST_SIGNIN="REQUEST_SIGNIN",l.RECEIVE_SIGNIN_SUCCESS="RECEIVE_SIGNIN_SUCCESS",l.RECEIVE_SIGNIN_FAIL="RECEIVE_SIGNIN_FAIL",l.RECEIVE_SIGNIN_ERROR="RECEIVE_SIGNIN_ERROR",l.RECEIVE_SIGNOUT_SUCCESS="RECEIVE_SIGNOUT_SUCCESS",l.RECEIVE_SIGNOUT_ERROR="RECEIVE_SIGNOUT_ERROR",(f=o||(o={})).RECEIVE_CREATE_ERROR="RECEIVE_CREATE_ERROR",f.RECEIVE_CREATE_SUCCESS="RECEIVE_CREATE_SUCCESS",f.RECEIVE_DELETE_ERROR="RECEIVE_DELETE_ERROR",f.RECEIVE_DELETE_SUCCESS="RECEIVE_DELETE_SUCCESS",f.RECEIVE_UPDATE_ERROR="RECEIVE_UPDATE_ERROR",f.RECEIVE_UPDATE_SUCCESS="RECEIVE_UPDATE_SUCCESS",f.REQUEST_CREATE="REQUEST_CREATE",f.REQUEST_DELETE="REQUEST_DELETE",f.REQUEST_UPDATE="REQUEST_UPDATE",f.UPSERT_CONFIGURATION="UPSERT_CONFIGURATION",f.UPSERT_JOB_RUN="UPSERT_JOB_RUN",f.UPSERT_JOB_RUNS="UPSERT_JOB_RUNS",f.UPSERT_TRANSACTION="UPSERT_TRANSACTION",f.UPSERT_TRANSACTIONS="UPSERT_TRANSACTIONS",f.UPSERT_BUILD_INFO="UPSERT_BUILD_INFO",(d=s||(s={})).FETCH_BUILD_INFO_REQUESTED="FETCH_BUILD_INFO_REQUESTED",d.FETCH_BUILD_INFO_SUCCEEDED="FETCH_BUILD_INFO_SUCCEEDED",d.FETCH_BUILD_INFO_FAILED="FETCH_BUILD_INFO_FAILED"},87013(e,t,n){"use strict";n.d(t,{Y:()=>o,Z:()=>u});var r=n(19084);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:o,t=arguments.length>1?arguments[1]:void 0;return t.type===r.Y0.FETCH_BUILD_INFO_SUCCEEDED?a({},t.buildInfo):e};let u=s},34823(e,t,n){"use strict";n.d(t,{N:()=>r});var r=function(e){return e.buildInfo}},73343(e,t,n){"use strict";n.d(t,{r:()=>u});var r=n(19350),i=n(32316),a=n(59114),o=n(5324),s={props:{MuiGrid:{spacing:3*o.default.unit},MuiCardHeader:{titleTypographyProps:{color:"secondary"}}},palette:{action:{hoverOpacity:.3},primary:{light:"#E5F1FF",main:"#3c40c6",contrastText:"#fff"},secondary:{main:"#3d5170"},success:{light:"#e8faf1",main:r.ek.A700,dark:r.ek[700],contrastText:r.y0.white},warning:{light:"#FFFBF1",main:"#fff6b6",contrastText:"#fad27a"},error:{light:"#ffdada",main:"#f44336",dark:"#d32f2f",contrastText:"#fff"},background:{default:"#f5f6f8",appBar:"#3c40c6"},text:{primary:(0,a.darken)(r.BA.A700,.7),secondary:"#818ea3"},listPendingStatus:{background:"#fef7e5",color:"#fecb4c"},listCompletedStatus:{background:"#e9faf2",color:"#4ed495"}},shape:{borderRadius:o.default.unit},overrides:{MuiButton:{root:{borderRadius:o.default.unit/2,textTransform:"none"},sizeLarge:{padding:void 0,fontSize:void 0,paddingTop:o.default.unit,paddingBottom:o.default.unit,paddingLeft:5*o.default.unit,paddingRight:5*o.default.unit}},MuiTableCell:{body:{fontSize:"1rem"},head:{fontSize:"1rem",fontWeight:400}},MuiCardHeader:{root:{borderBottom:"1px solid rgba(0, 0, 0, 0.12)"},action:{marginTop:-2,marginRight:0,"& >*":{marginLeft:2*o.default.unit}},subheader:{marginTop:.5*o.default.unit}}},typography:{useNextVariants:!0,fontFamily:"-apple-system,BlinkMacSystemFont,Roboto,Helvetica,Arial,sans-serif",button:{textTransform:"none",fontSize:"1.2em"},body1:{fontSize:"1.0rem",fontWeight:400,lineHeight:"1.46429em",color:"rgba(0, 0, 0, 0.87)",letterSpacing:-.4},body2:{fontSize:"1.0rem",fontWeight:500,lineHeight:"1.71429em",color:"rgba(0, 0, 0, 0.87)",letterSpacing:-.4},body1Next:{color:"rgb(29, 29, 29)",fontWeight:400,fontSize:"1rem",lineHeight:1.5,letterSpacing:-.4},body2Next:{color:"rgb(29, 29, 29)",fontWeight:400,fontSize:"0.875rem",lineHeight:1.5,letterSpacing:-.4},display1:{color:"#818ea3",fontSize:"2.125rem",fontWeight:400,lineHeight:"1.20588em",letterSpacing:-.4},display2:{color:"#818ea3",fontSize:"2.8125rem",fontWeight:400,lineHeight:"1.13333em",marginLeft:"-.02em",letterSpacing:-.4},display3:{color:"#818ea3",fontSize:"3.5rem",fontWeight:400,lineHeight:"1.30357em",marginLeft:"-.02em",letterSpacing:-.4},display4:{fontSize:14,fontWeightLight:300,fontWeightMedium:500,fontWeightRegular:400,letterSpacing:-.4},h1:{color:"rgb(29, 29, 29)",fontSize:"6rem",fontWeight:300,lineHeight:1},h2:{color:"rgb(29, 29, 29)",fontSize:"3.75rem",fontWeight:300,lineHeight:1},h3:{color:"rgb(29, 29, 29)",fontSize:"3rem",fontWeight:400,lineHeight:1.04},h4:{color:"rgb(29, 29, 29)",fontSize:"2.125rem",fontWeight:400,lineHeight:1.17},h5:{color:"rgb(29, 29, 29)",fontSize:"1.5rem",fontWeight:400,lineHeight:1.33,letterSpacing:-.4},h6:{fontSize:"0.8rem",fontWeight:450,lineHeight:"1.71429em",color:"rgba(0, 0, 0, 0.87)",letterSpacing:-.4},subheading:{color:"rgb(29, 29, 29)",fontSize:"1rem",fontWeight:400,lineHeight:"1.5em",letterSpacing:-.4},subtitle1:{color:"rgb(29, 29, 29)",fontSize:"1rem",fontWeight:400,lineHeight:1.75,letterSpacing:-.4},subtitle2:{color:"rgb(29, 29, 29)",fontSize:"0.875rem",fontWeight:500,lineHeight:1.57,letterSpacing:-.4}},shadows:["none","0px 1px 3px 0px rgba(0, 0, 0, 0.1),0px 1px 1px 0px rgba(0, 0, 0, 0.04),0px 2px 1px -1px rgba(0, 0, 0, 0.02)","0px 1px 5px 0px rgba(0, 0, 0, 0.1),0px 2px 2px 0px rgba(0, 0, 0, 0.04),0px 3px 1px -2px rgba(0, 0, 0, 0.02)","0px 1px 8px 0px rgba(0, 0, 0, 0.1),0px 3px 4px 0px rgba(0, 0, 0, 0.04),0px 3px 3px -2px rgba(0, 0, 0, 0.02)","0px 2px 4px -1px rgba(0, 0, 0, 0.1),0px 4px 5px 0px rgba(0, 0, 0, 0.04),0px 1px 10px 0px rgba(0, 0, 0, 0.02)","0px 3px 5px -1px rgba(0, 0, 0, 0.1),0px 5px 8px 0px rgba(0, 0, 0, 0.04),0px 1px 14px 0px rgba(0, 0, 0, 0.02)","0px 3px 5px -1px rgba(0, 0, 0, 0.1),0px 6px 10px 0px rgba(0, 0, 0, 0.04),0px 1px 18px 0px rgba(0, 0, 0, 0.02)","0px 4px 5px -2px rgba(0, 0, 0, 0.1),0px 7px 10px 1px rgba(0, 0, 0, 0.04),0px 2px 16px 1px rgba(0, 0, 0, 0.02)","0px 5px 5px -3px rgba(0, 0, 0, 0.1),0px 8px 10px 1px rgba(0, 0, 0, 0.04),0px 3px 14px 2px rgba(0, 0, 0, 0.02)","0px 5px 6px -3px rgba(0, 0, 0, 0.1),0px 9px 12px 1px rgba(0, 0, 0, 0.04),0px 3px 16px 2px rgba(0, 0, 0, 0.02)","0px 6px 6px -3px rgba(0, 0, 0, 0.1),0px 10px 14px 1px rgba(0, 0, 0, 0.04),0px 4px 18px 3px rgba(0, 0, 0, 0.02)","0px 6px 7px -4px rgba(0, 0, 0, 0.1),0px 11px 15px 1px rgba(0, 0, 0, 0.04),0px 4px 20px 3px rgba(0, 0, 0, 0.02)","0px 7px 8px -4px rgba(0, 0, 0, 0.1),0px 12px 17px 2px rgba(0, 0, 0, 0.04),0px 5px 22px 4px rgba(0, 0, 0, 0.02)","0px 7px 8px -4px rgba(0, 0, 0, 0.1),0px 13px 19px 2px rgba(0, 0, 0, 0.04),0px 5px 24px 4px rgba(0, 0, 0, 0.02)","0px 7px 9px -4px rgba(0, 0, 0, 0.1),0px 14px 21px 2px rgba(0, 0, 0, 0.04),0px 5px 26px 4px rgba(0, 0, 0, 0.02)","0px 8px 9px -5px rgba(0, 0, 0, 0.1),0px 15px 22px 2px rgba(0, 0, 0, 0.04),0px 6px 28px 5px rgba(0, 0, 0, 0.02)","0px 8px 10px -5px rgba(0, 0, 0, 0.1),0px 16px 24px 2px rgba(0, 0, 0, 0.04),0px 6px 30px 5px rgba(0, 0, 0, 0.02)","0px 8px 11px -5px rgba(0, 0, 0, 0.1),0px 17px 26px 2px rgba(0, 0, 0, 0.04),0px 6px 32px 5px rgba(0, 0, 0, 0.02)","0px 9px 11px -5px rgba(0, 0, 0, 0.1),0px 18px 28px 2px rgba(0, 0, 0, 0.04),0px 7px 34px 6px rgba(0, 0, 0, 0.02)","0px 9px 12px -6px rgba(0, 0, 0, 0.1),0px 19px 29px 2px rgba(0, 0, 0, 0.04),0px 7px 36px 6px rgba(0, 0, 0, 0.02)","0px 10px 13px -6px rgba(0, 0, 0, 0.1),0px 20px 31px 3px rgba(0, 0, 0, 0.04),0px 8px 38px 7px rgba(0, 0, 0, 0.02)","0px 10px 13px -6px rgba(0, 0, 0, 0.1),0px 21px 33px 3px rgba(0, 0, 0, 0.04),0px 8px 40px 7px rgba(0, 0, 0, 0.02)","0px 10px 14px -6px rgba(0, 0, 0, 0.1),0px 22px 35px 3px rgba(0, 0, 0, 0.04),0px 8px 42px 7px rgba(0, 0, 0, 0.02)","0px 11px 14px -7px rgba(0, 0, 0, 0.1),0px 23px 36px 3px rgba(0, 0, 0, 0.04),0px 9px 44px 8px rgba(0, 0, 0, 0.02)","0px 11px 15px -7px rgba(0, 0, 0, 0.1),0px 24px 38px 3px rgba(0, 0, 0, 0.04),0px 9px 46px 8px rgba(0, 0, 0, 0.02)",]},u=(0,i.createMuiTheme)(s)},66289(e,t,n){"use strict";function r(e){if(void 0===e)throw ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function i(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}function a(){if("undefined"==typeof Reflect||!Reflect.construct||Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],function(){})),!0}catch(e){return!1}}function o(e,t,n){return(o=a()?Reflect.construct:function(e,t,n){var r=[null];r.push.apply(r,t);var i=new(Function.bind.apply(e,r));return n&&f(i,n.prototype),i}).apply(null,arguments)}function s(e){return(s=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function u(e,t){if("function"!=typeof t&&null!==t)throw TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&f(e,t)}function c(e){return -1!==Function.toString.call(e).indexOf("[native code]")}function l(e,t){return t&&("object"===p(t)||"function"==typeof t)?t:r(e)}function f(e,t){return(f=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}n.d(t,{V0:()=>B,_7:()=>v});var d,h,p=function(e){return e&&"undefined"!=typeof Symbol&&e.constructor===Symbol?"symbol":typeof e};function b(e){var t="function"==typeof Map?new Map:void 0;return(b=function(e){if(null===e||!c(e))return e;if("function"!=typeof e)throw TypeError("Super expression must either be null or a function");if(void 0!==t){if(t.has(e))return t.get(e);t.set(e,n)}function n(){return o(e,arguments,s(this).constructor)}return n.prototype=Object.create(e.prototype,{constructor:{value:n,enumerable:!1,writable:!0,configurable:!0}}),f(n,e)})(e)}function m(){if("undefined"==typeof Reflect||!Reflect.construct||Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){})),!0}catch(e){return!1}}function g(e){var t=m();return function(){var n,r=s(e);if(t){var i=s(this).constructor;n=Reflect.construct(r,arguments,i)}else n=r.apply(this,arguments);return l(this,n)}}var v=function(e){u(n,e);var t=g(n);function n(e){var r;return i(this,n),(r=t.call(this,"AuthenticationError(".concat(e.statusText,")"))).errors=[{status:e.status,detail:e},],r}return n}(b(Error)),y=function(e){u(n,e);var t=g(n);function n(e){var r,a=e.errors;return i(this,n),(r=t.call(this,"BadRequestError")).errors=a,r}return n}(b(Error)),w=function(e){u(n,e);var t=g(n);function n(e){var r;return i(this,n),(r=t.call(this,"UnprocessableEntityError")).errors=e,r}return n}(b(Error)),_=function(e){u(n,e);var t=g(n);function n(e){var r;return i(this,n),(r=t.call(this,"ServerError")).errors=e,r}return n}(b(Error)),E=function(e){u(n,e);var t=g(n);function n(e){var r,a=e.errors;return i(this,n),(r=t.call(this,"ConflictError")).errors=a,r}return n}(b(Error)),S=function(e){u(n,e);var t=g(n);function n(e){var r;return i(this,n),(r=t.call(this,"UnknownResponseError(".concat(e.statusText,")"))).errors=[{status:e.status,detail:e.statusText},],r}return n}(b(Error));function k(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:2e4;return Promise.race([fetch(e,t),new Promise(function(e,t){return setTimeout(function(){return t(Error("timeout"))},n)}),])}function x(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]=200&&e.status<300))return[3,2];return[2,e.json()];case 2:if(400!==e.status)return[3,3];return[2,e.json().then(function(e){throw new y(e)})];case 3:if(401!==e.status)return[3,4];throw new v(e);case 4:if(422!==e.status)return[3,6];return[4,$(e)];case 5:throw n=i.sent(),new w(n);case 6:if(409!==e.status)return[3,7];return[2,e.json().then(function(e){throw new E(e)})];case 7:if(!(e.status>=500))return[3,9];return[4,$(e)];case 8:throw r=i.sent(),new _(r);case 9:throw new S(e);case 10:return[2]}})})).apply(this,arguments)}function $(e){return z.apply(this,arguments)}function z(){return(z=j(function(e){return Y(this,function(t){return[2,e.json().then(function(t){return t.errors?t.errors.map(function(t){return{status:e.status,detail:t.detail}}):G(e)}).catch(function(){return G(e)})]})})).apply(this,arguments)}function G(e){return[{status:e.status,detail:e.statusText},]}},50109(e,t,n){"use strict";n.d(t,{LK:()=>o,U2:()=>i,eT:()=>s,t8:()=>a});var r=n(12795);function i(e){return r.ZP.getItem("chainlink.".concat(e))}function a(e,t){r.ZP.setItem("chainlink.".concat(e),t)}function o(e){var t=i(e),n={};if(t)try{return JSON.parse(t)}catch(r){}return n}function s(e,t){a(e,JSON.stringify(t))}},9541(e,t,n){"use strict";n.d(t,{Ks:()=>u,Tp:()=>a,iR:()=>o,pm:()=>s});var r=n(50109),i="persistURL";function a(){return r.U2(i)||""}function o(e){r.t8(i,e)}function s(){return r.LK("authentication")}function u(e){r.eT("authentication",e)}},67121(e,t,n){"use strict";function r(e){var t,n=e.Symbol;return"function"==typeof n?n.observable?t=n.observable:(t=n("observable"),n.observable=t):t="@@observable",t}n.r(t),n.d(t,{default:()=>o}),e=n.hmd(e),i="undefined"!=typeof self?self:"undefined"!=typeof window?window:void 0!==n.g?n.g:e;var i,a=r(i);let o=a},2177(e,t,n){"use strict";n.d(t,{Z:()=>o});var r=!0,i="Invariant failed";function a(e,t){if(!e){if(r)throw Error(i);throw Error(i+": "+(t||""))}}let o=a},11742(e){e.exports=function(){var e=document.getSelection();if(!e.rangeCount)return function(){};for(var t=document.activeElement,n=[],r=0;ru,ZT:()=>i,_T:()=>o,ev:()=>c,mG:()=>s,pi:()=>a});var r=function(e,t){return(r=Object.setPrototypeOf||({__proto__:[]})instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])})(e,t)};function i(e,t){if("function"!=typeof t&&null!==t)throw TypeError("Class extends value "+String(t)+" is not a constructor or null");function n(){this.constructor=e}r(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}var a=function(){return(a=Object.assign||function(e){for(var t,n=1,r=arguments.length;nt.indexOf(r)&&(n[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var i=0,r=Object.getOwnPropertySymbols(e);it.indexOf(r[i])&&Object.prototype.propertyIsEnumerable.call(e,r[i])&&(n[r[i]]=e[r[i]]);return n}function s(e,t,n,r){function i(e){return e instanceof n?e:new n(function(t){t(e)})}return new(n||(n=Promise))(function(n,a){function o(e){try{u(r.next(e))}catch(t){a(t)}}function s(e){try{u(r.throw(e))}catch(t){a(t)}}function u(e){e.done?n(e.value):i(e.value).then(o,s)}u((r=r.apply(e,t||[])).next())})}function u(e,t){var n,r,i,a,o={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return a={next:s(0),throw:s(1),return:s(2)},"function"==typeof Symbol&&(a[Symbol.iterator]=function(){return this}),a;function s(e){return function(t){return u([e,t])}}function u(a){if(n)throw TypeError("Generator is already executing.");for(;o;)try{if(n=1,r&&(i=2&a[0]?r.return:a[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,a[1])).done)return i;switch(r=0,i&&(a=[2&a[0],i.value]),a[0]){case 0:case 1:i=a;break;case 4:return o.label++,{value:a[1],done:!1};case 5:o.label++,r=a[1],a=[0];continue;case 7:a=o.ops.pop(),o.trys.pop();continue;default:if(!(i=(i=o.trys).length>0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]r})},94927(e,t,n){function r(e,t){if(i("noDeprecation"))return e;var n=!1;function r(){if(!n){if(i("throwDeprecation"))throw Error(t);i("traceDeprecation")?console.trace(t):console.warn(t),n=!0}return e.apply(this,arguments)}return r}function i(e){try{if(!n.g.localStorage)return!1}catch(t){return!1}var r=n.g.localStorage[e];return null!=r&&"true"===String(r).toLowerCase()}e.exports=r},42473(e){"use strict";var t=function(){};e.exports=t},84763(e){e.exports=Worker},47529(e){e.exports=n;var t=Object.prototype.hasOwnProperty;function n(){for(var e={},n=0;ne.length)&&(t=e.length);for(var n=0,r=Array(t);n=0)&&Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}e.exports=i,e.exports.__esModule=!0,e.exports.default=e.exports},7071(e){function t(e,t){if(null==e)return{};var n,r,i={},a=Object.keys(e);for(r=0;r=0||(i[n]=e[n]);return i}e.exports=t,e.exports.__esModule=!0,e.exports.default=e.exports},94993(e,t,n){var r=n(18698).default,i=n(66115);function a(e,t){if(t&&("object"===r(t)||"function"==typeof t))return t;if(void 0!==t)throw TypeError("Derived constructors may only return object or undefined");return i(e)}e.exports=a,e.exports.__esModule=!0,e.exports.default=e.exports},6015(e){function t(n,r){return e.exports=t=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(e,t){return e.__proto__=t,e},e.exports.__esModule=!0,e.exports.default=e.exports,t(n,r)}e.exports=t,e.exports.__esModule=!0,e.exports.default=e.exports},861(e,t,n){var r=n(63405),i=n(79498),a=n(86116),o=n(42281);function s(e){return r(e)||i(e)||a(e)||o()}e.exports=s,e.exports.__esModule=!0,e.exports.default=e.exports},18698(e){function t(n){return e.exports=t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},e.exports.__esModule=!0,e.exports.default=e.exports,t(n)}e.exports=t,e.exports.__esModule=!0,e.exports.default=e.exports},86116(e,t,n){var r=n(73897);function i(e,t){if(e){if("string"==typeof e)return r(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);if("Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n)return Array.from(e);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return r(e,t)}}e.exports=i,e.exports.__esModule=!0,e.exports.default=e.exports},1644(e,t,n){"use strict";var r,i;function a(e){return!!e&&e<7}n.d(t,{I:()=>r,O:()=>a}),(i=r||(r={}))[i.loading=1]="loading",i[i.setVariables=2]="setVariables",i[i.fetchMore=3]="fetchMore",i[i.refetch=4]="refetch",i[i.poll=6]="poll",i[i.ready=7]="ready",i[i.error=8]="error"},30990(e,t,n){"use strict";n.d(t,{MS:()=>s,YG:()=>a,cA:()=>c,ls:()=>o});var r=n(70655);n(83952);var i=n(13154),a=Symbol();function o(e){return!!e.extensions&&Array.isArray(e.extensions[a])}function s(e){return e.hasOwnProperty("graphQLErrors")}var u=function(e){var t=(0,r.ev)((0,r.ev)((0,r.ev)([],e.graphQLErrors,!0),e.clientErrors,!0),e.protocolErrors,!0);return e.networkError&&t.push(e.networkError),t.map(function(e){return(0,i.s)(e)&&e.message||"Error message not found."}).join("\n")},c=function(e){function t(n){var r=n.graphQLErrors,i=n.protocolErrors,a=n.clientErrors,o=n.networkError,s=n.errorMessage,c=n.extraInfo,l=e.call(this,s)||this;return l.name="ApolloError",l.graphQLErrors=r||[],l.protocolErrors=i||[],l.clientErrors=a||[],l.networkError=o||null,l.message=s||u(l),l.extraInfo=c,l.__proto__=t.prototype,l}return(0,r.ZT)(t,e),t}(Error)},85317(e,t,n){"use strict";n.d(t,{K:()=>a});var r=n(67294),i=n(30320).aS?Symbol.for("__APOLLO_CONTEXT__"):"__APOLLO_CONTEXT__";function a(){var e=r.createContext[i];return e||(Object.defineProperty(r.createContext,i,{value:e=r.createContext({}),enumerable:!1,writable:!1,configurable:!0}),e.displayName="ApolloContext"),e}},21436(e,t,n){"use strict";n.d(t,{O:()=>i,k:()=>r});var r=Array.isArray;function i(e){return Array.isArray(e)&&e.length>0}},30320(e,t,n){"use strict";n.d(t,{DN:()=>s,JC:()=>l,aS:()=>o,mr:()=>i,sy:()=>a});var r=n(83952),i="function"==typeof WeakMap&&"ReactNative"!==(0,r.wY)(function(){return navigator.product}),a="function"==typeof WeakSet,o="function"==typeof Symbol&&"function"==typeof Symbol.for,s=o&&Symbol.asyncIterator,u="function"==typeof(0,r.wY)(function(){return window.document.createElement}),c=(0,r.wY)(function(){return navigator.userAgent.indexOf("jsdom")>=0})||!1,l=u&&!c},53712(e,t,n){"use strict";function r(){for(var e=[],t=0;tr})},10542(e,t,n){"use strict";n.d(t,{J:()=>o}),n(83952);var r=n(13154);function i(e){var t=new Set([e]);return t.forEach(function(e){(0,r.s)(e)&&a(e)===e&&Object.getOwnPropertyNames(e).forEach(function(n){(0,r.s)(e[n])&&t.add(e[n])})}),e}function a(e){if(__DEV__&&!Object.isFrozen(e))try{Object.freeze(e)}catch(t){if(t instanceof TypeError)return null;throw t}return e}function o(e){return __DEV__&&i(e),e}},14012(e,t,n){"use strict";n.d(t,{J:()=>a});var r=n(70655),i=n(53712);function a(e,t){return(0,i.o)(e,t,t.variables&&{variables:(0,r.pi)((0,r.pi)({},e&&e.variables),t.variables)})}},13154(e,t,n){"use strict";function r(e){return null!==e&&"object"==typeof e}n.d(t,{s:()=>r})},83952(e,t,n){"use strict";n.d(t,{ej:()=>u,kG:()=>c,wY:()=>h});var r,i=n(70655),a="Invariant Violation",o=Object.setPrototypeOf,s=void 0===o?function(e,t){return e.__proto__=t,e}:o,u=function(e){function t(n){void 0===n&&(n=a);var r=e.call(this,"number"==typeof n?a+": "+n+" (see https://github.com/apollographql/invariant-packages)":n)||this;return r.framesToPop=1,r.name=a,s(r,t.prototype),r}return(0,i.ZT)(t,e),t}(Error);function c(e,t){if(!e)throw new u(t)}var l=["debug","log","warn","error","silent"],f=l.indexOf("log");function d(e){return function(){if(l.indexOf(e)>=f)return(console[e]||console.log).apply(console,arguments)}}function h(e){try{return e()}catch(t){}}(r=c||(c={})).debug=d("debug"),r.log=d("log"),r.warn=d("warn"),r.error=d("error");let p=h(function(){return globalThis})||h(function(){return window})||h(function(){return self})||h(function(){return global})||h(function(){return h.constructor("return this")()});var b="__",m=[b,b].join("DEV");function g(){try{return Boolean(__DEV__)}catch(e){return Object.defineProperty(p,m,{value:"production"!==h(function(){return"production"}),enumerable:!1,configurable:!0,writable:!0}),p[m]}}let v=g();function y(e){try{return e()}catch(t){}}var w=y(function(){return globalThis})||y(function(){return window})||y(function(){return self})||y(function(){return global})||y(function(){return y.constructor("return this")()}),_=!1;function E(){!w||y(function(){return"production"})||y(function(){return process})||(Object.defineProperty(w,"process",{value:{env:{NODE_ENV:"production"}},configurable:!0,enumerable:!1,writable:!0}),_=!0)}function S(){_&&(delete w.process,_=!1)}E();var k=n(10143);function x(){return k.H,S()}function T(){__DEV__?c("boolean"==typeof v,v):c("boolean"==typeof v,39)}x(),T()},4942(e,t,n){"use strict";function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}n.d(t,{Z:()=>r})},87462(e,t,n){"use strict";function r(){return(r=Object.assign?Object.assign.bind():function(e){for(var t=1;tr})},51721(e,t,n){"use strict";function r(e,t){return(r=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(e,t){return e.__proto__=t,e})(e,t)}function i(e,t){e.prototype=Object.create(t.prototype),e.prototype.constructor=e,r(e,t)}n.d(t,{Z:()=>i})},63366(e,t,n){"use strict";function r(e,t){if(null==e)return{};var n,r,i={},a=Object.keys(e);for(r=0;r=0||(i[n]=e[n]);return i}n.d(t,{Z:()=>r})},25821(e,t,n){"use strict";n.d(t,{Z:()=>s});var r=n(45695);function i(e){return(i="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}var a=10,o=2;function s(e){return u(e,[])}function u(e,t){switch(i(e)){case"string":return JSON.stringify(e);case"function":return e.name?"[function ".concat(e.name,"]"):"[function]";case"object":if(null===e)return"null";return c(e,t);default:return String(e)}}function c(e,t){if(-1!==t.indexOf(e))return"[Circular]";var n=[].concat(t,[e]),r=d(e);if(void 0!==r){var i=r.call(e);if(i!==e)return"string"==typeof i?i:u(i,n)}else if(Array.isArray(e))return f(e,n);return l(e,n)}function l(e,t){var n=Object.keys(e);return 0===n.length?"{}":t.length>o?"["+h(e)+"]":"{ "+n.map(function(n){var r=u(e[n],t);return n+": "+r}).join(", ")+" }"}function f(e,t){if(0===e.length)return"[]";if(t.length>o)return"[Array]";for(var n=Math.min(a,e.length),r=e.length-n,i=[],s=0;s1&&i.push("... ".concat(r," more items")),"["+i.join(", ")+"]"}function d(e){var t=e[String(r.Z)];return"function"==typeof t?t:"function"==typeof e.inspect?e.inspect:void 0}function h(e){var t=Object.prototype.toString.call(e).replace(/^\[object /,"").replace(/]$/,"");if("Object"===t&&"function"==typeof e.constructor){var n=e.constructor.name;if("string"==typeof n&&""!==n)return n}return t}},45695(e,t,n){"use strict";n.d(t,{Z:()=>i});var r="function"==typeof Symbol&&"function"==typeof Symbol.for?Symbol.for("nodejs.util.inspect.custom"):void 0;let i=r},25217(e,t,n){"use strict";function r(e,t){if(!Boolean(e))throw Error(null!=t?t:"Unexpected invariant triggered.")}n.d(t,{Ye:()=>o,WU:()=>s,UG:()=>u});var i=n(45695);function a(e){var t=e.prototype.toJSON;"function"==typeof t||r(0),e.prototype.inspect=t,i.Z&&(e.prototype[i.Z]=t)}var o=function(){function e(e,t,n){this.start=e.start,this.end=t.end,this.startToken=e,this.endToken=t,this.source=n}return e.prototype.toJSON=function(){return{start:this.start,end:this.end}},e}();a(o);var s=function(){function e(e,t,n,r,i,a,o){this.kind=e,this.start=t,this.end=n,this.line=r,this.column=i,this.value=o,this.prev=a,this.next=null}return e.prototype.toJSON=function(){return{kind:this.kind,value:this.value,line:this.line,column:this.column}},e}();function u(e){return null!=e&&"string"==typeof e.kind}a(s)},87392(e,t,n){"use strict";function r(e){var t=e.split(/\r\n|[\n\r]/g),n=a(e);if(0!==n)for(var r=1;ro&&i(t[s-1]);)--s;return t.slice(o,s).join("\n")}function i(e){for(var t=0;t1&&void 0!==arguments[1]?arguments[1]:"",n=arguments.length>2&&void 0!==arguments[2]&&arguments[2],r=-1===e.indexOf("\n"),i=" "===e[0]||" "===e[0],a='"'===e[e.length-1],o="\\"===e[e.length-1],s=!r||a||o||n,u="";return s&&!(r&&i)&&(u+="\n"+t),u+=t?e.replace(/\n/g,"\n"+t):e,s&&(u+="\n"),'"""'+u.replace(/"""/g,'\\"""')+'"""'}n.d(t,{LZ:()=>o,W7:()=>r})},97359(e,t,n){"use strict";n.d(t,{h:()=>r});var r=Object.freeze({NAME:"Name",DOCUMENT:"Document",OPERATION_DEFINITION:"OperationDefinition",VARIABLE_DEFINITION:"VariableDefinition",SELECTION_SET:"SelectionSet",FIELD:"Field",ARGUMENT:"Argument",FRAGMENT_SPREAD:"FragmentSpread",INLINE_FRAGMENT:"InlineFragment",FRAGMENT_DEFINITION:"FragmentDefinition",VARIABLE:"Variable",INT:"IntValue",FLOAT:"FloatValue",STRING:"StringValue",BOOLEAN:"BooleanValue",NULL:"NullValue",ENUM:"EnumValue",LIST:"ListValue",OBJECT:"ObjectValue",OBJECT_FIELD:"ObjectField",DIRECTIVE:"Directive",NAMED_TYPE:"NamedType",LIST_TYPE:"ListType",NON_NULL_TYPE:"NonNullType",SCHEMA_DEFINITION:"SchemaDefinition",OPERATION_TYPE_DEFINITION:"OperationTypeDefinition",SCALAR_TYPE_DEFINITION:"ScalarTypeDefinition",OBJECT_TYPE_DEFINITION:"ObjectTypeDefinition",FIELD_DEFINITION:"FieldDefinition",INPUT_VALUE_DEFINITION:"InputValueDefinition",INTERFACE_TYPE_DEFINITION:"InterfaceTypeDefinition",UNION_TYPE_DEFINITION:"UnionTypeDefinition",ENUM_TYPE_DEFINITION:"EnumTypeDefinition",ENUM_VALUE_DEFINITION:"EnumValueDefinition",INPUT_OBJECT_TYPE_DEFINITION:"InputObjectTypeDefinition",DIRECTIVE_DEFINITION:"DirectiveDefinition",SCHEMA_EXTENSION:"SchemaExtension",SCALAR_TYPE_EXTENSION:"ScalarTypeExtension",OBJECT_TYPE_EXTENSION:"ObjectTypeExtension",INTERFACE_TYPE_EXTENSION:"InterfaceTypeExtension",UNION_TYPE_EXTENSION:"UnionTypeExtension",ENUM_TYPE_EXTENSION:"EnumTypeExtension",INPUT_OBJECT_TYPE_EXTENSION:"InputObjectTypeExtension"})},10143(e,t,n){"use strict";n.d(t,{H:()=>c,T:()=>l});var r=n(99763),i=n(25821);function a(e,t){if(!Boolean(e))throw Error(t)}let o=function(e,t){return e instanceof t};function s(e,t){for(var n=0;n1&&void 0!==arguments[1]?arguments[1]:"GraphQL request",n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{line:1,column:1};"string"==typeof e||a(0,"Body must be a string. Received: ".concat((0,i.Z)(e),".")),this.body=e,this.name=t,this.locationOffset=n,this.locationOffset.line>0||a(0,"line in locationOffset is 1-indexed and must be positive."),this.locationOffset.column>0||a(0,"column in locationOffset is 1-indexed and must be positive.")}return u(e,[{key:r.YF,get:function(){return"Source"}}]),e}();function l(e){return o(e,c)}},99763(e,t,n){"use strict";n.d(t,{YF:()=>r});var r="function"==typeof Symbol&&null!=Symbol.toStringTag?Symbol.toStringTag:"@@toStringTag"},37452(e){"use strict";e.exports=JSON.parse('{"AElig":"\xc6","AMP":"&","Aacute":"\xc1","Acirc":"\xc2","Agrave":"\xc0","Aring":"\xc5","Atilde":"\xc3","Auml":"\xc4","COPY":"\xa9","Ccedil":"\xc7","ETH":"\xd0","Eacute":"\xc9","Ecirc":"\xca","Egrave":"\xc8","Euml":"\xcb","GT":">","Iacute":"\xcd","Icirc":"\xce","Igrave":"\xcc","Iuml":"\xcf","LT":"<","Ntilde":"\xd1","Oacute":"\xd3","Ocirc":"\xd4","Ograve":"\xd2","Oslash":"\xd8","Otilde":"\xd5","Ouml":"\xd6","QUOT":"\\"","REG":"\xae","THORN":"\xde","Uacute":"\xda","Ucirc":"\xdb","Ugrave":"\xd9","Uuml":"\xdc","Yacute":"\xdd","aacute":"\xe1","acirc":"\xe2","acute":"\xb4","aelig":"\xe6","agrave":"\xe0","amp":"&","aring":"\xe5","atilde":"\xe3","auml":"\xe4","brvbar":"\xa6","ccedil":"\xe7","cedil":"\xb8","cent":"\xa2","copy":"\xa9","curren":"\xa4","deg":"\xb0","divide":"\xf7","eacute":"\xe9","ecirc":"\xea","egrave":"\xe8","eth":"\xf0","euml":"\xeb","frac12":"\xbd","frac14":"\xbc","frac34":"\xbe","gt":">","iacute":"\xed","icirc":"\xee","iexcl":"\xa1","igrave":"\xec","iquest":"\xbf","iuml":"\xef","laquo":"\xab","lt":"<","macr":"\xaf","micro":"\xb5","middot":"\xb7","nbsp":"\xa0","not":"\xac","ntilde":"\xf1","oacute":"\xf3","ocirc":"\xf4","ograve":"\xf2","ordf":"\xaa","ordm":"\xba","oslash":"\xf8","otilde":"\xf5","ouml":"\xf6","para":"\xb6","plusmn":"\xb1","pound":"\xa3","quot":"\\"","raquo":"\xbb","reg":"\xae","sect":"\xa7","shy":"\xad","sup1":"\xb9","sup2":"\xb2","sup3":"\xb3","szlig":"\xdf","thorn":"\xfe","times":"\xd7","uacute":"\xfa","ucirc":"\xfb","ugrave":"\xf9","uml":"\xa8","uuml":"\xfc","yacute":"\xfd","yen":"\xa5","yuml":"\xff"}')},93580(e){"use strict";e.exports=JSON.parse('{"0":"�","128":"€","130":"‚","131":"ƒ","132":"„","133":"…","134":"†","135":"‡","136":"ˆ","137":"‰","138":"Š","139":"‹","140":"Œ","142":"Ž","145":"‘","146":"’","147":"“","148":"”","149":"•","150":"–","151":"—","152":"˜","153":"™","154":"š","155":"›","156":"œ","158":"ž","159":"Ÿ"}')},67946(e){"use strict";e.exports=JSON.parse('{"locale":"en","long":{"year":{"previous":"last year","current":"this year","next":"next year","past":{"one":"{0} year ago","other":"{0} years ago"},"future":{"one":"in {0} year","other":"in {0} years"}},"quarter":{"previous":"last quarter","current":"this quarter","next":"next quarter","past":{"one":"{0} quarter ago","other":"{0} quarters ago"},"future":{"one":"in {0} quarter","other":"in {0} quarters"}},"month":{"previous":"last month","current":"this month","next":"next month","past":{"one":"{0} month ago","other":"{0} months ago"},"future":{"one":"in {0} month","other":"in {0} months"}},"week":{"previous":"last week","current":"this week","next":"next week","past":{"one":"{0} week ago","other":"{0} weeks ago"},"future":{"one":"in {0} week","other":"in {0} weeks"}},"day":{"previous":"yesterday","current":"today","next":"tomorrow","past":{"one":"{0} day ago","other":"{0} days ago"},"future":{"one":"in {0} day","other":"in {0} days"}},"hour":{"current":"this hour","past":{"one":"{0} hour ago","other":"{0} hours ago"},"future":{"one":"in {0} hour","other":"in {0} hours"}},"minute":{"current":"this minute","past":{"one":"{0} minute ago","other":"{0} minutes ago"},"future":{"one":"in {0} minute","other":"in {0} minutes"}},"second":{"current":"now","past":{"one":"{0} second ago","other":"{0} seconds ago"},"future":{"one":"in {0} second","other":"in {0} seconds"}}},"short":{"year":{"previous":"last yr.","current":"this yr.","next":"next yr.","past":"{0} yr. ago","future":"in {0} yr."},"quarter":{"previous":"last qtr.","current":"this qtr.","next":"next qtr.","past":{"one":"{0} qtr. ago","other":"{0} qtrs. ago"},"future":{"one":"in {0} qtr.","other":"in {0} qtrs."}},"month":{"previous":"last mo.","current":"this mo.","next":"next mo.","past":"{0} mo. ago","future":"in {0} mo."},"week":{"previous":"last wk.","current":"this wk.","next":"next wk.","past":"{0} wk. ago","future":"in {0} wk."},"day":{"previous":"yesterday","current":"today","next":"tomorrow","past":{"one":"{0} day ago","other":"{0} days ago"},"future":{"one":"in {0} day","other":"in {0} days"}},"hour":{"current":"this hour","past":"{0} hr. ago","future":"in {0} hr."},"minute":{"current":"this minute","past":"{0} min. ago","future":"in {0} min."},"second":{"current":"now","past":"{0} sec. ago","future":"in {0} sec."}},"narrow":{"year":{"previous":"last yr.","current":"this yr.","next":"next yr.","past":"{0} yr. ago","future":"in {0} yr."},"quarter":{"previous":"last qtr.","current":"this qtr.","next":"next qtr.","past":{"one":"{0} qtr. ago","other":"{0} qtrs. ago"},"future":{"one":"in {0} qtr.","other":"in {0} qtrs."}},"month":{"previous":"last mo.","current":"this mo.","next":"next mo.","past":"{0} mo. ago","future":"in {0} mo."},"week":{"previous":"last wk.","current":"this wk.","next":"next wk.","past":"{0} wk. ago","future":"in {0} wk."},"day":{"previous":"yesterday","current":"today","next":"tomorrow","past":{"one":"{0} day ago","other":"{0} days ago"},"future":{"one":"in {0} day","other":"in {0} days"}},"hour":{"current":"this hour","past":"{0} hr. ago","future":"in {0} hr."},"minute":{"current":"this minute","past":"{0} min. ago","future":"in {0} min."},"second":{"current":"now","past":"{0} sec. ago","future":"in {0} sec."}},"now":{"now":{"current":"now","future":"in a moment","past":"just now"}},"mini":{"year":"{0}yr","month":"{0}mo","week":"{0}wk","day":"{0}d","hour":"{0}h","minute":"{0}m","second":"{0}s","now":"now"},"short-time":{"year":"{0} yr.","month":"{0} mo.","week":"{0} wk.","day":{"one":"{0} day","other":"{0} days"},"hour":"{0} hr.","minute":"{0} min.","second":"{0} sec."},"long-time":{"year":{"one":"{0} year","other":"{0} years"},"month":{"one":"{0} month","other":"{0} months"},"week":{"one":"{0} week","other":"{0} weeks"},"day":{"one":"{0} day","other":"{0} days"},"hour":{"one":"{0} hour","other":"{0} hours"},"minute":{"one":"{0} minute","other":"{0} minutes"},"second":{"one":"{0} second","other":"{0} seconds"}}}')}},__webpack_module_cache__={};function __webpack_require__(e){var t=__webpack_module_cache__[e];if(void 0!==t)return t.exports;var n=__webpack_module_cache__[e]={id:e,loaded:!1,exports:{}};return __webpack_modules__[e].call(n.exports,n,n.exports,__webpack_require__),n.loaded=!0,n.exports}__webpack_require__.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return __webpack_require__.d(t,{a:t}),t},(()=>{var e,t=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__;__webpack_require__.t=function(n,r){if(1&r&&(n=this(n)),8&r||"object"==typeof n&&n&&(4&r&&n.__esModule||16&r&&"function"==typeof n.then))return n;var i=Object.create(null);__webpack_require__.r(i);var a={};e=e||[null,t({}),t([]),t(t)];for(var o=2&r&&n;"object"==typeof o&&!~e.indexOf(o);o=t(o))Object.getOwnPropertyNames(o).forEach(e=>a[e]=()=>n[e]);return a.default=()=>n,__webpack_require__.d(i,a),i}})(),__webpack_require__.d=(e,t)=>{for(var n in t)__webpack_require__.o(t,n)&&!__webpack_require__.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},__webpack_require__.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||Function("return this")()}catch(e){if("object"==typeof window)return window}}(),__webpack_require__.hmd=e=>((e=Object.create(e)).children||(e.children=[]),Object.defineProperty(e,"exports",{enumerable:!0,set(){throw Error("ES Modules may not assign module.exports or exports.*, Use ESM export syntax, instead: "+e.id)}}),e),__webpack_require__.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),__webpack_require__.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},__webpack_require__.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),__webpack_require__.p="/assets/",__webpack_require__.nc=void 0;var __webpack_exports__={};(()=>{"use strict";var e,t,n,r,i=__webpack_require__(32316),a=__webpack_require__(8126),o=__webpack_require__(5690),s=__webpack_require__(30381),u=__webpack_require__.n(s),c=__webpack_require__(67294),l=__webpack_require__(73935),f=__webpack_require__.n(l),d=__webpack_require__(57209),h=__webpack_require__(37703),p=__webpack_require__(97779),b=__webpack_require__(28500);function m(e){return function(t){var n=t.dispatch,r=t.getState;return function(t){return function(i){return"function"==typeof i?i(n,r,e):t(i)}}}}var g=m();g.withExtraArgument=m;let v=g;var y=__webpack_require__(76489);function w(e){return function(t){return function(n){return function(r){n(r);var i=e||document&&document.cookie||"",a=t.getState();if("MATCH_ROUTE"===r.type&&"/signin"!==a.notifications.currentUrl){var o=(0,y.Q)(i);if(o.explorer)try{var s=JSON.parse(o.explorer);if("error"===s.status){var u=_(s.url);n({type:"NOTIFY_ERROR_MSG",msg:u})}}catch(c){n({type:"NOTIFY_ERROR_MSG",msg:"Invalid explorer status"})}}}}}}function _(e){var t="Can't connect to explorer: ".concat(e);return e.match(/^wss?:.+/)?t:"".concat(t,". You must use a websocket.")}var E=__webpack_require__(16353);function S(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n=e.length?{done:!0}:{done:!1,value:e[r++]}}}throw TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function ei(e,t){if(e){if("string"==typeof e)return ea(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);if("Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n)return Array.from(e);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return ea(e,t)}}function ea(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n1,i=!1,a=arguments[1],o=a;return new n(function(n){return t.subscribe({next:function(t){var a=!i;if(i=!0,!a||r)try{o=e(o,t)}catch(s){return n.error(s)}else o=t},error:function(e){n.error(e)},complete:function(){if(!i&&!r)return n.error(TypeError("Cannot reduce an empty sequence"));n.next(o),n.complete()}})})},t.concat=function(){for(var e=this,t=arguments.length,n=Array(t),r=0;r=0&&i.splice(e,1),o()}});i.push(s)},error:function(e){r.error(e)},complete:function(){o()}});function o(){a.closed&&0===i.length&&r.complete()}return function(){i.forEach(function(e){return e.unsubscribe()}),a.unsubscribe()}})},t[ed]=function(){return this},e.from=function(t){var n="function"==typeof this?this:e;if(null==t)throw TypeError(t+" is not an object");var r=ep(t,ed);if(r){var i=r.call(t);if(Object(i)!==i)throw TypeError(i+" is not an object");return em(i)&&i.constructor===n?i:new n(function(e){return i.subscribe(e)})}if(ec("iterator")&&(r=ep(t,ef)))return new n(function(e){ev(function(){if(!e.closed){for(var n,i=er(r.call(t));!(n=i()).done;){var a=n.value;if(e.next(a),e.closed)return}e.complete()}})});if(Array.isArray(t))return new n(function(e){ev(function(){if(!e.closed){for(var n=0;n0))return n.connection.key;var r=n.connection.filter?n.connection.filter:[];r.sort();var i={};return r.forEach(function(e){i[e]=t[e]}),"".concat(n.connection.key,"(").concat(eV(i),")")}var a=e;if(t){var o=eV(t);a+="(".concat(o,")")}return n&&Object.keys(n).forEach(function(e){-1===eW.indexOf(e)&&(n[e]&&Object.keys(n[e]).length?a+="@".concat(e,"(").concat(eV(n[e]),")"):a+="@".concat(e))}),a},{setStringify:function(e){var t=eV;return eV=e,t}}),eV=function(e){return JSON.stringify(e,eq)};function eq(e,t){return(0,eO.s)(t)&&!Array.isArray(t)&&(t=Object.keys(t).sort().reduce(function(e,n){return e[n]=t[n],e},{})),t}function eZ(e,t){if(e.arguments&&e.arguments.length){var n={};return e.arguments.forEach(function(e){var r;return ez(n,e.name,e.value,t)}),n}return null}function eX(e){return e.alias?e.alias.value:e.name.value}function eJ(e,t,n){for(var r,i=0,a=t.selections;it.indexOf(i))throw __DEV__?new Q.ej("illegal argument: ".concat(i)):new Q.ej(27)}return e}function tt(e,t){return t?t(e):eT.of()}function tn(e){return"function"==typeof e?new ta(e):e}function tr(e){return e.request.length<=1}var ti=function(e){function t(t,n){var r=e.call(this,t)||this;return r.link=n,r}return(0,en.ZT)(t,e),t}(Error),ta=function(){function e(e){e&&(this.request=e)}return e.empty=function(){return new e(function(){return eT.of()})},e.from=function(t){return 0===t.length?e.empty():t.map(tn).reduce(function(e,t){return e.concat(t)})},e.split=function(t,n,r){var i=tn(n),a=tn(r||new e(tt));return new e(tr(i)&&tr(a)?function(e){return t(e)?i.request(e)||eT.of():a.request(e)||eT.of()}:function(e,n){return t(e)?i.request(e,n)||eT.of():a.request(e,n)||eT.of()})},e.execute=function(e,t){return e.request(eM(t.context,e7(te(t))))||eT.of()},e.concat=function(t,n){var r=tn(t);if(tr(r))return __DEV__&&Q.kG.warn(new ti("You are calling concat on a terminating link, which will have no effect",r)),r;var i=tn(n);return new e(tr(i)?function(e){return r.request(e,function(e){return i.request(e)||eT.of()})||eT.of()}:function(e,t){return r.request(e,function(e){return i.request(e,t)||eT.of()})||eT.of()})},e.prototype.split=function(t,n,r){return this.concat(e.split(t,n,r||new e(tt)))},e.prototype.concat=function(t){return e.concat(this,t)},e.prototype.request=function(e,t){throw __DEV__?new Q.ej("request is not implemented"):new Q.ej(22)},e.prototype.onError=function(e,t){if(t&&t.error)return t.error(e),!1;throw e},e.prototype.setOnError=function(e){return this.onError=e,this},e}(),to=__webpack_require__(25821),ts=__webpack_require__(25217),tu={Name:[],Document:["definitions"],OperationDefinition:["name","variableDefinitions","directives","selectionSet"],VariableDefinition:["variable","type","defaultValue","directives"],Variable:["name"],SelectionSet:["selections"],Field:["alias","name","arguments","directives","selectionSet"],Argument:["name","value"],FragmentSpread:["name","directives"],InlineFragment:["typeCondition","directives","selectionSet"],FragmentDefinition:["name","variableDefinitions","typeCondition","directives","selectionSet"],IntValue:[],FloatValue:[],StringValue:[],BooleanValue:[],NullValue:[],EnumValue:[],ListValue:["values"],ObjectValue:["fields"],ObjectField:["name","value"],Directive:["name","arguments"],NamedType:["name"],ListType:["type"],NonNullType:["type"],SchemaDefinition:["description","directives","operationTypes"],OperationTypeDefinition:["type"],ScalarTypeDefinition:["description","name","directives"],ObjectTypeDefinition:["description","name","interfaces","directives","fields"],FieldDefinition:["description","name","arguments","type","directives"],InputValueDefinition:["description","name","type","defaultValue","directives"],InterfaceTypeDefinition:["description","name","interfaces","directives","fields"],UnionTypeDefinition:["description","name","directives","types"],EnumTypeDefinition:["description","name","directives","values"],EnumValueDefinition:["description","name","directives"],InputObjectTypeDefinition:["description","name","directives","fields"],DirectiveDefinition:["description","name","arguments","locations"],SchemaExtension:["directives","operationTypes"],ScalarTypeExtension:["name","directives"],ObjectTypeExtension:["name","interfaces","directives","fields"],InterfaceTypeExtension:["name","interfaces","directives","fields"],UnionTypeExtension:["name","directives","types"],EnumTypeExtension:["name","directives","values"],InputObjectTypeExtension:["name","directives","fields"]},tc=Object.freeze({});function tl(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:tu,r=void 0,i=Array.isArray(e),a=[e],o=-1,s=[],u=void 0,c=void 0,l=void 0,f=[],d=[],h=e;do{var p,b=++o===a.length,m=b&&0!==s.length;if(b){if(c=0===d.length?void 0:f[f.length-1],u=l,l=d.pop(),m){if(i)u=u.slice();else{for(var g={},v=0,y=Object.keys(u);v1)for(var r=new tB,i=1;i=0;--a){var o=i[a],s=isNaN(+o)?{}:[];s[o]=t,t=s}n=r.merge(n,t)}),n}var tW=Object.prototype.hasOwnProperty;function tK(e,t){var n,r,i,a,o;return(0,en.mG)(this,void 0,void 0,function(){var s,u,c,l,f,d,h,p,b,m,g,v,y,w,_,E,S,k,x,T,M,O,A;return(0,en.Jh)(this,function(L){switch(L.label){case 0:if(void 0===TextDecoder)throw Error("TextDecoder must be defined in the environment: please import a polyfill.");s=new TextDecoder("utf-8"),u=null===(n=e.headers)||void 0===n?void 0:n.get("content-type"),c="boundary=",l=(null==u?void 0:u.includes(c))?null==u?void 0:u.substring((null==u?void 0:u.indexOf(c))+c.length).replace(/['"]/g,"").replace(/\;(.*)/gm,"").trim():"-",f="\r\n--".concat(l),d="",h=tI(e),p=!0,L.label=1;case 1:if(!p)return[3,3];return[4,h.next()];case 2:for(m=(b=L.sent()).value,g=b.done,v="string"==typeof m?m:s.decode(m),y=d.length-f.length+1,p=!g,d+=v,w=d.indexOf(f,y);w>-1;){if(_=void 0,_=(O=[d.slice(0,w),d.slice(w+f.length),])[0],d=O[1],E=_.indexOf("\r\n\r\n"),(k=(S=tV(_.slice(0,E)))["content-type"])&&-1===k.toLowerCase().indexOf("application/json"))throw Error("Unsupported patch content type: application/json is required.");if(x=_.slice(E))try{T=tq(e,x),Object.keys(T).length>1||"data"in T||"incremental"in T||"errors"in T||"payload"in T?tz(T)?(M={},"payload"in T&&(M=(0,en.pi)({},T.payload)),"errors"in T&&(M=(0,en.pi)((0,en.pi)({},M),{extensions:(0,en.pi)((0,en.pi)({},"extensions"in M?M.extensions:null),((A={})[tN.YG]=T.errors,A))})),null===(r=t.next)||void 0===r||r.call(t,M)):null===(i=t.next)||void 0===i||i.call(t,T):1===Object.keys(T).length&&"hasNext"in T&&!T.hasNext&&(null===(a=t.complete)||void 0===a||a.call(t))}catch(C){tZ(C,t)}w=d.indexOf(f)}return[3,1];case 3:return null===(o=t.complete)||void 0===o||o.call(t),[2]}})})}function tV(e){var t={};return e.split("\n").forEach(function(e){var n=e.indexOf(":");if(n>-1){var r=e.slice(0,n).trim().toLowerCase(),i=e.slice(n+1).trim();t[r]=i}}),t}function tq(e,t){e.status>=300&&tD(e,function(){try{return JSON.parse(t)}catch(e){return t}}(),"Response not successful: Received status code ".concat(e.status));try{return JSON.parse(t)}catch(n){var r=n;throw r.name="ServerParseError",r.response=e,r.statusCode=e.status,r.bodyText=t,r}}function tZ(e,t){var n,r;"AbortError"!==e.name&&(e.result&&e.result.errors&&e.result.data&&(null===(n=t.next)||void 0===n||n.call(t,e.result)),null===(r=t.error)||void 0===r||r.call(t,e))}function tX(e,t,n){tJ(t)(e).then(function(e){var t,r;null===(t=n.next)||void 0===t||t.call(n,e),null===(r=n.complete)||void 0===r||r.call(n)}).catch(function(e){return tZ(e,n)})}function tJ(e){return function(t){return t.text().then(function(e){return tq(t,e)}).then(function(n){return t.status>=300&&tD(t,n,"Response not successful: Received status code ".concat(t.status)),Array.isArray(n)||tW.call(n,"data")||tW.call(n,"errors")||tD(t,n,"Server response was missing for query '".concat(Array.isArray(e)?e.map(function(e){return e.operationName}):e.operationName,"'.")),n})}}var tQ=function(e){if(!e&&"undefined"==typeof fetch)throw __DEV__?new Q.ej("\n\"fetch\" has not been found globally and no fetcher has been configured. To fix this, install a fetch package (like https://www.npmjs.com/package/cross-fetch), instantiate the fetcher, and pass it into your HttpLink constructor. For example:\n\nimport fetch from 'cross-fetch';\nimport { ApolloClient, HttpLink } from '@apollo/client';\nconst client = new ApolloClient({\n link: new HttpLink({ uri: '/graphql', fetch })\n});\n "):new Q.ej(23)},t1=__webpack_require__(87392);function t0(e){return tl(e,{leave:t3})}var t2=80,t3={Name:function(e){return e.value},Variable:function(e){return"$"+e.name},Document:function(e){return t6(e.definitions,"\n\n")+"\n"},OperationDefinition:function(e){var t=e.operation,n=e.name,r=t8("(",t6(e.variableDefinitions,", "),")"),i=t6(e.directives," "),a=e.selectionSet;return n||i||r||"query"!==t?t6([t,t6([n,r]),i,a]," "):a},VariableDefinition:function(e){var t=e.variable,n=e.type,r=e.defaultValue,i=e.directives;return t+": "+n+t8(" = ",r)+t8(" ",t6(i," "))},SelectionSet:function(e){return t5(e.selections)},Field:function(e){var t=e.alias,n=e.name,r=e.arguments,i=e.directives,a=e.selectionSet,o=t8("",t,": ")+n,s=o+t8("(",t6(r,", "),")");return s.length>t2&&(s=o+t8("(\n",t9(t6(r,"\n")),"\n)")),t6([s,t6(i," "),a]," ")},Argument:function(e){var t;return e.name+": "+e.value},FragmentSpread:function(e){var t;return"..."+e.name+t8(" ",t6(e.directives," "))},InlineFragment:function(e){var t=e.typeCondition,n=e.directives,r=e.selectionSet;return t6(["...",t8("on ",t),t6(n," "),r]," ")},FragmentDefinition:function(e){var t=e.name,n=e.typeCondition,r=e.variableDefinitions,i=e.directives,a=e.selectionSet;return"fragment ".concat(t).concat(t8("(",t6(r,", "),")")," ")+"on ".concat(n," ").concat(t8("",t6(i," ")," "))+a},IntValue:function(e){return e.value},FloatValue:function(e){return e.value},StringValue:function(e,t){var n=e.value;return e.block?(0,t1.LZ)(n,"description"===t?"":" "):JSON.stringify(n)},BooleanValue:function(e){return e.value?"true":"false"},NullValue:function(){return"null"},EnumValue:function(e){return e.value},ListValue:function(e){return"["+t6(e.values,", ")+"]"},ObjectValue:function(e){return"{"+t6(e.fields,", ")+"}"},ObjectField:function(e){var t;return e.name+": "+e.value},Directive:function(e){var t;return"@"+e.name+t8("(",t6(e.arguments,", "),")")},NamedType:function(e){return e.name},ListType:function(e){return"["+e.type+"]"},NonNullType:function(e){return e.type+"!"},SchemaDefinition:t4(function(e){var t=e.directives,n=e.operationTypes;return t6(["schema",t6(t," "),t5(n)]," ")}),OperationTypeDefinition:function(e){var t;return e.operation+": "+e.type},ScalarTypeDefinition:t4(function(e){var t;return t6(["scalar",e.name,t6(e.directives," ")]," ")}),ObjectTypeDefinition:t4(function(e){var t=e.name,n=e.interfaces,r=e.directives,i=e.fields;return t6(["type",t,t8("implements ",t6(n," & ")),t6(r," "),t5(i)]," ")}),FieldDefinition:t4(function(e){var t=e.name,n=e.arguments,r=e.type,i=e.directives;return t+(ne(n)?t8("(\n",t9(t6(n,"\n")),"\n)"):t8("(",t6(n,", "),")"))+": "+r+t8(" ",t6(i," "))}),InputValueDefinition:t4(function(e){var t=e.name,n=e.type,r=e.defaultValue,i=e.directives;return t6([t+": "+n,t8("= ",r),t6(i," ")]," ")}),InterfaceTypeDefinition:t4(function(e){var t=e.name,n=e.interfaces,r=e.directives,i=e.fields;return t6(["interface",t,t8("implements ",t6(n," & ")),t6(r," "),t5(i)]," ")}),UnionTypeDefinition:t4(function(e){var t=e.name,n=e.directives,r=e.types;return t6(["union",t,t6(n," "),r&&0!==r.length?"= "+t6(r," | "):""]," ")}),EnumTypeDefinition:t4(function(e){var t=e.name,n=e.directives,r=e.values;return t6(["enum",t,t6(n," "),t5(r)]," ")}),EnumValueDefinition:t4(function(e){var t;return t6([e.name,t6(e.directives," ")]," ")}),InputObjectTypeDefinition:t4(function(e){var t=e.name,n=e.directives,r=e.fields;return t6(["input",t,t6(n," "),t5(r)]," ")}),DirectiveDefinition:t4(function(e){var t=e.name,n=e.arguments,r=e.repeatable,i=e.locations;return"directive @"+t+(ne(n)?t8("(\n",t9(t6(n,"\n")),"\n)"):t8("(",t6(n,", "),")"))+(r?" repeatable":"")+" on "+t6(i," | ")}),SchemaExtension:function(e){var t=e.directives,n=e.operationTypes;return t6(["extend schema",t6(t," "),t5(n)]," ")},ScalarTypeExtension:function(e){var t;return t6(["extend scalar",e.name,t6(e.directives," ")]," ")},ObjectTypeExtension:function(e){var t=e.name,n=e.interfaces,r=e.directives,i=e.fields;return t6(["extend type",t,t8("implements ",t6(n," & ")),t6(r," "),t5(i)]," ")},InterfaceTypeExtension:function(e){var t=e.name,n=e.interfaces,r=e.directives,i=e.fields;return t6(["extend interface",t,t8("implements ",t6(n," & ")),t6(r," "),t5(i)]," ")},UnionTypeExtension:function(e){var t=e.name,n=e.directives,r=e.types;return t6(["extend union",t,t6(n," "),r&&0!==r.length?"= "+t6(r," | "):""]," ")},EnumTypeExtension:function(e){var t=e.name,n=e.directives,r=e.values;return t6(["extend enum",t,t6(n," "),t5(r)]," ")},InputObjectTypeExtension:function(e){var t=e.name,n=e.directives,r=e.fields;return t6(["extend input",t,t6(n," "),t5(r)]," ")}};function t4(e){return function(t){return t6([t.description,e(t)],"\n")}}function t6(e){var t,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";return null!==(t=null==e?void 0:e.filter(function(e){return e}).join(n))&&void 0!==t?t:""}function t5(e){return t8("{\n",t9(t6(e,"\n")),"\n}")}function t8(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"";return null!=t&&""!==t?e+t+n:""}function t9(e){return t8(" ",e.replace(/\n/g,"\n "))}function t7(e){return -1!==e.indexOf("\n")}function ne(e){return null!=e&&e.some(t7)}var nt,nn,nr,ni={http:{includeQuery:!0,includeExtensions:!1,preserveHeaderCase:!1},headers:{accept:"*/*","content-type":"application/json"},options:{method:"POST"}},na=function(e,t){return t(e)};function no(e,t){for(var n=[],r=2;rObject.create(null),{forEach:nv,slice:ny}=Array.prototype,{hasOwnProperty:nw}=Object.prototype;class n_{constructor(e=!0,t=ng){this.weakness=e,this.makeData=t}lookup(...e){return this.lookupArray(e)}lookupArray(e){let t=this;return nv.call(e,e=>t=t.getChildTrie(e)),nw.call(t,"data")?t.data:t.data=this.makeData(ny.call(e))}peek(...e){return this.peekArray(e)}peekArray(e){let t=this;for(let n=0,r=e.length;t&&n=0;--o)t.definitions[o].kind===nL.h.OPERATION_DEFINITION&&++a;var s=nN(e),u=e.some(function(e){return e.remove}),c=function(e){return u&&e&&e.some(s)},l=new Map,f=!1,d={enter:function(e){if(c(e.directives))return f=!0,null}},h=tl(t,{Field:d,InlineFragment:d,VariableDefinition:{enter:function(){return!1}},Variable:{enter:function(e,t,n,r,a){var o=i(a);o&&o.variables.add(e.name.value)}},FragmentSpread:{enter:function(e,t,n,r,a){if(c(e.directives))return f=!0,null;var o=i(a);o&&o.fragmentSpreads.add(e.name.value)}},FragmentDefinition:{enter:function(e,t,n,r){l.set(JSON.stringify(r),e)},leave:function(e,t,n,i){return e===l.get(JSON.stringify(i))?e:a>0&&e.selectionSet.selections.every(function(e){return e.kind===nL.h.FIELD&&"__typename"===e.name.value})?(r(e.name.value).removed=!0,f=!0,null):void 0}},Directive:{leave:function(e){if(s(e))return f=!0,null}}});if(!f)return t;var p=function(e){return e.transitiveVars||(e.transitiveVars=new Set(e.variables),e.removed||e.fragmentSpreads.forEach(function(t){p(r(t)).transitiveVars.forEach(function(t){e.transitiveVars.add(t)})})),e},b=new Set;h.definitions.forEach(function(e){e.kind===nL.h.OPERATION_DEFINITION?p(n(e.name&&e.name.value)).fragmentSpreads.forEach(function(e){b.add(e)}):e.kind!==nL.h.FRAGMENT_DEFINITION||0!==a||r(e.name.value).removed||b.add(e.name.value)}),b.forEach(function(e){p(r(e)).fragmentSpreads.forEach(function(e){b.add(e)})});var m=function(e){return!!(!b.has(e)||r(e).removed)},g={enter:function(e){if(m(e.name.value))return null}};return nD(tl(h,{FragmentSpread:g,FragmentDefinition:g,OperationDefinition:{leave:function(e){if(e.variableDefinitions){var t=p(n(e.name&&e.name.value)).transitiveVars;if(t.size0},t.prototype.tearDownQuery=function(){this.isTornDown||(this.concast&&this.observer&&(this.concast.removeObserver(this.observer),delete this.concast,delete this.observer),this.stopPolling(),this.subscriptions.forEach(function(e){return e.unsubscribe()}),this.subscriptions.clear(),this.queryManager.stopQuery(this.queryId),this.observers.clear(),this.isTornDown=!0)},t}(eT);function n4(e){var t=e.options,n=t.fetchPolicy,r=t.nextFetchPolicy;return"cache-and-network"===n||"network-only"===n?e.reobserve({fetchPolicy:"cache-first",nextFetchPolicy:function(){return(this.nextFetchPolicy=r,"function"==typeof r)?r.apply(this,arguments):n}}):e.reobserve()}function n6(e){__DEV__&&Q.kG.error("Unhandled error",e.message,e.stack)}function n5(e){__DEV__&&e&&__DEV__&&Q.kG.debug("Missing cache result fields: ".concat(JSON.stringify(e)),e)}function n8(e){return"network-only"===e||"no-cache"===e||"standby"===e}nK(n3);function n9(e){return e.kind===nL.h.FIELD||e.kind===nL.h.FRAGMENT_SPREAD||e.kind===nL.h.INLINE_FRAGMENT}function n7(e){return e.kind===Kind.SCALAR_TYPE_DEFINITION||e.kind===Kind.OBJECT_TYPE_DEFINITION||e.kind===Kind.INTERFACE_TYPE_DEFINITION||e.kind===Kind.UNION_TYPE_DEFINITION||e.kind===Kind.ENUM_TYPE_DEFINITION||e.kind===Kind.INPUT_OBJECT_TYPE_DEFINITION}function re(e){return e.kind===Kind.SCALAR_TYPE_EXTENSION||e.kind===Kind.OBJECT_TYPE_EXTENSION||e.kind===Kind.INTERFACE_TYPE_EXTENSION||e.kind===Kind.UNION_TYPE_EXTENSION||e.kind===Kind.ENUM_TYPE_EXTENSION||e.kind===Kind.INPUT_OBJECT_TYPE_EXTENSION}var rt=function(){return Object.create(null)},rn=Array.prototype,rr=rn.forEach,ri=rn.slice,ra=function(){function e(e,t){void 0===e&&(e=!0),void 0===t&&(t=rt),this.weakness=e,this.makeData=t}return e.prototype.lookup=function(){for(var e=[],t=0;tclass{constructor(){this.id=["slot",rc++,Date.now(),Math.random().toString(36).slice(2),].join(":")}hasValue(){for(let e=rs;e;e=e.parent)if(this.id in e.slots){let t=e.slots[this.id];if(t===ru)break;return e!==rs&&(rs.slots[this.id]=t),!0}return rs&&(rs.slots[this.id]=ru),!1}getValue(){if(this.hasValue())return rs.slots[this.id]}withValue(e,t,n,r){let i={__proto__:null,[this.id]:e},a=rs;rs={parent:a,slots:i};try{return t.apply(r,n)}finally{rs=a}}static bind(e){let t=rs;return function(){let n=rs;try{return rs=t,e.apply(this,arguments)}finally{rs=n}}}static noContext(e,t,n){if(!rs)return e.apply(n,t);{let r=rs;try{return rs=null,e.apply(n,t)}finally{rs=r}}}};function rf(e){try{return e()}catch(t){}}let rd="@wry/context:Slot",rh=rf(()=>globalThis)||rf(()=>global)||Object.create(null),rp=rh,rb=rp[rd]||Array[rd]||function(e){try{Object.defineProperty(rp,rd,{value:e,enumerable:!1,writable:!1,configurable:!0})}finally{return e}}(rl()),{bind:rm,noContext:rg}=rb;function rv(){}var ry=function(){function e(e,t){void 0===e&&(e=1/0),void 0===t&&(t=rv),this.max=e,this.dispose=t,this.map=new Map,this.newest=null,this.oldest=null}return e.prototype.has=function(e){return this.map.has(e)},e.prototype.get=function(e){var t=this.getNode(e);return t&&t.value},e.prototype.getNode=function(e){var t=this.map.get(e);if(t&&t!==this.newest){var n=t.older,r=t.newer;r&&(r.older=n),n&&(n.newer=r),t.older=this.newest,t.older.newer=t,t.newer=null,this.newest=t,t===this.oldest&&(this.oldest=r)}return t},e.prototype.set=function(e,t){var n=this.getNode(e);return n?n.value=t:(n={key:e,value:t,newer:null,older:this.newest},this.newest&&(this.newest.newer=n),this.newest=n,this.oldest=this.oldest||n,this.map.set(e,n),n.value)},e.prototype.clean=function(){for(;this.oldest&&this.map.size>this.max;)this.delete(this.oldest.key)},e.prototype.delete=function(e){var t=this.map.get(e);return!!t&&(t===this.newest&&(this.newest=t.older),t===this.oldest&&(this.oldest=t.newer),t.newer&&(t.newer.older=t.older),t.older&&(t.older.newer=t.newer),this.map.delete(e),this.dispose(t.value,e),!0)},e}(),rw=new rb,r_=Object.prototype.hasOwnProperty,rE=void 0===(n=Array.from)?function(e){var t=[];return e.forEach(function(e){return t.push(e)}),t}:n;function rS(e){var t=e.unsubscribe;"function"==typeof t&&(e.unsubscribe=void 0,t())}var rk=[],rx=100;function rT(e,t){if(!e)throw Error(t||"assertion failure")}function rM(e,t){var n=e.length;return n>0&&n===t.length&&e[n-1]===t[n-1]}function rO(e){switch(e.length){case 0:throw Error("unknown value");case 1:return e[0];case 2:throw e[1]}}function rA(e){return e.slice(0)}var rL=function(){function e(t){this.fn=t,this.parents=new Set,this.childValues=new Map,this.dirtyChildren=null,this.dirty=!0,this.recomputing=!1,this.value=[],this.deps=null,++e.count}return e.prototype.peek=function(){if(1===this.value.length&&!rN(this))return rC(this),this.value[0]},e.prototype.recompute=function(e){return rT(!this.recomputing,"already recomputing"),rC(this),rN(this)?rI(this,e):rO(this.value)},e.prototype.setDirty=function(){this.dirty||(this.dirty=!0,this.value.length=0,rR(this),rS(this))},e.prototype.dispose=function(){var e=this;this.setDirty(),rH(this),rF(this,function(t,n){t.setDirty(),r$(t,e)})},e.prototype.forget=function(){this.dispose()},e.prototype.dependOn=function(e){e.add(this),this.deps||(this.deps=rk.pop()||new Set),this.deps.add(e)},e.prototype.forgetDeps=function(){var e=this;this.deps&&(rE(this.deps).forEach(function(t){return t.delete(e)}),this.deps.clear(),rk.push(this.deps),this.deps=null)},e.count=0,e}();function rC(e){var t=rw.getValue();if(t)return e.parents.add(t),t.childValues.has(e)||t.childValues.set(e,[]),rN(e)?rY(t,e):rB(t,e),t}function rI(e,t){return rH(e),rw.withValue(e,rD,[e,t]),rz(e,t)&&rP(e),rO(e.value)}function rD(e,t){e.recomputing=!0,e.value.length=0;try{e.value[0]=e.fn.apply(null,t)}catch(n){e.value[1]=n}e.recomputing=!1}function rN(e){return e.dirty||!!(e.dirtyChildren&&e.dirtyChildren.size)}function rP(e){e.dirty=!1,!rN(e)&&rj(e)}function rR(e){rF(e,rY)}function rj(e){rF(e,rB)}function rF(e,t){var n=e.parents.size;if(n)for(var r=rE(e.parents),i=0;i0&&e.childValues.forEach(function(t,n){r$(e,n)}),e.forgetDeps(),rT(null===e.dirtyChildren)}function r$(e,t){t.parents.delete(e),e.childValues.delete(t),rU(e,t)}function rz(e,t){if("function"==typeof e.subscribe)try{rS(e),e.unsubscribe=e.subscribe.apply(null,t)}catch(n){return e.setDirty(),!1}return!0}var rG={setDirty:!0,dispose:!0,forget:!0};function rW(e){var t=new Map,n=e&&e.subscribe;function r(e){var r=rw.getValue();if(r){var i=t.get(e);i||t.set(e,i=new Set),r.dependOn(i),"function"==typeof n&&(rS(i),i.unsubscribe=n(e))}}return r.dirty=function(e,n){var r=t.get(e);if(r){var i=n&&r_.call(rG,n)?n:"setDirty";rE(r).forEach(function(e){return e[i]()}),t.delete(e),rS(r)}},r}function rK(){var e=new ra("function"==typeof WeakMap);return function(){return e.lookupArray(arguments)}}var rV=rK(),rq=new Set;function rZ(e,t){void 0===t&&(t=Object.create(null));var n=new ry(t.max||65536,function(e){return e.dispose()}),r=t.keyArgs,i=t.makeCacheKey||rK(),a=function(){var a=i.apply(null,r?r.apply(null,arguments):arguments);if(void 0===a)return e.apply(null,arguments);var o=n.get(a);o||(n.set(a,o=new rL(e)),o.subscribe=t.subscribe,o.forget=function(){return n.delete(a)});var s=o.recompute(Array.prototype.slice.call(arguments));return n.set(a,o),rq.add(n),rw.hasValue()||(rq.forEach(function(e){return e.clean()}),rq.clear()),s};function o(e){var t=n.get(e);t&&t.setDirty()}function s(e){var t=n.get(e);if(t)return t.peek()}function u(e){return n.delete(e)}return Object.defineProperty(a,"size",{get:function(){return n.map.size},configurable:!1,enumerable:!1}),a.dirtyKey=o,a.dirty=function(){o(i.apply(null,arguments))},a.peekKey=s,a.peek=function(){return s(i.apply(null,arguments))},a.forgetKey=u,a.forget=function(){return u(i.apply(null,arguments))},a.makeCacheKey=i,a.getKey=r?function(){return i.apply(null,r.apply(null,arguments))}:i,Object.freeze(a)}var rX=new rb,rJ=new WeakMap;function rQ(e){var t=rJ.get(e);return t||rJ.set(e,t={vars:new Set,dep:rW()}),t}function r1(e){rQ(e).vars.forEach(function(t){return t.forgetCache(e)})}function r0(e){rQ(e).vars.forEach(function(t){return t.attachCache(e)})}function r2(e){var t=new Set,n=new Set,r=function(a){if(arguments.length>0){if(e!==a){e=a,t.forEach(function(e){rQ(e).dep.dirty(r),r3(e)});var o=Array.from(n);n.clear(),o.forEach(function(t){return t(e)})}}else{var s=rX.getValue();s&&(i(s),rQ(s).dep(r))}return e};r.onNextChange=function(e){return n.add(e),function(){n.delete(e)}};var i=r.attachCache=function(e){return t.add(e),rQ(e).vars.add(r),r};return r.forgetCache=function(e){return t.delete(e)},r}function r3(e){e.broadcastWatches&&e.broadcastWatches()}var r4=function(){function e(e){var t=e.cache,n=e.client,r=e.resolvers,i=e.fragmentMatcher;this.selectionsToResolveCache=new WeakMap,this.cache=t,n&&(this.client=n),r&&this.addResolvers(r),i&&this.setFragmentMatcher(i)}return e.prototype.addResolvers=function(e){var t=this;this.resolvers=this.resolvers||{},Array.isArray(e)?e.forEach(function(e){t.resolvers=tj(t.resolvers,e)}):this.resolvers=tj(this.resolvers,e)},e.prototype.setResolvers=function(e){this.resolvers={},this.addResolvers(e)},e.prototype.getResolvers=function(){return this.resolvers||{}},e.prototype.runResolvers=function(e){var t=e.document,n=e.remoteResult,r=e.context,i=e.variables,a=e.onlyRunForcedResolvers,o=void 0!==a&&a;return(0,en.mG)(this,void 0,void 0,function(){return(0,en.Jh)(this,function(e){return t?[2,this.resolveDocument(t,n.data,r,i,this.fragmentMatcher,o).then(function(e){return(0,en.pi)((0,en.pi)({},n),{data:e.result})})]:[2,n]})})},e.prototype.setFragmentMatcher=function(e){this.fragmentMatcher=e},e.prototype.getFragmentMatcher=function(){return this.fragmentMatcher},e.prototype.clientQuery=function(e){return tb(["client"],e)&&this.resolvers?e:null},e.prototype.serverQuery=function(e){return n$(e)},e.prototype.prepareContext=function(e){var t=this.cache;return(0,en.pi)((0,en.pi)({},e),{cache:t,getCacheKey:function(e){return t.identify(e)}})},e.prototype.addExportedVariables=function(e,t,n){return void 0===t&&(t={}),void 0===n&&(n={}),(0,en.mG)(this,void 0,void 0,function(){return(0,en.Jh)(this,function(r){return e?[2,this.resolveDocument(e,this.buildRootValueFromCache(e,t)||{},this.prepareContext(n),t).then(function(e){return(0,en.pi)((0,en.pi)({},t),e.exportedVariables)})]:[2,(0,en.pi)({},t)]})})},e.prototype.shouldForceResolvers=function(e){var t=!1;return tl(e,{Directive:{enter:function(e){if("client"===e.name.value&&e.arguments&&(t=e.arguments.some(function(e){return"always"===e.name.value&&"BooleanValue"===e.value.kind&&!0===e.value.value})))return tc}}}),t},e.prototype.buildRootValueFromCache=function(e,t){return this.cache.diff({query:nH(e),variables:t,returnPartialData:!0,optimistic:!1}).result},e.prototype.resolveDocument=function(e,t,n,r,i,a){return void 0===n&&(n={}),void 0===r&&(r={}),void 0===i&&(i=function(){return!0}),void 0===a&&(a=!1),(0,en.mG)(this,void 0,void 0,function(){var o,s,u,c,l,f,d,h,p,b,m;return(0,en.Jh)(this,function(g){return o=e8(e),s=e4(e),u=eL(s),c=this.collectSelectionsToResolve(o,u),f=(l=o.operation)?l.charAt(0).toUpperCase()+l.slice(1):"Query",d=this,h=d.cache,p=d.client,b={fragmentMap:u,context:(0,en.pi)((0,en.pi)({},n),{cache:h,client:p}),variables:r,fragmentMatcher:i,defaultOperationType:f,exportedVariables:{},selectionsToResolve:c,onlyRunForcedResolvers:a},m=!1,[2,this.resolveSelectionSet(o.selectionSet,m,t,b).then(function(e){return{result:e,exportedVariables:b.exportedVariables}})]})})},e.prototype.resolveSelectionSet=function(e,t,n,r){return(0,en.mG)(this,void 0,void 0,function(){var i,a,o,s,u,c=this;return(0,en.Jh)(this,function(l){return i=r.fragmentMap,a=r.context,o=r.variables,s=[n],u=function(e){return(0,en.mG)(c,void 0,void 0,function(){var u,c;return(0,en.Jh)(this,function(l){return(t||r.selectionsToResolve.has(e))&&td(e,o)?eQ(e)?[2,this.resolveField(e,t,n,r).then(function(t){var n;void 0!==t&&s.push(((n={})[eX(e)]=t,n))})]:(e1(e)?u=e:(u=i[e.name.value],__DEV__?(0,Q.kG)(u,"No fragment named ".concat(e.name.value)):(0,Q.kG)(u,11)),u&&u.typeCondition&&(c=u.typeCondition.name.value,r.fragmentMatcher(n,c,a)))?[2,this.resolveSelectionSet(u.selectionSet,t,n,r).then(function(e){s.push(e)})]:[2]:[2]})})},[2,Promise.all(e.selections.map(u)).then(function(){return tF(s)})]})})},e.prototype.resolveField=function(e,t,n,r){return(0,en.mG)(this,void 0,void 0,function(){var i,a,o,s,u,c,l,f,d,h=this;return(0,en.Jh)(this,function(p){return n?(i=r.variables,a=e.name.value,o=eX(e),s=a!==o,c=Promise.resolve(u=n[o]||n[a]),(!r.onlyRunForcedResolvers||this.shouldForceResolvers(e))&&(l=n.__typename||r.defaultOperationType,(f=this.resolvers&&this.resolvers[l])&&(d=f[s?a:o])&&(c=Promise.resolve(rX.withValue(this.cache,d,[n,eZ(e,i),r.context,{field:e,fragmentMap:r.fragmentMap},])))),[2,c.then(function(n){if(void 0===n&&(n=u),e.directives&&e.directives.forEach(function(e){"export"===e.name.value&&e.arguments&&e.arguments.forEach(function(e){"as"===e.name.value&&"StringValue"===e.value.kind&&(r.exportedVariables[e.value.value]=n)})}),!e.selectionSet||null==n)return n;var i,a,o=null!==(a=null===(i=e.directives)||void 0===i?void 0:i.some(function(e){return"client"===e.name.value}))&&void 0!==a&&a;return Array.isArray(n)?h.resolveSubSelectedArray(e,t||o,n,r):e.selectionSet?h.resolveSelectionSet(e.selectionSet,t||o,n,r):void 0})]):[2,null]})})},e.prototype.resolveSubSelectedArray=function(e,t,n,r){var i=this;return Promise.all(n.map(function(n){return null===n?null:Array.isArray(n)?i.resolveSubSelectedArray(e,t,n,r):e.selectionSet?i.resolveSelectionSet(e.selectionSet,t,n,r):void 0}))},e.prototype.collectSelectionsToResolve=function(e,t){var n=function(e){return!Array.isArray(e)},r=this.selectionsToResolveCache;function i(e){if(!r.has(e)){var a=new Set;r.set(e,a),tl(e,{Directive:function(e,t,r,i,o){"client"===e.name.value&&o.forEach(function(e){n(e)&&n9(e)&&a.add(e)})},FragmentSpread:function(e,r,o,s,u){var c=t[e.name.value];__DEV__?(0,Q.kG)(c,"No fragment named ".concat(e.name.value)):(0,Q.kG)(c,12);var l=i(c);l.size>0&&(u.forEach(function(e){n(e)&&n9(e)&&a.add(e)}),a.add(e),l.forEach(function(e){a.add(e)}))}})}return r.get(e)}return i(e)},e}(),r6=new(t_.mr?WeakMap:Map);function r5(e,t){var n=e[t];"function"==typeof n&&(e[t]=function(){return r6.set(e,(r6.get(e)+1)%1e15),n.apply(this,arguments)})}function r8(e){e.notifyTimeout&&(clearTimeout(e.notifyTimeout),e.notifyTimeout=void 0)}var r9=function(){function e(e,t){void 0===t&&(t=e.generateQueryId()),this.queryId=t,this.listeners=new Set,this.document=null,this.lastRequestId=1,this.subscriptions=new Set,this.stopped=!1,this.dirty=!1,this.observableQuery=null;var n=this.cache=e.cache;r6.has(n)||(r6.set(n,0),r5(n,"evict"),r5(n,"modify"),r5(n,"reset"))}return e.prototype.init=function(e){var t=e.networkStatus||nZ.I.loading;return this.variables&&this.networkStatus!==nZ.I.loading&&!(0,nm.D)(this.variables,e.variables)&&(t=nZ.I.setVariables),(0,nm.D)(e.variables,this.variables)||(this.lastDiff=void 0),Object.assign(this,{document:e.document,variables:e.variables,networkError:null,graphQLErrors:this.graphQLErrors||[],networkStatus:t}),e.observableQuery&&this.setObservableQuery(e.observableQuery),e.lastRequestId&&(this.lastRequestId=e.lastRequestId),this},e.prototype.reset=function(){r8(this),this.dirty=!1},e.prototype.getDiff=function(e){void 0===e&&(e=this.variables);var t=this.getDiffOptions(e);if(this.lastDiff&&(0,nm.D)(t,this.lastDiff.options))return this.lastDiff.diff;this.updateWatch(this.variables=e);var n=this.observableQuery;if(n&&"no-cache"===n.options.fetchPolicy)return{complete:!1};var r=this.cache.diff(t);return this.updateLastDiff(r,t),r},e.prototype.updateLastDiff=function(e,t){this.lastDiff=e?{diff:e,options:t||this.getDiffOptions()}:void 0},e.prototype.getDiffOptions=function(e){var t;return void 0===e&&(e=this.variables),{query:this.document,variables:e,returnPartialData:!0,optimistic:!0,canonizeResults:null===(t=this.observableQuery)||void 0===t?void 0:t.options.canonizeResults}},e.prototype.setDiff=function(e){var t=this,n=this.lastDiff&&this.lastDiff.diff;this.updateLastDiff(e),this.dirty||(0,nm.D)(n&&n.result,e&&e.result)||(this.dirty=!0,this.notifyTimeout||(this.notifyTimeout=setTimeout(function(){return t.notify()},0)))},e.prototype.setObservableQuery=function(e){var t=this;e!==this.observableQuery&&(this.oqListener&&this.listeners.delete(this.oqListener),this.observableQuery=e,e?(e.queryInfo=this,this.listeners.add(this.oqListener=function(){t.getDiff().fromOptimisticTransaction?e.observe():n4(e)})):delete this.oqListener)},e.prototype.notify=function(){var e=this;r8(this),this.shouldNotify()&&this.listeners.forEach(function(t){return t(e)}),this.dirty=!1},e.prototype.shouldNotify=function(){if(!this.dirty||!this.listeners.size)return!1;if((0,nZ.O)(this.networkStatus)&&this.observableQuery){var e=this.observableQuery.options.fetchPolicy;if("cache-only"!==e&&"cache-and-network"!==e)return!1}return!0},e.prototype.stop=function(){if(!this.stopped){this.stopped=!0,this.reset(),this.cancel(),this.cancel=e.prototype.cancel,this.subscriptions.forEach(function(e){return e.unsubscribe()});var t=this.observableQuery;t&&t.stopPolling()}},e.prototype.cancel=function(){},e.prototype.updateWatch=function(e){var t=this;void 0===e&&(e=this.variables);var n=this.observableQuery;if(!n||"no-cache"!==n.options.fetchPolicy){var r=(0,en.pi)((0,en.pi)({},this.getDiffOptions(e)),{watcher:this,callback:function(e){return t.setDiff(e)}});this.lastWatch&&(0,nm.D)(r,this.lastWatch)||(this.cancel(),this.cancel=this.cache.watch(this.lastWatch=r))}},e.prototype.resetLastWrite=function(){this.lastWrite=void 0},e.prototype.shouldWrite=function(e,t){var n=this.lastWrite;return!(n&&n.dmCount===r6.get(this.cache)&&(0,nm.D)(t,n.variables)&&(0,nm.D)(e.data,n.result.data))},e.prototype.markResult=function(e,t,n,r){var i=this,a=new tB,o=(0,tP.O)(e.errors)?e.errors.slice(0):[];if(this.reset(),"incremental"in e&&(0,tP.O)(e.incremental)){var s=tG(this.getDiff().result,e);e.data=s}else if("hasNext"in e&&e.hasNext){var u=this.getDiff();e.data=a.merge(u.result,e.data)}this.graphQLErrors=o,"no-cache"===n.fetchPolicy?this.updateLastDiff({result:e.data,complete:!0},this.getDiffOptions(n.variables)):0!==r&&(r7(e,n.errorPolicy)?this.cache.performTransaction(function(a){if(i.shouldWrite(e,n.variables))a.writeQuery({query:t,data:e.data,variables:n.variables,overwrite:1===r}),i.lastWrite={result:e,variables:n.variables,dmCount:r6.get(i.cache)};else if(i.lastDiff&&i.lastDiff.diff.complete){e.data=i.lastDiff.diff.result;return}var o=i.getDiffOptions(n.variables),s=a.diff(o);i.stopped||i.updateWatch(n.variables),i.updateLastDiff(s,o),s.complete&&(e.data=s.result)}):this.lastWrite=void 0)},e.prototype.markReady=function(){return this.networkError=null,this.networkStatus=nZ.I.ready},e.prototype.markError=function(e){return this.networkStatus=nZ.I.error,this.lastWrite=void 0,this.reset(),e.graphQLErrors&&(this.graphQLErrors=e.graphQLErrors),e.networkError&&(this.networkError=e.networkError),e},e}();function r7(e,t){void 0===t&&(t="none");var n="ignore"===t||"all"===t,r=!nO(e);return!r&&n&&e.data&&(r=!0),r}var ie=Object.prototype.hasOwnProperty,it=function(){function e(e){var t=e.cache,n=e.link,r=e.defaultOptions,i=e.queryDeduplication,a=void 0!==i&&i,o=e.onBroadcast,s=e.ssrMode,u=void 0!==s&&s,c=e.clientAwareness,l=void 0===c?{}:c,f=e.localState,d=e.assumeImmutableResults;this.clientAwareness={},this.queries=new Map,this.fetchCancelFns=new Map,this.transformCache=new(t_.mr?WeakMap:Map),this.queryIdCounter=1,this.requestIdCounter=1,this.mutationIdCounter=1,this.inFlightLinkObservables=new Map,this.cache=t,this.link=n,this.defaultOptions=r||Object.create(null),this.queryDeduplication=a,this.clientAwareness=l,this.localState=f||new r4({cache:t}),this.ssrMode=u,this.assumeImmutableResults=!!d,(this.onBroadcast=o)&&(this.mutationStore=Object.create(null))}return e.prototype.stop=function(){var e=this;this.queries.forEach(function(t,n){e.stopQueryNoBroadcast(n)}),this.cancelPendingFetches(__DEV__?new Q.ej("QueryManager stopped while query was in flight"):new Q.ej(14))},e.prototype.cancelPendingFetches=function(e){this.fetchCancelFns.forEach(function(t){return t(e)}),this.fetchCancelFns.clear()},e.prototype.mutate=function(e){var t,n,r=e.mutation,i=e.variables,a=e.optimisticResponse,o=e.updateQueries,s=e.refetchQueries,u=void 0===s?[]:s,c=e.awaitRefetchQueries,l=void 0!==c&&c,f=e.update,d=e.onQueryUpdated,h=e.fetchPolicy,p=void 0===h?(null===(t=this.defaultOptions.mutate)||void 0===t?void 0:t.fetchPolicy)||"network-only":h,b=e.errorPolicy,m=void 0===b?(null===(n=this.defaultOptions.mutate)||void 0===n?void 0:n.errorPolicy)||"none":b,g=e.keepRootFields,v=e.context;return(0,en.mG)(this,void 0,void 0,function(){var e,t,n,s,c,h;return(0,en.Jh)(this,function(b){switch(b.label){case 0:if(__DEV__?(0,Q.kG)(r,"mutation option is required. You must specify your GraphQL document in the mutation option."):(0,Q.kG)(r,15),__DEV__?(0,Q.kG)("network-only"===p||"no-cache"===p,"Mutations support only 'network-only' or 'no-cache' fetchPolicy strings. The default `network-only` behavior automatically writes mutation results to the cache. Passing `no-cache` skips the cache write."):(0,Q.kG)("network-only"===p||"no-cache"===p,16),e=this.generateMutationId(),n=(t=this.transform(r)).document,s=t.hasClientExports,r=this.cache.transformForLink(n),i=this.getVariables(r,i),!s)return[3,2];return[4,this.localState.addExportedVariables(r,i,v)];case 1:i=b.sent(),b.label=2;case 2:return c=this.mutationStore&&(this.mutationStore[e]={mutation:r,variables:i,loading:!0,error:null}),a&&this.markMutationOptimistic(a,{mutationId:e,document:r,variables:i,fetchPolicy:p,errorPolicy:m,context:v,updateQueries:o,update:f,keepRootFields:g}),this.broadcastQueries(),h=this,[2,new Promise(function(t,n){return nM(h.getObservableFromLink(r,(0,en.pi)((0,en.pi)({},v),{optimisticResponse:a}),i,!1),function(t){if(nO(t)&&"none"===m)throw new tN.cA({graphQLErrors:nA(t)});c&&(c.loading=!1,c.error=null);var n=(0,en.pi)({},t);return"function"==typeof u&&(u=u(n)),"ignore"===m&&nO(n)&&delete n.errors,h.markMutationResult({mutationId:e,result:n,document:r,variables:i,fetchPolicy:p,errorPolicy:m,context:v,update:f,updateQueries:o,awaitRefetchQueries:l,refetchQueries:u,removeOptimistic:a?e:void 0,onQueryUpdated:d,keepRootFields:g})}).subscribe({next:function(e){h.broadcastQueries(),"hasNext"in e&&!1!==e.hasNext||t(e)},error:function(t){c&&(c.loading=!1,c.error=t),a&&h.cache.removeOptimistic(e),h.broadcastQueries(),n(t instanceof tN.cA?t:new tN.cA({networkError:t}))}})})]}})})},e.prototype.markMutationResult=function(e,t){var n=this;void 0===t&&(t=this.cache);var r=e.result,i=[],a="no-cache"===e.fetchPolicy;if(!a&&r7(r,e.errorPolicy)){if(tU(r)||i.push({result:r.data,dataId:"ROOT_MUTATION",query:e.document,variables:e.variables}),tU(r)&&(0,tP.O)(r.incremental)){var o=t.diff({id:"ROOT_MUTATION",query:this.transform(e.document).asQuery,variables:e.variables,optimistic:!1,returnPartialData:!0}),s=void 0;o.result&&(s=tG(o.result,r)),void 0!==s&&(r.data=s,i.push({result:s,dataId:"ROOT_MUTATION",query:e.document,variables:e.variables}))}var u=e.updateQueries;u&&this.queries.forEach(function(e,a){var o=e.observableQuery,s=o&&o.queryName;if(s&&ie.call(u,s)){var c,l=u[s],f=n.queries.get(a),d=f.document,h=f.variables,p=t.diff({query:d,variables:h,returnPartialData:!0,optimistic:!1}),b=p.result;if(p.complete&&b){var m=l(b,{mutationResult:r,queryName:d&&e3(d)||void 0,queryVariables:h});m&&i.push({result:m,dataId:"ROOT_QUERY",query:d,variables:h})}}})}if(i.length>0||e.refetchQueries||e.update||e.onQueryUpdated||e.removeOptimistic){var c=[];if(this.refetchQueries({updateCache:function(t){a||i.forEach(function(e){return t.write(e)});var o=e.update,s=!t$(r)||tU(r)&&!r.hasNext;if(o){if(!a){var u=t.diff({id:"ROOT_MUTATION",query:n.transform(e.document).asQuery,variables:e.variables,optimistic:!1,returnPartialData:!0});u.complete&&("incremental"in(r=(0,en.pi)((0,en.pi)({},r),{data:u.result}))&&delete r.incremental,"hasNext"in r&&delete r.hasNext)}s&&o(t,r,{context:e.context,variables:e.variables})}a||e.keepRootFields||!s||t.modify({id:"ROOT_MUTATION",fields:function(e,t){var n=t.fieldName,r=t.DELETE;return"__typename"===n?e:r}})},include:e.refetchQueries,optimistic:!1,removeOptimistic:e.removeOptimistic,onQueryUpdated:e.onQueryUpdated||null}).forEach(function(e){return c.push(e)}),e.awaitRefetchQueries||e.onQueryUpdated)return Promise.all(c).then(function(){return r})}return Promise.resolve(r)},e.prototype.markMutationOptimistic=function(e,t){var n=this,r="function"==typeof e?e(t.variables):e;return this.cache.recordOptimisticTransaction(function(e){try{n.markMutationResult((0,en.pi)((0,en.pi)({},t),{result:{data:r}}),e)}catch(i){__DEV__&&Q.kG.error(i)}},t.mutationId)},e.prototype.fetchQuery=function(e,t,n){return this.fetchQueryObservable(e,t,n).promise},e.prototype.getQueryStore=function(){var e=Object.create(null);return this.queries.forEach(function(t,n){e[n]={variables:t.variables,networkStatus:t.networkStatus,networkError:t.networkError,graphQLErrors:t.graphQLErrors}}),e},e.prototype.resetErrors=function(e){var t=this.queries.get(e);t&&(t.networkError=void 0,t.graphQLErrors=[])},e.prototype.transform=function(e){var t=this.transformCache;if(!t.has(e)){var n=this.cache.transformDocument(e),r=nY(n),i=this.localState.clientQuery(n),a=r&&this.localState.serverQuery(r),o={document:n,hasClientExports:tm(n),hasForcedResolvers:this.localState.shouldForceResolvers(n),clientQuery:i,serverQuery:a,defaultVars:e9(e2(n)),asQuery:(0,en.pi)((0,en.pi)({},n),{definitions:n.definitions.map(function(e){return"OperationDefinition"===e.kind&&"query"!==e.operation?(0,en.pi)((0,en.pi)({},e),{operation:"query"}):e})})},s=function(e){e&&!t.has(e)&&t.set(e,o)};s(e),s(n),s(i),s(a)}return t.get(e)},e.prototype.getVariables=function(e,t){return(0,en.pi)((0,en.pi)({},this.transform(e).defaultVars),t)},e.prototype.watchQuery=function(e){void 0===(e=(0,en.pi)((0,en.pi)({},e),{variables:this.getVariables(e.query,e.variables)})).notifyOnNetworkStatusChange&&(e.notifyOnNetworkStatusChange=!1);var t=new r9(this),n=new n3({queryManager:this,queryInfo:t,options:e});return this.queries.set(n.queryId,t),t.init({document:n.query,observableQuery:n,variables:n.variables}),n},e.prototype.query=function(e,t){var n=this;return void 0===t&&(t=this.generateQueryId()),__DEV__?(0,Q.kG)(e.query,"query option is required. You must specify your GraphQL document in the query option."):(0,Q.kG)(e.query,17),__DEV__?(0,Q.kG)("Document"===e.query.kind,'You must wrap the query string in a "gql" tag.'):(0,Q.kG)("Document"===e.query.kind,18),__DEV__?(0,Q.kG)(!e.returnPartialData,"returnPartialData option only supported on watchQuery."):(0,Q.kG)(!e.returnPartialData,19),__DEV__?(0,Q.kG)(!e.pollInterval,"pollInterval option only supported on watchQuery."):(0,Q.kG)(!e.pollInterval,20),this.fetchQuery(t,e).finally(function(){return n.stopQuery(t)})},e.prototype.generateQueryId=function(){return String(this.queryIdCounter++)},e.prototype.generateRequestId=function(){return this.requestIdCounter++},e.prototype.generateMutationId=function(){return String(this.mutationIdCounter++)},e.prototype.stopQueryInStore=function(e){this.stopQueryInStoreNoBroadcast(e),this.broadcastQueries()},e.prototype.stopQueryInStoreNoBroadcast=function(e){var t=this.queries.get(e);t&&t.stop()},e.prototype.clearStore=function(e){return void 0===e&&(e={discardWatches:!0}),this.cancelPendingFetches(__DEV__?new Q.ej("Store reset while query was in flight (not completed in link chain)"):new Q.ej(21)),this.queries.forEach(function(e){e.observableQuery?e.networkStatus=nZ.I.loading:e.stop()}),this.mutationStore&&(this.mutationStore=Object.create(null)),this.cache.reset(e)},e.prototype.getObservableQueries=function(e){var t=this;void 0===e&&(e="active");var n=new Map,r=new Map,i=new Set;return Array.isArray(e)&&e.forEach(function(e){"string"==typeof e?r.set(e,!1):eN(e)?r.set(t.transform(e).document,!1):(0,eO.s)(e)&&e.query&&i.add(e)}),this.queries.forEach(function(t,i){var a=t.observableQuery,o=t.document;if(a){if("all"===e){n.set(i,a);return}var s=a.queryName;if("standby"===a.options.fetchPolicy||"active"===e&&!a.hasObservers())return;("active"===e||s&&r.has(s)||o&&r.has(o))&&(n.set(i,a),s&&r.set(s,!0),o&&r.set(o,!0))}}),i.size&&i.forEach(function(e){var r=nG("legacyOneTimeQuery"),i=t.getQuery(r).init({document:e.query,variables:e.variables}),a=new n3({queryManager:t,queryInfo:i,options:(0,en.pi)((0,en.pi)({},e),{fetchPolicy:"network-only"})});(0,Q.kG)(a.queryId===r),i.setObservableQuery(a),n.set(r,a)}),__DEV__&&r.size&&r.forEach(function(e,t){!e&&__DEV__&&Q.kG.warn("Unknown query ".concat("string"==typeof t?"named ":"").concat(JSON.stringify(t,null,2)," requested in refetchQueries options.include array"))}),n},e.prototype.reFetchObservableQueries=function(e){var t=this;void 0===e&&(e=!1);var n=[];return this.getObservableQueries(e?"all":"active").forEach(function(r,i){var a=r.options.fetchPolicy;r.resetLastResults(),(e||"standby"!==a&&"cache-only"!==a)&&n.push(r.refetch()),t.getQuery(i).setDiff(null)}),this.broadcastQueries(),Promise.all(n)},e.prototype.setObservableQuery=function(e){this.getQuery(e.queryId).setObservableQuery(e)},e.prototype.startGraphQLSubscription=function(e){var t=this,n=e.query,r=e.fetchPolicy,i=e.errorPolicy,a=e.variables,o=e.context,s=void 0===o?{}:o;n=this.transform(n).document,a=this.getVariables(n,a);var u=function(e){return t.getObservableFromLink(n,s,e).map(function(a){"no-cache"!==r&&(r7(a,i)&&t.cache.write({query:n,result:a.data,dataId:"ROOT_SUBSCRIPTION",variables:e}),t.broadcastQueries());var o=nO(a),s=(0,tN.ls)(a);if(o||s){var u={};throw o&&(u.graphQLErrors=a.errors),s&&(u.protocolErrors=a.extensions[tN.YG]),new tN.cA(u)}return a})};if(this.transform(n).hasClientExports){var c=this.localState.addExportedVariables(n,a,s).then(u);return new eT(function(e){var t=null;return c.then(function(n){return t=n.subscribe(e)},e.error),function(){return t&&t.unsubscribe()}})}return u(a)},e.prototype.stopQuery=function(e){this.stopQueryNoBroadcast(e),this.broadcastQueries()},e.prototype.stopQueryNoBroadcast=function(e){this.stopQueryInStoreNoBroadcast(e),this.removeQuery(e)},e.prototype.removeQuery=function(e){this.fetchCancelFns.delete(e),this.queries.has(e)&&(this.getQuery(e).stop(),this.queries.delete(e))},e.prototype.broadcastQueries=function(){this.onBroadcast&&this.onBroadcast(),this.queries.forEach(function(e){return e.notify()})},e.prototype.getLocalState=function(){return this.localState},e.prototype.getObservableFromLink=function(e,t,n,r){var i,a,o=this;void 0===r&&(r=null!==(i=null==t?void 0:t.queryDeduplication)&&void 0!==i?i:this.queryDeduplication);var s=this.transform(e).serverQuery;if(s){var u=this,c=u.inFlightLinkObservables,l=u.link,f={query:s,variables:n,operationName:e3(s)||void 0,context:this.prepareContext((0,en.pi)((0,en.pi)({},t),{forceFetch:!r}))};if(t=f.context,r){var d=c.get(s)||new Map;c.set(s,d);var h=nx(n);if(!(a=d.get(h))){var p=new nq([np(l,f)]);d.set(h,a=p),p.beforeNext(function(){d.delete(h)&&d.size<1&&c.delete(s)})}}else a=new nq([np(l,f)])}else a=new nq([eT.of({data:{}})]),t=this.prepareContext(t);var b=this.transform(e).clientQuery;return b&&(a=nM(a,function(e){return o.localState.runResolvers({document:b,remoteResult:e,context:t,variables:n})})),a},e.prototype.getResultsFromLink=function(e,t,n){var r=e.lastRequestId=this.generateRequestId(),i=this.cache.transformForLink(this.transform(e.document).document);return nM(this.getObservableFromLink(i,n.context,n.variables),function(a){var o=nA(a),s=o.length>0;if(r>=e.lastRequestId){if(s&&"none"===n.errorPolicy)throw e.markError(new tN.cA({graphQLErrors:o}));e.markResult(a,i,n,t),e.markReady()}var u={data:a.data,loading:!1,networkStatus:nZ.I.ready};return s&&"ignore"!==n.errorPolicy&&(u.errors=o,u.networkStatus=nZ.I.error),u},function(t){var n=(0,tN.MS)(t)?t:new tN.cA({networkError:t});throw r>=e.lastRequestId&&e.markError(n),n})},e.prototype.fetchQueryObservable=function(e,t,n){return this.fetchConcastWithInfo(e,t,n).concast},e.prototype.fetchConcastWithInfo=function(e,t,n){var r,i,a=this;void 0===n&&(n=nZ.I.loading);var o=this.transform(t.query).document,s=this.getVariables(o,t.variables),u=this.getQuery(e),c=this.defaultOptions.watchQuery,l=t.fetchPolicy,f=void 0===l?c&&c.fetchPolicy||"cache-first":l,d=t.errorPolicy,h=void 0===d?c&&c.errorPolicy||"none":d,p=t.returnPartialData,b=void 0!==p&&p,m=t.notifyOnNetworkStatusChange,g=void 0!==m&&m,v=t.context,y=void 0===v?{}:v,w=Object.assign({},t,{query:o,variables:s,fetchPolicy:f,errorPolicy:h,returnPartialData:b,notifyOnNetworkStatusChange:g,context:y}),_=function(e){w.variables=e;var r=a.fetchQueryByPolicy(u,w,n);return"standby"!==w.fetchPolicy&&r.sources.length>0&&u.observableQuery&&u.observableQuery.applyNextFetchPolicy("after-fetch",t),r},E=function(){return a.fetchCancelFns.delete(e)};if(this.fetchCancelFns.set(e,function(e){E(),setTimeout(function(){return r.cancel(e)})}),this.transform(w.query).hasClientExports)r=new nq(this.localState.addExportedVariables(w.query,w.variables,w.context).then(_).then(function(e){return e.sources})),i=!0;else{var S=_(w.variables);i=S.fromLink,r=new nq(S.sources)}return r.promise.then(E,E),{concast:r,fromLink:i}},e.prototype.refetchQueries=function(e){var t=this,n=e.updateCache,r=e.include,i=e.optimistic,a=void 0!==i&&i,o=e.removeOptimistic,s=void 0===o?a?nG("refetchQueries"):void 0:o,u=e.onQueryUpdated,c=new Map;r&&this.getObservableQueries(r).forEach(function(e,n){c.set(n,{oq:e,lastDiff:t.getQuery(n).getDiff()})});var l=new Map;return n&&this.cache.batch({update:n,optimistic:a&&s||!1,removeOptimistic:s,onWatchUpdated:function(e,t,n){var r=e.watcher instanceof r9&&e.watcher.observableQuery;if(r){if(u){c.delete(r.queryId);var i=u(r,t,n);return!0===i&&(i=r.refetch()),!1!==i&&l.set(r,i),i}null!==u&&c.set(r.queryId,{oq:r,lastDiff:n,diff:t})}}}),c.size&&c.forEach(function(e,n){var r,i=e.oq,a=e.lastDiff,o=e.diff;if(u){if(!o){var s=i.queryInfo;s.reset(),o=s.getDiff()}r=u(i,o,a)}u&&!0!==r||(r=i.refetch()),!1!==r&&l.set(i,r),n.indexOf("legacyOneTimeQuery")>=0&&t.stopQueryNoBroadcast(n)}),s&&this.cache.removeOptimistic(s),l},e.prototype.fetchQueryByPolicy=function(e,t,n){var r=this,i=t.query,a=t.variables,o=t.fetchPolicy,s=t.refetchWritePolicy,u=t.errorPolicy,c=t.returnPartialData,l=t.context,f=t.notifyOnNetworkStatusChange,d=e.networkStatus;e.init({document:this.transform(i).document,variables:a,networkStatus:n});var h=function(){return e.getDiff(a)},p=function(t,n){void 0===n&&(n=e.networkStatus||nZ.I.loading);var o=t.result;!__DEV__||c||(0,nm.D)(o,{})||n5(t.missing);var s=function(e){return eT.of((0,en.pi)({data:e,loading:(0,nZ.O)(n),networkStatus:n},t.complete?null:{partial:!0}))};return o&&r.transform(i).hasForcedResolvers?r.localState.runResolvers({document:i,remoteResult:{data:o},context:l,variables:a,onlyRunForcedResolvers:!0}).then(function(e){return s(e.data||void 0)}):"none"===u&&n===nZ.I.refetch&&Array.isArray(t.missing)?s(void 0):s(o)},b="no-cache"===o?0:n===nZ.I.refetch&&"merge"!==s?1:2,m=function(){return r.getResultsFromLink(e,b,{variables:a,context:l,fetchPolicy:o,errorPolicy:u})},g=f&&"number"==typeof d&&d!==n&&(0,nZ.O)(n);switch(o){default:case"cache-first":var v=h();if(v.complete)return{fromLink:!1,sources:[p(v,e.markReady())]};if(c||g)return{fromLink:!0,sources:[p(v),m()]};return{fromLink:!0,sources:[m()]};case"cache-and-network":var v=h();if(v.complete||c||g)return{fromLink:!0,sources:[p(v),m()]};return{fromLink:!0,sources:[m()]};case"cache-only":return{fromLink:!1,sources:[p(h(),e.markReady())]};case"network-only":if(g)return{fromLink:!0,sources:[p(h()),m()]};return{fromLink:!0,sources:[m()]};case"no-cache":if(g)return{fromLink:!0,sources:[p(e.getDiff()),m(),]};return{fromLink:!0,sources:[m()]};case"standby":return{fromLink:!1,sources:[]}}},e.prototype.getQuery=function(e){return e&&!this.queries.has(e)&&this.queries.set(e,new r9(this,e)),this.queries.get(e)},e.prototype.prepareContext=function(e){void 0===e&&(e={});var t=this.localState.prepareContext(e);return(0,en.pi)((0,en.pi)({},t),{clientAwareness:this.clientAwareness})},e}(),ir=__webpack_require__(14012),ii=!1,ia=function(){function e(e){var t=this;this.resetStoreCallbacks=[],this.clearStoreCallbacks=[];var n=e.uri,r=e.credentials,i=e.headers,a=e.cache,o=e.ssrMode,s=void 0!==o&&o,u=e.ssrForceFetchDelay,c=void 0===u?0:u,l=e.connectToDevTools,f=void 0===l?"object"==typeof window&&!window.__APOLLO_CLIENT__&&__DEV__:l,d=e.queryDeduplication,h=void 0===d||d,p=e.defaultOptions,b=e.assumeImmutableResults,m=void 0!==b&&b,g=e.resolvers,v=e.typeDefs,y=e.fragmentMatcher,w=e.name,_=e.version,E=e.link;if(E||(E=n?new nh({uri:n,credentials:r,headers:i}):ta.empty()),!a)throw __DEV__?new Q.ej("To initialize Apollo Client, you must specify a 'cache' property in the options object. \nFor more information, please visit: https://go.apollo.dev/c/docs"):new Q.ej(9);if(this.link=E,this.cache=a,this.disableNetworkFetches=s||c>0,this.queryDeduplication=h,this.defaultOptions=p||Object.create(null),this.typeDefs=v,c&&setTimeout(function(){return t.disableNetworkFetches=!1},c),this.watchQuery=this.watchQuery.bind(this),this.query=this.query.bind(this),this.mutate=this.mutate.bind(this),this.resetStore=this.resetStore.bind(this),this.reFetchObservableQueries=this.reFetchObservableQueries.bind(this),f&&"object"==typeof window&&(window.__APOLLO_CLIENT__=this),!ii&&f&&__DEV__&&(ii=!0,"undefined"!=typeof window&&window.document&&window.top===window.self&&!window.__APOLLO_DEVTOOLS_GLOBAL_HOOK__)){var S=window.navigator,k=S&&S.userAgent,x=void 0;"string"==typeof k&&(k.indexOf("Chrome/")>-1?x="https://chrome.google.com/webstore/detail/apollo-client-developer-t/jdkknkkbebbapilgoeccciglkfbmbnfm":k.indexOf("Firefox/")>-1&&(x="https://addons.mozilla.org/en-US/firefox/addon/apollo-developer-tools/")),x&&__DEV__&&Q.kG.log("Download the Apollo DevTools for a better development experience: "+x)}this.version=nb,this.localState=new r4({cache:a,client:this,resolvers:g,fragmentMatcher:y}),this.queryManager=new it({cache:this.cache,link:this.link,defaultOptions:this.defaultOptions,queryDeduplication:h,ssrMode:s,clientAwareness:{name:w,version:_},localState:this.localState,assumeImmutableResults:m,onBroadcast:f?function(){t.devToolsHookCb&&t.devToolsHookCb({action:{},state:{queries:t.queryManager.getQueryStore(),mutations:t.queryManager.mutationStore||{}},dataWithOptimisticResults:t.cache.extract(!0)})}:void 0})}return e.prototype.stop=function(){this.queryManager.stop()},e.prototype.watchQuery=function(e){return this.defaultOptions.watchQuery&&(e=(0,ir.J)(this.defaultOptions.watchQuery,e)),this.disableNetworkFetches&&("network-only"===e.fetchPolicy||"cache-and-network"===e.fetchPolicy)&&(e=(0,en.pi)((0,en.pi)({},e),{fetchPolicy:"cache-first"})),this.queryManager.watchQuery(e)},e.prototype.query=function(e){return this.defaultOptions.query&&(e=(0,ir.J)(this.defaultOptions.query,e)),__DEV__?(0,Q.kG)("cache-and-network"!==e.fetchPolicy,"The cache-and-network fetchPolicy does not work with client.query, because client.query can only return a single result. Please use client.watchQuery to receive multiple results from the cache and the network, or consider using a different fetchPolicy, such as cache-first or network-only."):(0,Q.kG)("cache-and-network"!==e.fetchPolicy,10),this.disableNetworkFetches&&"network-only"===e.fetchPolicy&&(e=(0,en.pi)((0,en.pi)({},e),{fetchPolicy:"cache-first"})),this.queryManager.query(e)},e.prototype.mutate=function(e){return this.defaultOptions.mutate&&(e=(0,ir.J)(this.defaultOptions.mutate,e)),this.queryManager.mutate(e)},e.prototype.subscribe=function(e){return this.queryManager.startGraphQLSubscription(e)},e.prototype.readQuery=function(e,t){return void 0===t&&(t=!1),this.cache.readQuery(e,t)},e.prototype.readFragment=function(e,t){return void 0===t&&(t=!1),this.cache.readFragment(e,t)},e.prototype.writeQuery=function(e){var t=this.cache.writeQuery(e);return!1!==e.broadcast&&this.queryManager.broadcastQueries(),t},e.prototype.writeFragment=function(e){var t=this.cache.writeFragment(e);return!1!==e.broadcast&&this.queryManager.broadcastQueries(),t},e.prototype.__actionHookForDevTools=function(e){this.devToolsHookCb=e},e.prototype.__requestRaw=function(e){return np(this.link,e)},e.prototype.resetStore=function(){var e=this;return Promise.resolve().then(function(){return e.queryManager.clearStore({discardWatches:!1})}).then(function(){return Promise.all(e.resetStoreCallbacks.map(function(e){return e()}))}).then(function(){return e.reFetchObservableQueries()})},e.prototype.clearStore=function(){var e=this;return Promise.resolve().then(function(){return e.queryManager.clearStore({discardWatches:!0})}).then(function(){return Promise.all(e.clearStoreCallbacks.map(function(e){return e()}))})},e.prototype.onResetStore=function(e){var t=this;return this.resetStoreCallbacks.push(e),function(){t.resetStoreCallbacks=t.resetStoreCallbacks.filter(function(t){return t!==e})}},e.prototype.onClearStore=function(e){var t=this;return this.clearStoreCallbacks.push(e),function(){t.clearStoreCallbacks=t.clearStoreCallbacks.filter(function(t){return t!==e})}},e.prototype.reFetchObservableQueries=function(e){return this.queryManager.reFetchObservableQueries(e)},e.prototype.refetchQueries=function(e){var t=this.queryManager.refetchQueries(e),n=[],r=[];t.forEach(function(e,t){n.push(t),r.push(e)});var i=Promise.all(r);return i.queries=n,i.results=r,i.catch(function(e){__DEV__&&Q.kG.debug("In client.refetchQueries, Promise.all promise rejected with error ".concat(e))}),i},e.prototype.getObservableQueries=function(e){return void 0===e&&(e="active"),this.queryManager.getObservableQueries(e)},e.prototype.extract=function(e){return this.cache.extract(e)},e.prototype.restore=function(e){return this.cache.restore(e)},e.prototype.addResolvers=function(e){this.localState.addResolvers(e)},e.prototype.setResolvers=function(e){this.localState.setResolvers(e)},e.prototype.getResolvers=function(){return this.localState.getResolvers()},e.prototype.setLocalStateFragmentMatcher=function(e){this.localState.setFragmentMatcher(e)},e.prototype.setLink=function(e){this.link=this.queryManager.link=e},e}(),io=function(){function e(){this.getFragmentDoc=rZ(eA)}return e.prototype.batch=function(e){var t,n=this,r="string"==typeof e.optimistic?e.optimistic:!1===e.optimistic?null:void 0;return this.performTransaction(function(){return t=e.update(n)},r),t},e.prototype.recordOptimisticTransaction=function(e,t){this.performTransaction(e,t)},e.prototype.transformDocument=function(e){return e},e.prototype.transformForLink=function(e){return e},e.prototype.identify=function(e){},e.prototype.gc=function(){return[]},e.prototype.modify=function(e){return!1},e.prototype.readQuery=function(e,t){return void 0===t&&(t=!!e.optimistic),this.read((0,en.pi)((0,en.pi)({},e),{rootId:e.id||"ROOT_QUERY",optimistic:t}))},e.prototype.readFragment=function(e,t){return void 0===t&&(t=!!e.optimistic),this.read((0,en.pi)((0,en.pi)({},e),{query:this.getFragmentDoc(e.fragment,e.fragmentName),rootId:e.id,optimistic:t}))},e.prototype.writeQuery=function(e){var t=e.id,n=e.data,r=(0,en._T)(e,["id","data"]);return this.write(Object.assign(r,{dataId:t||"ROOT_QUERY",result:n}))},e.prototype.writeFragment=function(e){var t=e.id,n=e.data,r=e.fragment,i=e.fragmentName,a=(0,en._T)(e,["id","data","fragment","fragmentName"]);return this.write(Object.assign(a,{query:this.getFragmentDoc(r,i),dataId:t,result:n}))},e.prototype.updateQuery=function(e,t){return this.batch({update:function(n){var r=n.readQuery(e),i=t(r);return null==i?r:(n.writeQuery((0,en.pi)((0,en.pi)({},e),{data:i})),i)}})},e.prototype.updateFragment=function(e,t){return this.batch({update:function(n){var r=n.readFragment(e),i=t(r);return null==i?r:(n.writeFragment((0,en.pi)((0,en.pi)({},e),{data:i})),i)}})},e}(),is=function(e){function t(n,r,i,a){var o,s=e.call(this,n)||this;if(s.message=n,s.path=r,s.query=i,s.variables=a,Array.isArray(s.path)){s.missing=s.message;for(var u=s.path.length-1;u>=0;--u)s.missing=((o={})[s.path[u]]=s.missing,o)}else s.missing=s.path;return s.__proto__=t.prototype,s}return(0,en.ZT)(t,e),t}(Error),iu=__webpack_require__(10542),ic=Object.prototype.hasOwnProperty;function il(e){return null==e}function id(e,t){var n=e.__typename,r=e.id,i=e._id;if("string"==typeof n&&(t&&(t.keyObject=il(r)?il(i)?void 0:{_id:i}:{id:r}),il(r)&&!il(i)&&(r=i),!il(r)))return"".concat(n,":").concat("number"==typeof r||"string"==typeof r?r:JSON.stringify(r))}var ih={dataIdFromObject:id,addTypename:!0,resultCaching:!0,canonizeResults:!1};function ip(e){return(0,n1.o)(ih,e)}function ib(e){var t=e.canonizeResults;return void 0===t?ih.canonizeResults:t}function im(e,t){return eD(t)?e.get(t.__ref,"__typename"):t&&t.__typename}var ig=/^[_a-z][_0-9a-z]*/i;function iv(e){var t=e.match(ig);return t?t[0]:e}function iy(e,t,n){return!!(0,eO.s)(t)&&((0,tP.k)(t)?t.every(function(t){return iy(e,t,n)}):e.selections.every(function(e){if(eQ(e)&&td(e,n)){var r=eX(e);return ic.call(t,r)&&(!e.selectionSet||iy(e.selectionSet,t[r],n))}return!0}))}function iw(e){return(0,eO.s)(e)&&!eD(e)&&!(0,tP.k)(e)}function i_(){return new tB}function iE(e,t){var n=eL(e4(e));return{fragmentMap:n,lookupFragment:function(e){var r=n[e];return!r&&t&&(r=t.lookup(e)),r||null}}}var iS=Object.create(null),ik=function(){return iS},ix=Object.create(null),iT=function(){function e(e,t){var n=this;this.policies=e,this.group=t,this.data=Object.create(null),this.rootIds=Object.create(null),this.refs=Object.create(null),this.getFieldValue=function(e,t){return(0,iu.J)(eD(e)?n.get(e.__ref,t):e&&e[t])},this.canRead=function(e){return eD(e)?n.has(e.__ref):"object"==typeof e},this.toReference=function(e,t){if("string"==typeof e)return eI(e);if(eD(e))return e;var r=n.policies.identify(e)[0];if(r){var i=eI(r);return t&&n.merge(r,e),i}}}return e.prototype.toObject=function(){return(0,en.pi)({},this.data)},e.prototype.has=function(e){return void 0!==this.lookup(e,!0)},e.prototype.get=function(e,t){if(this.group.depend(e,t),ic.call(this.data,e)){var n=this.data[e];if(n&&ic.call(n,t))return n[t]}return"__typename"===t&&ic.call(this.policies.rootTypenamesById,e)?this.policies.rootTypenamesById[e]:this instanceof iL?this.parent.get(e,t):void 0},e.prototype.lookup=function(e,t){return(t&&this.group.depend(e,"__exists"),ic.call(this.data,e))?this.data[e]:this instanceof iL?this.parent.lookup(e,t):this.policies.rootTypenamesById[e]?Object.create(null):void 0},e.prototype.merge=function(e,t){var n,r=this;eD(e)&&(e=e.__ref),eD(t)&&(t=t.__ref);var i="string"==typeof e?this.lookup(n=e):e,a="string"==typeof t?this.lookup(n=t):t;if(a){__DEV__?(0,Q.kG)("string"==typeof n,"store.merge expects a string ID"):(0,Q.kG)("string"==typeof n,1);var o=new tB(iI).merge(i,a);if(this.data[n]=o,o!==i&&(delete this.refs[n],this.group.caching)){var s=Object.create(null);i||(s.__exists=1),Object.keys(a).forEach(function(e){if(!i||i[e]!==o[e]){s[e]=1;var t=iv(e);t===e||r.policies.hasKeyArgs(o.__typename,t)||(s[t]=1),void 0!==o[e]||r instanceof iL||delete o[e]}}),s.__typename&&!(i&&i.__typename)&&this.policies.rootTypenamesById[n]===o.__typename&&delete s.__typename,Object.keys(s).forEach(function(e){return r.group.dirty(n,e)})}}},e.prototype.modify=function(e,t){var n=this,r=this.lookup(e);if(r){var i=Object.create(null),a=!1,o=!0,s={DELETE:iS,INVALIDATE:ix,isReference:eD,toReference:this.toReference,canRead:this.canRead,readField:function(t,r){return n.policies.readField("string"==typeof t?{fieldName:t,from:r||eI(e)}:t,{store:n})}};if(Object.keys(r).forEach(function(u){var c=iv(u),l=r[u];if(void 0!==l){var f="function"==typeof t?t:t[u]||t[c];if(f){var d=f===ik?iS:f((0,iu.J)(l),(0,en.pi)((0,en.pi)({},s),{fieldName:c,storeFieldName:u,storage:n.getStorage(e,u)}));d===ix?n.group.dirty(e,u):(d===iS&&(d=void 0),d!==l&&(i[u]=d,a=!0,l=d))}void 0!==l&&(o=!1)}}),a)return this.merge(e,i),o&&(this instanceof iL?this.data[e]=void 0:delete this.data[e],this.group.dirty(e,"__exists")),!0}return!1},e.prototype.delete=function(e,t,n){var r,i=this.lookup(e);if(i){var a=this.getFieldValue(i,"__typename"),o=t&&n?this.policies.getStoreFieldName({typename:a,fieldName:t,args:n}):t;return this.modify(e,o?((r={})[o]=ik,r):ik)}return!1},e.prototype.evict=function(e,t){var n=!1;return e.id&&(ic.call(this.data,e.id)&&(n=this.delete(e.id,e.fieldName,e.args)),this instanceof iL&&this!==t&&(n=this.parent.evict(e,t)||n),(e.fieldName||n)&&this.group.dirty(e.id,e.fieldName||"__exists")),n},e.prototype.clear=function(){this.replace(null)},e.prototype.extract=function(){var e=this,t=this.toObject(),n=[];return this.getRootIdSet().forEach(function(t){ic.call(e.policies.rootTypenamesById,t)||n.push(t)}),n.length&&(t.__META={extraRootIds:n.sort()}),t},e.prototype.replace=function(e){var t=this;if(Object.keys(this.data).forEach(function(n){e&&ic.call(e,n)||t.delete(n)}),e){var n=e.__META,r=(0,en._T)(e,["__META"]);Object.keys(r).forEach(function(e){t.merge(e,r[e])}),n&&n.extraRootIds.forEach(this.retain,this)}},e.prototype.retain=function(e){return this.rootIds[e]=(this.rootIds[e]||0)+1},e.prototype.release=function(e){if(this.rootIds[e]>0){var t=--this.rootIds[e];return t||delete this.rootIds[e],t}return 0},e.prototype.getRootIdSet=function(e){return void 0===e&&(e=new Set),Object.keys(this.rootIds).forEach(e.add,e),this instanceof iL?this.parent.getRootIdSet(e):Object.keys(this.policies.rootTypenamesById).forEach(e.add,e),e},e.prototype.gc=function(){var e=this,t=this.getRootIdSet(),n=this.toObject();t.forEach(function(r){ic.call(n,r)&&(Object.keys(e.findChildRefIds(r)).forEach(t.add,t),delete n[r])});var r=Object.keys(n);if(r.length){for(var i=this;i instanceof iL;)i=i.parent;r.forEach(function(e){return i.delete(e)})}return r},e.prototype.findChildRefIds=function(e){if(!ic.call(this.refs,e)){var t=this.refs[e]=Object.create(null),n=this.data[e];if(!n)return t;var r=new Set([n]);r.forEach(function(e){eD(e)&&(t[e.__ref]=!0),(0,eO.s)(e)&&Object.keys(e).forEach(function(t){var n=e[t];(0,eO.s)(n)&&r.add(n)})})}return this.refs[e]},e.prototype.makeCacheKey=function(){return this.group.keyMaker.lookupArray(arguments)},e}(),iM=function(){function e(e,t){void 0===t&&(t=null),this.caching=e,this.parent=t,this.d=null,this.resetCaching()}return e.prototype.resetCaching=function(){this.d=this.caching?rW():null,this.keyMaker=new n_(t_.mr)},e.prototype.depend=function(e,t){if(this.d){this.d(iO(e,t));var n=iv(t);n!==t&&this.d(iO(e,n)),this.parent&&this.parent.depend(e,t)}},e.prototype.dirty=function(e,t){this.d&&this.d.dirty(iO(e,t),"__exists"===t?"forget":"setDirty")},e}();function iO(e,t){return t+"#"+e}function iA(e,t){iD(e)&&e.group.depend(t,"__exists")}!function(e){var t=function(e){function t(t){var n=t.policies,r=t.resultCaching,i=void 0===r||r,a=t.seed,o=e.call(this,n,new iM(i))||this;return o.stump=new iC(o),o.storageTrie=new n_(t_.mr),a&&o.replace(a),o}return(0,en.ZT)(t,e),t.prototype.addLayer=function(e,t){return this.stump.addLayer(e,t)},t.prototype.removeLayer=function(){return this},t.prototype.getStorage=function(){return this.storageTrie.lookupArray(arguments)},t}(e);e.Root=t}(iT||(iT={}));var iL=function(e){function t(t,n,r,i){var a=e.call(this,n.policies,i)||this;return a.id=t,a.parent=n,a.replay=r,a.group=i,r(a),a}return(0,en.ZT)(t,e),t.prototype.addLayer=function(e,n){return new t(e,this,n,this.group)},t.prototype.removeLayer=function(e){var t=this,n=this.parent.removeLayer(e);return e===this.id?(this.group.caching&&Object.keys(this.data).forEach(function(e){var r=t.data[e],i=n.lookup(e);i?r?r!==i&&Object.keys(r).forEach(function(n){(0,nm.D)(r[n],i[n])||t.group.dirty(e,n)}):(t.group.dirty(e,"__exists"),Object.keys(i).forEach(function(n){t.group.dirty(e,n)})):t.delete(e)}),n):n===this.parent?this:n.addLayer(this.id,this.replay)},t.prototype.toObject=function(){return(0,en.pi)((0,en.pi)({},this.parent.toObject()),this.data)},t.prototype.findChildRefIds=function(t){var n=this.parent.findChildRefIds(t);return ic.call(this.data,t)?(0,en.pi)((0,en.pi)({},n),e.prototype.findChildRefIds.call(this,t)):n},t.prototype.getStorage=function(){for(var e=this.parent;e.parent;)e=e.parent;return e.getStorage.apply(e,arguments)},t}(iT),iC=function(e){function t(t){return e.call(this,"EntityStore.Stump",t,function(){},new iM(t.group.caching,t.group))||this}return(0,en.ZT)(t,e),t.prototype.removeLayer=function(){return this},t.prototype.merge=function(){return this.parent.merge.apply(this.parent,arguments)},t}(iL);function iI(e,t,n){var r=e[n],i=t[n];return(0,nm.D)(r,i)?r:i}function iD(e){return!!(e instanceof iT&&e.group.caching)}function iN(e){return[e.selectionSet,e.objectOrReference,e.context,e.context.canonizeResults,]}var iP=function(){function e(e){var t=this;this.knownResults=new(t_.mr?WeakMap:Map),this.config=(0,n1.o)(e,{addTypename:!1!==e.addTypename,canonizeResults:ib(e)}),this.canon=e.canon||new nk,this.executeSelectionSet=rZ(function(e){var n,r=e.context.canonizeResults,i=iN(e);i[3]=!r;var a=(n=t.executeSelectionSet).peek.apply(n,i);return a?r?(0,en.pi)((0,en.pi)({},a),{result:t.canon.admit(a.result)}):a:(iA(e.context.store,e.enclosingRef.__ref),t.execSelectionSetImpl(e))},{max:this.config.resultCacheMaxSize,keyArgs:iN,makeCacheKey:function(e,t,n,r){if(iD(n.store))return n.store.makeCacheKey(e,eD(t)?t.__ref:t,n.varString,r)}}),this.executeSubSelectedArray=rZ(function(e){return iA(e.context.store,e.enclosingRef.__ref),t.execSubSelectedArrayImpl(e)},{max:this.config.resultCacheMaxSize,makeCacheKey:function(e){var t=e.field,n=e.array,r=e.context;if(iD(r.store))return r.store.makeCacheKey(t,n,r.varString)}})}return e.prototype.resetCanon=function(){this.canon=new nk},e.prototype.diffQueryAgainstStore=function(e){var t,n=e.store,r=e.query,i=e.rootId,a=void 0===i?"ROOT_QUERY":i,o=e.variables,s=e.returnPartialData,u=void 0===s||s,c=e.canonizeResults,l=void 0===c?this.config.canonizeResults:c,f=this.config.cache.policies;o=(0,en.pi)((0,en.pi)({},e9(e6(r))),o);var d=eI(a),h=this.executeSelectionSet({selectionSet:e8(r).selectionSet,objectOrReference:d,enclosingRef:d,context:(0,en.pi)({store:n,query:r,policies:f,variables:o,varString:nx(o),canonizeResults:l},iE(r,this.config.fragments))});if(h.missing&&(t=[new is(iR(h.missing),h.missing,r,o)],!u))throw t[0];return{result:h.result,complete:!t,missing:t}},e.prototype.isFresh=function(e,t,n,r){if(iD(r.store)&&this.knownResults.get(e)===n){var i=this.executeSelectionSet.peek(n,t,r,this.canon.isKnown(e));if(i&&e===i.result)return!0}return!1},e.prototype.execSelectionSetImpl=function(e){var t,n=this,r=e.selectionSet,i=e.objectOrReference,a=e.enclosingRef,o=e.context;if(eD(i)&&!o.policies.rootTypenamesById[i.__ref]&&!o.store.has(i.__ref))return{result:this.canon.empty,missing:"Dangling reference to missing ".concat(i.__ref," object")};var s=o.variables,u=o.policies,c=o.store.getFieldValue(i,"__typename"),l=[],f=new tB;function d(e,n){var r;return e.missing&&(t=f.merge(t,((r={})[n]=e.missing,r))),e.result}this.config.addTypename&&"string"==typeof c&&!u.rootIdsByTypename[c]&&l.push({__typename:c});var h=new Set(r.selections);h.forEach(function(e){var r,p;if(td(e,s)){if(eQ(e)){var b=u.readField({fieldName:e.name.value,field:e,variables:o.variables,from:i},o),m=eX(e);void 0===b?nj.added(e)||(t=f.merge(t,((r={})[m]="Can't find field '".concat(e.name.value,"' on ").concat(eD(i)?i.__ref+" object":"object "+JSON.stringify(i,null,2)),r))):(0,tP.k)(b)?b=d(n.executeSubSelectedArray({field:e,array:b,enclosingRef:a,context:o}),m):e.selectionSet?null!=b&&(b=d(n.executeSelectionSet({selectionSet:e.selectionSet,objectOrReference:b,enclosingRef:eD(b)?b:a,context:o}),m)):o.canonizeResults&&(b=n.canon.pass(b)),void 0!==b&&l.push(((p={})[m]=b,p))}else{var g=eC(e,o.lookupFragment);if(!g&&e.kind===nL.h.FRAGMENT_SPREAD)throw __DEV__?new Q.ej("No fragment named ".concat(e.name.value)):new Q.ej(5);g&&u.fragmentMatches(g,c)&&g.selectionSet.selections.forEach(h.add,h)}}});var p={result:tF(l),missing:t},b=o.canonizeResults?this.canon.admit(p):(0,iu.J)(p);return b.result&&this.knownResults.set(b.result,r),b},e.prototype.execSubSelectedArrayImpl=function(e){var t,n=this,r=e.field,i=e.array,a=e.enclosingRef,o=e.context,s=new tB;function u(e,n){var r;return e.missing&&(t=s.merge(t,((r={})[n]=e.missing,r))),e.result}return r.selectionSet&&(i=i.filter(o.store.canRead)),i=i.map(function(e,t){return null===e?null:(0,tP.k)(e)?u(n.executeSubSelectedArray({field:r,array:e,enclosingRef:a,context:o}),t):r.selectionSet?u(n.executeSelectionSet({selectionSet:r.selectionSet,objectOrReference:e,enclosingRef:eD(e)?e:a,context:o}),t):(__DEV__&&ij(o.store,r,e),e)}),{result:o.canonizeResults?this.canon.admit(i):i,missing:t}},e}();function iR(e){try{JSON.stringify(e,function(e,t){if("string"==typeof t)throw t;return t})}catch(t){return t}}function ij(e,t,n){if(!t.selectionSet){var r=new Set([n]);r.forEach(function(n){(0,eO.s)(n)&&(__DEV__?(0,Q.kG)(!eD(n),"Missing selection set for object of type ".concat(im(e,n)," returned for query field ").concat(t.name.value)):(0,Q.kG)(!eD(n),6),Object.values(n).forEach(r.add,r))})}}function iF(e){var t=nG("stringifyForDisplay");return JSON.stringify(e,function(e,n){return void 0===n?t:n}).split(JSON.stringify(t)).join("")}var iY=Object.create(null);function iB(e){var t=JSON.stringify(e);return iY[t]||(iY[t]=Object.create(null))}function iU(e){var t=iB(e);return t.keyFieldsFn||(t.keyFieldsFn=function(t,n){var r=function(e,t){return n.readField(t,e)},i=n.keyObject=i$(e,function(e){var i=iW(n.storeObject,e,r);return void 0===i&&t!==n.storeObject&&ic.call(t,e[0])&&(i=iW(t,e,iG)),__DEV__?(0,Q.kG)(void 0!==i,"Missing field '".concat(e.join("."),"' while extracting keyFields from ").concat(JSON.stringify(t))):(0,Q.kG)(void 0!==i,2),i});return"".concat(n.typename,":").concat(JSON.stringify(i))})}function iH(e){var t=iB(e);return t.keyArgsFn||(t.keyArgsFn=function(t,n){var r=n.field,i=n.variables,a=n.fieldName,o=JSON.stringify(i$(e,function(e){var n=e[0],a=n.charAt(0);if("@"===a){if(r&&(0,tP.O)(r.directives)){var o=n.slice(1),s=r.directives.find(function(e){return e.name.value===o}),u=s&&eZ(s,i);return u&&iW(u,e.slice(1))}return}if("$"===a){var c=n.slice(1);if(i&&ic.call(i,c)){var l=e.slice(0);return l[0]=c,iW(i,l)}return}if(t)return iW(t,e)}));return(t||"{}"!==o)&&(a+=":"+o),a})}function i$(e,t){var n=new tB;return iz(e).reduce(function(e,r){var i,a=t(r);if(void 0!==a){for(var o=r.length-1;o>=0;--o)a=((i={})[r[o]]=a,i);e=n.merge(e,a)}return e},Object.create(null))}function iz(e){var t=iB(e);if(!t.paths){var n=t.paths=[],r=[];e.forEach(function(t,i){(0,tP.k)(t)?(iz(t).forEach(function(e){return n.push(r.concat(e))}),r.length=0):(r.push(t),(0,tP.k)(e[i+1])||(n.push(r.slice(0)),r.length=0))})}return t.paths}function iG(e,t){return e[t]}function iW(e,t,n){return n=n||iG,iK(t.reduce(function e(t,r){return(0,tP.k)(t)?t.map(function(t){return e(t,r)}):t&&n(t,r)},e))}function iK(e){return(0,eO.s)(e)?(0,tP.k)(e)?e.map(iK):i$(Object.keys(e).sort(),function(t){return iW(e,t)}):e}function iV(e){return void 0!==e.args?e.args:e.field?eZ(e.field,e.variables):null}eK.setStringify(nx);var iq=function(){},iZ=function(e,t){return t.fieldName},iX=function(e,t,n){return(0,n.mergeObjects)(e,t)},iJ=function(e,t){return t},iQ=function(){function e(e){this.config=e,this.typePolicies=Object.create(null),this.toBeAdded=Object.create(null),this.supertypeMap=new Map,this.fuzzySubtypes=new Map,this.rootIdsByTypename=Object.create(null),this.rootTypenamesById=Object.create(null),this.usingPossibleTypes=!1,this.config=(0,en.pi)({dataIdFromObject:id},e),this.cache=this.config.cache,this.setRootTypename("Query"),this.setRootTypename("Mutation"),this.setRootTypename("Subscription"),e.possibleTypes&&this.addPossibleTypes(e.possibleTypes),e.typePolicies&&this.addTypePolicies(e.typePolicies)}return e.prototype.identify=function(e,t){var n,r,i=this,a=t&&(t.typename||(null===(n=t.storeObject)||void 0===n?void 0:n.__typename))||e.__typename;if(a===this.rootTypenamesById.ROOT_QUERY)return["ROOT_QUERY"];for(var o=t&&t.storeObject||e,s=(0,en.pi)((0,en.pi)({},t),{typename:a,storeObject:o,readField:t&&t.readField||function(){var e=i0(arguments,o);return i.readField(e,{store:i.cache.data,variables:e.variables})}}),u=a&&this.getTypePolicy(a),c=u&&u.keyFn||this.config.dataIdFromObject;c;){var l=c((0,en.pi)((0,en.pi)({},e),o),s);if((0,tP.k)(l))c=iU(l);else{r=l;break}}return r=r?String(r):void 0,s.keyObject?[r,s.keyObject]:[r]},e.prototype.addTypePolicies=function(e){var t=this;Object.keys(e).forEach(function(n){var r=e[n],i=r.queryType,a=r.mutationType,o=r.subscriptionType,s=(0,en._T)(r,["queryType","mutationType","subscriptionType"]);i&&t.setRootTypename("Query",n),a&&t.setRootTypename("Mutation",n),o&&t.setRootTypename("Subscription",n),ic.call(t.toBeAdded,n)?t.toBeAdded[n].push(s):t.toBeAdded[n]=[s]})},e.prototype.updateTypePolicy=function(e,t){var n=this,r=this.getTypePolicy(e),i=t.keyFields,a=t.fields;function o(e,t){e.merge="function"==typeof t?t:!0===t?iX:!1===t?iJ:e.merge}o(r,t.merge),r.keyFn=!1===i?iq:(0,tP.k)(i)?iU(i):"function"==typeof i?i:r.keyFn,a&&Object.keys(a).forEach(function(t){var r=n.getFieldPolicy(e,t,!0),i=a[t];if("function"==typeof i)r.read=i;else{var s=i.keyArgs,u=i.read,c=i.merge;r.keyFn=!1===s?iZ:(0,tP.k)(s)?iH(s):"function"==typeof s?s:r.keyFn,"function"==typeof u&&(r.read=u),o(r,c)}r.read&&r.merge&&(r.keyFn=r.keyFn||iZ)})},e.prototype.setRootTypename=function(e,t){void 0===t&&(t=e);var n="ROOT_"+e.toUpperCase(),r=this.rootTypenamesById[n];t!==r&&(__DEV__?(0,Q.kG)(!r||r===e,"Cannot change root ".concat(e," __typename more than once")):(0,Q.kG)(!r||r===e,3),r&&delete this.rootIdsByTypename[r],this.rootIdsByTypename[t]=n,this.rootTypenamesById[n]=t)},e.prototype.addPossibleTypes=function(e){var t=this;this.usingPossibleTypes=!0,Object.keys(e).forEach(function(n){t.getSupertypeSet(n,!0),e[n].forEach(function(e){t.getSupertypeSet(e,!0).add(n);var r=e.match(ig);r&&r[0]===e||t.fuzzySubtypes.set(e,RegExp(e))})})},e.prototype.getTypePolicy=function(e){var t=this;if(!ic.call(this.typePolicies,e)){var n=this.typePolicies[e]=Object.create(null);n.fields=Object.create(null);var r=this.supertypeMap.get(e);r&&r.size&&r.forEach(function(e){var r=t.getTypePolicy(e),i=r.fields;Object.assign(n,(0,en._T)(r,["fields"])),Object.assign(n.fields,i)})}var i=this.toBeAdded[e];return i&&i.length&&i.splice(0).forEach(function(n){t.updateTypePolicy(e,n)}),this.typePolicies[e]},e.prototype.getFieldPolicy=function(e,t,n){if(e){var r=this.getTypePolicy(e).fields;return r[t]||n&&(r[t]=Object.create(null))}},e.prototype.getSupertypeSet=function(e,t){var n=this.supertypeMap.get(e);return!n&&t&&this.supertypeMap.set(e,n=new Set),n},e.prototype.fragmentMatches=function(e,t,n,r){var i=this;if(!e.typeCondition)return!0;if(!t)return!1;var a=e.typeCondition.name.value;if(t===a)return!0;if(this.usingPossibleTypes&&this.supertypeMap.has(a))for(var o=this.getSupertypeSet(t,!0),s=[o],u=function(e){var t=i.getSupertypeSet(e,!1);t&&t.size&&0>s.indexOf(t)&&s.push(t)},c=!!(n&&this.fuzzySubtypes.size),l=!1,f=0;f1?a:t}:(r=(0,en.pi)({},i),ic.call(r,"from")||(r.from=t)),__DEV__&&void 0===r.from&&__DEV__&&Q.kG.warn("Undefined 'from' passed to readField with arguments ".concat(iF(Array.from(e)))),void 0===r.variables&&(r.variables=n),r}function i2(e){return function(t,n){if((0,tP.k)(t)||(0,tP.k)(n))throw __DEV__?new Q.ej("Cannot automatically merge arrays"):new Q.ej(4);if((0,eO.s)(t)&&(0,eO.s)(n)){var r=e.getFieldValue(t,"__typename"),i=e.getFieldValue(n,"__typename");if(r&&i&&r!==i)return n;if(eD(t)&&iw(n))return e.merge(t.__ref,n),t;if(iw(t)&&eD(n))return e.merge(t,n.__ref),n;if(iw(t)&&iw(n))return(0,en.pi)((0,en.pi)({},t),n)}return n}}function i3(e,t,n){var r="".concat(t).concat(n),i=e.flavors.get(r);return i||e.flavors.set(r,i=e.clientOnly===t&&e.deferred===n?e:(0,en.pi)((0,en.pi)({},e),{clientOnly:t,deferred:n})),i}var i4=function(){function e(e,t,n){this.cache=e,this.reader=t,this.fragments=n}return e.prototype.writeToStore=function(e,t){var n=this,r=t.query,i=t.result,a=t.dataId,o=t.variables,s=t.overwrite,u=e2(r),c=i_();o=(0,en.pi)((0,en.pi)({},e9(u)),o);var l=(0,en.pi)((0,en.pi)({store:e,written:Object.create(null),merge:function(e,t){return c.merge(e,t)},variables:o,varString:nx(o)},iE(r,this.fragments)),{overwrite:!!s,incomingById:new Map,clientOnly:!1,deferred:!1,flavors:new Map}),f=this.processSelectionSet({result:i||Object.create(null),dataId:a,selectionSet:u.selectionSet,mergeTree:{map:new Map},context:l});if(!eD(f))throw __DEV__?new Q.ej("Could not identify object ".concat(JSON.stringify(i))):new Q.ej(7);return l.incomingById.forEach(function(t,r){var i=t.storeObject,a=t.mergeTree,o=t.fieldNodeSet,s=eI(r);if(a&&a.map.size){var u=n.applyMerges(a,s,i,l);if(eD(u))return;i=u}if(__DEV__&&!l.overwrite){var c=Object.create(null);o.forEach(function(e){e.selectionSet&&(c[e.name.value]=!0)});var f=function(e){return!0===c[iv(e)]},d=function(e){var t=a&&a.map.get(e);return Boolean(t&&t.info&&t.info.merge)};Object.keys(i).forEach(function(e){f(e)&&!d(e)&&at(s,i,e,l.store)})}e.merge(r,i)}),e.retain(f.__ref),f},e.prototype.processSelectionSet=function(e){var t=this,n=e.dataId,r=e.result,i=e.selectionSet,a=e.context,o=e.mergeTree,s=this.cache.policies,u=Object.create(null),c=n&&s.rootTypenamesById[n]||eJ(r,i,a.fragmentMap)||n&&a.store.get(n,"__typename");"string"==typeof c&&(u.__typename=c);var l=function(){var e=i0(arguments,u,a.variables);if(eD(e.from)){var t=a.incomingById.get(e.from.__ref);if(t){var n=s.readField((0,en.pi)((0,en.pi)({},e),{from:t.storeObject}),a);if(void 0!==n)return n}}return s.readField(e,a)},f=new Set;this.flattenFields(i,r,a,c).forEach(function(e,n){var i,a=r[eX(n)];if(f.add(n),void 0!==a){var d=s.getStoreFieldName({typename:c,fieldName:n.name.value,field:n,variables:e.variables}),h=i5(o,d),p=t.processFieldValue(a,n,n.selectionSet?i3(e,!1,!1):e,h),b=void 0;n.selectionSet&&(eD(p)||iw(p))&&(b=l("__typename",p));var m=s.getMergeFunction(c,n.name.value,b);m?h.info={field:n,typename:c,merge:m}:i7(o,d),u=e.merge(u,((i={})[d]=p,i))}else __DEV__&&!e.clientOnly&&!e.deferred&&!nj.added(n)&&!s.getReadFunction(c,n.name.value)&&__DEV__&&Q.kG.error("Missing field '".concat(eX(n),"' while writing result ").concat(JSON.stringify(r,null,2)).substring(0,1e3))});try{var d=s.identify(r,{typename:c,selectionSet:i,fragmentMap:a.fragmentMap,storeObject:u,readField:l}),h=d[0],p=d[1];n=n||h,p&&(u=a.merge(u,p))}catch(b){if(!n)throw b}if("string"==typeof n){var m=eI(n),g=a.written[n]||(a.written[n]=[]);if(g.indexOf(i)>=0||(g.push(i),this.reader&&this.reader.isFresh(r,m,i,a)))return m;var v=a.incomingById.get(n);return v?(v.storeObject=a.merge(v.storeObject,u),v.mergeTree=i8(v.mergeTree,o),f.forEach(function(e){return v.fieldNodeSet.add(e)})):a.incomingById.set(n,{storeObject:u,mergeTree:i9(o)?void 0:o,fieldNodeSet:f}),m}return u},e.prototype.processFieldValue=function(e,t,n,r){var i=this;return t.selectionSet&&null!==e?(0,tP.k)(e)?e.map(function(e,a){var o=i.processFieldValue(e,t,n,i5(r,a));return i7(r,a),o}):this.processSelectionSet({result:e,selectionSet:t.selectionSet,context:n,mergeTree:r}):__DEV__?nJ(e):e},e.prototype.flattenFields=function(e,t,n,r){void 0===r&&(r=eJ(t,e,n.fragmentMap));var i=new Map,a=this.cache.policies,o=new n_(!1);return function e(s,u){var c=o.lookup(s,u.clientOnly,u.deferred);c.visited||(c.visited=!0,s.selections.forEach(function(o){if(td(o,n.variables)){var s=u.clientOnly,c=u.deferred;if(!(s&&c)&&(0,tP.O)(o.directives)&&o.directives.forEach(function(e){var t=e.name.value;if("client"===t&&(s=!0),"defer"===t){var r=eZ(e,n.variables);r&&!1===r.if||(c=!0)}}),eQ(o)){var l=i.get(o);l&&(s=s&&l.clientOnly,c=c&&l.deferred),i.set(o,i3(n,s,c))}else{var f=eC(o,n.lookupFragment);if(!f&&o.kind===nL.h.FRAGMENT_SPREAD)throw __DEV__?new Q.ej("No fragment named ".concat(o.name.value)):new Q.ej(8);f&&a.fragmentMatches(f,r,t,n.variables)&&e(f.selectionSet,i3(n,s,c))}}}))}(e,n),i},e.prototype.applyMerges=function(e,t,n,r,i){var a=this;if(e.map.size&&!eD(n)){var o,s,u=!(0,tP.k)(n)&&(eD(t)||iw(t))?t:void 0,c=n;u&&!i&&(i=[eD(u)?u.__ref:u]);var l=function(e,t){return(0,tP.k)(e)?"number"==typeof t?e[t]:void 0:r.store.getFieldValue(e,String(t))};e.map.forEach(function(e,t){var n=l(u,t),o=l(c,t);if(void 0!==o){i&&i.push(t);var f=a.applyMerges(e,n,o,r,i);f!==o&&(s=s||new Map).set(t,f),i&&(0,Q.kG)(i.pop()===t)}}),s&&(n=(0,tP.k)(c)?c.slice(0):(0,en.pi)({},c),s.forEach(function(e,t){n[t]=e}))}return e.info?this.cache.policies.runMergeFunction(t,n,e.info,r,i&&(o=r.store).getStorage.apply(o,i)):n},e}(),i6=[];function i5(e,t){var n=e.map;return n.has(t)||n.set(t,i6.pop()||{map:new Map}),n.get(t)}function i8(e,t){if(e===t||!t||i9(t))return e;if(!e||i9(e))return t;var n=e.info&&t.info?(0,en.pi)((0,en.pi)({},e.info),t.info):e.info||t.info,r=e.map.size&&t.map.size,i=r?new Map:e.map.size?e.map:t.map,a={info:n,map:i};if(r){var o=new Set(t.map.keys());e.map.forEach(function(e,n){a.map.set(n,i8(e,t.map.get(n))),o.delete(n)}),o.forEach(function(n){a.map.set(n,i8(t.map.get(n),e.map.get(n)))})}return a}function i9(e){return!e||!(e.info||e.map.size)}function i7(e,t){var n=e.map,r=n.get(t);r&&i9(r)&&(i6.push(r),n.delete(t))}var ae=new Set;function at(e,t,n,r){var i=function(e){var t=r.getFieldValue(e,n);return"object"==typeof t&&t},a=i(e);if(a){var o=i(t);if(!(!o||eD(a)||(0,nm.D)(a,o)||Object.keys(a).every(function(e){return void 0!==r.getFieldValue(o,e)}))){var s=r.getFieldValue(e,"__typename")||r.getFieldValue(t,"__typename"),u=iv(n),c="".concat(s,".").concat(u);if(!ae.has(c)){ae.add(c);var l=[];(0,tP.k)(a)||(0,tP.k)(o)||[a,o].forEach(function(e){var t=r.getFieldValue(e,"__typename");"string"!=typeof t||l.includes(t)||l.push(t)}),__DEV__&&Q.kG.warn("Cache data may be lost when replacing the ".concat(u," field of a ").concat(s," object.\n\nThis could cause additional (usually avoidable) network requests to fetch data that were otherwise cached.\n\nTo address this problem (which is not a bug in Apollo Client), ").concat(l.length?"either ensure all objects of type "+l.join(" and ")+" have an ID or a custom merge function, or ":"","define a custom merge function for the ").concat(c," field, so InMemoryCache can safely merge these objects:\n\n existing: ").concat(JSON.stringify(a).slice(0,1e3),"\n incoming: ").concat(JSON.stringify(o).slice(0,1e3),"\n\nFor more information about these options, please refer to the documentation:\n\n * Ensuring entity objects have IDs: https://go.apollo.dev/c/generating-unique-identifiers\n * Defining custom merge functions: https://go.apollo.dev/c/merging-non-normalized-objects\n"))}}}}var an=function(e){function t(t){void 0===t&&(t={});var n=e.call(this)||this;return n.watches=new Set,n.typenameDocumentCache=new Map,n.makeVar=r2,n.txCount=0,n.config=ip(t),n.addTypename=!!n.config.addTypename,n.policies=new iQ({cache:n,dataIdFromObject:n.config.dataIdFromObject,possibleTypes:n.config.possibleTypes,typePolicies:n.config.typePolicies}),n.init(),n}return(0,en.ZT)(t,e),t.prototype.init=function(){var e=this.data=new iT.Root({policies:this.policies,resultCaching:this.config.resultCaching});this.optimisticData=e.stump,this.resetResultCache()},t.prototype.resetResultCache=function(e){var t=this,n=this.storeReader,r=this.config.fragments;this.storeWriter=new i4(this,this.storeReader=new iP({cache:this,addTypename:this.addTypename,resultCacheMaxSize:this.config.resultCacheMaxSize,canonizeResults:ib(this.config),canon:e?void 0:n&&n.canon,fragments:r}),r),this.maybeBroadcastWatch=rZ(function(e,n){return t.broadcastWatch(e,n)},{max:this.config.resultCacheMaxSize,makeCacheKey:function(e){var n=e.optimistic?t.optimisticData:t.data;if(iD(n)){var r=e.optimistic,i=e.id,a=e.variables;return n.makeCacheKey(e.query,e.callback,nx({optimistic:r,id:i,variables:a}))}}}),new Set([this.data.group,this.optimisticData.group,]).forEach(function(e){return e.resetCaching()})},t.prototype.restore=function(e){return this.init(),e&&this.data.replace(e),this},t.prototype.extract=function(e){return void 0===e&&(e=!1),(e?this.optimisticData:this.data).extract()},t.prototype.read=function(e){var t=e.returnPartialData,n=void 0!==t&&t;try{return this.storeReader.diffQueryAgainstStore((0,en.pi)((0,en.pi)({},e),{store:e.optimistic?this.optimisticData:this.data,config:this.config,returnPartialData:n})).result||null}catch(r){if(r instanceof is)return null;throw r}},t.prototype.write=function(e){try{return++this.txCount,this.storeWriter.writeToStore(this.data,e)}finally{--this.txCount||!1===e.broadcast||this.broadcastWatches()}},t.prototype.modify=function(e){if(ic.call(e,"id")&&!e.id)return!1;var t=e.optimistic?this.optimisticData:this.data;try{return++this.txCount,t.modify(e.id||"ROOT_QUERY",e.fields)}finally{--this.txCount||!1===e.broadcast||this.broadcastWatches()}},t.prototype.diff=function(e){return this.storeReader.diffQueryAgainstStore((0,en.pi)((0,en.pi)({},e),{store:e.optimistic?this.optimisticData:this.data,rootId:e.id||"ROOT_QUERY",config:this.config}))},t.prototype.watch=function(e){var t=this;return this.watches.size||r0(this),this.watches.add(e),e.immediate&&this.maybeBroadcastWatch(e),function(){t.watches.delete(e)&&!t.watches.size&&r1(t),t.maybeBroadcastWatch.forget(e)}},t.prototype.gc=function(e){nx.reset();var t=this.optimisticData.gc();return e&&!this.txCount&&(e.resetResultCache?this.resetResultCache(e.resetResultIdentities):e.resetResultIdentities&&this.storeReader.resetCanon()),t},t.prototype.retain=function(e,t){return(t?this.optimisticData:this.data).retain(e)},t.prototype.release=function(e,t){return(t?this.optimisticData:this.data).release(e)},t.prototype.identify=function(e){if(eD(e))return e.__ref;try{return this.policies.identify(e)[0]}catch(t){__DEV__&&Q.kG.warn(t)}},t.prototype.evict=function(e){if(!e.id){if(ic.call(e,"id"))return!1;e=(0,en.pi)((0,en.pi)({},e),{id:"ROOT_QUERY"})}try{return++this.txCount,this.optimisticData.evict(e,this.data)}finally{--this.txCount||!1===e.broadcast||this.broadcastWatches()}},t.prototype.reset=function(e){var t=this;return this.init(),nx.reset(),e&&e.discardWatches?(this.watches.forEach(function(e){return t.maybeBroadcastWatch.forget(e)}),this.watches.clear(),r1(this)):this.broadcastWatches(),Promise.resolve()},t.prototype.removeOptimistic=function(e){var t=this.optimisticData.removeLayer(e);t!==this.optimisticData&&(this.optimisticData=t,this.broadcastWatches())},t.prototype.batch=function(e){var t,n=this,r=e.update,i=e.optimistic,a=void 0===i||i,o=e.removeOptimistic,s=e.onWatchUpdated,u=function(e){var i=n,a=i.data,o=i.optimisticData;++n.txCount,e&&(n.data=n.optimisticData=e);try{return t=r(n)}finally{--n.txCount,n.data=a,n.optimisticData=o}},c=new Set;return s&&!this.txCount&&this.broadcastWatches((0,en.pi)((0,en.pi)({},e),{onWatchUpdated:function(e){return c.add(e),!1}})),"string"==typeof a?this.optimisticData=this.optimisticData.addLayer(a,u):!1===a?u(this.data):u(),"string"==typeof o&&(this.optimisticData=this.optimisticData.removeLayer(o)),s&&c.size?(this.broadcastWatches((0,en.pi)((0,en.pi)({},e),{onWatchUpdated:function(e,t){var n=s.call(this,e,t);return!1!==n&&c.delete(e),n}})),c.size&&c.forEach(function(e){return n.maybeBroadcastWatch.dirty(e)})):this.broadcastWatches(e),t},t.prototype.performTransaction=function(e,t){return this.batch({update:e,optimistic:t||null!==t})},t.prototype.transformDocument=function(e){if(this.addTypename){var t=this.typenameDocumentCache.get(e);return t||(t=nj(e),this.typenameDocumentCache.set(e,t),this.typenameDocumentCache.set(t,t)),t}return e},t.prototype.transformForLink=function(e){var t=this.config.fragments;return t?t.transform(e):e},t.prototype.broadcastWatches=function(e){var t=this;this.txCount||this.watches.forEach(function(n){return t.maybeBroadcastWatch(n,e)})},t.prototype.broadcastWatch=function(e,t){var n=e.lastDiff,r=this.diff(e);(!t||(e.optimistic&&"string"==typeof t.optimistic&&(r.fromOptimisticTransaction=!0),!t.onWatchUpdated||!1!==t.onWatchUpdated.call(this,e,r,n)))&&(n&&(0,nm.D)(n.result,r.result)||e.callback(e.lastDiff=r,n))},t}(io),ar={possibleTypes:{ApproveJobProposalSpecPayload:["ApproveJobProposalSpecSuccess","JobAlreadyExistsError","NotFoundError"],BridgePayload:["Bridge","NotFoundError"],CancelJobProposalSpecPayload:["CancelJobProposalSpecSuccess","NotFoundError"],ChainPayload:["Chain","NotFoundError"],CreateAPITokenPayload:["CreateAPITokenSuccess","InputErrors"],CreateBridgePayload:["CreateBridgeSuccess"],CreateCSAKeyPayload:["CSAKeyExistsError","CreateCSAKeySuccess"],CreateFeedsManagerChainConfigPayload:["CreateFeedsManagerChainConfigSuccess","InputErrors","NotFoundError"],CreateFeedsManagerPayload:["CreateFeedsManagerSuccess","InputErrors","NotFoundError","SingleFeedsManagerError"],CreateJobPayload:["CreateJobSuccess","InputErrors"],CreateOCR2KeyBundlePayload:["CreateOCR2KeyBundleSuccess"],CreateOCRKeyBundlePayload:["CreateOCRKeyBundleSuccess"],CreateP2PKeyPayload:["CreateP2PKeySuccess"],DeleteAPITokenPayload:["DeleteAPITokenSuccess","InputErrors"],DeleteBridgePayload:["DeleteBridgeConflictError","DeleteBridgeInvalidNameError","DeleteBridgeSuccess","NotFoundError"],DeleteCSAKeyPayload:["DeleteCSAKeySuccess","NotFoundError"],DeleteFeedsManagerChainConfigPayload:["DeleteFeedsManagerChainConfigSuccess","NotFoundError"],DeleteJobPayload:["DeleteJobSuccess","NotFoundError"],DeleteOCR2KeyBundlePayload:["DeleteOCR2KeyBundleSuccess","NotFoundError"],DeleteOCRKeyBundlePayload:["DeleteOCRKeyBundleSuccess","NotFoundError"],DeleteP2PKeyPayload:["DeleteP2PKeySuccess","NotFoundError"],DeleteVRFKeyPayload:["DeleteVRFKeySuccess","NotFoundError"],DismissJobErrorPayload:["DismissJobErrorSuccess","NotFoundError"],Error:["CSAKeyExistsError","DeleteBridgeConflictError","DeleteBridgeInvalidNameError","InputError","JobAlreadyExistsError","NotFoundError","RunJobCannotRunError","SingleFeedsManagerError"],EthTransactionPayload:["EthTransaction","NotFoundError"],FeaturesPayload:["Features"],FeedsManagerPayload:["FeedsManager","NotFoundError"],GetSQLLoggingPayload:["SQLLogging"],GlobalLogLevelPayload:["GlobalLogLevel"],JobPayload:["Job","NotFoundError"],JobProposalPayload:["JobProposal","NotFoundError"],JobRunPayload:["JobRun","NotFoundError"],JobSpec:["BlockHeaderFeederSpec","BlockhashStoreSpec","BootstrapSpec","CronSpec","DirectRequestSpec","FluxMonitorSpec","GatewaySpec","KeeperSpec","OCR2Spec","OCRSpec","StandardCapabilitiesSpec","VRFSpec","WebhookSpec","WorkflowSpec"],NodePayload:["Node","NotFoundError"],PaginatedPayload:["BridgesPayload","ChainsPayload","EthTransactionAttemptsPayload","EthTransactionsPayload","JobRunsPayload","JobsPayload","NodesPayload"],RejectJobProposalSpecPayload:["NotFoundError","RejectJobProposalSpecSuccess"],RunJobPayload:["NotFoundError","RunJobCannotRunError","RunJobSuccess"],SetGlobalLogLevelPayload:["InputErrors","SetGlobalLogLevelSuccess"],SetSQLLoggingPayload:["SetSQLLoggingSuccess"],SetServicesLogLevelsPayload:["InputErrors","SetServicesLogLevelsSuccess"],UpdateBridgePayload:["NotFoundError","UpdateBridgeSuccess"],UpdateFeedsManagerChainConfigPayload:["InputErrors","NotFoundError","UpdateFeedsManagerChainConfigSuccess"],UpdateFeedsManagerPayload:["InputErrors","NotFoundError","UpdateFeedsManagerSuccess"],UpdateJobProposalSpecDefinitionPayload:["NotFoundError","UpdateJobProposalSpecDefinitionSuccess"],UpdatePasswordPayload:["InputErrors","UpdatePasswordSuccess"],VRFKeyPayload:["NotFoundError","VRFKeySuccess"]}};let ai=ar;var aa=(r=void 0,location.origin),ao=new nh({uri:"".concat(aa,"/query"),credentials:"include"}),as=new ia({cache:new an({possibleTypes:ai.possibleTypes}),link:ao});if(a.Z.locale(o),u().defaultFormat="YYYY-MM-DD h:mm:ss A","undefined"!=typeof document){var au,ac,al=f().hydrate;ac=X,al(c.createElement(et,{client:as},c.createElement(d.zj,null,c.createElement(i.MuiThemeProvider,{theme:J.r},c.createElement(ac,null)))),document.getElementById("root"))}})()})(); \ No newline at end of file +`+(a!==i?`result of cast: ${a}`:""))}return r}_cast(e,t){let n=void 0===e?e:this.transforms.reduce((t,n)=>n.call(this,t,e,this),e);return void 0===n&&(n=this.getDefault()),n}_validate(e,t={},n){let{sync:r,path:i,from:a=[],originalValue:o=e,strict:s=this.spec.strict,abortEarly:u=this.spec.abortEarly}=t,c=e;s||(c=this._cast(c,pB({assert:!1},t)));let l={value:c,path:i,options:t,originalValue:o,schema:this,label:this.spec.label,sync:r,from:a},f=[];this._typeError&&f.push(this._typeError),this._whitelistError&&f.push(this._whitelistError),this._blacklistError&&f.push(this._blacklistError),pO({args:l,value:c,path:i,sync:r,tests:f,endEarly:u},e=>{if(e)return void n(e,c);pO({tests:this.tests,args:l,path:i,sync:r,value:c,endEarly:u},n)})}validate(e,t,n){let r=this.resolve(pB({},t,{value:e}));return"function"==typeof n?r._validate(e,t,n):new Promise((n,i)=>r._validate(e,t,(e,t)=>{e?i(e):n(t)}))}validateSync(e,t){let n;return this.resolve(pB({},t,{value:e}))._validate(e,pB({},t,{sync:!0}),(e,t)=>{if(e)throw e;n=t}),n}isValid(e,t){return this.validate(e,t).then(()=>!0,e=>{if(pT.isError(e))return!1;throw e})}isValidSync(e,t){try{return this.validateSync(e,t),!0}catch(n){if(pT.isError(n))return!1;throw n}}_getDefault(){let e=this.spec.default;return null==e?e:"function"==typeof e?e.call(this):pn(e)}getDefault(e){return this.resolve(e||{})._getDefault()}default(e){return 0===arguments.length?this._getDefault():this.clone({default:e})}strict(e=!0){var t=this.clone();return t.spec.strict=e,t}_isPresent(e){return null!=e}defined(e=pf.defined){return this.test({message:e,name:"defined",exclusive:!0,test:e=>void 0!==e})}required(e=pf.required){return this.clone({presence:"required"}).withMutation(t=>t.test({message:e,name:"required",exclusive:!0,test(e){return this.schema._isPresent(e)}}))}notRequired(){var e=this.clone({presence:"optional"});return e.tests=e.tests.filter(e=>"required"!==e.OPTIONS.name),e}nullable(e=!0){return this.clone({nullable:!1!==e})}transform(e){var t=this.clone();return t.transforms.push(e),t}test(...e){let t;if(void 0===(t=1===e.length?"function"==typeof e[0]?{test:e[0]}:e[0]:2===e.length?{name:e[0],test:e[1]}:{name:e[0],message:e[1],test:e[2]}).message&&(t.message=pf.default),"function"!=typeof t.test)throw TypeError("`test` is a required parameters");let n=this.clone(),r=pR(t),i=t.exclusive||t.name&&!0===n.exclusiveTests[t.name];if(t.exclusive&&!t.name)throw TypeError("Exclusive tests must provide a unique `name` identifying the test");return t.name&&(n.exclusiveTests[t.name]=!!t.exclusive),n.tests=n.tests.filter(e=>e.OPTIONS.name!==t.name||!i&&e.OPTIONS.test!==r.OPTIONS.test),n.tests.push(r),n}when(e,t){Array.isArray(e)||"string"==typeof e||(t=e,e=".");let n=this.clone(),r=pS(e).map(e=>new pD(e));return r.forEach(e=>{e.isSibling&&n.deps.push(e.key)}),n.conditions.push(new pE(r,t)),n}typeError(e){var t=this.clone();return t._typeError=pR({message:e,name:"typeError",test(e){return!!(void 0===e||this.schema.isType(e))||this.createError({params:{type:this.schema._type}})}}),t}oneOf(e,t=pf.oneOf){var n=this.clone();return e.forEach(e=>{n._whitelist.add(e),n._blacklist.delete(e)}),n._whitelistError=pR({message:t,name:"oneOf",test(e){if(void 0===e)return!0;let t=this.schema._whitelist;return!!t.has(e,this.resolve)||this.createError({params:{values:t.toArray().join(", ")}})}}),n}notOneOf(e,t=pf.notOneOf){var n=this.clone();return e.forEach(e=>{n._blacklist.add(e),n._whitelist.delete(e)}),n._blacklistError=pR({message:t,name:"notOneOf",test(e){let t=this.schema._blacklist;return!t.has(e,this.resolve)||this.createError({params:{values:t.toArray().join(", ")}})}}),n}strip(e=!0){let t=this.clone();return t.spec.strip=e,t}describe(){let e=this.clone(),{label:t,meta:n}=e.spec,r={meta:n,label:t,type:e.type,oneOf:e._whitelist.describe(),notOneOf:e._blacklist.describe(),tests:e.tests.map(e=>({name:e.OPTIONS.name,params:e.OPTIONS.params})).filter((e,t,n)=>n.findIndex(t=>t.name===e.name)===t)};return r}}for(let pH of(pU.prototype.__isYupSchema__=!0,["validate","validateSync"]))pU.prototype[`${pH}At`]=function(e,t,n={}){let{parent:r,parentPath:i,schema:a}=pF(this,e,t,n.context);return a[pH](r&&r[i],pB({},n,{parent:r,path:e}))};for(let p$ of["equals","is"])pU.prototype[p$]=pU.prototype.oneOf;for(let pz of["not","nope"])pU.prototype[pz]=pU.prototype.notOneOf;pU.prototype.optional=pU.prototype.notRequired;let pG=pU;function pW(){return new pG}pW.prototype=pG.prototype;let pK=e=>null==e;function pV(){return new pq}class pq extends pU{constructor(){super({type:"boolean"}),this.withMutation(()=>{this.transform(function(e){if(!this.isType(e)){if(/^(true|1)$/i.test(String(e)))return!0;if(/^(false|0)$/i.test(String(e)))return!1}return e})})}_typeCheck(e){return e instanceof Boolean&&(e=e.valueOf()),"boolean"==typeof e}isTrue(e=pb.isValue){return this.test({message:e,name:"is-value",exclusive:!0,params:{value:"true"},test:e=>pK(e)||!0===e})}isFalse(e=pb.isValue){return this.test({message:e,name:"is-value",exclusive:!0,params:{value:"false"},test:e=>pK(e)||!1===e})}}pV.prototype=pq.prototype;let pZ=/^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i,pX=/^((https?|ftp):)?\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i,pJ=/^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i,pQ=e=>pK(e)||e===e.trim(),p1=({}).toString();function p0(){return new p2}class p2 extends pU{constructor(){super({type:"string"}),this.withMutation(()=>{this.transform(function(e){if(this.isType(e)||Array.isArray(e))return e;let t=null!=e&&e.toString?e.toString():e;return t===p1?e:t})})}_typeCheck(e){return e instanceof String&&(e=e.valueOf()),"string"==typeof e}_isPresent(e){return super._isPresent(e)&&!!e.length}length(e,t=pd.length){return this.test({message:t,name:"length",exclusive:!0,params:{length:e},test(t){return pK(t)||t.length===this.resolve(e)}})}min(e,t=pd.min){return this.test({message:t,name:"min",exclusive:!0,params:{min:e},test(t){return pK(t)||t.length>=this.resolve(e)}})}max(e,t=pd.max){return this.test({name:"max",exclusive:!0,message:t,params:{max:e},test(t){return pK(t)||t.length<=this.resolve(e)}})}matches(e,t){let n=!1,r,i;return t&&("object"==typeof t?{excludeEmptyString:n=!1,message:r,name:i}=t:r=t),this.test({name:i||"matches",message:r||pd.matches,params:{regex:e},test:t=>pK(t)||""===t&&n||-1!==t.search(e)})}email(e=pd.email){return this.matches(pZ,{name:"email",message:e,excludeEmptyString:!0})}url(e=pd.url){return this.matches(pX,{name:"url",message:e,excludeEmptyString:!0})}uuid(e=pd.uuid){return this.matches(pJ,{name:"uuid",message:e,excludeEmptyString:!1})}ensure(){return this.default("").transform(e=>null===e?"":e)}trim(e=pd.trim){return this.transform(e=>null!=e?e.trim():e).test({message:e,name:"trim",test:pQ})}lowercase(e=pd.lowercase){return this.transform(e=>pK(e)?e:e.toLowerCase()).test({message:e,name:"string_case",exclusive:!0,test:e=>pK(e)||e===e.toLowerCase()})}uppercase(e=pd.uppercase){return this.transform(e=>pK(e)?e:e.toUpperCase()).test({message:e,name:"string_case",exclusive:!0,test:e=>pK(e)||e===e.toUpperCase()})}}p0.prototype=p2.prototype;let p3=e=>e!=+e;function p4(){return new p6}class p6 extends pU{constructor(){super({type:"number"}),this.withMutation(()=>{this.transform(function(e){let t=e;if("string"==typeof t){if(""===(t=t.replace(/\s/g,"")))return NaN;t=+t}return this.isType(t)?t:parseFloat(t)})})}_typeCheck(e){return e instanceof Number&&(e=e.valueOf()),"number"==typeof e&&!p3(e)}min(e,t=ph.min){return this.test({message:t,name:"min",exclusive:!0,params:{min:e},test(t){return pK(t)||t>=this.resolve(e)}})}max(e,t=ph.max){return this.test({message:t,name:"max",exclusive:!0,params:{max:e},test(t){return pK(t)||t<=this.resolve(e)}})}lessThan(e,t=ph.lessThan){return this.test({message:t,name:"max",exclusive:!0,params:{less:e},test(t){return pK(t)||tthis.resolve(e)}})}positive(e=ph.positive){return this.moreThan(0,e)}negative(e=ph.negative){return this.lessThan(0,e)}integer(e=ph.integer){return this.test({name:"integer",message:e,test:e=>pK(e)||Number.isInteger(e)})}truncate(){return this.transform(e=>pK(e)?e:0|e)}round(e){var t,n=["ceil","floor","round","trunc"];if("trunc"===(e=(null==(t=e)?void 0:t.toLowerCase())||"round"))return this.truncate();if(-1===n.indexOf(e.toLowerCase()))throw TypeError("Only valid options for round() are: "+n.join(", "));return this.transform(t=>pK(t)?t:Math[e](t))}}p4.prototype=p6.prototype;var p5=/^(\d{4}|[+\-]\d{6})(?:-?(\d{2})(?:-?(\d{2}))?)?(?:[ T]?(\d{2}):?(\d{2})(?::?(\d{2})(?:[,\.](\d{1,}))?)?(?:(Z)|([+\-])(\d{2})(?::?(\d{2}))?)?)?$/;function p8(e){var t,n,r=[1,4,5,6,7,10,11],i=0;if(n=p5.exec(e)){for(var a,o=0;a=r[o];++o)n[a]=+n[a]||0;n[2]=(+n[2]||1)-1,n[3]=+n[3]||1,n[7]=n[7]?String(n[7]).substr(0,3):0,(void 0===n[8]||""===n[8])&&(void 0===n[9]||""===n[9])?t=+new Date(n[1],n[2],n[3],n[4],n[5],n[6],n[7]):("Z"!==n[8]&&void 0!==n[9]&&(i=60*n[10]+n[11],"+"===n[9]&&(i=0-i)),t=Date.UTC(n[1],n[2],n[3],n[4],n[5]+i,n[6],n[7]))}else t=Date.parse?Date.parse(e):NaN;return t}let p9=new Date(""),p7=e=>"[object Date]"===Object.prototype.toString.call(e);function be(){return new bt}class bt extends pU{constructor(){super({type:"date"}),this.withMutation(()=>{this.transform(function(e){return this.isType(e)?e:(e=p8(e),isNaN(e)?p9:new Date(e))})})}_typeCheck(e){return p7(e)&&!isNaN(e.getTime())}prepareParam(e,t){let n;if(pD.isRef(e))n=e;else{let r=this.cast(e);if(!this._typeCheck(r))throw TypeError(`\`${t}\` must be a Date or a value that can be \`cast()\` to a Date`);n=r}return n}min(e,t=pp.min){let n=this.prepareParam(e,"min");return this.test({message:t,name:"min",exclusive:!0,params:{min:e},test(e){return pK(e)||e>=this.resolve(n)}})}max(e,t=pp.max){var n=this.prepareParam(e,"max");return this.test({message:t,name:"max",exclusive:!0,params:{max:e},test(e){return pK(e)||e<=this.resolve(n)}})}}bt.INVALID_DATE=p9,be.prototype=bt.prototype,be.INVALID_DATE=p9;var bn=n(11865),br=n.n(bn),bi=n(68929),ba=n.n(bi),bo=n(67523),bs=n.n(bo),bu=n(94633),bc=n.n(bu);function bl(e,t=[]){let n=[],r=[];function i(e,i){var a=(0,pC.split)(e)[0];~r.indexOf(a)||r.push(a),~t.indexOf(`${i}-${a}`)||n.push([i,a])}for(let a in e)if(py()(e,a)){let o=e[a];~r.indexOf(a)||r.push(a),pD.isRef(o)&&o.isSibling?i(o.path,a):pw(o)&&"deps"in o&&o.deps.forEach(e=>i(e,a))}return bc().array(r,n).reverse()}function bf(e,t){let n=1/0;return e.some((e,r)=>{var i;if((null==(i=t.path)?void 0:i.indexOf(e))!==-1)return n=r,!0}),n}function bd(e){return(t,n)=>bf(e,t)-bf(e,n)}function bh(){return(bh=Object.assign||function(e){for(var t=1;t"[object Object]"===Object.prototype.toString.call(e);function bb(e,t){let n=Object.keys(e.fields);return Object.keys(t).filter(e=>-1===n.indexOf(e))}let bm=bd([]);class bg extends pU{constructor(e){super({type:"object"}),this.fields=Object.create(null),this._sortErrors=bm,this._nodes=[],this._excludedEdges=[],this.withMutation(()=>{this.transform(function(e){if("string"==typeof e)try{e=JSON.parse(e)}catch(t){e=null}return this.isType(e)?e:null}),e&&this.shape(e)})}_typeCheck(e){return bp(e)||"function"==typeof e}_cast(e,t={}){var n;let r=super._cast(e,t);if(void 0===r)return this.getDefault();if(!this._typeCheck(r))return r;let i=this.fields,a=null!=(n=t.stripUnknown)?n:this.spec.noUnknown,o=this._nodes.concat(Object.keys(r).filter(e=>-1===this._nodes.indexOf(e))),s={},u=bh({},t,{parent:s,__validating:t.__validating||!1}),c=!1;for(let l of o){let f=i[l],d=py()(r,l);if(f){let h,p=r[l];u.path=(t.path?`${t.path}.`:"")+l;let b="spec"in(f=f.resolve({value:p,context:t.context,parent:s}))?f.spec:void 0,m=null==b?void 0:b.strict;if(null==b?void 0:b.strip){c=c||l in r;continue}void 0!==(h=t.__validating&&m?r[l]:f.cast(r[l],u))&&(s[l]=h)}else d&&!a&&(s[l]=r[l]);s[l]!==r[l]&&(c=!0)}return c?s:r}_validate(e,t={},n){let r=[],{sync:i,from:a=[],originalValue:o=e,abortEarly:s=this.spec.abortEarly,recursive:u=this.spec.recursive}=t;a=[{schema:this,value:o},...a],t.__validating=!0,t.originalValue=o,t.from=a,super._validate(e,t,(e,c)=>{if(e){if(!pT.isError(e)||s)return void n(e,c);r.push(e)}if(!u||!bp(c)){n(r[0]||null,c);return}o=o||c;let l=this._nodes.map(e=>(n,r)=>{let i=-1===e.indexOf(".")?(t.path?`${t.path}.`:"")+e:`${t.path||""}["${e}"]`,s=this.fields[e];if(s&&"validate"in s){s.validate(c[e],bh({},t,{path:i,from:a,strict:!0,parent:c,originalValue:o[e]}),r);return}r(null)});pO({sync:i,tests:l,value:c,errors:r,endEarly:s,sort:this._sortErrors,path:t.path},n)})}clone(e){let t=super.clone(e);return t.fields=bh({},this.fields),t._nodes=this._nodes,t._excludedEdges=this._excludedEdges,t._sortErrors=this._sortErrors,t}concat(e){let t=super.concat(e),n=t.fields;for(let[r,i]of Object.entries(this.fields)){let a=n[r];void 0===a?n[r]=i:a instanceof pU&&i instanceof pU&&(n[r]=i.concat(a))}return t.withMutation(()=>t.shape(n))}getDefaultFromShape(){let e={};return this._nodes.forEach(t=>{let n=this.fields[t];e[t]="default"in n?n.getDefault():void 0}),e}_getDefault(){return"default"in this.spec?super._getDefault():this._nodes.length?this.getDefaultFromShape():void 0}shape(e,t=[]){let n=this.clone(),r=Object.assign(n.fields,e);if(n.fields=r,n._sortErrors=bd(Object.keys(r)),t.length){Array.isArray(t[0])||(t=[t]);let i=t.map(([e,t])=>`${e}-${t}`);n._excludedEdges=n._excludedEdges.concat(i)}return n._nodes=bl(r,n._excludedEdges),n}pick(e){let t={};for(let n of e)this.fields[n]&&(t[n]=this.fields[n]);return this.clone().withMutation(e=>(e.fields={},e.shape(t)))}omit(e){let t=this.clone(),n=t.fields;for(let r of(t.fields={},e))delete n[r];return t.withMutation(()=>t.shape(n))}from(e,t,n){let r=(0,pC.getter)(e,!0);return this.transform(i=>{if(null==i)return i;let a=i;return py()(i,e)&&(a=bh({},i),n||delete a[e],a[t]=r(i)),a})}noUnknown(e=!0,t=pm.noUnknown){"string"==typeof e&&(t=e,e=!0);let n=this.test({name:"noUnknown",exclusive:!0,message:t,test(t){if(null==t)return!0;let n=bb(this.schema,t);return!e||0===n.length||this.createError({params:{unknown:n.join(", ")}})}});return n.spec.noUnknown=e,n}unknown(e=!0,t=pm.noUnknown){return this.noUnknown(!e,t)}transformKeys(e){return this.transform(t=>t&&bs()(t,(t,n)=>e(n)))}camelCase(){return this.transformKeys(ba())}snakeCase(){return this.transformKeys(br())}constantCase(){return this.transformKeys(e=>br()(e).toUpperCase())}describe(){let e=super.describe();return e.fields=pL()(this.fields,e=>e.describe()),e}}function bv(e){return new bg(e)}function by(){return(by=Object.assign||function(e){for(var t=1;t{this.transform(function(e){if("string"==typeof e)try{e=JSON.parse(e)}catch(t){e=null}return this.isType(e)?e:null})})}_typeCheck(e){return Array.isArray(e)}get _subType(){return this.innerType}_cast(e,t){let n=super._cast(e,t);if(!this._typeCheck(n)||!this.innerType)return n;let r=!1,i=n.map((e,n)=>{let i=this.innerType.cast(e,by({},t,{path:`${t.path||""}[${n}]`}));return i!==e&&(r=!0),i});return r?i:n}_validate(e,t={},n){var r,i;let a=[],o=t.sync,s=t.path,u=this.innerType,c=null!=(r=t.abortEarly)?r:this.spec.abortEarly,l=null!=(i=t.recursive)?i:this.spec.recursive,f=null!=t.originalValue?t.originalValue:e;super._validate(e,t,(e,r)=>{if(e){if(!pT.isError(e)||c)return void n(e,r);a.push(e)}if(!l||!u||!this._typeCheck(r)){n(a[0]||null,r);return}f=f||r;let i=Array(r.length);for(let d=0;du.validate(h,b,t)}pO({sync:o,path:s,value:r,errors:a,endEarly:c,tests:i},n)})}clone(e){let t=super.clone(e);return t.innerType=this.innerType,t}concat(e){let t=super.concat(e);return t.innerType=this.innerType,e.innerType&&(t.innerType=t.innerType?t.innerType.concat(e.innerType):e.innerType),t}of(e){let t=this.clone();if(!pw(e))throw TypeError("`array.of()` sub-schema must be a valid yup schema not: "+pl(e));return t.innerType=e,t}length(e,t=pg.length){return this.test({message:t,name:"length",exclusive:!0,params:{length:e},test(t){return pK(t)||t.length===this.resolve(e)}})}min(e,t){return t=t||pg.min,this.test({message:t,name:"min",exclusive:!0,params:{min:e},test(t){return pK(t)||t.length>=this.resolve(e)}})}max(e,t){return t=t||pg.max,this.test({message:t,name:"max",exclusive:!0,params:{max:e},test(t){return pK(t)||t.length<=this.resolve(e)}})}ensure(){return this.default(()=>[]).transform((e,t)=>this._typeCheck(e)?e:null==t?[]:[].concat(t))}compact(e){let t=e?(t,n,r)=>!e(t,n,r):e=>!!e;return this.transform(e=>null!=e?e.filter(t):e)}describe(){let e=super.describe();return this.innerType&&(e.innerType=this.innerType.describe()),e}nullable(e=!0){return super.nullable(e)}defined(){return super.defined()}required(e){return super.required(e)}}bw.prototype=b_.prototype;var bE=bv().shape({name:p0().required("Required"),url:p0().required("Required")}),bS=function(e){var t=e.initialValues,n=e.onSubmit,r=e.submitButtonText,i=e.nameDisabled,a=void 0!==i&&i;return l.createElement(hT,{initialValues:t,validationSchema:bE,onSubmit:n},function(e){var t=e.isSubmitting;return l.createElement(l.Fragment,null,l.createElement(hR,{"data-testid":"bridge-form",noValidate:!0},l.createElement(d.Z,{container:!0,spacing:16},l.createElement(d.Z,{item:!0,xs:12,md:7},l.createElement(hP,{component:hX,id:"name",name:"name",label:"Name",disabled:a,required:!0,fullWidth:!0,FormHelperTextProps:{"data-testid":"name-helper-text"}})),l.createElement(d.Z,{item:!0,xs:12,md:7},l.createElement(hP,{component:hX,id:"url",name:"url",label:"Bridge URL",placeholder:"https://",required:!0,fullWidth:!0,FormHelperTextProps:{"data-testid":"url-helper-text"}})),l.createElement(d.Z,{item:!0,xs:12,md:7},l.createElement(d.Z,{container:!0,spacing:16},l.createElement(d.Z,{item:!0,xs:7},l.createElement(hP,{component:hX,id:"minimumContractPayment",name:"minimumContractPayment",label:"Minimum Contract Payment",placeholder:"0",fullWidth:!0,inputProps:{min:0},FormHelperTextProps:{"data-testid":"minimumContractPayment-helper-text"}})),l.createElement(d.Z,{item:!0,xs:7},l.createElement(hP,{component:hX,id:"confirmations",name:"confirmations",label:"Confirmations",placeholder:"0",type:"number",fullWidth:!0,inputProps:{min:0},FormHelperTextProps:{"data-testid":"confirmations-helper-text"}})))),l.createElement(d.Z,{item:!0,xs:12,md:7},l.createElement(ok.default,{variant:"contained",color:"primary",type:"submit",disabled:t,size:"large"},r)))))})},bk=function(e){var t=e.bridge,n=e.onSubmit,r={name:t.name,url:t.url,minimumContractPayment:t.minimumContractPayment,confirmations:t.confirmations};return l.createElement(ig,null,l.createElement(d.Z,{container:!0,spacing:40},l.createElement(d.Z,{item:!0,xs:12,md:11,lg:9},l.createElement(r5.Z,null,l.createElement(sl.Z,{title:"Edit Bridge",action:l.createElement(aA.Z,{component:tz,href:"/bridges/".concat(t.id)},"Cancel")}),l.createElement(aW.Z,null,l.createElement(bS,{nameDisabled:!0,initialValues:r,onSubmit:n,submitButtonText:"Save Bridge"}))))))};function bx(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&void 0!==arguments[0]&&arguments[0],t=e?function(){return l.createElement(x.default,{variant:"body1"},"Loading...")}:function(){return null};return{isLoading:e,LoadingPlaceholder:t}},mc=n(76023);function ml(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]=0)&&Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}function mB(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n=4?[e[0],e[1],e[2],e[3],"".concat(e[0],".").concat(e[1]),"".concat(e[0],".").concat(e[2]),"".concat(e[0],".").concat(e[3]),"".concat(e[1],".").concat(e[0]),"".concat(e[1],".").concat(e[2]),"".concat(e[1],".").concat(e[3]),"".concat(e[2],".").concat(e[0]),"".concat(e[2],".").concat(e[1]),"".concat(e[2],".").concat(e[3]),"".concat(e[3],".").concat(e[0]),"".concat(e[3],".").concat(e[1]),"".concat(e[3],".").concat(e[2]),"".concat(e[0],".").concat(e[1],".").concat(e[2]),"".concat(e[0],".").concat(e[1],".").concat(e[3]),"".concat(e[0],".").concat(e[2],".").concat(e[1]),"".concat(e[0],".").concat(e[2],".").concat(e[3]),"".concat(e[0],".").concat(e[3],".").concat(e[1]),"".concat(e[0],".").concat(e[3],".").concat(e[2]),"".concat(e[1],".").concat(e[0],".").concat(e[2]),"".concat(e[1],".").concat(e[0],".").concat(e[3]),"".concat(e[1],".").concat(e[2],".").concat(e[0]),"".concat(e[1],".").concat(e[2],".").concat(e[3]),"".concat(e[1],".").concat(e[3],".").concat(e[0]),"".concat(e[1],".").concat(e[3],".").concat(e[2]),"".concat(e[2],".").concat(e[0],".").concat(e[1]),"".concat(e[2],".").concat(e[0],".").concat(e[3]),"".concat(e[2],".").concat(e[1],".").concat(e[0]),"".concat(e[2],".").concat(e[1],".").concat(e[3]),"".concat(e[2],".").concat(e[3],".").concat(e[0]),"".concat(e[2],".").concat(e[3],".").concat(e[1]),"".concat(e[3],".").concat(e[0],".").concat(e[1]),"".concat(e[3],".").concat(e[0],".").concat(e[2]),"".concat(e[3],".").concat(e[1],".").concat(e[0]),"".concat(e[3],".").concat(e[1],".").concat(e[2]),"".concat(e[3],".").concat(e[2],".").concat(e[0]),"".concat(e[3],".").concat(e[2],".").concat(e[1]),"".concat(e[0],".").concat(e[1],".").concat(e[2],".").concat(e[3]),"".concat(e[0],".").concat(e[1],".").concat(e[3],".").concat(e[2]),"".concat(e[0],".").concat(e[2],".").concat(e[1],".").concat(e[3]),"".concat(e[0],".").concat(e[2],".").concat(e[3],".").concat(e[1]),"".concat(e[0],".").concat(e[3],".").concat(e[1],".").concat(e[2]),"".concat(e[0],".").concat(e[3],".").concat(e[2],".").concat(e[1]),"".concat(e[1],".").concat(e[0],".").concat(e[2],".").concat(e[3]),"".concat(e[1],".").concat(e[0],".").concat(e[3],".").concat(e[2]),"".concat(e[1],".").concat(e[2],".").concat(e[0],".").concat(e[3]),"".concat(e[1],".").concat(e[2],".").concat(e[3],".").concat(e[0]),"".concat(e[1],".").concat(e[3],".").concat(e[0],".").concat(e[2]),"".concat(e[1],".").concat(e[3],".").concat(e[2],".").concat(e[0]),"".concat(e[2],".").concat(e[0],".").concat(e[1],".").concat(e[3]),"".concat(e[2],".").concat(e[0],".").concat(e[3],".").concat(e[1]),"".concat(e[2],".").concat(e[1],".").concat(e[0],".").concat(e[3]),"".concat(e[2],".").concat(e[1],".").concat(e[3],".").concat(e[0]),"".concat(e[2],".").concat(e[3],".").concat(e[0],".").concat(e[1]),"".concat(e[2],".").concat(e[3],".").concat(e[1],".").concat(e[0]),"".concat(e[3],".").concat(e[0],".").concat(e[1],".").concat(e[2]),"".concat(e[3],".").concat(e[0],".").concat(e[2],".").concat(e[1]),"".concat(e[3],".").concat(e[1],".").concat(e[0],".").concat(e[2]),"".concat(e[3],".").concat(e[1],".").concat(e[2],".").concat(e[0]),"".concat(e[3],".").concat(e[2],".").concat(e[0],".").concat(e[1]),"".concat(e[3],".").concat(e[2],".").concat(e[1],".").concat(e[0])]:void 0}var mZ={};function mX(e){if(0===e.length||1===e.length)return e;var t=e.join(".");return mZ[t]||(mZ[t]=mq(e)),mZ[t]}function mJ(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=arguments.length>2?arguments[2]:void 0;return mX(e.filter(function(e){return"token"!==e})).reduce(function(e,t){return mK({},e,n[t])},t)}function mQ(e){return e.join(" ")}function m1(e,t){var n=0;return function(r){return n+=1,r.map(function(r,i){return m0({node:r,stylesheet:e,useInlineStyles:t,key:"code-segment-".concat(n,"-").concat(i)})})}}function m0(e){var t=e.node,n=e.stylesheet,r=e.style,i=void 0===r?{}:r,a=e.useInlineStyles,o=e.key,s=t.properties,u=t.type,c=t.tagName,f=t.value;if("text"===u)return f;if(c){var d,h=m1(n,a);if(a){var p=Object.keys(n).reduce(function(e,t){return t.split(".").forEach(function(t){e.includes(t)||e.push(t)}),e},[]),b=s.className&&s.className.includes("token")?["token"]:[],m=s.className&&b.concat(s.className.filter(function(e){return!p.includes(e)}));d=mK({},s,{className:mQ(m)||void 0,style:mJ(s.className,Object.assign({},s.style,i),n)})}else d=mK({},s,{className:mQ(s.className)});var g=h(t.children);return l.createElement(c,(0,mV.Z)({key:o},d),g)}}let m2=function(e,t){return -1!==e.listLanguages().indexOf(t)};var m3=/\n/g;function m4(e){return e.match(m3)}function m6(e){var t=e.lines,n=e.startingLineNumber,r=e.style;return t.map(function(e,t){var i=t+n;return l.createElement("span",{key:"line-".concat(t),className:"react-syntax-highlighter-line-number",style:"function"==typeof r?r(i):r},"".concat(i,"\n"))})}function m5(e){var t=e.codeString,n=e.codeStyle,r=e.containerStyle,i=void 0===r?{float:"left",paddingRight:"10px"}:r,a=e.numberStyle,o=void 0===a?{}:a,s=e.startingLineNumber;return l.createElement("code",{style:Object.assign({},n,i)},m6({lines:t.replace(/\n$/,"").split("\n"),style:o,startingLineNumber:s}))}function m8(e){return"".concat(e.toString().length,".25em")}function m9(e,t){return{type:"element",tagName:"span",properties:{key:"line-number--".concat(e),className:["comment","linenumber","react-syntax-highlighter-line-number"],style:t},children:[{type:"text",value:e}]}}function m7(e,t,n){var r,i={display:"inline-block",minWidth:m8(n),paddingRight:"1em",textAlign:"right",userSelect:"none"};return mK({},i,"function"==typeof e?e(t):e)}function ge(e){var t=e.children,n=e.lineNumber,r=e.lineNumberStyle,i=e.largestLineNumber,a=e.showInlineLineNumbers,o=e.lineProps,s=void 0===o?{}:o,u=e.className,c=void 0===u?[]:u,l=e.showLineNumbers,f=e.wrapLongLines,d="function"==typeof s?s(n):s;if(d.className=c,n&&a){var h=m7(r,n,i);t.unshift(m9(n,h))}return f&l&&(d.style=mK({},d.style,{display:"flex"})),{type:"element",tagName:"span",properties:d,children:t}}function gt(e){for(var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[],n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:[],r=0;r2&&void 0!==arguments[2]?arguments[2]:[];return ge({children:e,lineNumber:t,lineNumberStyle:s,largestLineNumber:o,showInlineLineNumbers:i,lineProps:n,className:a,showLineNumbers:r,wrapLongLines:u})}function b(e,t){if(r&&t&&i){var n=m7(s,t,o);e.unshift(m9(t,n))}return e}function m(e,n){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:[];return t||r.length>0?p(e,n,r):b(e,n)}for(var g=function(){var e=l[h],t=e.children[0].value;if(m4(t)){var n=t.split("\n");n.forEach(function(t,i){var o=r&&f.length+a,s={type:"text",value:"".concat(t,"\n")};if(0===i){var u=l.slice(d+1,h).concat(ge({children:[s],className:e.properties.className})),c=m(u,o);f.push(c)}else if(i===n.length-1){if(l[h+1]&&l[h+1].children&&l[h+1].children[0]){var p={type:"text",value:"".concat(t)},b=ge({children:[p],className:e.properties.className});l.splice(h+1,0,b)}else{var g=[s],v=m(g,o,e.properties.className);f.push(v)}}else{var y=[s],w=m(y,o,e.properties.className);f.push(w)}}),d=h}h++};h code[class*="language-"]':{background:"#f5f2f0",padding:".1em",borderRadius:".3em",whiteSpace:"normal"},comment:{color:"slategray"},prolog:{color:"slategray"},doctype:{color:"slategray"},cdata:{color:"slategray"},punctuation:{color:"#999"},namespace:{Opacity:".7"},property:{color:"#905"},tag:{color:"#905"},boolean:{color:"#905"},number:{color:"#905"},constant:{color:"#905"},symbol:{color:"#905"},deleted:{color:"#905"},selector:{color:"#690"},"attr-name":{color:"#690"},string:{color:"#690"},char:{color:"#690"},builtin:{color:"#690"},inserted:{color:"#690"},operator:{color:"#9a6e3a",background:"hsla(0, 0%, 100%, .5)"},entity:{color:"#9a6e3a",background:"hsla(0, 0%, 100%, .5)",cursor:"help"},url:{color:"#9a6e3a",background:"hsla(0, 0%, 100%, .5)"},".language-css .token.string":{color:"#9a6e3a",background:"hsla(0, 0%, 100%, .5)"},".style .token.string":{color:"#9a6e3a",background:"hsla(0, 0%, 100%, .5)"},atrule:{color:"#07a"},"attr-value":{color:"#07a"},keyword:{color:"#07a"},function:{color:"#DD4A68"},"class-name":{color:"#DD4A68"},regex:{color:"#e90"},important:{color:"#e90",fontWeight:"bold"},variable:{color:"#e90"},bold:{fontWeight:"bold"},italic:{fontStyle:"italic"}};var gu=n(98695),gc=n.n(gu);let gl=["abap","abnf","actionscript","ada","agda","al","antlr4","apacheconf","apl","applescript","aql","arduino","arff","asciidoc","asm6502","aspnet","autohotkey","autoit","bash","basic","batch","bbcode","birb","bison","bnf","brainfuck","brightscript","bro","bsl","c","cil","clike","clojure","cmake","coffeescript","concurnas","cpp","crystal","csharp","csp","css-extras","css","cypher","d","dart","dax","dhall","diff","django","dns-zone-file","docker","ebnf","editorconfig","eiffel","ejs","elixir","elm","erb","erlang","etlua","excel-formula","factor","firestore-security-rules","flow","fortran","fsharp","ftl","gcode","gdscript","gedcom","gherkin","git","glsl","gml","go","graphql","groovy","haml","handlebars","haskell","haxe","hcl","hlsl","hpkp","hsts","http","ichigojam","icon","iecst","ignore","inform7","ini","io","j","java","javadoc","javadoclike","javascript","javastacktrace","jolie","jq","js-extras","js-templates","jsdoc","json","json5","jsonp","jsstacktrace","jsx","julia","keyman","kotlin","latex","latte","less","lilypond","liquid","lisp","livescript","llvm","lolcode","lua","makefile","markdown","markup-templating","markup","matlab","mel","mizar","mongodb","monkey","moonscript","n1ql","n4js","nand2tetris-hdl","naniscript","nasm","neon","nginx","nim","nix","nsis","objectivec","ocaml","opencl","oz","parigp","parser","pascal","pascaligo","pcaxis","peoplecode","perl","php-extras","php","phpdoc","plsql","powerquery","powershell","processing","prolog","properties","protobuf","pug","puppet","pure","purebasic","purescript","python","q","qml","qore","r","racket","reason","regex","renpy","rest","rip","roboconf","robotframework","ruby","rust","sas","sass","scala","scheme","scss","shell-session","smali","smalltalk","smarty","sml","solidity","solution-file","soy","sparql","splunk-spl","sqf","sql","stan","stylus","swift","t4-cs","t4-templating","t4-vb","tap","tcl","textile","toml","tsx","tt2","turtle","twig","typescript","typoscript","unrealscript","vala","vbnet","velocity","verilog","vhdl","vim","visual-basic","warpscript","wasm","wiki","xeora","xml-doc","xojo","xquery","yaml","yang","zig"];var gf=go(gc(),gs);gf.supportedLanguages=gl;let gd=gf;var gh=n(64566);function gp(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function gb(){var e=gp(["\n query FetchConfigV2 {\n configv2 {\n user\n effective\n }\n }\n"]);return gb=function(){return e},e}var gm=n0(gb()),gg=function(e){var t=e.children;return l.createElement(ir.Z,null,l.createElement(r7.default,{component:"th",scope:"row",colSpan:3},t))},gv=function(){return l.createElement(gg,null,"...")},gy=function(e){var t=e.children;return l.createElement(gg,null,t)},gw=function(e){var t=e.loading,n=e.toml,r=e.error,i=void 0===r?"":r,a=e.title,o=e.expanded;if(i)return l.createElement(gy,null,i);if(t)return l.createElement(gv,null);a||(a="TOML");var s={display:"block"};return l.createElement(x.default,null,l.createElement(mP.Z,{defaultExpanded:o},l.createElement(mR.Z,{expandIcon:l.createElement(gh.Z,null)},a),l.createElement(mj.Z,{style:s},l.createElement(gd,{language:"toml",style:gs},n))))},g_=function(){var e=rv(gm,{fetchPolicy:"cache-and-network"}),t=e.data,n=e.loading,r=e.error;return(null==t?void 0:t.configv2.effective)=="N/A"?l.createElement(l.Fragment,null,l.createElement(d.Z,{item:!0,xs:12},l.createElement(r5.Z,null,l.createElement(sl.Z,{title:"TOML Configuration"}),l.createElement(gw,{title:"V2 config dump:",error:null==r?void 0:r.message,loading:n,toml:null==t?void 0:t.configv2.user,showHead:!0})))):l.createElement(l.Fragment,null,l.createElement(d.Z,{container:!0},l.createElement(d.Z,{item:!0,xs:12},l.createElement(r5.Z,null,l.createElement(sl.Z,{title:"TOML Configuration"}),l.createElement(gw,{title:"User specified:",error:null==r?void 0:r.message,loading:n,toml:null==t?void 0:t.configv2.user,showHead:!0,expanded:!0}),l.createElement(gw,{title:"Effective (with defaults):",error:null==r?void 0:r.message,loading:n,toml:null==t?void 0:t.configv2.effective,showHead:!0})))))},gE=n(34823),gS=function(e){return(0,b.createStyles)({cell:{paddingTop:1.5*e.spacing.unit,paddingBottom:1.5*e.spacing.unit}})},gk=(0,b.withStyles)(gS)(function(e){var t=e.classes,n=(0,A.I0)();(0,l.useEffect)(function(){n((0,ty.DQ)())});var r=(0,A.v9)(gE.N,A.wU);return l.createElement(r5.Z,null,l.createElement(sl.Z,{title:"Node"}),l.createElement(r8.Z,null,l.createElement(r9.Z,null,l.createElement(ir.Z,null,l.createElement(r7.default,{className:t.cell},l.createElement(x.default,null,"Version"),l.createElement(x.default,{variant:"subtitle1",color:"textSecondary"},r.version))),l.createElement(ir.Z,null,l.createElement(r7.default,{className:t.cell},l.createElement(x.default,null,"SHA"),l.createElement(x.default,{variant:"subtitle1",color:"textSecondary"},r.commitSHA))))))}),gx=function(){return l.createElement(ig,null,l.createElement(d.Z,{container:!0},l.createElement(d.Z,{item:!0,sm:12,md:8},l.createElement(d.Z,{container:!0},l.createElement(g_,null))),l.createElement(d.Z,{item:!0,sm:12,md:4},l.createElement(d.Z,{container:!0},l.createElement(d.Z,{item:!0,xs:12},l.createElement(gk,null)),l.createElement(d.Z,{item:!0,xs:12},l.createElement(mN,null)),l.createElement(d.Z,{item:!0,xs:12},l.createElement(mE,null))))))},gT=function(){return l.createElement(gx,null)},gM=function(){return l.createElement(gT,null)},gO=n(44431),gA=1e18,gL=function(e){return new gO.BigNumber(e).dividedBy(gA).toFixed(8)},gC=function(e){var t=e.keys,n=e.chainID,r=e.hideHeaderTitle;return l.createElement(l.Fragment,null,l.createElement(sl.Z,{title:!r&&"Account Balances",subheader:"Chain ID "+n}),l.createElement(aW.Z,null,l.createElement(w.default,{dense:!1,disablePadding:!0},t&&t.map(function(e,r){return l.createElement(l.Fragment,null,l.createElement(_.default,{disableGutters:!0,key:["acc-balance",n.toString(),r.toString()].join("-")},l.createElement(E.Z,{primary:l.createElement(l.Fragment,null,l.createElement(d.Z,{container:!0,spacing:16},l.createElement(d.Z,{item:!0,xs:12},l.createElement(op,{title:"Address"}),l.createElement(ob,{value:e.address})),l.createElement(d.Z,{item:!0,xs:6},l.createElement(op,{title:"Native Token Balance"}),l.createElement(ob,{value:e.ethBalance||"--"})),l.createElement(d.Z,{item:!0,xs:6},l.createElement(op,{title:"LINK Balance"}),l.createElement(ob,{value:e.linkBalance?gL(e.linkBalance):"--"}))))})),r+1s&&l.createElement(gB.Z,null,l.createElement(ir.Z,null,l.createElement(r7.default,{className:r.footer},l.createElement(aA.Z,{href:"/runs",component:tz},"View More"))))))});function vt(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function vn(){var e=vt(["\n ","\n query FetchRecentJobRuns($offset: Int, $limit: Int) {\n jobRuns(offset: $offset, limit: $limit) {\n results {\n ...RecentJobRunsPayload_ResultsFields\n }\n metadata {\n total\n }\n }\n }\n"]);return vn=function(){return e},e}var vr=5,vi=n0(vn(),g9),va=function(){var e=rv(vi,{variables:{offset:0,limit:vr},fetchPolicy:"cache-and-network"}),t=e.data,n=e.loading,r=e.error;return l.createElement(ve,{data:t,errorMsg:null==r?void 0:r.message,loading:n,maxRunsSize:vr})},vo=function(e){return(0,b.createStyles)({style:{textAlign:"center",padding:2.5*e.spacing.unit,position:"fixed",left:"0",bottom:"0",width:"100%",borderRadius:0},bareAnchor:{color:e.palette.common.black,textDecoration:"none"}})},vs=(0,b.withStyles)(vo)(function(e){var t=e.classes,n=(0,A.v9)(gE.N,A.wU),r=(0,A.I0)();return(0,l.useEffect)(function(){r((0,ty.DQ)())}),l.createElement(ii.default,{className:t.style},l.createElement(x.default,null,"Chainlink Node ",n.version," at commit"," ",l.createElement("a",{target:"_blank",rel:"noopener noreferrer",href:"https://github.com/smartcontractkit/chainlink/commit/".concat(n.commitSHA),className:t.bareAnchor},n.commitSHA)))}),vu=function(e){return(0,b.createStyles)({cell:{borderColor:e.palette.divider,borderTop:"1px solid",borderBottom:"none",paddingTop:2*e.spacing.unit,paddingBottom:2*e.spacing.unit,paddingLeft:2*e.spacing.unit},block:{display:"block"},overflowEllipsis:{textOverflow:"ellipsis",overflow:"hidden"}})},vc=(0,b.withStyles)(vu)(function(e){var t=e.classes,n=e.job;return l.createElement(ir.Z,null,l.createElement(r7.default,{scope:"row",className:t.cell},l.createElement(d.Z,{container:!0,spacing:0},l.createElement(d.Z,{item:!0,xs:12},l.createElement(ih,{href:"/jobs/".concat(n.id),classes:{linkContent:t.block}},l.createElement(x.default,{className:t.overflowEllipsis,variant:"body1",component:"span",color:"primary"},n.name||n.id))),l.createElement(d.Z,{item:!0,xs:12},l.createElement(x.default,{variant:"body1",color:"textSecondary"},"Created ",l.createElement(aO,{tooltip:!0},n.createdAt))))))});function vl(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function vf(){var e=vl(["\n fragment RecentJobsPayload_ResultsFields on Job {\n id\n name\n createdAt\n }\n"]);return vf=function(){return e},e}var vd=n0(vf()),vh=function(){return(0,b.createStyles)({cardHeader:{borderBottom:0},table:{tableLayout:"fixed"}})},vp=(0,b.withStyles)(vh)(function(e){var t,n,r=e.classes,i=e.data,a=e.errorMsg,o=e.loading;return l.createElement(r5.Z,null,l.createElement(sl.Z,{title:"Recent Jobs",className:r.cardHeader}),l.createElement(r8.Z,{className:r.table},l.createElement(r9.Z,null,l.createElement(g$,{visible:o}),l.createElement(gz,{visible:(null===(t=null==i?void 0:i.jobs.results)||void 0===t?void 0:t.length)===0},"No recently created jobs"),l.createElement(gU,{msg:a}),null===(n=null==i?void 0:i.jobs.results)||void 0===n?void 0:n.map(function(e,t){return l.createElement(vc,{job:e,key:t})}))))});function vb(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function vm(){var e=vb(["\n ","\n query FetchRecentJobs($offset: Int, $limit: Int) {\n jobs(offset: $offset, limit: $limit) {\n results {\n ...RecentJobsPayload_ResultsFields\n }\n }\n }\n"]);return vm=function(){return e},e}var vg=5,vv=n0(vm(),vd),vy=function(){var e=rv(vv,{variables:{offset:0,limit:vg},fetchPolicy:"cache-and-network"}),t=e.data,n=e.loading,r=e.error;return l.createElement(vp,{data:t,errorMsg:null==r?void 0:r.message,loading:n})},vw=function(){return l.createElement(ig,null,l.createElement(d.Z,{container:!0},l.createElement(d.Z,{item:!0,xs:8},l.createElement(va,null)),l.createElement(d.Z,{item:!0,xs:4},l.createElement(d.Z,{container:!0},l.createElement(d.Z,{item:!0,xs:12},l.createElement(gY,null)),l.createElement(d.Z,{item:!0,xs:12},l.createElement(vy,null))))),l.createElement(vs,null))},v_=function(){return l.createElement(vw,null)},vE=function(){return l.createElement(v_,null)},vS=n(87239),vk=function(e){switch(e){case"DirectRequestSpec":return"Direct Request";case"FluxMonitorSpec":return"Flux Monitor";default:return e.replace(/Spec$/,"")}},vx=n(5022),vT=n(78718),vM=n.n(vT);function vO(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n1?t-1:0),r=1;r1?t-1:0),r=1;re.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&n.map(function(e){return l.createElement(ir.Z,{key:e.id,style:{cursor:"pointer"},onClick:function(){return r.push("/runs/".concat(e.id))}},l.createElement(r7.default,{className:t.idCell,scope:"row"},l.createElement("div",{className:t.runDetails},l.createElement(x.default,{variant:"h5",color:"primary",component:"span"},e.id))),l.createElement(r7.default,{className:t.stampCell},l.createElement(x.default,{variant:"body1",color:"textSecondary",className:t.stamp},"Created ",l.createElement(aO,{tooltip:!0},e.createdAt))),l.createElement(r7.default,{className:t.statusCell,scope:"row"},l.createElement(x.default,{variant:"body1",className:O()(t.status,yh(t,e.status))},e.status.toLowerCase())))})))}),yb=n(16839),ym=n.n(yb);function yg(e){var t=e.replace(/\w+\s*=\s*<([^>]|[\r\n])*>/g,""),n=ym().read(t),r=n.edges();return n.nodes().map(function(e){var t={id:e,parentIds:r.filter(function(t){return t.w===e}).map(function(e){return e.v})};return Object.keys(n.node(e)).length>0&&(t.attributes=n.node(e)),t})}var yv=n(94164),yy=function(e){var t=e.data,n=[];return(null==t?void 0:t.attributes)&&Object.keys(t.attributes).forEach(function(e){var r;n.push(l.createElement("div",{key:e},l.createElement(x.default,{variant:"body1",color:"textSecondary",component:"div"},l.createElement("b",null,e,":")," ",null===(r=t.attributes)||void 0===r?void 0:r[e])))}),l.createElement("div",null,t&&l.createElement(x.default,{variant:"body1",color:"textPrimary"},l.createElement("b",null,t.id)),n)},yw=n(73343),y_=n(3379),yE=n.n(y_);function yS(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);nwindow.innerWidth?u-r.getBoundingClientRect().width-a:u+a,n=c+r.getBoundingClientRect().height+i>window.innerHeight?c-r.getBoundingClientRect().height-a:c+a,r.style.opacity=String(1),r.style.top="".concat(n,"px"),r.style.left="".concat(t,"px"),r.style.zIndex=String(1)}},h=function(e){var t=document.getElementById("tooltip-d3-chart-".concat(e));t&&(t.style.opacity=String(0),t.style.zIndex=String(-1))};return l.createElement("div",{style:{fontFamily:"sans-serif",fontWeight:"normal"}},l.createElement(yv.kJ,{id:"task-list-graph-d3",data:i,config:s,onMouseOverNode:d,onMouseOutNode:h},"D3 chart"),n.map(function(e){return l.createElement("div",{key:"d3-tooltip-key-".concat(e.id),id:"tooltip-d3-chart-".concat(e.id),style:{position:"absolute",opacity:"0",border:"1px solid rgba(0, 0, 0, 0.1)",padding:yw.r.spacing.unit,background:"white",borderRadius:5,zIndex:-1,inlineSize:"min-content"}},l.createElement(yy,{data:e}))}))};function yL(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);nyY&&l.createElement("div",{className:t.runDetails},l.createElement(aA.Z,{href:"/jobs/".concat(n.id,"/runs"),component:tz},"View more")))),l.createElement(d.Z,{item:!0,xs:12,sm:6},l.createElement(yF,{observationSource:n.observationSource})))});function yH(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&void 0!==arguments[0]?arguments[0]:"";try{return vx.parse(e),!0}catch(t){return!1}})}),wW=function(e){var t=e.initialValues,n=e.onSubmit,r=e.onTOMLChange;return l.createElement(hT,{initialValues:t,validationSchema:wG,onSubmit:n},function(e){var t=e.isSubmitting,n=e.values;return r&&r(n.toml),l.createElement(hR,{"data-testid":"job-form",noValidate:!0},l.createElement(d.Z,{container:!0,spacing:16},l.createElement(d.Z,{item:!0,xs:12},l.createElement(hP,{component:hX,id:"toml",name:"toml",label:"Job Spec (TOML)",required:!0,fullWidth:!0,multiline:!0,rows:10,rowsMax:25,variant:"outlined",autoComplete:"off",FormHelperTextProps:{"data-testid":"toml-helper-text"}})),l.createElement(d.Z,{item:!0,xs:12,md:7},l.createElement(ok.default,{variant:"contained",color:"primary",type:"submit",disabled:t,size:"large"},"Create Job"))))})},wK=n(50109),wV="persistSpec";function wq(e){var t=e.query,n=new URLSearchParams(t).get("definition");return n?(wK.t8(wV,n),{toml:n}):{toml:wK.U2(wV)||""}}var wZ=function(e){var t=e.onSubmit,n=e.onTOMLChange,r=wq({query:(0,h.TH)().search}),i=function(e){var t=e.replace(/[\u200B-\u200D\uFEFF]/g,"");wK.t8("".concat(wV),t),n&&n(t)};return l.createElement(r5.Z,null,l.createElement(sl.Z,{title:"New Job"}),l.createElement(aW.Z,null,l.createElement(wW,{initialValues:r,onSubmit:t,onTOMLChange:i})))};function wX(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n1&&void 0!==arguments[1]?arguments[1]:{},n=t.start,r=void 0===n?6:n,i=t.end,a=void 0===i?4:i;return e.substring(0,r)+"..."+e.substring(e.length-a)}function _M(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0&&void 0!==arguments[0]?arguments[0]:{};return rv(_W,e)},_V=function(){var e=_K({fetchPolicy:"cache-and-network"}),t=e.data,n=e.loading,r=e.error,i=e.refetch;return l.createElement(_U,{loading:n,data:t,errorMsg:null==r?void 0:r.message,refetch:i})},_q=function(e){var t=e.csaKey;return l.createElement(ir.Z,{hover:!0},l.createElement(r7.default,null,l.createElement(x.default,{variant:"body1"},t.publicKey," ",l.createElement(_x,{data:t.publicKey}))))};function _Z(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function _X(){var e=_Z(["\n fragment CSAKeysPayload_ResultsFields on CSAKey {\n id\n publicKey\n }\n"]);return _X=function(){return e},e}var _J=n0(_X()),_Q=function(e){var t,n,r,i=e.data,a=e.errorMsg,o=e.loading,s=e.onCreate;return l.createElement(r5.Z,null,l.createElement(sl.Z,{action:(null===(t=null==i?void 0:i.csaKeys.results)||void 0===t?void 0:t.length)===0&&l.createElement(ok.default,{variant:"outlined",color:"primary",onClick:s},"New CSA Key"),title:"CSA Key",subheader:"Manage your CSA Key"}),l.createElement(r8.Z,null,l.createElement(ie.Z,null,l.createElement(ir.Z,null,l.createElement(r7.default,null,"Public Key"))),l.createElement(r9.Z,null,l.createElement(g$,{visible:o}),l.createElement(gz,{visible:(null===(n=null==i?void 0:i.csaKeys.results)||void 0===n?void 0:n.length)===0}),l.createElement(gU,{msg:a}),null===(r=null==i?void 0:i.csaKeys.results)||void 0===r?void 0:r.map(function(e,t){return l.createElement(_q,{csaKey:e,key:t})}))))};function _1(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0&&void 0!==arguments[0]?arguments[0]:{};return rv(EM,e)};function EA(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0&&void 0!==arguments[0]?arguments[0]:{};return rv(EJ,e)},E3=function(){return oo(EQ)},E4=function(){return oo(E1)},E6=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return rv(E0,e)};function E5(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0&&void 0!==arguments[0]?arguments[0]:{};return rv(SK,e)};function Sq(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n=0)&&Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}function kV(e,t){if(null==e)return{};var n,r,i={},a=Object.keys(e);for(r=0;r=0||(i[n]=e[n]);return i}var kq=function(e){var t=e.run,n=l.useMemo(function(){var e=t.inputs,n=t.outputs,r=t.taskRuns,i=kK(t,["inputs","outputs","taskRuns"]),a={};try{a=JSON.parse(e)}catch(o){a={}}return kW(kz({},i),{inputs:a,outputs:n,taskRuns:r})},[t]);return l.createElement(r5.Z,null,l.createElement(aW.Z,null,l.createElement(kH,{object:n})))};function kZ(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function kX(e){for(var t=1;t0&&l.createElement(kr,{errors:t.allErrors})),l.createElement(d.Z,{item:!0,xs:12},l.createElement(h.rs,null,l.createElement(h.AW,{path:"".concat(n,"/json")},l.createElement(kq,{run:t})),l.createElement(h.AW,{path:n},t.taskRuns.length>0&&l.createElement(kN,{taskRuns:t.taskRuns,observationSource:t.job.observationSource}))))))))};function k5(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function k8(){var e=k5(["\n ","\n query FetchJobRun($id: ID!) {\n jobRun(id: $id) {\n __typename\n ... on JobRun {\n ...JobRunPayload_Fields\n }\n ... on NotFoundError {\n message\n }\n }\n }\n"]);return k8=function(){return e},e}var k9=n0(k8(),k4),k7=function(){var e=rv(k9,{variables:{id:(0,h.UO)().id}}),t=e.data,n=e.loading,r=e.error;if(n)return l.createElement(iR,null);if(r)return l.createElement(iD,{error:r});var i=null==t?void 0:t.jobRun;switch(null==i?void 0:i.__typename){case"JobRun":return l.createElement(k6,{run:i});case"NotFoundError":return l.createElement(oa,null);default:return null}};function xe(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function xt(){var e=xe(["\n fragment JobRunsPayload_ResultsFields on JobRun {\n id\n allErrors\n createdAt\n finishedAt\n status\n job {\n id\n }\n }\n"]);return xt=function(){return e},e}var xn=n0(xt()),xr=function(e){var t=e.loading,n=e.data,r=e.page,i=e.pageSize,a=(0,h.k6)(),o=l.useMemo(function(){return null==n?void 0:n.jobRuns.results.map(function(e){var t,n=e.allErrors,r=e.id,i=e.createdAt;return{id:r,createdAt:i,errors:n,finishedAt:e.finishedAt,status:e.status}})},[n]);return l.createElement(ig,null,l.createElement(d.Z,{container:!0,spacing:32},l.createElement(d.Z,{item:!0,xs:12},l.createElement(iy,null,"Job Runs")),t&&l.createElement(iR,null),n&&o&&l.createElement(d.Z,{item:!0,xs:12},l.createElement(r5.Z,null,l.createElement(yp,{runs:o}),l.createElement(it.Z,{component:"div",count:n.jobRuns.metadata.total,rowsPerPage:i,rowsPerPageOptions:[i],page:r-1,onChangePage:function(e,t){a.push("/runs?page=".concat(t+1,"&per=").concat(i))},onChangeRowsPerPage:function(){},backIconButtonProps:{"aria-label":"prev-page"},nextIconButtonProps:{"aria-label":"next-page"}})))))};function xi(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function xa(){var e=xi(["\n ","\n query FetchJobRuns($offset: Int, $limit: Int) {\n jobRuns(offset: $offset, limit: $limit) {\n results {\n ...JobRunsPayload_ResultsFields\n }\n metadata {\n total\n }\n }\n }\n"]);return xa=function(){return e},e}var xo=n0(xa(),xn),xs=function(){var e=ij(),t=parseInt(e.get("page")||"1",10),n=parseInt(e.get("per")||"25",10),r=rv(xo,{variables:{offset:(t-1)*n,limit:n},fetchPolicy:"cache-and-network"}),i=r.data,a=r.loading,o=r.error;return o?l.createElement(iD,{error:o}):l.createElement(xr,{loading:a,data:i,page:t,pageSize:n})},xu=function(){var e=(0,h.$B)().path;return l.createElement(h.rs,null,l.createElement(h.AW,{exact:!0,path:e},l.createElement(xs,null)),l.createElement(h.AW,{path:"".concat(e,"/:id")},l.createElement(k7,null)))},xc=bv().shape({name:p0().required("Required"),uri:p0().required("Required"),publicKey:p0().required("Required")}),xl=function(e){var t=e.initialValues,n=e.onSubmit;return l.createElement(hT,{initialValues:t,validationSchema:xc,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 Feeds Manager 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 Feeds Manager 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"))))})},xf=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 Feeds Manager"}),l.createElement(aW.Z,null,l.createElement(xl,{initialValues:r,onSubmit:n})))))};function xd(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function xh(){var e=xd(["\n query FetchFeedsManagers {\n feedsManagers {\n results {\n __typename\n id\n name\n uri\n publicKey\n isConnectionActive\n createdAt\n }\n }\n }\n"]);return xh=function(){return e},e}var xp=n0(xh()),xb=function(){return rv(xp)};function xm(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 xF(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 xY(e,t){return xD(e)||xP(e,t)||xB(e,t)||xR()}function xB(e,t){if(e){if("string"==typeof e)return xI(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 xI(e,t)}}var xU=function(e){return"SN_MAIN"===e||"SN_SEPOLIA"===e},xH=bv().shape({chainID:p0().required("Required"),chainType:p0().required("Required"),accountAddr:p0().required("Required"),accountAddrPubKey:p0().nullable(),adminAddr:p0().required("Required"),ocr1Multiaddr:p0().when(["ocr1Enabled","ocr1IsBootstrap"],{is:function(e,t){return e&&t},then:p0().required("Required").nullable()}).nullable(),ocr1P2PPeerID:p0().when(["ocr1Enabled","ocr1IsBootstrap"],{is:function(e,t){return e&&!t},then:p0().required("Required").nullable()}).nullable(),ocr1KeyBundleID:p0().when(["ocr1Enabled","ocr1IsBootstrap"],{is:function(e,t){return e&&!t},then:p0().required("Required").nullable()}).nullable(),ocr2Multiaddr:p0().when(["ocr2Enabled","ocr2IsBootstrap"],{is:function(e,t){return e&&t},then:p0().required("Required").nullable()}).nullable(),ocr2P2PPeerID:p0().when(["ocr2Enabled","ocr2IsBootstrap"],{is:function(e,t){return e&&!t},then:p0().required("Required").nullable()}).nullable(),ocr2KeyBundleID:p0().when(["ocr2Enabled","ocr2IsBootstrap"],{is:function(e,t){return e&&!t},then:p0().required("Required").nullable()}).nullable(),ocr2CommitPluginEnabled:pV().required("Required"),ocr2ExecutePluginEnabled:pV().required("Required"),ocr2MedianPluginEnabled:pV().required("Required"),ocr2MercuryPluginEnabled:pV().required("Required"),ocr2ForwarderAddress:p0().nullable()}),x$=function(e){return(0,b.createStyles)({supportedJobOptionsPaper:{padding:2*e.spacing.unit}})},xz=function(e){var t=e.chainAccounts,n=xj(e,["chainAccounts"]),r=h_(),i=r.values,a=i.chainID,o=i.accountAddr,s=r.setFieldValue,u=xY(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,!xU(a)&&l.createElement(hP,xN({},n,{select:!0,value:c?"custom":o,onChange:h}),t.map(function(e){return l.createElement(tE.default,{key:e.address,value:e.address},e.address)})),xU(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}),xU(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"}})))},xG=(0,b.withStyles)(x$)(function(e){var t=e.classes,n=e.editing,r=void 0!==n&&n,i=e.innerRef,a=e.initialValues,o=e.onSubmit,s=e.chainIDs,u=void 0===s?[]:s,c=e.accounts,f=void 0===c?[]:c,h=e.p2pKeys,p=void 0===h?[]:h,b=e.ocrKeys,m=void 0===b?[]:b,g=e.ocr2Keys,v=void 0===g?[]:g,y=e.showSubmit,w=void 0!==y&&y;return l.createElement(hT,{innerRef:i,initialValues:a,validationSchema:xH,onSubmit:o},function(e){var n=e.values,i=f.filter(function(e){return e.chain.id==n.chainID&&!e.isDisabled});return l.createElement(hR,{"data-testid":"feeds-manager-form",id:"chain-configuration-form",noValidate:!0},l.createElement(d.Z,{container:!0,spacing:16},l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(hP,{component:hX,id:"chainType",name:"chainType",label:"Chain Type",select:!0,required:!0,fullWidth:!0,disabled:!0},l.createElement(tE.default,{key:"EVM",value:"EVM"},"EVM"))),l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(hP,{component:hX,id:"chainID",name:"chainID",label:"Chain ID",required:!0,fullWidth:!0,select:!0,disabled:r,inputProps:{"data-testid":"chainID-input"},FormHelperTextProps:{"data-testid":"chainID-helper-text"}},u.map(function(e){return l.createElement(tE.default,{key:e,value:e},e)}))),l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(xz,{component:hX,id:"accountAddr",name:"accountAddr",label:"Account Address",inputProps:{"data-testid":"accountAddr-input"},required:!0,fullWidth:!0,select:!0,helperText:"The account address used for this chain",chainAccounts:i,FormHelperTextProps:{"data-testid":"accountAddr-helper-text"}})),l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(hP,{component:hX,id:"adminAddr",name:"adminAddr",label:"Admin Address",required:!0,fullWidth:!0,helperText:"The address used for LINK payments",FormHelperTextProps:{"data-testid":"adminAddr-helper-text"}})),l.createElement(d.Z,{item:!0,xs:12},l.createElement(x.default,null,"Supported Job Types")),l.createElement(d.Z,{item:!0,xs:12},l.createElement(hP,{component:h2,name:"fluxMonitorEnabled",type:"checkbox",Label:{label:"Flux Monitor"}})),l.createElement(d.Z,{item:!0,xs:12},l.createElement(hP,{component:h2,name:"ocr1Enabled",type:"checkbox",Label:{label:"OCR"}}),n.ocr1Enabled&&l.createElement(ii.default,{className:t.supportedJobOptionsPaper},l.createElement(d.Z,{container:!0,spacing:8},l.createElement(l.Fragment,null,l.createElement(d.Z,{item:!0,xs:12},l.createElement(hP,{component:h2,name:"ocr1IsBootstrap",type:"checkbox",Label:{label:"Is this node running as a bootstrap peer?"}})),n.ocr1IsBootstrap?l.createElement(d.Z,{item:!0,xs:12},l.createElement(hP,{component:hX,id:"ocr1Multiaddr",name:"ocr1Multiaddr",label:"Multiaddr",required:!0,fullWidth:!0,helperText:"The OCR Multiaddr which oracles use to query for network information",FormHelperTextProps:{"data-testid":"ocr1Multiaddr-helper-text"}})):l.createElement(l.Fragment,null,l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(hP,{component:hX,id:"ocr1P2PPeerID",name:"ocr1P2PPeerID",label:"Peer ID",select:!0,required:!0,fullWidth:!0,helperText:"The Peer ID used for this chain",FormHelperTextProps:{"data-testid":"ocr1P2PPeerID-helper-text"}},p.map(function(e){return l.createElement(tE.default,{key:e.peerID,value:e.peerID},e.peerID)}))),l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(hP,{component:hX,id:"ocr1KeyBundleID",name:"ocr1KeyBundleID",label:"Key Bundle ID",select:!0,required:!0,fullWidth:!0,helperText:"The OCR Key Bundle ID used for this chain",FormHelperTextProps:{"data-testid":"ocr1KeyBundleID-helper-text"}},m.map(function(e){return l.createElement(tE.default,{key:e.id,value:e.id},e.id)})))))))),l.createElement(d.Z,{item:!0,xs:12},l.createElement(hP,{component:h2,name:"ocr2Enabled",type:"checkbox",Label:{label:"OCR2"}}),n.ocr2Enabled&&l.createElement(ii.default,{className:t.supportedJobOptionsPaper},l.createElement(d.Z,{container:!0,spacing:8},l.createElement(l.Fragment,null,l.createElement(d.Z,{item:!0,xs:12},l.createElement(hP,{component:h2,name:"ocr2IsBootstrap",type:"checkbox",Label:{label:"Is this node running as a bootstrap peer?"}})),n.ocr2IsBootstrap?l.createElement(d.Z,{item:!0,xs:12},l.createElement(hP,{component:hX,id:"ocr2Multiaddr",name:"ocr2Multiaddr",label:"Multiaddr",required:!0,fullWidth:!0,helperText:"The OCR2 Multiaddr which oracles use to query for network information",FormHelperTextProps:{"data-testid":"ocr2Multiaddr-helper-text"}})):l.createElement(l.Fragment,null,l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(hP,{component:hX,id:"ocr2P2PPeerID",name:"ocr2P2PPeerID",label:"Peer ID",select:!0,required:!0,fullWidth:!0,helperText:"The Peer ID used for this chain",FormHelperTextProps:{"data-testid":"ocr2P2PPeerID-helper-text"}},p.map(function(e){return l.createElement(tE.default,{key:e.peerID,value:e.peerID},e.peerID)}))),l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(hP,{component:hX,id:"ocr2KeyBundleID",name:"ocr2KeyBundleID",label:"Key Bundle ID",select:!0,required:!0,fullWidth:!0,helperText:"The OCR2 Key Bundle ID used for this chain",FormHelperTextProps:{"data-testid":"ocr2KeyBundleID-helper-text"}},v.map(function(e){return l.createElement(tE.default,{key:e.id,value:e.id},e.id)}))),l.createElement(d.Z,{item:!0,xs:12},l.createElement(x.default,null,"Supported Plugins")),l.createElement(d.Z,{item:!0,xs:6},l.createElement(hP,{component:h2,name:"ocr2CommitPluginEnabled",type:"checkbox",Label:{label:"Commit"}})),l.createElement(d.Z,{item:!0,xs:6},l.createElement(hP,{component:h2,name:"ocr2ExecutePluginEnabled",type:"checkbox",Label:{label:"Execute"}})),l.createElement(d.Z,{item:!0,xs:6},l.createElement(hP,{component:h2,name:"ocr2RebalancerPluginEnabled",type:"checkbox",Label:{label:"Rebalancer"}})),l.createElement(d.Z,{item:!0,xs:6},l.createElement(hP,{component:h2,name:"ocr2MedianPluginEnabled",type:"checkbox",Label:{label:"Median"}})),l.createElement(d.Z,{item:!0,xs:6},l.createElement(hP,{component:h2,name:"ocr2MercuryPluginEnabled",type:"checkbox",Label:{label:"Mercury"}})),l.createElement(d.Z,{item:!0,xs:12,md:12},l.createElement(hP,{component:hX,id:"ocr2ForwarderAddress",name:"ocr2ForwarderAddress",label:"Forwarder Address (optional)",fullWidth:!0,helperText:"The forwarder address from the Operator Forwarder Contract",FormHelperTextProps:{"data-testid":"ocr2ForwarderAddress-helper-text"}}))))))),w&&l.createElement(d.Z,{item:!0,xs:12,md:7},l.createElement(ok.default,{variant:"contained",color:"primary",type:"submit",size:"large"},"Submit"))))})}),xW=function(e){var t=e.onClose,n=e.open,r=e.onSubmit,i=l.useRef(),a=i$({fetchPolicy:"network-only"}).data,o=_K({fetchPolicy:"cache-and-network"}).data,s=SV({fetchPolicy:"cache-and-network"}).data,u=EO({fetchPolicy:"cache-and-network"}).data,c=E2({fetchPolicy:"cache-and-network"}).data,f={chainID:"",chainType:"EVM",accountAddr:"",adminAddr:"",accountAddrPubKey:"",fluxMonitorEnabled:!1,ocr1Enabled:!1,ocr1IsBootstrap:!1,ocr1Multiaddr:"",ocr1P2PPeerID:"",ocr1KeyBundleID:"",ocr2Enabled:!1,ocr2IsBootstrap:!1,ocr2Multiaddr:"",ocr2P2PPeerID:"",ocr2KeyBundleID:"",ocr2CommitPluginEnabled:!1,ocr2ExecutePluginEnabled:!1,ocr2MedianPluginEnabled:!1,ocr2MercuryPluginEnabled:!1,ocr2RebalancerPluginEnabled:!1,ocr2ForwarderAddress:""},d=a?a.chains.results.map(function(e){return e.id}):[],h=o?o.ethKeys.results:[],p=s?s.p2pKeys.results:[],b=u?u.ocrKeyBundles.results:[],m=c?c.ocr2KeyBundles.results:[];return l.createElement(aD.Z,{onClose:t,open:n,disableBackdropClick:!0},l.createElement(oO.Z,{disableTypography:!0},l.createElement(x.default,{variant:"body2"},"New Supported Chain")),l.createElement(oT.Z,null,l.createElement(xG,{innerRef:i,initialValues:f,onSubmit:r,chainIDs:d,accounts:h,p2pKeys:p,ocrKeys:b,ocr2Keys:m})),l.createElement(ox.Z,null,l.createElement(ok.default,{onClick:t},"Cancel"),l.createElement(ok.default,{color:"primary",type:"submit",form:"chain-configuration-form",variant:"contained"},"Submit")))},xK=function(e){var t=e.cfg,n=e.onClose,r=e.open,i=e.onSubmit,a=l.useRef(),o=i$({fetchPolicy:"network-only"}).data,s=_K({fetchPolicy:"cache-and-network"}).data,u=SV({fetchPolicy:"cache-and-network"}).data,c=EO({fetchPolicy:"cache-and-network"}).data,f=E2({fetchPolicy:"cache-and-network"}).data;if(!t)return null;var d={chainID:t.chainID,chainType:"EVM",accountAddr:t.accountAddr,adminAddr:t.adminAddr,accountAddrPubKey:t.accountAddrPubKey,fluxMonitorEnabled:t.fluxMonitorJobConfig.enabled,ocr1Enabled:t.ocr1JobConfig.enabled,ocr1IsBootstrap:t.ocr1JobConfig.isBootstrap,ocr1Multiaddr:t.ocr1JobConfig.multiaddr,ocr1P2PPeerID:t.ocr1JobConfig.p2pPeerID,ocr1KeyBundleID:t.ocr1JobConfig.keyBundleID,ocr2Enabled:t.ocr2JobConfig.enabled,ocr2IsBootstrap:t.ocr2JobConfig.isBootstrap,ocr2Multiaddr:t.ocr2JobConfig.multiaddr,ocr2P2PPeerID:t.ocr2JobConfig.p2pPeerID,ocr2KeyBundleID:t.ocr2JobConfig.keyBundleID,ocr2CommitPluginEnabled:t.ocr2JobConfig.plugins.commit,ocr2ExecutePluginEnabled:t.ocr2JobConfig.plugins.execute,ocr2MedianPluginEnabled:t.ocr2JobConfig.plugins.median,ocr2MercuryPluginEnabled:t.ocr2JobConfig.plugins.mercury,ocr2RebalancerPluginEnabled:t.ocr2JobConfig.plugins.rebalancer,ocr2ForwarderAddress:t.ocr2JobConfig.forwarderAddress},h=o?o.chains.results.map(function(e){return e.id}):[],p=s?s.ethKeys.results:[],b=u?u.p2pKeys.results:[],m=c?c.ocrKeyBundles.results:[],g=f?f.ocr2KeyBundles.results:[];return l.createElement(aD.Z,{onClose:n,open:r,disableBackdropClick:!0},l.createElement(oO.Z,{disableTypography:!0},l.createElement(x.default,{variant:"body2"},"Edit Supported Chain")),l.createElement(oT.Z,null,l.createElement(xG,{innerRef:a,initialValues:d,onSubmit:i,chainIDs:h,accounts:p,p2pKeys:b,ocrKeys:m,ocr2Keys:g,editing:!0})),l.createElement(ox.Z,null,l.createElement(ok.default,{onClick:n},"Cancel"),l.createElement(ok.default,{color:"primary",type:"submit",form:"chain-configuration-form",variant:"contained"},"Submit")))};function xV(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function xq(){var e=xV(["\n fragment FeedsManager_ChainConfigFields on FeedsManagerChainConfig {\n id\n chainID\n chainType\n accountAddr\n adminAddr\n accountAddrPubKey\n fluxMonitorJobConfig {\n enabled\n }\n ocr1JobConfig {\n enabled\n isBootstrap\n multiaddr\n p2pPeerID\n keyBundleID\n }\n ocr2JobConfig {\n enabled\n isBootstrap\n multiaddr\n forwarderAddress\n p2pPeerID\n keyBundleID\n plugins {\n commit\n execute\n median\n mercury\n rebalancer\n }\n }\n }\n"]);return xq=function(){return e},e}function xZ(){var e=xV(["\n ","\n fragment FeedsManagerFields on FeedsManager {\n id\n name\n uri\n publicKey\n isConnectionActive\n chainConfigs {\n ...FeedsManager_ChainConfigFields\n }\n }\n"]);return xZ=function(){return e},e}function xX(){var e=xV(["\n fragment FeedsManager_JobProposalsFields on JobProposal {\n id\n name\n externalJobID\n remoteUUID\n status\n pendingUpdate\n latestSpec {\n createdAt\n version\n }\n }\n"]);return xX=function(){return e},e}function xJ(){var e=xV(["\n ","\n ","\n fragment FeedsManagerPayload_ResultsFields on FeedsManager {\n ...FeedsManagerFields\n jobProposals {\n ...FeedsManager_JobProposalsFields\n }\n }\n"]);return xJ=function(){return e},e}function xQ(){var e=xV(["\n ","\n query FetchFeedManagersWithProposals {\n feedsManagers {\n results {\n ...FeedsManagerPayload_ResultsFields\n }\n }\n }\n"]);return xQ=function(){return e},e}var x1=n0(xq()),x0=n0(xZ(),x1),x2=n0(xX()),x3=n0(xJ(),x0,x2),x4=n0(xQ(),x3),x6=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return rv(x4,e)};function x5(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0?n.feedsManagers.results[0]:void 0;return n&&a?l.createElement(TZ,{manager:a}):l.createElement(h.l_,{to:{pathname:"/feeds_manager/new",state:{from:e}}})},TJ={name:"Chainlink Feeds Manager",uri:"",publicKey:""},TQ=function(e){var t=e.onSubmit;return l.createElement(d.Z,{container:!0},l.createElement(d.Z,{item:!0,xs:12,md:11,lg:9},l.createElement(r5.Z,null,l.createElement(sl.Z,{title:"Register Feeds Manager"}),l.createElement(aW.Z,null,l.createElement(xl,{initialValues:TJ,onSubmit:t})))))};function T1(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);nt.version?e:t})},[o]),g=l.useMemo(function(){return ME(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?MM[c.action].title:"",body:c?MM[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(Md,{open:h,onClose:function(){return p(!1)},initialValues:{definition:m.definition,id:m.id},onSubmit:a}))});function MA(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function ML(){var e=MA(["\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 ML=function(){return e},e}var MC=n0(ML(),Mx),MI=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(Mo,{proposal:a}),l.createElement(d.Z,{container:!0,spacing:32},l.createElement(d.Z,{item:!0,xs:9},l.createElement(Tq,null,"Specs"))),l.createElement(d.Z,{container:!0,spacing:32},l.createElement(d.Z,{item:!0,xs:12},l.createElement(MO,{proposal:a,specs:a.specs,onReject:r,onApprove:t,onCancel:n,onUpdateSpec:i}))))};function MD(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);nU,tA:()=>$,KL:()=>H,Iw:()=>V,DQ:()=>W,cB:()=>T,LO:()=>M,t5:()=>k,qt:()=>x,Jc:()=>C,L7:()=>Y,EO:()=>B});var r,i,a=n(66289),o=n(41800),s=n.n(o),u=n(67932);(i=r||(r={})).IN_PROGRESS="in_progress",i.PENDING_INCOMING_CONFIRMATIONS="pending_incoming_confirmations",i.PENDING_CONNECTION="pending_connection",i.PENDING_BRIDGE="pending_bridge",i.PENDING_SLEEP="pending_sleep",i.ERRORED="errored",i.COMPLETED="completed";var c=n(87013),l=n(19084),f=n(34823);function d(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]j,v2:()=>F});var r=n(66289);function i(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var a="/sessions",o="/sessions",s=function e(t){var n=this;i(this,e),this.api=t,this.createSession=function(e){return n.create(e)},this.destroySession=function(){return n.destroy()},this.create=this.api.createResource(a),this.destroy=this.api.deleteResource(o)};function u(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var c="/v2/bulk_delete_runs",l=function e(t){var n=this;u(this,e),this.api=t,this.bulkDeleteJobRuns=function(e){return n.destroy(e)},this.destroy=this.api.deleteResource(c)};function f(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var d="/v2/chains/evm",h="".concat(d,"/:id"),p=function e(t){var n=this;f(this,e),this.api=t,this.getChains=function(){return n.index()},this.createChain=function(e){return n.create(e)},this.destroyChain=function(e){return n.destroy(void 0,{id:e})},this.updateChain=function(e,t){return n.update(t,{id:e})},this.index=this.api.fetchResource(d),this.create=this.api.createResource(d),this.destroy=this.api.deleteResource(h),this.update=this.api.updateResource(h)};function b(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var m="/v2/keys/evm/chain",g=function e(t){var n=this;b(this,e),this.api=t,this.chain=function(e){var t=new URLSearchParams;t.append("address",e.address),t.append("evmChainID",e.evmChainID),null!==e.nextNonce&&t.append("nextNonce",e.nextNonce),null!==e.abandon&&t.append("abandon",String(e.abandon)),null!==e.enabled&&t.append("enabled",String(e.enabled));var r=m+"?"+t.toString();return n.api.createResource(r)()}};function v(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var y="/v2/jobs",w="".concat(y,"/:specId/runs"),_=function e(t){var n=this;v(this,e),this.api=t,this.createJobRunV2=function(e,t){return n.post(t,{specId:e})},this.post=this.api.createResource(w,!0)};function E(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var S="/v2/log",k=function e(t){var n=this;E(this,e),this.api=t,this.getLogConfig=function(){return n.show()},this.updateLogConfig=function(e){return n.update(e)},this.show=this.api.fetchResource(S),this.update=this.api.updateResource(S)};function x(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var T="/v2/nodes",M=function e(t){var n=this;x(this,e),this.api=t,this.getNodes=function(){return n.index()},this.createNode=function(e){return n.create(e)},this.index=this.api.fetchResource(T),this.create=this.api.createResource(T)};function O(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var A="/v2/enroll_webauthn",L=function e(t){var n=this;O(this,e),this.api=t,this.beginKeyRegistration=function(e){return n.create(e)},this.finishKeyRegistration=function(e){return n.put(e)},this.create=this.api.fetchResource(A),this.put=this.api.createResource(A)};function C(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var I="/v2/build_info",D=function e(t){var n=this;C(this,e),this.api=t,this.show=function(){return n.api.GET(I)()}};function N(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var P=function e(t){N(this,e),this.api=t,this.buildInfo=new D(this.api),this.bulkDeleteRuns=new l(this.api),this.chains=new p(this.api),this.logConfig=new k(this.api),this.nodes=new M(this.api),this.jobs=new _(this.api),this.webauthn=new L(this.api),this.evmKeys=new g(this.api)},R=new r.V0({base:void 0}),j=new s(R),F=new P(R)},1398(e,t,n){"use strict";n.d(t,{Z:()=>d});var r=n(67294),i=n(32316),a=n(83638),o=n(94184),s=n.n(o);function u(){return(u=Object.assign||function(e){for(var t=1;tc});var r=n(67294),i=n(32316);function a(){return(a=Object.assign||function(e){for(var t=1;tx,jK:()=>v});var r=n(67294),i=n(37703),a=n(45697),o=n.n(a),s=n(82204),u=n(71426),c=n(94184),l=n.n(c),f=n(32316),d=function(e){var t=e.palette.success||{},n=e.palette.warning||{};return{base:{paddingLeft:5*e.spacing.unit,paddingRight:5*e.spacing.unit},success:{backgroundColor:t.main,color:t.contrastText},error:{backgroundColor:e.palette.error.dark,color:e.palette.error.contrastText},warning:{backgroundColor:n.contrastText,color:n.main}}},h=function(e){var t,n=e.success,r=e.error,i=e.warning,a=e.classes,o=e.className;return n?t=a.success:r?t=a.error:i&&(t=a.warning),l()(a.base,o,t)},p=function(e){return r.createElement(s.Z,{className:h(e),square:!0},r.createElement(u.default,{variant:"body2",color:"inherit",component:"div"},e.children))};p.defaultProps={success:!1,error:!1,warning:!1},p.propTypes={success:o().bool,error:o().bool,warning:o().bool};let b=(0,f.withStyles)(d)(p);var m=function(){return r.createElement(r.Fragment,null,"Unhandled error. Please help us by opening a"," ",r.createElement("a",{href:"https://github.com/smartcontractkit/chainlink/issues/new"},"bug report"))};let g=m;function v(e){return"string"==typeof e?e:e.component?e.component(e.props):r.createElement(g,null)}function y(e,t){var n;return n="string"==typeof e?e:e.component?e.component(e.props):r.createElement(g,null),r.createElement("p",{key:t},n)}var w=function(e){var t=e.notifications;return r.createElement(b,{error:!0},t.map(y))},_=function(e){var t=e.notifications;return r.createElement(b,{success:!0},t.map(y))},E=function(e){var t=e.errors,n=e.successes;return r.createElement("div",null,(null==t?void 0:t.length)>0&&r.createElement(w,{notifications:t}),n.length>0&&r.createElement(_,{notifications:n}))},S=function(e){return{errors:e.notifications.errors,successes:e.notifications.successes}},k=(0,i.$j)(S)(E);let x=k},9409(e,t,n){"use strict";n.d(t,{ZP:()=>j});var r=n(67294),i=n(37703),a=n(5977),o=n(32316),s=n(1398),u=n(82204),c=n(30060),l=n(71426),f=n(60520),d=n(39814),h=n(57209),p=n(26842),b=n(3950),m=n(5536),g=n(45697),v=n.n(g);let y=n.p+"9f6d832ef97e8493764e.svg";function w(){return(w=Object.assign||function(e){for(var t=1;te.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&_.map(function(e,t){return r.createElement(d.Z,{item:!0,xs:12,key:t},r.createElement(u.Z,{raised:!1,className:v.error},r.createElement(c.Z,null,r.createElement(l.default,{variant:"body1",className:v.errorText},(0,b.jK)(e)))))}),r.createElement(d.Z,{item:!0,xs:12},r.createElement(f.Z,{id:"email",label:"Email",margin:"normal",value:n,onChange:m("email"),error:_.length>0,variant:"outlined",fullWidth:!0})),r.createElement(d.Z,{item:!0,xs:12},r.createElement(f.Z,{id:"password",label:"Password",type:"password",autoComplete:"password",margin:"normal",value:h,onChange:m("password"),error:_.length>0,variant:"outlined",fullWidth:!0})),r.createElement(d.Z,{item:!0,xs:12},r.createElement(d.Z,{container:!0,spacing:0,justify:"center"},r.createElement(d.Z,{item:!0},r.createElement(s.Z,{type:"submit",variant:"primary"},"Access Account")))),y&&r.createElement(l.default,{variant:"body1",color:"textSecondary"},"Signing in...")))))))},P=function(e){return{fetching:e.authentication.fetching,authenticated:e.authentication.allowed,errors:e.notifications.errors}},R=(0,i.$j)(P,x({submitSignIn:p.L7}))(N);let j=(0,h.wU)(e)((0,o.withStyles)(D)(R))},16353(e,t,n){"use strict";n.d(t,{ZP:()=>H,rH:()=>U});var r,i=n(37703),a=n(97779),o=n(9541),s=n(19084);function u(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function c(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:h,t=arguments.length>1?arguments[1]:void 0;switch(t.type){case s.Mk.RECEIVE_SIGNOUT_SUCCESS:case s.Mk.RECEIVE_SIGNIN_SUCCESS:var n={allowed:t.authenticated};return o.Ks(n),f(c({},e,n),{errors:[]});case s.Mk.RECEIVE_SIGNIN_FAIL:var r={allowed:!1};return o.Ks(r),f(c({},e,r),{errors:[]});case s.Mk.RECEIVE_SIGNIN_ERROR:case s.Mk.RECEIVE_SIGNOUT_ERROR:var i={allowed:!1};return o.Ks(i),f(c({},e,i),{errors:t.errors||[]});default:return e}};let b=p;function m(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function g(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:_,t=arguments.length>1?arguments[1]:void 0;return t.type?t.type.startsWith(r.REQUEST)?y(g({},e),{count:e.count+1}):t.type.startsWith(r.RECEIVE)?y(g({},e),{count:Math.max(e.count-1,0)}):t.type.startsWith(r.RESPONSE)?y(g({},e),{count:Math.max(e.count-1,0)}):t.type===s.di.REDIRECT?y(g({},e),{count:0}):e:e};let S=E;function k(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function x(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:O,t=arguments.length>1?arguments[1]:void 0;switch(t.type){case s.di.MATCH_ROUTE:return M(x({},O),{currentUrl:t.pathname});case s.Ih.NOTIFY_SUCCESS:var n={component:t.component,props:t.props};return M(x({},e),{successes:[n],errors:[]});case s.Ih.NOTIFY_SUCCESS_MSG:return M(x({},e),{successes:[t.msg],errors:[]});case s.Ih.NOTIFY_ERROR:var r=t.error.errors,i=null==r?void 0:r.map(function(e){return L(t,e)});return M(x({},e),{successes:[],errors:i});case s.Ih.NOTIFY_ERROR_MSG:return M(x({},e),{successes:[],errors:[t.msg]});case s.Mk.RECEIVE_SIGNIN_FAIL:return M(x({},e),{successes:[],errors:["Your email or password is incorrect. Please try again"]});default:return e}};function L(e,t){return{component:e.component,props:{msg:t.detail}}}let C=A;function I(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function D(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:R,t=arguments.length>1?arguments[1]:void 0;switch(t.type){case s.di.REDIRECT:return P(D({},e),{to:t.to});case s.di.MATCH_ROUTE:return P(D({},e),{to:void 0});default:return e}};let F=j;var Y=n(87013),B=(0,a.UY)({authentication:b,fetching:S,notifications:C,redirect:F,buildInfo:Y.Z});B(void 0,{type:"INITIAL_STATE"});var U=i.v9;let H=B},19084(e,t,n){"use strict";var r,i,a,o,s,u,c,l,f,d;n.d(t,{Ih:()=>i,Mk:()=>a,Y0:()=>s,di:()=>r,jp:()=>o}),n(67294),(u=r||(r={})).REDIRECT="REDIRECT",u.MATCH_ROUTE="MATCH_ROUTE",(c=i||(i={})).NOTIFY_SUCCESS="NOTIFY_SUCCESS",c.NOTIFY_SUCCESS_MSG="NOTIFY_SUCCESS_MSG",c.NOTIFY_ERROR="NOTIFY_ERROR",c.NOTIFY_ERROR_MSG="NOTIFY_ERROR_MSG",(l=a||(a={})).REQUEST_SIGNIN="REQUEST_SIGNIN",l.RECEIVE_SIGNIN_SUCCESS="RECEIVE_SIGNIN_SUCCESS",l.RECEIVE_SIGNIN_FAIL="RECEIVE_SIGNIN_FAIL",l.RECEIVE_SIGNIN_ERROR="RECEIVE_SIGNIN_ERROR",l.RECEIVE_SIGNOUT_SUCCESS="RECEIVE_SIGNOUT_SUCCESS",l.RECEIVE_SIGNOUT_ERROR="RECEIVE_SIGNOUT_ERROR",(f=o||(o={})).RECEIVE_CREATE_ERROR="RECEIVE_CREATE_ERROR",f.RECEIVE_CREATE_SUCCESS="RECEIVE_CREATE_SUCCESS",f.RECEIVE_DELETE_ERROR="RECEIVE_DELETE_ERROR",f.RECEIVE_DELETE_SUCCESS="RECEIVE_DELETE_SUCCESS",f.RECEIVE_UPDATE_ERROR="RECEIVE_UPDATE_ERROR",f.RECEIVE_UPDATE_SUCCESS="RECEIVE_UPDATE_SUCCESS",f.REQUEST_CREATE="REQUEST_CREATE",f.REQUEST_DELETE="REQUEST_DELETE",f.REQUEST_UPDATE="REQUEST_UPDATE",f.UPSERT_CONFIGURATION="UPSERT_CONFIGURATION",f.UPSERT_JOB_RUN="UPSERT_JOB_RUN",f.UPSERT_JOB_RUNS="UPSERT_JOB_RUNS",f.UPSERT_TRANSACTION="UPSERT_TRANSACTION",f.UPSERT_TRANSACTIONS="UPSERT_TRANSACTIONS",f.UPSERT_BUILD_INFO="UPSERT_BUILD_INFO",(d=s||(s={})).FETCH_BUILD_INFO_REQUESTED="FETCH_BUILD_INFO_REQUESTED",d.FETCH_BUILD_INFO_SUCCEEDED="FETCH_BUILD_INFO_SUCCEEDED",d.FETCH_BUILD_INFO_FAILED="FETCH_BUILD_INFO_FAILED"},87013(e,t,n){"use strict";n.d(t,{Y:()=>o,Z:()=>u});var r=n(19084);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:o,t=arguments.length>1?arguments[1]:void 0;return t.type===r.Y0.FETCH_BUILD_INFO_SUCCEEDED?a({},t.buildInfo):e};let u=s},34823(e,t,n){"use strict";n.d(t,{N:()=>r});var r=function(e){return e.buildInfo}},73343(e,t,n){"use strict";n.d(t,{r:()=>u});var r=n(19350),i=n(32316),a=n(59114),o=n(5324),s={props:{MuiGrid:{spacing:3*o.default.unit},MuiCardHeader:{titleTypographyProps:{color:"secondary"}}},palette:{action:{hoverOpacity:.3},primary:{light:"#E5F1FF",main:"#3c40c6",contrastText:"#fff"},secondary:{main:"#3d5170"},success:{light:"#e8faf1",main:r.ek.A700,dark:r.ek[700],contrastText:r.y0.white},warning:{light:"#FFFBF1",main:"#fff6b6",contrastText:"#fad27a"},error:{light:"#ffdada",main:"#f44336",dark:"#d32f2f",contrastText:"#fff"},background:{default:"#f5f6f8",appBar:"#3c40c6"},text:{primary:(0,a.darken)(r.BA.A700,.7),secondary:"#818ea3"},listPendingStatus:{background:"#fef7e5",color:"#fecb4c"},listCompletedStatus:{background:"#e9faf2",color:"#4ed495"}},shape:{borderRadius:o.default.unit},overrides:{MuiButton:{root:{borderRadius:o.default.unit/2,textTransform:"none"},sizeLarge:{padding:void 0,fontSize:void 0,paddingTop:o.default.unit,paddingBottom:o.default.unit,paddingLeft:5*o.default.unit,paddingRight:5*o.default.unit}},MuiTableCell:{body:{fontSize:"1rem"},head:{fontSize:"1rem",fontWeight:400}},MuiCardHeader:{root:{borderBottom:"1px solid rgba(0, 0, 0, 0.12)"},action:{marginTop:-2,marginRight:0,"& >*":{marginLeft:2*o.default.unit}},subheader:{marginTop:.5*o.default.unit}}},typography:{useNextVariants:!0,fontFamily:"-apple-system,BlinkMacSystemFont,Roboto,Helvetica,Arial,sans-serif",button:{textTransform:"none",fontSize:"1.2em"},body1:{fontSize:"1.0rem",fontWeight:400,lineHeight:"1.46429em",color:"rgba(0, 0, 0, 0.87)",letterSpacing:-.4},body2:{fontSize:"1.0rem",fontWeight:500,lineHeight:"1.71429em",color:"rgba(0, 0, 0, 0.87)",letterSpacing:-.4},body1Next:{color:"rgb(29, 29, 29)",fontWeight:400,fontSize:"1rem",lineHeight:1.5,letterSpacing:-.4},body2Next:{color:"rgb(29, 29, 29)",fontWeight:400,fontSize:"0.875rem",lineHeight:1.5,letterSpacing:-.4},display1:{color:"#818ea3",fontSize:"2.125rem",fontWeight:400,lineHeight:"1.20588em",letterSpacing:-.4},display2:{color:"#818ea3",fontSize:"2.8125rem",fontWeight:400,lineHeight:"1.13333em",marginLeft:"-.02em",letterSpacing:-.4},display3:{color:"#818ea3",fontSize:"3.5rem",fontWeight:400,lineHeight:"1.30357em",marginLeft:"-.02em",letterSpacing:-.4},display4:{fontSize:14,fontWeightLight:300,fontWeightMedium:500,fontWeightRegular:400,letterSpacing:-.4},h1:{color:"rgb(29, 29, 29)",fontSize:"6rem",fontWeight:300,lineHeight:1},h2:{color:"rgb(29, 29, 29)",fontSize:"3.75rem",fontWeight:300,lineHeight:1},h3:{color:"rgb(29, 29, 29)",fontSize:"3rem",fontWeight:400,lineHeight:1.04},h4:{color:"rgb(29, 29, 29)",fontSize:"2.125rem",fontWeight:400,lineHeight:1.17},h5:{color:"rgb(29, 29, 29)",fontSize:"1.5rem",fontWeight:400,lineHeight:1.33,letterSpacing:-.4},h6:{fontSize:"0.8rem",fontWeight:450,lineHeight:"1.71429em",color:"rgba(0, 0, 0, 0.87)",letterSpacing:-.4},subheading:{color:"rgb(29, 29, 29)",fontSize:"1rem",fontWeight:400,lineHeight:"1.5em",letterSpacing:-.4},subtitle1:{color:"rgb(29, 29, 29)",fontSize:"1rem",fontWeight:400,lineHeight:1.75,letterSpacing:-.4},subtitle2:{color:"rgb(29, 29, 29)",fontSize:"0.875rem",fontWeight:500,lineHeight:1.57,letterSpacing:-.4}},shadows:["none","0px 1px 3px 0px rgba(0, 0, 0, 0.1),0px 1px 1px 0px rgba(0, 0, 0, 0.04),0px 2px 1px -1px rgba(0, 0, 0, 0.02)","0px 1px 5px 0px rgba(0, 0, 0, 0.1),0px 2px 2px 0px rgba(0, 0, 0, 0.04),0px 3px 1px -2px rgba(0, 0, 0, 0.02)","0px 1px 8px 0px rgba(0, 0, 0, 0.1),0px 3px 4px 0px rgba(0, 0, 0, 0.04),0px 3px 3px -2px rgba(0, 0, 0, 0.02)","0px 2px 4px -1px rgba(0, 0, 0, 0.1),0px 4px 5px 0px rgba(0, 0, 0, 0.04),0px 1px 10px 0px rgba(0, 0, 0, 0.02)","0px 3px 5px -1px rgba(0, 0, 0, 0.1),0px 5px 8px 0px rgba(0, 0, 0, 0.04),0px 1px 14px 0px rgba(0, 0, 0, 0.02)","0px 3px 5px -1px rgba(0, 0, 0, 0.1),0px 6px 10px 0px rgba(0, 0, 0, 0.04),0px 1px 18px 0px rgba(0, 0, 0, 0.02)","0px 4px 5px -2px rgba(0, 0, 0, 0.1),0px 7px 10px 1px rgba(0, 0, 0, 0.04),0px 2px 16px 1px rgba(0, 0, 0, 0.02)","0px 5px 5px -3px rgba(0, 0, 0, 0.1),0px 8px 10px 1px rgba(0, 0, 0, 0.04),0px 3px 14px 2px rgba(0, 0, 0, 0.02)","0px 5px 6px -3px rgba(0, 0, 0, 0.1),0px 9px 12px 1px rgba(0, 0, 0, 0.04),0px 3px 16px 2px rgba(0, 0, 0, 0.02)","0px 6px 6px -3px rgba(0, 0, 0, 0.1),0px 10px 14px 1px rgba(0, 0, 0, 0.04),0px 4px 18px 3px rgba(0, 0, 0, 0.02)","0px 6px 7px -4px rgba(0, 0, 0, 0.1),0px 11px 15px 1px rgba(0, 0, 0, 0.04),0px 4px 20px 3px rgba(0, 0, 0, 0.02)","0px 7px 8px -4px rgba(0, 0, 0, 0.1),0px 12px 17px 2px rgba(0, 0, 0, 0.04),0px 5px 22px 4px rgba(0, 0, 0, 0.02)","0px 7px 8px -4px rgba(0, 0, 0, 0.1),0px 13px 19px 2px rgba(0, 0, 0, 0.04),0px 5px 24px 4px rgba(0, 0, 0, 0.02)","0px 7px 9px -4px rgba(0, 0, 0, 0.1),0px 14px 21px 2px rgba(0, 0, 0, 0.04),0px 5px 26px 4px rgba(0, 0, 0, 0.02)","0px 8px 9px -5px rgba(0, 0, 0, 0.1),0px 15px 22px 2px rgba(0, 0, 0, 0.04),0px 6px 28px 5px rgba(0, 0, 0, 0.02)","0px 8px 10px -5px rgba(0, 0, 0, 0.1),0px 16px 24px 2px rgba(0, 0, 0, 0.04),0px 6px 30px 5px rgba(0, 0, 0, 0.02)","0px 8px 11px -5px rgba(0, 0, 0, 0.1),0px 17px 26px 2px rgba(0, 0, 0, 0.04),0px 6px 32px 5px rgba(0, 0, 0, 0.02)","0px 9px 11px -5px rgba(0, 0, 0, 0.1),0px 18px 28px 2px rgba(0, 0, 0, 0.04),0px 7px 34px 6px rgba(0, 0, 0, 0.02)","0px 9px 12px -6px rgba(0, 0, 0, 0.1),0px 19px 29px 2px rgba(0, 0, 0, 0.04),0px 7px 36px 6px rgba(0, 0, 0, 0.02)","0px 10px 13px -6px rgba(0, 0, 0, 0.1),0px 20px 31px 3px rgba(0, 0, 0, 0.04),0px 8px 38px 7px rgba(0, 0, 0, 0.02)","0px 10px 13px -6px rgba(0, 0, 0, 0.1),0px 21px 33px 3px rgba(0, 0, 0, 0.04),0px 8px 40px 7px rgba(0, 0, 0, 0.02)","0px 10px 14px -6px rgba(0, 0, 0, 0.1),0px 22px 35px 3px rgba(0, 0, 0, 0.04),0px 8px 42px 7px rgba(0, 0, 0, 0.02)","0px 11px 14px -7px rgba(0, 0, 0, 0.1),0px 23px 36px 3px rgba(0, 0, 0, 0.04),0px 9px 44px 8px rgba(0, 0, 0, 0.02)","0px 11px 15px -7px rgba(0, 0, 0, 0.1),0px 24px 38px 3px rgba(0, 0, 0, 0.04),0px 9px 46px 8px rgba(0, 0, 0, 0.02)",]},u=(0,i.createMuiTheme)(s)},66289(e,t,n){"use strict";function r(e){if(void 0===e)throw ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function i(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}function a(){if("undefined"==typeof Reflect||!Reflect.construct||Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],function(){})),!0}catch(e){return!1}}function o(e,t,n){return(o=a()?Reflect.construct:function(e,t,n){var r=[null];r.push.apply(r,t);var i=new(Function.bind.apply(e,r));return n&&f(i,n.prototype),i}).apply(null,arguments)}function s(e){return(s=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function u(e,t){if("function"!=typeof t&&null!==t)throw TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&f(e,t)}function c(e){return -1!==Function.toString.call(e).indexOf("[native code]")}function l(e,t){return t&&("object"===p(t)||"function"==typeof t)?t:r(e)}function f(e,t){return(f=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}n.d(t,{V0:()=>B,_7:()=>v});var d,h,p=function(e){return e&&"undefined"!=typeof Symbol&&e.constructor===Symbol?"symbol":typeof e};function b(e){var t="function"==typeof Map?new Map:void 0;return(b=function(e){if(null===e||!c(e))return e;if("function"!=typeof e)throw TypeError("Super expression must either be null or a function");if(void 0!==t){if(t.has(e))return t.get(e);t.set(e,n)}function n(){return o(e,arguments,s(this).constructor)}return n.prototype=Object.create(e.prototype,{constructor:{value:n,enumerable:!1,writable:!0,configurable:!0}}),f(n,e)})(e)}function m(){if("undefined"==typeof Reflect||!Reflect.construct||Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){})),!0}catch(e){return!1}}function g(e){var t=m();return function(){var n,r=s(e);if(t){var i=s(this).constructor;n=Reflect.construct(r,arguments,i)}else n=r.apply(this,arguments);return l(this,n)}}var v=function(e){u(n,e);var t=g(n);function n(e){var r;return i(this,n),(r=t.call(this,"AuthenticationError(".concat(e.statusText,")"))).errors=[{status:e.status,detail:e},],r}return n}(b(Error)),y=function(e){u(n,e);var t=g(n);function n(e){var r,a=e.errors;return i(this,n),(r=t.call(this,"BadRequestError")).errors=a,r}return n}(b(Error)),w=function(e){u(n,e);var t=g(n);function n(e){var r;return i(this,n),(r=t.call(this,"UnprocessableEntityError")).errors=e,r}return n}(b(Error)),_=function(e){u(n,e);var t=g(n);function n(e){var r;return i(this,n),(r=t.call(this,"ServerError")).errors=e,r}return n}(b(Error)),E=function(e){u(n,e);var t=g(n);function n(e){var r,a=e.errors;return i(this,n),(r=t.call(this,"ConflictError")).errors=a,r}return n}(b(Error)),S=function(e){u(n,e);var t=g(n);function n(e){var r;return i(this,n),(r=t.call(this,"UnknownResponseError(".concat(e.statusText,")"))).errors=[{status:e.status,detail:e.statusText},],r}return n}(b(Error));function k(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:2e4;return Promise.race([fetch(e,t),new Promise(function(e,t){return setTimeout(function(){return t(Error("timeout"))},n)}),])}function x(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]=200&&e.status<300))return[3,2];return[2,e.json()];case 2:if(400!==e.status)return[3,3];return[2,e.json().then(function(e){throw new y(e)})];case 3:if(401!==e.status)return[3,4];throw new v(e);case 4:if(422!==e.status)return[3,6];return[4,$(e)];case 5:throw n=i.sent(),new w(n);case 6:if(409!==e.status)return[3,7];return[2,e.json().then(function(e){throw new E(e)})];case 7:if(!(e.status>=500))return[3,9];return[4,$(e)];case 8:throw r=i.sent(),new _(r);case 9:throw new S(e);case 10:return[2]}})})).apply(this,arguments)}function $(e){return z.apply(this,arguments)}function z(){return(z=j(function(e){return Y(this,function(t){return[2,e.json().then(function(t){return t.errors?t.errors.map(function(t){return{status:e.status,detail:t.detail}}):G(e)}).catch(function(){return G(e)})]})})).apply(this,arguments)}function G(e){return[{status:e.status,detail:e.statusText},]}},50109(e,t,n){"use strict";n.d(t,{LK:()=>o,U2:()=>i,eT:()=>s,t8:()=>a});var r=n(12795);function i(e){return r.ZP.getItem("chainlink.".concat(e))}function a(e,t){r.ZP.setItem("chainlink.".concat(e),t)}function o(e){var t=i(e),n={};if(t)try{return JSON.parse(t)}catch(r){}return n}function s(e,t){a(e,JSON.stringify(t))}},9541(e,t,n){"use strict";n.d(t,{Ks:()=>u,Tp:()=>a,iR:()=>o,pm:()=>s});var r=n(50109),i="persistURL";function a(){return r.U2(i)||""}function o(e){r.t8(i,e)}function s(){return r.LK("authentication")}function u(e){r.eT("authentication",e)}},67121(e,t,n){"use strict";function r(e){var t,n=e.Symbol;return"function"==typeof n?n.observable?t=n.observable:(t=n("observable"),n.observable=t):t="@@observable",t}n.r(t),n.d(t,{default:()=>o}),e=n.hmd(e),i="undefined"!=typeof self?self:"undefined"!=typeof window?window:void 0!==n.g?n.g:e;var i,a=r(i);let o=a},2177(e,t,n){"use strict";n.d(t,{Z:()=>o});var r=!0,i="Invariant failed";function a(e,t){if(!e){if(r)throw Error(i);throw Error(i+": "+(t||""))}}let o=a},11742(e){e.exports=function(){var e=document.getSelection();if(!e.rangeCount)return function(){};for(var t=document.activeElement,n=[],r=0;ru,ZT:()=>i,_T:()=>o,ev:()=>c,mG:()=>s,pi:()=>a});var r=function(e,t){return(r=Object.setPrototypeOf||({__proto__:[]})instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])})(e,t)};function i(e,t){if("function"!=typeof t&&null!==t)throw TypeError("Class extends value "+String(t)+" is not a constructor or null");function n(){this.constructor=e}r(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}var a=function(){return(a=Object.assign||function(e){for(var t,n=1,r=arguments.length;nt.indexOf(r)&&(n[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var i=0,r=Object.getOwnPropertySymbols(e);it.indexOf(r[i])&&Object.prototype.propertyIsEnumerable.call(e,r[i])&&(n[r[i]]=e[r[i]]);return n}function s(e,t,n,r){function i(e){return e instanceof n?e:new n(function(t){t(e)})}return new(n||(n=Promise))(function(n,a){function o(e){try{u(r.next(e))}catch(t){a(t)}}function s(e){try{u(r.throw(e))}catch(t){a(t)}}function u(e){e.done?n(e.value):i(e.value).then(o,s)}u((r=r.apply(e,t||[])).next())})}function u(e,t){var n,r,i,a,o={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return a={next:s(0),throw:s(1),return:s(2)},"function"==typeof Symbol&&(a[Symbol.iterator]=function(){return this}),a;function s(e){return function(t){return u([e,t])}}function u(a){if(n)throw TypeError("Generator is already executing.");for(;o;)try{if(n=1,r&&(i=2&a[0]?r.return:a[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,a[1])).done)return i;switch(r=0,i&&(a=[2&a[0],i.value]),a[0]){case 0:case 1:i=a;break;case 4:return o.label++,{value:a[1],done:!1};case 5:o.label++,r=a[1],a=[0];continue;case 7:a=o.ops.pop(),o.trys.pop();continue;default:if(!(i=(i=o.trys).length>0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]r})},94927(e,t,n){function r(e,t){if(i("noDeprecation"))return e;var n=!1;function r(){if(!n){if(i("throwDeprecation"))throw Error(t);i("traceDeprecation")?console.trace(t):console.warn(t),n=!0}return e.apply(this,arguments)}return r}function i(e){try{if(!n.g.localStorage)return!1}catch(t){return!1}var r=n.g.localStorage[e];return null!=r&&"true"===String(r).toLowerCase()}e.exports=r},42473(e){"use strict";var t=function(){};e.exports=t},84763(e){e.exports=Worker},47529(e){e.exports=n;var t=Object.prototype.hasOwnProperty;function n(){for(var e={},n=0;ne.length)&&(t=e.length);for(var n=0,r=Array(t);n=0)&&Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}e.exports=i,e.exports.__esModule=!0,e.exports.default=e.exports},7071(e){function t(e,t){if(null==e)return{};var n,r,i={},a=Object.keys(e);for(r=0;r=0||(i[n]=e[n]);return i}e.exports=t,e.exports.__esModule=!0,e.exports.default=e.exports},94993(e,t,n){var r=n(18698).default,i=n(66115);function a(e,t){if(t&&("object"===r(t)||"function"==typeof t))return t;if(void 0!==t)throw TypeError("Derived constructors may only return object or undefined");return i(e)}e.exports=a,e.exports.__esModule=!0,e.exports.default=e.exports},6015(e){function t(n,r){return e.exports=t=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(e,t){return e.__proto__=t,e},e.exports.__esModule=!0,e.exports.default=e.exports,t(n,r)}e.exports=t,e.exports.__esModule=!0,e.exports.default=e.exports},861(e,t,n){var r=n(63405),i=n(79498),a=n(86116),o=n(42281);function s(e){return r(e)||i(e)||a(e)||o()}e.exports=s,e.exports.__esModule=!0,e.exports.default=e.exports},18698(e){function t(n){return e.exports=t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},e.exports.__esModule=!0,e.exports.default=e.exports,t(n)}e.exports=t,e.exports.__esModule=!0,e.exports.default=e.exports},86116(e,t,n){var r=n(73897);function i(e,t){if(e){if("string"==typeof e)return r(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);if("Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n)return Array.from(e);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return r(e,t)}}e.exports=i,e.exports.__esModule=!0,e.exports.default=e.exports},1644(e,t,n){"use strict";var r,i;function a(e){return!!e&&e<7}n.d(t,{I:()=>r,O:()=>a}),(i=r||(r={}))[i.loading=1]="loading",i[i.setVariables=2]="setVariables",i[i.fetchMore=3]="fetchMore",i[i.refetch=4]="refetch",i[i.poll=6]="poll",i[i.ready=7]="ready",i[i.error=8]="error"},30990(e,t,n){"use strict";n.d(t,{MS:()=>s,YG:()=>a,cA:()=>c,ls:()=>o});var r=n(70655);n(83952);var i=n(13154),a=Symbol();function o(e){return!!e.extensions&&Array.isArray(e.extensions[a])}function s(e){return e.hasOwnProperty("graphQLErrors")}var u=function(e){var t=(0,r.ev)((0,r.ev)((0,r.ev)([],e.graphQLErrors,!0),e.clientErrors,!0),e.protocolErrors,!0);return e.networkError&&t.push(e.networkError),t.map(function(e){return(0,i.s)(e)&&e.message||"Error message not found."}).join("\n")},c=function(e){function t(n){var r=n.graphQLErrors,i=n.protocolErrors,a=n.clientErrors,o=n.networkError,s=n.errorMessage,c=n.extraInfo,l=e.call(this,s)||this;return l.name="ApolloError",l.graphQLErrors=r||[],l.protocolErrors=i||[],l.clientErrors=a||[],l.networkError=o||null,l.message=s||u(l),l.extraInfo=c,l.__proto__=t.prototype,l}return(0,r.ZT)(t,e),t}(Error)},85317(e,t,n){"use strict";n.d(t,{K:()=>a});var r=n(67294),i=n(30320).aS?Symbol.for("__APOLLO_CONTEXT__"):"__APOLLO_CONTEXT__";function a(){var e=r.createContext[i];return e||(Object.defineProperty(r.createContext,i,{value:e=r.createContext({}),enumerable:!1,writable:!1,configurable:!0}),e.displayName="ApolloContext"),e}},21436(e,t,n){"use strict";n.d(t,{O:()=>i,k:()=>r});var r=Array.isArray;function i(e){return Array.isArray(e)&&e.length>0}},30320(e,t,n){"use strict";n.d(t,{DN:()=>s,JC:()=>l,aS:()=>o,mr:()=>i,sy:()=>a});var r=n(83952),i="function"==typeof WeakMap&&"ReactNative"!==(0,r.wY)(function(){return navigator.product}),a="function"==typeof WeakSet,o="function"==typeof Symbol&&"function"==typeof Symbol.for,s=o&&Symbol.asyncIterator,u="function"==typeof(0,r.wY)(function(){return window.document.createElement}),c=(0,r.wY)(function(){return navigator.userAgent.indexOf("jsdom")>=0})||!1,l=u&&!c},53712(e,t,n){"use strict";function r(){for(var e=[],t=0;tr})},10542(e,t,n){"use strict";n.d(t,{J:()=>o}),n(83952);var r=n(13154);function i(e){var t=new Set([e]);return t.forEach(function(e){(0,r.s)(e)&&a(e)===e&&Object.getOwnPropertyNames(e).forEach(function(n){(0,r.s)(e[n])&&t.add(e[n])})}),e}function a(e){if(__DEV__&&!Object.isFrozen(e))try{Object.freeze(e)}catch(t){if(t instanceof TypeError)return null;throw t}return e}function o(e){return __DEV__&&i(e),e}},14012(e,t,n){"use strict";n.d(t,{J:()=>a});var r=n(70655),i=n(53712);function a(e,t){return(0,i.o)(e,t,t.variables&&{variables:(0,r.pi)((0,r.pi)({},e&&e.variables),t.variables)})}},13154(e,t,n){"use strict";function r(e){return null!==e&&"object"==typeof e}n.d(t,{s:()=>r})},83952(e,t,n){"use strict";n.d(t,{ej:()=>u,kG:()=>c,wY:()=>h});var r,i=n(70655),a="Invariant Violation",o=Object.setPrototypeOf,s=void 0===o?function(e,t){return e.__proto__=t,e}:o,u=function(e){function t(n){void 0===n&&(n=a);var r=e.call(this,"number"==typeof n?a+": "+n+" (see https://github.com/apollographql/invariant-packages)":n)||this;return r.framesToPop=1,r.name=a,s(r,t.prototype),r}return(0,i.ZT)(t,e),t}(Error);function c(e,t){if(!e)throw new u(t)}var l=["debug","log","warn","error","silent"],f=l.indexOf("log");function d(e){return function(){if(l.indexOf(e)>=f)return(console[e]||console.log).apply(console,arguments)}}function h(e){try{return e()}catch(t){}}(r=c||(c={})).debug=d("debug"),r.log=d("log"),r.warn=d("warn"),r.error=d("error");let p=h(function(){return globalThis})||h(function(){return window})||h(function(){return self})||h(function(){return global})||h(function(){return h.constructor("return this")()});var b="__",m=[b,b].join("DEV");function g(){try{return Boolean(__DEV__)}catch(e){return Object.defineProperty(p,m,{value:"production"!==h(function(){return"production"}),enumerable:!1,configurable:!0,writable:!0}),p[m]}}let v=g();function y(e){try{return e()}catch(t){}}var w=y(function(){return globalThis})||y(function(){return window})||y(function(){return self})||y(function(){return global})||y(function(){return y.constructor("return this")()}),_=!1;function E(){!w||y(function(){return"production"})||y(function(){return process})||(Object.defineProperty(w,"process",{value:{env:{NODE_ENV:"production"}},configurable:!0,enumerable:!1,writable:!0}),_=!0)}function S(){_&&(delete w.process,_=!1)}E();var k=n(10143);function x(){return k.H,S()}function T(){__DEV__?c("boolean"==typeof v,v):c("boolean"==typeof v,39)}x(),T()},4942(e,t,n){"use strict";function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}n.d(t,{Z:()=>r})},87462(e,t,n){"use strict";function r(){return(r=Object.assign?Object.assign.bind():function(e){for(var t=1;tr})},51721(e,t,n){"use strict";function r(e,t){return(r=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(e,t){return e.__proto__=t,e})(e,t)}function i(e,t){e.prototype=Object.create(t.prototype),e.prototype.constructor=e,r(e,t)}n.d(t,{Z:()=>i})},63366(e,t,n){"use strict";function r(e,t){if(null==e)return{};var n,r,i={},a=Object.keys(e);for(r=0;r=0||(i[n]=e[n]);return i}n.d(t,{Z:()=>r})},25821(e,t,n){"use strict";n.d(t,{Z:()=>s});var r=n(45695);function i(e){return(i="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}var a=10,o=2;function s(e){return u(e,[])}function u(e,t){switch(i(e)){case"string":return JSON.stringify(e);case"function":return e.name?"[function ".concat(e.name,"]"):"[function]";case"object":if(null===e)return"null";return c(e,t);default:return String(e)}}function c(e,t){if(-1!==t.indexOf(e))return"[Circular]";var n=[].concat(t,[e]),r=d(e);if(void 0!==r){var i=r.call(e);if(i!==e)return"string"==typeof i?i:u(i,n)}else if(Array.isArray(e))return f(e,n);return l(e,n)}function l(e,t){var n=Object.keys(e);return 0===n.length?"{}":t.length>o?"["+h(e)+"]":"{ "+n.map(function(n){var r=u(e[n],t);return n+": "+r}).join(", ")+" }"}function f(e,t){if(0===e.length)return"[]";if(t.length>o)return"[Array]";for(var n=Math.min(a,e.length),r=e.length-n,i=[],s=0;s1&&i.push("... ".concat(r," more items")),"["+i.join(", ")+"]"}function d(e){var t=e[String(r.Z)];return"function"==typeof t?t:"function"==typeof e.inspect?e.inspect:void 0}function h(e){var t=Object.prototype.toString.call(e).replace(/^\[object /,"").replace(/]$/,"");if("Object"===t&&"function"==typeof e.constructor){var n=e.constructor.name;if("string"==typeof n&&""!==n)return n}return t}},45695(e,t,n){"use strict";n.d(t,{Z:()=>i});var r="function"==typeof Symbol&&"function"==typeof Symbol.for?Symbol.for("nodejs.util.inspect.custom"):void 0;let i=r},25217(e,t,n){"use strict";function r(e,t){if(!Boolean(e))throw Error(null!=t?t:"Unexpected invariant triggered.")}n.d(t,{Ye:()=>o,WU:()=>s,UG:()=>u});var i=n(45695);function a(e){var t=e.prototype.toJSON;"function"==typeof t||r(0),e.prototype.inspect=t,i.Z&&(e.prototype[i.Z]=t)}var o=function(){function e(e,t,n){this.start=e.start,this.end=t.end,this.startToken=e,this.endToken=t,this.source=n}return e.prototype.toJSON=function(){return{start:this.start,end:this.end}},e}();a(o);var s=function(){function e(e,t,n,r,i,a,o){this.kind=e,this.start=t,this.end=n,this.line=r,this.column=i,this.value=o,this.prev=a,this.next=null}return e.prototype.toJSON=function(){return{kind:this.kind,value:this.value,line:this.line,column:this.column}},e}();function u(e){return null!=e&&"string"==typeof e.kind}a(s)},87392(e,t,n){"use strict";function r(e){var t=e.split(/\r\n|[\n\r]/g),n=a(e);if(0!==n)for(var r=1;ro&&i(t[s-1]);)--s;return t.slice(o,s).join("\n")}function i(e){for(var t=0;t1&&void 0!==arguments[1]?arguments[1]:"",n=arguments.length>2&&void 0!==arguments[2]&&arguments[2],r=-1===e.indexOf("\n"),i=" "===e[0]||" "===e[0],a='"'===e[e.length-1],o="\\"===e[e.length-1],s=!r||a||o||n,u="";return s&&!(r&&i)&&(u+="\n"+t),u+=t?e.replace(/\n/g,"\n"+t):e,s&&(u+="\n"),'"""'+u.replace(/"""/g,'\\"""')+'"""'}n.d(t,{LZ:()=>o,W7:()=>r})},97359(e,t,n){"use strict";n.d(t,{h:()=>r});var r=Object.freeze({NAME:"Name",DOCUMENT:"Document",OPERATION_DEFINITION:"OperationDefinition",VARIABLE_DEFINITION:"VariableDefinition",SELECTION_SET:"SelectionSet",FIELD:"Field",ARGUMENT:"Argument",FRAGMENT_SPREAD:"FragmentSpread",INLINE_FRAGMENT:"InlineFragment",FRAGMENT_DEFINITION:"FragmentDefinition",VARIABLE:"Variable",INT:"IntValue",FLOAT:"FloatValue",STRING:"StringValue",BOOLEAN:"BooleanValue",NULL:"NullValue",ENUM:"EnumValue",LIST:"ListValue",OBJECT:"ObjectValue",OBJECT_FIELD:"ObjectField",DIRECTIVE:"Directive",NAMED_TYPE:"NamedType",LIST_TYPE:"ListType",NON_NULL_TYPE:"NonNullType",SCHEMA_DEFINITION:"SchemaDefinition",OPERATION_TYPE_DEFINITION:"OperationTypeDefinition",SCALAR_TYPE_DEFINITION:"ScalarTypeDefinition",OBJECT_TYPE_DEFINITION:"ObjectTypeDefinition",FIELD_DEFINITION:"FieldDefinition",INPUT_VALUE_DEFINITION:"InputValueDefinition",INTERFACE_TYPE_DEFINITION:"InterfaceTypeDefinition",UNION_TYPE_DEFINITION:"UnionTypeDefinition",ENUM_TYPE_DEFINITION:"EnumTypeDefinition",ENUM_VALUE_DEFINITION:"EnumValueDefinition",INPUT_OBJECT_TYPE_DEFINITION:"InputObjectTypeDefinition",DIRECTIVE_DEFINITION:"DirectiveDefinition",SCHEMA_EXTENSION:"SchemaExtension",SCALAR_TYPE_EXTENSION:"ScalarTypeExtension",OBJECT_TYPE_EXTENSION:"ObjectTypeExtension",INTERFACE_TYPE_EXTENSION:"InterfaceTypeExtension",UNION_TYPE_EXTENSION:"UnionTypeExtension",ENUM_TYPE_EXTENSION:"EnumTypeExtension",INPUT_OBJECT_TYPE_EXTENSION:"InputObjectTypeExtension"})},10143(e,t,n){"use strict";n.d(t,{H:()=>c,T:()=>l});var r=n(99763),i=n(25821);function a(e,t){if(!Boolean(e))throw Error(t)}let o=function(e,t){return e instanceof t};function s(e,t){for(var n=0;n1&&void 0!==arguments[1]?arguments[1]:"GraphQL request",n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{line:1,column:1};"string"==typeof e||a(0,"Body must be a string. Received: ".concat((0,i.Z)(e),".")),this.body=e,this.name=t,this.locationOffset=n,this.locationOffset.line>0||a(0,"line in locationOffset is 1-indexed and must be positive."),this.locationOffset.column>0||a(0,"column in locationOffset is 1-indexed and must be positive.")}return u(e,[{key:r.YF,get:function(){return"Source"}}]),e}();function l(e){return o(e,c)}},99763(e,t,n){"use strict";n.d(t,{YF:()=>r});var r="function"==typeof Symbol&&null!=Symbol.toStringTag?Symbol.toStringTag:"@@toStringTag"},37452(e){"use strict";e.exports=JSON.parse('{"AElig":"\xc6","AMP":"&","Aacute":"\xc1","Acirc":"\xc2","Agrave":"\xc0","Aring":"\xc5","Atilde":"\xc3","Auml":"\xc4","COPY":"\xa9","Ccedil":"\xc7","ETH":"\xd0","Eacute":"\xc9","Ecirc":"\xca","Egrave":"\xc8","Euml":"\xcb","GT":">","Iacute":"\xcd","Icirc":"\xce","Igrave":"\xcc","Iuml":"\xcf","LT":"<","Ntilde":"\xd1","Oacute":"\xd3","Ocirc":"\xd4","Ograve":"\xd2","Oslash":"\xd8","Otilde":"\xd5","Ouml":"\xd6","QUOT":"\\"","REG":"\xae","THORN":"\xde","Uacute":"\xda","Ucirc":"\xdb","Ugrave":"\xd9","Uuml":"\xdc","Yacute":"\xdd","aacute":"\xe1","acirc":"\xe2","acute":"\xb4","aelig":"\xe6","agrave":"\xe0","amp":"&","aring":"\xe5","atilde":"\xe3","auml":"\xe4","brvbar":"\xa6","ccedil":"\xe7","cedil":"\xb8","cent":"\xa2","copy":"\xa9","curren":"\xa4","deg":"\xb0","divide":"\xf7","eacute":"\xe9","ecirc":"\xea","egrave":"\xe8","eth":"\xf0","euml":"\xeb","frac12":"\xbd","frac14":"\xbc","frac34":"\xbe","gt":">","iacute":"\xed","icirc":"\xee","iexcl":"\xa1","igrave":"\xec","iquest":"\xbf","iuml":"\xef","laquo":"\xab","lt":"<","macr":"\xaf","micro":"\xb5","middot":"\xb7","nbsp":"\xa0","not":"\xac","ntilde":"\xf1","oacute":"\xf3","ocirc":"\xf4","ograve":"\xf2","ordf":"\xaa","ordm":"\xba","oslash":"\xf8","otilde":"\xf5","ouml":"\xf6","para":"\xb6","plusmn":"\xb1","pound":"\xa3","quot":"\\"","raquo":"\xbb","reg":"\xae","sect":"\xa7","shy":"\xad","sup1":"\xb9","sup2":"\xb2","sup3":"\xb3","szlig":"\xdf","thorn":"\xfe","times":"\xd7","uacute":"\xfa","ucirc":"\xfb","ugrave":"\xf9","uml":"\xa8","uuml":"\xfc","yacute":"\xfd","yen":"\xa5","yuml":"\xff"}')},93580(e){"use strict";e.exports=JSON.parse('{"0":"�","128":"€","130":"‚","131":"ƒ","132":"„","133":"…","134":"†","135":"‡","136":"ˆ","137":"‰","138":"Š","139":"‹","140":"Œ","142":"Ž","145":"‘","146":"’","147":"“","148":"”","149":"•","150":"–","151":"—","152":"˜","153":"™","154":"š","155":"›","156":"œ","158":"ž","159":"Ÿ"}')},67946(e){"use strict";e.exports=JSON.parse('{"locale":"en","long":{"year":{"previous":"last year","current":"this year","next":"next year","past":{"one":"{0} year ago","other":"{0} years ago"},"future":{"one":"in {0} year","other":"in {0} years"}},"quarter":{"previous":"last quarter","current":"this quarter","next":"next quarter","past":{"one":"{0} quarter ago","other":"{0} quarters ago"},"future":{"one":"in {0} quarter","other":"in {0} quarters"}},"month":{"previous":"last month","current":"this month","next":"next month","past":{"one":"{0} month ago","other":"{0} months ago"},"future":{"one":"in {0} month","other":"in {0} months"}},"week":{"previous":"last week","current":"this week","next":"next week","past":{"one":"{0} week ago","other":"{0} weeks ago"},"future":{"one":"in {0} week","other":"in {0} weeks"}},"day":{"previous":"yesterday","current":"today","next":"tomorrow","past":{"one":"{0} day ago","other":"{0} days ago"},"future":{"one":"in {0} day","other":"in {0} days"}},"hour":{"current":"this hour","past":{"one":"{0} hour ago","other":"{0} hours ago"},"future":{"one":"in {0} hour","other":"in {0} hours"}},"minute":{"current":"this minute","past":{"one":"{0} minute ago","other":"{0} minutes ago"},"future":{"one":"in {0} minute","other":"in {0} minutes"}},"second":{"current":"now","past":{"one":"{0} second ago","other":"{0} seconds ago"},"future":{"one":"in {0} second","other":"in {0} seconds"}}},"short":{"year":{"previous":"last yr.","current":"this yr.","next":"next yr.","past":"{0} yr. ago","future":"in {0} yr."},"quarter":{"previous":"last qtr.","current":"this qtr.","next":"next qtr.","past":{"one":"{0} qtr. ago","other":"{0} qtrs. ago"},"future":{"one":"in {0} qtr.","other":"in {0} qtrs."}},"month":{"previous":"last mo.","current":"this mo.","next":"next mo.","past":"{0} mo. ago","future":"in {0} mo."},"week":{"previous":"last wk.","current":"this wk.","next":"next wk.","past":"{0} wk. ago","future":"in {0} wk."},"day":{"previous":"yesterday","current":"today","next":"tomorrow","past":{"one":"{0} day ago","other":"{0} days ago"},"future":{"one":"in {0} day","other":"in {0} days"}},"hour":{"current":"this hour","past":"{0} hr. ago","future":"in {0} hr."},"minute":{"current":"this minute","past":"{0} min. ago","future":"in {0} min."},"second":{"current":"now","past":"{0} sec. ago","future":"in {0} sec."}},"narrow":{"year":{"previous":"last yr.","current":"this yr.","next":"next yr.","past":"{0} yr. ago","future":"in {0} yr."},"quarter":{"previous":"last qtr.","current":"this qtr.","next":"next qtr.","past":{"one":"{0} qtr. ago","other":"{0} qtrs. ago"},"future":{"one":"in {0} qtr.","other":"in {0} qtrs."}},"month":{"previous":"last mo.","current":"this mo.","next":"next mo.","past":"{0} mo. ago","future":"in {0} mo."},"week":{"previous":"last wk.","current":"this wk.","next":"next wk.","past":"{0} wk. ago","future":"in {0} wk."},"day":{"previous":"yesterday","current":"today","next":"tomorrow","past":{"one":"{0} day ago","other":"{0} days ago"},"future":{"one":"in {0} day","other":"in {0} days"}},"hour":{"current":"this hour","past":"{0} hr. ago","future":"in {0} hr."},"minute":{"current":"this minute","past":"{0} min. ago","future":"in {0} min."},"second":{"current":"now","past":"{0} sec. ago","future":"in {0} sec."}},"now":{"now":{"current":"now","future":"in a moment","past":"just now"}},"mini":{"year":"{0}yr","month":"{0}mo","week":"{0}wk","day":"{0}d","hour":"{0}h","minute":"{0}m","second":"{0}s","now":"now"},"short-time":{"year":"{0} yr.","month":"{0} mo.","week":"{0} wk.","day":{"one":"{0} day","other":"{0} days"},"hour":"{0} hr.","minute":"{0} min.","second":"{0} sec."},"long-time":{"year":{"one":"{0} year","other":"{0} years"},"month":{"one":"{0} month","other":"{0} months"},"week":{"one":"{0} week","other":"{0} weeks"},"day":{"one":"{0} day","other":"{0} days"},"hour":{"one":"{0} hour","other":"{0} hours"},"minute":{"one":"{0} minute","other":"{0} minutes"},"second":{"one":"{0} second","other":"{0} seconds"}}}')}},__webpack_module_cache__={};function __webpack_require__(e){var t=__webpack_module_cache__[e];if(void 0!==t)return t.exports;var n=__webpack_module_cache__[e]={id:e,loaded:!1,exports:{}};return __webpack_modules__[e].call(n.exports,n,n.exports,__webpack_require__),n.loaded=!0,n.exports}__webpack_require__.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return __webpack_require__.d(t,{a:t}),t},(()=>{var e,t=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__;__webpack_require__.t=function(n,r){if(1&r&&(n=this(n)),8&r||"object"==typeof n&&n&&(4&r&&n.__esModule||16&r&&"function"==typeof n.then))return n;var i=Object.create(null);__webpack_require__.r(i);var a={};e=e||[null,t({}),t([]),t(t)];for(var o=2&r&&n;"object"==typeof o&&!~e.indexOf(o);o=t(o))Object.getOwnPropertyNames(o).forEach(e=>a[e]=()=>n[e]);return a.default=()=>n,__webpack_require__.d(i,a),i}})(),__webpack_require__.d=(e,t)=>{for(var n in t)__webpack_require__.o(t,n)&&!__webpack_require__.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},__webpack_require__.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||Function("return this")()}catch(e){if("object"==typeof window)return window}}(),__webpack_require__.hmd=e=>((e=Object.create(e)).children||(e.children=[]),Object.defineProperty(e,"exports",{enumerable:!0,set(){throw Error("ES Modules may not assign module.exports or exports.*, Use ESM export syntax, instead: "+e.id)}}),e),__webpack_require__.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),__webpack_require__.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},__webpack_require__.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),__webpack_require__.p="/assets/",__webpack_require__.nc=void 0;var __webpack_exports__={};(()=>{"use strict";var e,t,n,r,i=__webpack_require__(32316),a=__webpack_require__(8126),o=__webpack_require__(5690),s=__webpack_require__(30381),u=__webpack_require__.n(s),c=__webpack_require__(67294),l=__webpack_require__(73935),f=__webpack_require__.n(l),d=__webpack_require__(57209),h=__webpack_require__(37703),p=__webpack_require__(97779),b=__webpack_require__(28500);function m(e){return function(t){var n=t.dispatch,r=t.getState;return function(t){return function(i){return"function"==typeof i?i(n,r,e):t(i)}}}}var g=m();g.withExtraArgument=m;let v=g;var y=__webpack_require__(76489);function w(e){return function(t){return function(n){return function(r){n(r);var i=e||document&&document.cookie||"",a=t.getState();if("MATCH_ROUTE"===r.type&&"/signin"!==a.notifications.currentUrl){var o=(0,y.Q)(i);if(o.explorer)try{var s=JSON.parse(o.explorer);if("error"===s.status){var u=_(s.url);n({type:"NOTIFY_ERROR_MSG",msg:u})}}catch(c){n({type:"NOTIFY_ERROR_MSG",msg:"Invalid explorer status"})}}}}}}function _(e){var t="Can't connect to explorer: ".concat(e);return e.match(/^wss?:.+/)?t:"".concat(t,". You must use a websocket.")}var E=__webpack_require__(16353);function S(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n=e.length?{done:!0}:{done:!1,value:e[r++]}}}throw TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function ei(e,t){if(e){if("string"==typeof e)return ea(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);if("Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n)return Array.from(e);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return ea(e,t)}}function ea(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n1,i=!1,a=arguments[1],o=a;return new n(function(n){return t.subscribe({next:function(t){var a=!i;if(i=!0,!a||r)try{o=e(o,t)}catch(s){return n.error(s)}else o=t},error:function(e){n.error(e)},complete:function(){if(!i&&!r)return n.error(TypeError("Cannot reduce an empty sequence"));n.next(o),n.complete()}})})},t.concat=function(){for(var e=this,t=arguments.length,n=Array(t),r=0;r=0&&i.splice(e,1),o()}});i.push(s)},error:function(e){r.error(e)},complete:function(){o()}});function o(){a.closed&&0===i.length&&r.complete()}return function(){i.forEach(function(e){return e.unsubscribe()}),a.unsubscribe()}})},t[ed]=function(){return this},e.from=function(t){var n="function"==typeof this?this:e;if(null==t)throw TypeError(t+" is not an object");var r=ep(t,ed);if(r){var i=r.call(t);if(Object(i)!==i)throw TypeError(i+" is not an object");return em(i)&&i.constructor===n?i:new n(function(e){return i.subscribe(e)})}if(ec("iterator")&&(r=ep(t,ef)))return new n(function(e){ev(function(){if(!e.closed){for(var n,i=er(r.call(t));!(n=i()).done;){var a=n.value;if(e.next(a),e.closed)return}e.complete()}})});if(Array.isArray(t))return new n(function(e){ev(function(){if(!e.closed){for(var n=0;n0))return n.connection.key;var r=n.connection.filter?n.connection.filter:[];r.sort();var i={};return r.forEach(function(e){i[e]=t[e]}),"".concat(n.connection.key,"(").concat(eV(i),")")}var a=e;if(t){var o=eV(t);a+="(".concat(o,")")}return n&&Object.keys(n).forEach(function(e){-1===eW.indexOf(e)&&(n[e]&&Object.keys(n[e]).length?a+="@".concat(e,"(").concat(eV(n[e]),")"):a+="@".concat(e))}),a},{setStringify:function(e){var t=eV;return eV=e,t}}),eV=function(e){return JSON.stringify(e,eq)};function eq(e,t){return(0,eO.s)(t)&&!Array.isArray(t)&&(t=Object.keys(t).sort().reduce(function(e,n){return e[n]=t[n],e},{})),t}function eZ(e,t){if(e.arguments&&e.arguments.length){var n={};return e.arguments.forEach(function(e){var r;return ez(n,e.name,e.value,t)}),n}return null}function eX(e){return e.alias?e.alias.value:e.name.value}function eJ(e,t,n){for(var r,i=0,a=t.selections;it.indexOf(i))throw __DEV__?new Q.ej("illegal argument: ".concat(i)):new Q.ej(27)}return e}function tt(e,t){return t?t(e):eT.of()}function tn(e){return"function"==typeof e?new ta(e):e}function tr(e){return e.request.length<=1}var ti=function(e){function t(t,n){var r=e.call(this,t)||this;return r.link=n,r}return(0,en.ZT)(t,e),t}(Error),ta=function(){function e(e){e&&(this.request=e)}return e.empty=function(){return new e(function(){return eT.of()})},e.from=function(t){return 0===t.length?e.empty():t.map(tn).reduce(function(e,t){return e.concat(t)})},e.split=function(t,n,r){var i=tn(n),a=tn(r||new e(tt));return new e(tr(i)&&tr(a)?function(e){return t(e)?i.request(e)||eT.of():a.request(e)||eT.of()}:function(e,n){return t(e)?i.request(e,n)||eT.of():a.request(e,n)||eT.of()})},e.execute=function(e,t){return e.request(eM(t.context,e7(te(t))))||eT.of()},e.concat=function(t,n){var r=tn(t);if(tr(r))return __DEV__&&Q.kG.warn(new ti("You are calling concat on a terminating link, which will have no effect",r)),r;var i=tn(n);return new e(tr(i)?function(e){return r.request(e,function(e){return i.request(e)||eT.of()})||eT.of()}:function(e,t){return r.request(e,function(e){return i.request(e,t)||eT.of()})||eT.of()})},e.prototype.split=function(t,n,r){return this.concat(e.split(t,n,r||new e(tt)))},e.prototype.concat=function(t){return e.concat(this,t)},e.prototype.request=function(e,t){throw __DEV__?new Q.ej("request is not implemented"):new Q.ej(22)},e.prototype.onError=function(e,t){if(t&&t.error)return t.error(e),!1;throw e},e.prototype.setOnError=function(e){return this.onError=e,this},e}(),to=__webpack_require__(25821),ts=__webpack_require__(25217),tu={Name:[],Document:["definitions"],OperationDefinition:["name","variableDefinitions","directives","selectionSet"],VariableDefinition:["variable","type","defaultValue","directives"],Variable:["name"],SelectionSet:["selections"],Field:["alias","name","arguments","directives","selectionSet"],Argument:["name","value"],FragmentSpread:["name","directives"],InlineFragment:["typeCondition","directives","selectionSet"],FragmentDefinition:["name","variableDefinitions","typeCondition","directives","selectionSet"],IntValue:[],FloatValue:[],StringValue:[],BooleanValue:[],NullValue:[],EnumValue:[],ListValue:["values"],ObjectValue:["fields"],ObjectField:["name","value"],Directive:["name","arguments"],NamedType:["name"],ListType:["type"],NonNullType:["type"],SchemaDefinition:["description","directives","operationTypes"],OperationTypeDefinition:["type"],ScalarTypeDefinition:["description","name","directives"],ObjectTypeDefinition:["description","name","interfaces","directives","fields"],FieldDefinition:["description","name","arguments","type","directives"],InputValueDefinition:["description","name","type","defaultValue","directives"],InterfaceTypeDefinition:["description","name","interfaces","directives","fields"],UnionTypeDefinition:["description","name","directives","types"],EnumTypeDefinition:["description","name","directives","values"],EnumValueDefinition:["description","name","directives"],InputObjectTypeDefinition:["description","name","directives","fields"],DirectiveDefinition:["description","name","arguments","locations"],SchemaExtension:["directives","operationTypes"],ScalarTypeExtension:["name","directives"],ObjectTypeExtension:["name","interfaces","directives","fields"],InterfaceTypeExtension:["name","interfaces","directives","fields"],UnionTypeExtension:["name","directives","types"],EnumTypeExtension:["name","directives","values"],InputObjectTypeExtension:["name","directives","fields"]},tc=Object.freeze({});function tl(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:tu,r=void 0,i=Array.isArray(e),a=[e],o=-1,s=[],u=void 0,c=void 0,l=void 0,f=[],d=[],h=e;do{var p,b=++o===a.length,m=b&&0!==s.length;if(b){if(c=0===d.length?void 0:f[f.length-1],u=l,l=d.pop(),m){if(i)u=u.slice();else{for(var g={},v=0,y=Object.keys(u);v1)for(var r=new tB,i=1;i=0;--a){var o=i[a],s=isNaN(+o)?{}:[];s[o]=t,t=s}n=r.merge(n,t)}),n}var tW=Object.prototype.hasOwnProperty;function tK(e,t){var n,r,i,a,o;return(0,en.mG)(this,void 0,void 0,function(){var s,u,c,l,f,d,h,p,b,m,g,v,y,w,_,E,S,k,x,T,M,O,A;return(0,en.Jh)(this,function(L){switch(L.label){case 0:if(void 0===TextDecoder)throw Error("TextDecoder must be defined in the environment: please import a polyfill.");s=new TextDecoder("utf-8"),u=null===(n=e.headers)||void 0===n?void 0:n.get("content-type"),c="boundary=",l=(null==u?void 0:u.includes(c))?null==u?void 0:u.substring((null==u?void 0:u.indexOf(c))+c.length).replace(/['"]/g,"").replace(/\;(.*)/gm,"").trim():"-",f="\r\n--".concat(l),d="",h=tI(e),p=!0,L.label=1;case 1:if(!p)return[3,3];return[4,h.next()];case 2:for(m=(b=L.sent()).value,g=b.done,v="string"==typeof m?m:s.decode(m),y=d.length-f.length+1,p=!g,d+=v,w=d.indexOf(f,y);w>-1;){if(_=void 0,_=(O=[d.slice(0,w),d.slice(w+f.length),])[0],d=O[1],E=_.indexOf("\r\n\r\n"),(k=(S=tV(_.slice(0,E)))["content-type"])&&-1===k.toLowerCase().indexOf("application/json"))throw Error("Unsupported patch content type: application/json is required.");if(x=_.slice(E))try{T=tq(e,x),Object.keys(T).length>1||"data"in T||"incremental"in T||"errors"in T||"payload"in T?tz(T)?(M={},"payload"in T&&(M=(0,en.pi)({},T.payload)),"errors"in T&&(M=(0,en.pi)((0,en.pi)({},M),{extensions:(0,en.pi)((0,en.pi)({},"extensions"in M?M.extensions:null),((A={})[tN.YG]=T.errors,A))})),null===(r=t.next)||void 0===r||r.call(t,M)):null===(i=t.next)||void 0===i||i.call(t,T):1===Object.keys(T).length&&"hasNext"in T&&!T.hasNext&&(null===(a=t.complete)||void 0===a||a.call(t))}catch(C){tZ(C,t)}w=d.indexOf(f)}return[3,1];case 3:return null===(o=t.complete)||void 0===o||o.call(t),[2]}})})}function tV(e){var t={};return e.split("\n").forEach(function(e){var n=e.indexOf(":");if(n>-1){var r=e.slice(0,n).trim().toLowerCase(),i=e.slice(n+1).trim();t[r]=i}}),t}function tq(e,t){e.status>=300&&tD(e,function(){try{return JSON.parse(t)}catch(e){return t}}(),"Response not successful: Received status code ".concat(e.status));try{return JSON.parse(t)}catch(n){var r=n;throw r.name="ServerParseError",r.response=e,r.statusCode=e.status,r.bodyText=t,r}}function tZ(e,t){var n,r;"AbortError"!==e.name&&(e.result&&e.result.errors&&e.result.data&&(null===(n=t.next)||void 0===n||n.call(t,e.result)),null===(r=t.error)||void 0===r||r.call(t,e))}function tX(e,t,n){tJ(t)(e).then(function(e){var t,r;null===(t=n.next)||void 0===t||t.call(n,e),null===(r=n.complete)||void 0===r||r.call(n)}).catch(function(e){return tZ(e,n)})}function tJ(e){return function(t){return t.text().then(function(e){return tq(t,e)}).then(function(n){return t.status>=300&&tD(t,n,"Response not successful: Received status code ".concat(t.status)),Array.isArray(n)||tW.call(n,"data")||tW.call(n,"errors")||tD(t,n,"Server response was missing for query '".concat(Array.isArray(e)?e.map(function(e){return e.operationName}):e.operationName,"'.")),n})}}var tQ=function(e){if(!e&&"undefined"==typeof fetch)throw __DEV__?new Q.ej("\n\"fetch\" has not been found globally and no fetcher has been configured. To fix this, install a fetch package (like https://www.npmjs.com/package/cross-fetch), instantiate the fetcher, and pass it into your HttpLink constructor. For example:\n\nimport fetch from 'cross-fetch';\nimport { ApolloClient, HttpLink } from '@apollo/client';\nconst client = new ApolloClient({\n link: new HttpLink({ uri: '/graphql', fetch })\n});\n "):new Q.ej(23)},t1=__webpack_require__(87392);function t0(e){return tl(e,{leave:t3})}var t2=80,t3={Name:function(e){return e.value},Variable:function(e){return"$"+e.name},Document:function(e){return t6(e.definitions,"\n\n")+"\n"},OperationDefinition:function(e){var t=e.operation,n=e.name,r=t8("(",t6(e.variableDefinitions,", "),")"),i=t6(e.directives," "),a=e.selectionSet;return n||i||r||"query"!==t?t6([t,t6([n,r]),i,a]," "):a},VariableDefinition:function(e){var t=e.variable,n=e.type,r=e.defaultValue,i=e.directives;return t+": "+n+t8(" = ",r)+t8(" ",t6(i," "))},SelectionSet:function(e){return t5(e.selections)},Field:function(e){var t=e.alias,n=e.name,r=e.arguments,i=e.directives,a=e.selectionSet,o=t8("",t,": ")+n,s=o+t8("(",t6(r,", "),")");return s.length>t2&&(s=o+t8("(\n",t9(t6(r,"\n")),"\n)")),t6([s,t6(i," "),a]," ")},Argument:function(e){var t;return e.name+": "+e.value},FragmentSpread:function(e){var t;return"..."+e.name+t8(" ",t6(e.directives," "))},InlineFragment:function(e){var t=e.typeCondition,n=e.directives,r=e.selectionSet;return t6(["...",t8("on ",t),t6(n," "),r]," ")},FragmentDefinition:function(e){var t=e.name,n=e.typeCondition,r=e.variableDefinitions,i=e.directives,a=e.selectionSet;return"fragment ".concat(t).concat(t8("(",t6(r,", "),")")," ")+"on ".concat(n," ").concat(t8("",t6(i," ")," "))+a},IntValue:function(e){return e.value},FloatValue:function(e){return e.value},StringValue:function(e,t){var n=e.value;return e.block?(0,t1.LZ)(n,"description"===t?"":" "):JSON.stringify(n)},BooleanValue:function(e){return e.value?"true":"false"},NullValue:function(){return"null"},EnumValue:function(e){return e.value},ListValue:function(e){return"["+t6(e.values,", ")+"]"},ObjectValue:function(e){return"{"+t6(e.fields,", ")+"}"},ObjectField:function(e){var t;return e.name+": "+e.value},Directive:function(e){var t;return"@"+e.name+t8("(",t6(e.arguments,", "),")")},NamedType:function(e){return e.name},ListType:function(e){return"["+e.type+"]"},NonNullType:function(e){return e.type+"!"},SchemaDefinition:t4(function(e){var t=e.directives,n=e.operationTypes;return t6(["schema",t6(t," "),t5(n)]," ")}),OperationTypeDefinition:function(e){var t;return e.operation+": "+e.type},ScalarTypeDefinition:t4(function(e){var t;return t6(["scalar",e.name,t6(e.directives," ")]," ")}),ObjectTypeDefinition:t4(function(e){var t=e.name,n=e.interfaces,r=e.directives,i=e.fields;return t6(["type",t,t8("implements ",t6(n," & ")),t6(r," "),t5(i)]," ")}),FieldDefinition:t4(function(e){var t=e.name,n=e.arguments,r=e.type,i=e.directives;return t+(ne(n)?t8("(\n",t9(t6(n,"\n")),"\n)"):t8("(",t6(n,", "),")"))+": "+r+t8(" ",t6(i," "))}),InputValueDefinition:t4(function(e){var t=e.name,n=e.type,r=e.defaultValue,i=e.directives;return t6([t+": "+n,t8("= ",r),t6(i," ")]," ")}),InterfaceTypeDefinition:t4(function(e){var t=e.name,n=e.interfaces,r=e.directives,i=e.fields;return t6(["interface",t,t8("implements ",t6(n," & ")),t6(r," "),t5(i)]," ")}),UnionTypeDefinition:t4(function(e){var t=e.name,n=e.directives,r=e.types;return t6(["union",t,t6(n," "),r&&0!==r.length?"= "+t6(r," | "):""]," ")}),EnumTypeDefinition:t4(function(e){var t=e.name,n=e.directives,r=e.values;return t6(["enum",t,t6(n," "),t5(r)]," ")}),EnumValueDefinition:t4(function(e){var t;return t6([e.name,t6(e.directives," ")]," ")}),InputObjectTypeDefinition:t4(function(e){var t=e.name,n=e.directives,r=e.fields;return t6(["input",t,t6(n," "),t5(r)]," ")}),DirectiveDefinition:t4(function(e){var t=e.name,n=e.arguments,r=e.repeatable,i=e.locations;return"directive @"+t+(ne(n)?t8("(\n",t9(t6(n,"\n")),"\n)"):t8("(",t6(n,", "),")"))+(r?" repeatable":"")+" on "+t6(i," | ")}),SchemaExtension:function(e){var t=e.directives,n=e.operationTypes;return t6(["extend schema",t6(t," "),t5(n)]," ")},ScalarTypeExtension:function(e){var t;return t6(["extend scalar",e.name,t6(e.directives," ")]," ")},ObjectTypeExtension:function(e){var t=e.name,n=e.interfaces,r=e.directives,i=e.fields;return t6(["extend type",t,t8("implements ",t6(n," & ")),t6(r," "),t5(i)]," ")},InterfaceTypeExtension:function(e){var t=e.name,n=e.interfaces,r=e.directives,i=e.fields;return t6(["extend interface",t,t8("implements ",t6(n," & ")),t6(r," "),t5(i)]," ")},UnionTypeExtension:function(e){var t=e.name,n=e.directives,r=e.types;return t6(["extend union",t,t6(n," "),r&&0!==r.length?"= "+t6(r," | "):""]," ")},EnumTypeExtension:function(e){var t=e.name,n=e.directives,r=e.values;return t6(["extend enum",t,t6(n," "),t5(r)]," ")},InputObjectTypeExtension:function(e){var t=e.name,n=e.directives,r=e.fields;return t6(["extend input",t,t6(n," "),t5(r)]," ")}};function t4(e){return function(t){return t6([t.description,e(t)],"\n")}}function t6(e){var t,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";return null!==(t=null==e?void 0:e.filter(function(e){return e}).join(n))&&void 0!==t?t:""}function t5(e){return t8("{\n",t9(t6(e,"\n")),"\n}")}function t8(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"";return null!=t&&""!==t?e+t+n:""}function t9(e){return t8(" ",e.replace(/\n/g,"\n "))}function t7(e){return -1!==e.indexOf("\n")}function ne(e){return null!=e&&e.some(t7)}var nt,nn,nr,ni={http:{includeQuery:!0,includeExtensions:!1,preserveHeaderCase:!1},headers:{accept:"*/*","content-type":"application/json"},options:{method:"POST"}},na=function(e,t){return t(e)};function no(e,t){for(var n=[],r=2;rObject.create(null),{forEach:nv,slice:ny}=Array.prototype,{hasOwnProperty:nw}=Object.prototype;class n_{constructor(e=!0,t=ng){this.weakness=e,this.makeData=t}lookup(...e){return this.lookupArray(e)}lookupArray(e){let t=this;return nv.call(e,e=>t=t.getChildTrie(e)),nw.call(t,"data")?t.data:t.data=this.makeData(ny.call(e))}peek(...e){return this.peekArray(e)}peekArray(e){let t=this;for(let n=0,r=e.length;t&&n=0;--o)t.definitions[o].kind===nL.h.OPERATION_DEFINITION&&++a;var s=nN(e),u=e.some(function(e){return e.remove}),c=function(e){return u&&e&&e.some(s)},l=new Map,f=!1,d={enter:function(e){if(c(e.directives))return f=!0,null}},h=tl(t,{Field:d,InlineFragment:d,VariableDefinition:{enter:function(){return!1}},Variable:{enter:function(e,t,n,r,a){var o=i(a);o&&o.variables.add(e.name.value)}},FragmentSpread:{enter:function(e,t,n,r,a){if(c(e.directives))return f=!0,null;var o=i(a);o&&o.fragmentSpreads.add(e.name.value)}},FragmentDefinition:{enter:function(e,t,n,r){l.set(JSON.stringify(r),e)},leave:function(e,t,n,i){return e===l.get(JSON.stringify(i))?e:a>0&&e.selectionSet.selections.every(function(e){return e.kind===nL.h.FIELD&&"__typename"===e.name.value})?(r(e.name.value).removed=!0,f=!0,null):void 0}},Directive:{leave:function(e){if(s(e))return f=!0,null}}});if(!f)return t;var p=function(e){return e.transitiveVars||(e.transitiveVars=new Set(e.variables),e.removed||e.fragmentSpreads.forEach(function(t){p(r(t)).transitiveVars.forEach(function(t){e.transitiveVars.add(t)})})),e},b=new Set;h.definitions.forEach(function(e){e.kind===nL.h.OPERATION_DEFINITION?p(n(e.name&&e.name.value)).fragmentSpreads.forEach(function(e){b.add(e)}):e.kind!==nL.h.FRAGMENT_DEFINITION||0!==a||r(e.name.value).removed||b.add(e.name.value)}),b.forEach(function(e){p(r(e)).fragmentSpreads.forEach(function(e){b.add(e)})});var m=function(e){return!!(!b.has(e)||r(e).removed)},g={enter:function(e){if(m(e.name.value))return null}};return nD(tl(h,{FragmentSpread:g,FragmentDefinition:g,OperationDefinition:{leave:function(e){if(e.variableDefinitions){var t=p(n(e.name&&e.name.value)).transitiveVars;if(t.size0},t.prototype.tearDownQuery=function(){this.isTornDown||(this.concast&&this.observer&&(this.concast.removeObserver(this.observer),delete this.concast,delete this.observer),this.stopPolling(),this.subscriptions.forEach(function(e){return e.unsubscribe()}),this.subscriptions.clear(),this.queryManager.stopQuery(this.queryId),this.observers.clear(),this.isTornDown=!0)},t}(eT);function n4(e){var t=e.options,n=t.fetchPolicy,r=t.nextFetchPolicy;return"cache-and-network"===n||"network-only"===n?e.reobserve({fetchPolicy:"cache-first",nextFetchPolicy:function(){return(this.nextFetchPolicy=r,"function"==typeof r)?r.apply(this,arguments):n}}):e.reobserve()}function n6(e){__DEV__&&Q.kG.error("Unhandled error",e.message,e.stack)}function n5(e){__DEV__&&e&&__DEV__&&Q.kG.debug("Missing cache result fields: ".concat(JSON.stringify(e)),e)}function n8(e){return"network-only"===e||"no-cache"===e||"standby"===e}nK(n3);function n9(e){return e.kind===nL.h.FIELD||e.kind===nL.h.FRAGMENT_SPREAD||e.kind===nL.h.INLINE_FRAGMENT}function n7(e){return e.kind===Kind.SCALAR_TYPE_DEFINITION||e.kind===Kind.OBJECT_TYPE_DEFINITION||e.kind===Kind.INTERFACE_TYPE_DEFINITION||e.kind===Kind.UNION_TYPE_DEFINITION||e.kind===Kind.ENUM_TYPE_DEFINITION||e.kind===Kind.INPUT_OBJECT_TYPE_DEFINITION}function re(e){return e.kind===Kind.SCALAR_TYPE_EXTENSION||e.kind===Kind.OBJECT_TYPE_EXTENSION||e.kind===Kind.INTERFACE_TYPE_EXTENSION||e.kind===Kind.UNION_TYPE_EXTENSION||e.kind===Kind.ENUM_TYPE_EXTENSION||e.kind===Kind.INPUT_OBJECT_TYPE_EXTENSION}var rt=function(){return Object.create(null)},rn=Array.prototype,rr=rn.forEach,ri=rn.slice,ra=function(){function e(e,t){void 0===e&&(e=!0),void 0===t&&(t=rt),this.weakness=e,this.makeData=t}return e.prototype.lookup=function(){for(var e=[],t=0;tclass{constructor(){this.id=["slot",rc++,Date.now(),Math.random().toString(36).slice(2),].join(":")}hasValue(){for(let e=rs;e;e=e.parent)if(this.id in e.slots){let t=e.slots[this.id];if(t===ru)break;return e!==rs&&(rs.slots[this.id]=t),!0}return rs&&(rs.slots[this.id]=ru),!1}getValue(){if(this.hasValue())return rs.slots[this.id]}withValue(e,t,n,r){let i={__proto__:null,[this.id]:e},a=rs;rs={parent:a,slots:i};try{return t.apply(r,n)}finally{rs=a}}static bind(e){let t=rs;return function(){let n=rs;try{return rs=t,e.apply(this,arguments)}finally{rs=n}}}static noContext(e,t,n){if(!rs)return e.apply(n,t);{let r=rs;try{return rs=null,e.apply(n,t)}finally{rs=r}}}};function rf(e){try{return e()}catch(t){}}let rd="@wry/context:Slot",rh=rf(()=>globalThis)||rf(()=>global)||Object.create(null),rp=rh,rb=rp[rd]||Array[rd]||function(e){try{Object.defineProperty(rp,rd,{value:e,enumerable:!1,writable:!1,configurable:!0})}finally{return e}}(rl()),{bind:rm,noContext:rg}=rb;function rv(){}var ry=function(){function e(e,t){void 0===e&&(e=1/0),void 0===t&&(t=rv),this.max=e,this.dispose=t,this.map=new Map,this.newest=null,this.oldest=null}return e.prototype.has=function(e){return this.map.has(e)},e.prototype.get=function(e){var t=this.getNode(e);return t&&t.value},e.prototype.getNode=function(e){var t=this.map.get(e);if(t&&t!==this.newest){var n=t.older,r=t.newer;r&&(r.older=n),n&&(n.newer=r),t.older=this.newest,t.older.newer=t,t.newer=null,this.newest=t,t===this.oldest&&(this.oldest=r)}return t},e.prototype.set=function(e,t){var n=this.getNode(e);return n?n.value=t:(n={key:e,value:t,newer:null,older:this.newest},this.newest&&(this.newest.newer=n),this.newest=n,this.oldest=this.oldest||n,this.map.set(e,n),n.value)},e.prototype.clean=function(){for(;this.oldest&&this.map.size>this.max;)this.delete(this.oldest.key)},e.prototype.delete=function(e){var t=this.map.get(e);return!!t&&(t===this.newest&&(this.newest=t.older),t===this.oldest&&(this.oldest=t.newer),t.newer&&(t.newer.older=t.older),t.older&&(t.older.newer=t.newer),this.map.delete(e),this.dispose(t.value,e),!0)},e}(),rw=new rb,r_=Object.prototype.hasOwnProperty,rE=void 0===(n=Array.from)?function(e){var t=[];return e.forEach(function(e){return t.push(e)}),t}:n;function rS(e){var t=e.unsubscribe;"function"==typeof t&&(e.unsubscribe=void 0,t())}var rk=[],rx=100;function rT(e,t){if(!e)throw Error(t||"assertion failure")}function rM(e,t){var n=e.length;return n>0&&n===t.length&&e[n-1]===t[n-1]}function rO(e){switch(e.length){case 0:throw Error("unknown value");case 1:return e[0];case 2:throw e[1]}}function rA(e){return e.slice(0)}var rL=function(){function e(t){this.fn=t,this.parents=new Set,this.childValues=new Map,this.dirtyChildren=null,this.dirty=!0,this.recomputing=!1,this.value=[],this.deps=null,++e.count}return e.prototype.peek=function(){if(1===this.value.length&&!rN(this))return rC(this),this.value[0]},e.prototype.recompute=function(e){return rT(!this.recomputing,"already recomputing"),rC(this),rN(this)?rI(this,e):rO(this.value)},e.prototype.setDirty=function(){this.dirty||(this.dirty=!0,this.value.length=0,rR(this),rS(this))},e.prototype.dispose=function(){var e=this;this.setDirty(),rH(this),rF(this,function(t,n){t.setDirty(),r$(t,e)})},e.prototype.forget=function(){this.dispose()},e.prototype.dependOn=function(e){e.add(this),this.deps||(this.deps=rk.pop()||new Set),this.deps.add(e)},e.prototype.forgetDeps=function(){var e=this;this.deps&&(rE(this.deps).forEach(function(t){return t.delete(e)}),this.deps.clear(),rk.push(this.deps),this.deps=null)},e.count=0,e}();function rC(e){var t=rw.getValue();if(t)return e.parents.add(t),t.childValues.has(e)||t.childValues.set(e,[]),rN(e)?rY(t,e):rB(t,e),t}function rI(e,t){return rH(e),rw.withValue(e,rD,[e,t]),rz(e,t)&&rP(e),rO(e.value)}function rD(e,t){e.recomputing=!0,e.value.length=0;try{e.value[0]=e.fn.apply(null,t)}catch(n){e.value[1]=n}e.recomputing=!1}function rN(e){return e.dirty||!!(e.dirtyChildren&&e.dirtyChildren.size)}function rP(e){e.dirty=!1,!rN(e)&&rj(e)}function rR(e){rF(e,rY)}function rj(e){rF(e,rB)}function rF(e,t){var n=e.parents.size;if(n)for(var r=rE(e.parents),i=0;i0&&e.childValues.forEach(function(t,n){r$(e,n)}),e.forgetDeps(),rT(null===e.dirtyChildren)}function r$(e,t){t.parents.delete(e),e.childValues.delete(t),rU(e,t)}function rz(e,t){if("function"==typeof e.subscribe)try{rS(e),e.unsubscribe=e.subscribe.apply(null,t)}catch(n){return e.setDirty(),!1}return!0}var rG={setDirty:!0,dispose:!0,forget:!0};function rW(e){var t=new Map,n=e&&e.subscribe;function r(e){var r=rw.getValue();if(r){var i=t.get(e);i||t.set(e,i=new Set),r.dependOn(i),"function"==typeof n&&(rS(i),i.unsubscribe=n(e))}}return r.dirty=function(e,n){var r=t.get(e);if(r){var i=n&&r_.call(rG,n)?n:"setDirty";rE(r).forEach(function(e){return e[i]()}),t.delete(e),rS(r)}},r}function rK(){var e=new ra("function"==typeof WeakMap);return function(){return e.lookupArray(arguments)}}var rV=rK(),rq=new Set;function rZ(e,t){void 0===t&&(t=Object.create(null));var n=new ry(t.max||65536,function(e){return e.dispose()}),r=t.keyArgs,i=t.makeCacheKey||rK(),a=function(){var a=i.apply(null,r?r.apply(null,arguments):arguments);if(void 0===a)return e.apply(null,arguments);var o=n.get(a);o||(n.set(a,o=new rL(e)),o.subscribe=t.subscribe,o.forget=function(){return n.delete(a)});var s=o.recompute(Array.prototype.slice.call(arguments));return n.set(a,o),rq.add(n),rw.hasValue()||(rq.forEach(function(e){return e.clean()}),rq.clear()),s};function o(e){var t=n.get(e);t&&t.setDirty()}function s(e){var t=n.get(e);if(t)return t.peek()}function u(e){return n.delete(e)}return Object.defineProperty(a,"size",{get:function(){return n.map.size},configurable:!1,enumerable:!1}),a.dirtyKey=o,a.dirty=function(){o(i.apply(null,arguments))},a.peekKey=s,a.peek=function(){return s(i.apply(null,arguments))},a.forgetKey=u,a.forget=function(){return u(i.apply(null,arguments))},a.makeCacheKey=i,a.getKey=r?function(){return i.apply(null,r.apply(null,arguments))}:i,Object.freeze(a)}var rX=new rb,rJ=new WeakMap;function rQ(e){var t=rJ.get(e);return t||rJ.set(e,t={vars:new Set,dep:rW()}),t}function r1(e){rQ(e).vars.forEach(function(t){return t.forgetCache(e)})}function r0(e){rQ(e).vars.forEach(function(t){return t.attachCache(e)})}function r2(e){var t=new Set,n=new Set,r=function(a){if(arguments.length>0){if(e!==a){e=a,t.forEach(function(e){rQ(e).dep.dirty(r),r3(e)});var o=Array.from(n);n.clear(),o.forEach(function(t){return t(e)})}}else{var s=rX.getValue();s&&(i(s),rQ(s).dep(r))}return e};r.onNextChange=function(e){return n.add(e),function(){n.delete(e)}};var i=r.attachCache=function(e){return t.add(e),rQ(e).vars.add(r),r};return r.forgetCache=function(e){return t.delete(e)},r}function r3(e){e.broadcastWatches&&e.broadcastWatches()}var r4=function(){function e(e){var t=e.cache,n=e.client,r=e.resolvers,i=e.fragmentMatcher;this.selectionsToResolveCache=new WeakMap,this.cache=t,n&&(this.client=n),r&&this.addResolvers(r),i&&this.setFragmentMatcher(i)}return e.prototype.addResolvers=function(e){var t=this;this.resolvers=this.resolvers||{},Array.isArray(e)?e.forEach(function(e){t.resolvers=tj(t.resolvers,e)}):this.resolvers=tj(this.resolvers,e)},e.prototype.setResolvers=function(e){this.resolvers={},this.addResolvers(e)},e.prototype.getResolvers=function(){return this.resolvers||{}},e.prototype.runResolvers=function(e){var t=e.document,n=e.remoteResult,r=e.context,i=e.variables,a=e.onlyRunForcedResolvers,o=void 0!==a&&a;return(0,en.mG)(this,void 0,void 0,function(){return(0,en.Jh)(this,function(e){return t?[2,this.resolveDocument(t,n.data,r,i,this.fragmentMatcher,o).then(function(e){return(0,en.pi)((0,en.pi)({},n),{data:e.result})})]:[2,n]})})},e.prototype.setFragmentMatcher=function(e){this.fragmentMatcher=e},e.prototype.getFragmentMatcher=function(){return this.fragmentMatcher},e.prototype.clientQuery=function(e){return tb(["client"],e)&&this.resolvers?e:null},e.prototype.serverQuery=function(e){return n$(e)},e.prototype.prepareContext=function(e){var t=this.cache;return(0,en.pi)((0,en.pi)({},e),{cache:t,getCacheKey:function(e){return t.identify(e)}})},e.prototype.addExportedVariables=function(e,t,n){return void 0===t&&(t={}),void 0===n&&(n={}),(0,en.mG)(this,void 0,void 0,function(){return(0,en.Jh)(this,function(r){return e?[2,this.resolveDocument(e,this.buildRootValueFromCache(e,t)||{},this.prepareContext(n),t).then(function(e){return(0,en.pi)((0,en.pi)({},t),e.exportedVariables)})]:[2,(0,en.pi)({},t)]})})},e.prototype.shouldForceResolvers=function(e){var t=!1;return tl(e,{Directive:{enter:function(e){if("client"===e.name.value&&e.arguments&&(t=e.arguments.some(function(e){return"always"===e.name.value&&"BooleanValue"===e.value.kind&&!0===e.value.value})))return tc}}}),t},e.prototype.buildRootValueFromCache=function(e,t){return this.cache.diff({query:nH(e),variables:t,returnPartialData:!0,optimistic:!1}).result},e.prototype.resolveDocument=function(e,t,n,r,i,a){return void 0===n&&(n={}),void 0===r&&(r={}),void 0===i&&(i=function(){return!0}),void 0===a&&(a=!1),(0,en.mG)(this,void 0,void 0,function(){var o,s,u,c,l,f,d,h,p,b,m;return(0,en.Jh)(this,function(g){return o=e8(e),s=e4(e),u=eL(s),c=this.collectSelectionsToResolve(o,u),f=(l=o.operation)?l.charAt(0).toUpperCase()+l.slice(1):"Query",d=this,h=d.cache,p=d.client,b={fragmentMap:u,context:(0,en.pi)((0,en.pi)({},n),{cache:h,client:p}),variables:r,fragmentMatcher:i,defaultOperationType:f,exportedVariables:{},selectionsToResolve:c,onlyRunForcedResolvers:a},m=!1,[2,this.resolveSelectionSet(o.selectionSet,m,t,b).then(function(e){return{result:e,exportedVariables:b.exportedVariables}})]})})},e.prototype.resolveSelectionSet=function(e,t,n,r){return(0,en.mG)(this,void 0,void 0,function(){var i,a,o,s,u,c=this;return(0,en.Jh)(this,function(l){return i=r.fragmentMap,a=r.context,o=r.variables,s=[n],u=function(e){return(0,en.mG)(c,void 0,void 0,function(){var u,c;return(0,en.Jh)(this,function(l){return(t||r.selectionsToResolve.has(e))&&td(e,o)?eQ(e)?[2,this.resolveField(e,t,n,r).then(function(t){var n;void 0!==t&&s.push(((n={})[eX(e)]=t,n))})]:(e1(e)?u=e:(u=i[e.name.value],__DEV__?(0,Q.kG)(u,"No fragment named ".concat(e.name.value)):(0,Q.kG)(u,11)),u&&u.typeCondition&&(c=u.typeCondition.name.value,r.fragmentMatcher(n,c,a)))?[2,this.resolveSelectionSet(u.selectionSet,t,n,r).then(function(e){s.push(e)})]:[2]:[2]})})},[2,Promise.all(e.selections.map(u)).then(function(){return tF(s)})]})})},e.prototype.resolveField=function(e,t,n,r){return(0,en.mG)(this,void 0,void 0,function(){var i,a,o,s,u,c,l,f,d,h=this;return(0,en.Jh)(this,function(p){return n?(i=r.variables,a=e.name.value,o=eX(e),s=a!==o,c=Promise.resolve(u=n[o]||n[a]),(!r.onlyRunForcedResolvers||this.shouldForceResolvers(e))&&(l=n.__typename||r.defaultOperationType,(f=this.resolvers&&this.resolvers[l])&&(d=f[s?a:o])&&(c=Promise.resolve(rX.withValue(this.cache,d,[n,eZ(e,i),r.context,{field:e,fragmentMap:r.fragmentMap},])))),[2,c.then(function(n){if(void 0===n&&(n=u),e.directives&&e.directives.forEach(function(e){"export"===e.name.value&&e.arguments&&e.arguments.forEach(function(e){"as"===e.name.value&&"StringValue"===e.value.kind&&(r.exportedVariables[e.value.value]=n)})}),!e.selectionSet||null==n)return n;var i,a,o=null!==(a=null===(i=e.directives)||void 0===i?void 0:i.some(function(e){return"client"===e.name.value}))&&void 0!==a&&a;return Array.isArray(n)?h.resolveSubSelectedArray(e,t||o,n,r):e.selectionSet?h.resolveSelectionSet(e.selectionSet,t||o,n,r):void 0})]):[2,null]})})},e.prototype.resolveSubSelectedArray=function(e,t,n,r){var i=this;return Promise.all(n.map(function(n){return null===n?null:Array.isArray(n)?i.resolveSubSelectedArray(e,t,n,r):e.selectionSet?i.resolveSelectionSet(e.selectionSet,t,n,r):void 0}))},e.prototype.collectSelectionsToResolve=function(e,t){var n=function(e){return!Array.isArray(e)},r=this.selectionsToResolveCache;function i(e){if(!r.has(e)){var a=new Set;r.set(e,a),tl(e,{Directive:function(e,t,r,i,o){"client"===e.name.value&&o.forEach(function(e){n(e)&&n9(e)&&a.add(e)})},FragmentSpread:function(e,r,o,s,u){var c=t[e.name.value];__DEV__?(0,Q.kG)(c,"No fragment named ".concat(e.name.value)):(0,Q.kG)(c,12);var l=i(c);l.size>0&&(u.forEach(function(e){n(e)&&n9(e)&&a.add(e)}),a.add(e),l.forEach(function(e){a.add(e)}))}})}return r.get(e)}return i(e)},e}(),r6=new(t_.mr?WeakMap:Map);function r5(e,t){var n=e[t];"function"==typeof n&&(e[t]=function(){return r6.set(e,(r6.get(e)+1)%1e15),n.apply(this,arguments)})}function r8(e){e.notifyTimeout&&(clearTimeout(e.notifyTimeout),e.notifyTimeout=void 0)}var r9=function(){function e(e,t){void 0===t&&(t=e.generateQueryId()),this.queryId=t,this.listeners=new Set,this.document=null,this.lastRequestId=1,this.subscriptions=new Set,this.stopped=!1,this.dirty=!1,this.observableQuery=null;var n=this.cache=e.cache;r6.has(n)||(r6.set(n,0),r5(n,"evict"),r5(n,"modify"),r5(n,"reset"))}return e.prototype.init=function(e){var t=e.networkStatus||nZ.I.loading;return this.variables&&this.networkStatus!==nZ.I.loading&&!(0,nm.D)(this.variables,e.variables)&&(t=nZ.I.setVariables),(0,nm.D)(e.variables,this.variables)||(this.lastDiff=void 0),Object.assign(this,{document:e.document,variables:e.variables,networkError:null,graphQLErrors:this.graphQLErrors||[],networkStatus:t}),e.observableQuery&&this.setObservableQuery(e.observableQuery),e.lastRequestId&&(this.lastRequestId=e.lastRequestId),this},e.prototype.reset=function(){r8(this),this.dirty=!1},e.prototype.getDiff=function(e){void 0===e&&(e=this.variables);var t=this.getDiffOptions(e);if(this.lastDiff&&(0,nm.D)(t,this.lastDiff.options))return this.lastDiff.diff;this.updateWatch(this.variables=e);var n=this.observableQuery;if(n&&"no-cache"===n.options.fetchPolicy)return{complete:!1};var r=this.cache.diff(t);return this.updateLastDiff(r,t),r},e.prototype.updateLastDiff=function(e,t){this.lastDiff=e?{diff:e,options:t||this.getDiffOptions()}:void 0},e.prototype.getDiffOptions=function(e){var t;return void 0===e&&(e=this.variables),{query:this.document,variables:e,returnPartialData:!0,optimistic:!0,canonizeResults:null===(t=this.observableQuery)||void 0===t?void 0:t.options.canonizeResults}},e.prototype.setDiff=function(e){var t=this,n=this.lastDiff&&this.lastDiff.diff;this.updateLastDiff(e),this.dirty||(0,nm.D)(n&&n.result,e&&e.result)||(this.dirty=!0,this.notifyTimeout||(this.notifyTimeout=setTimeout(function(){return t.notify()},0)))},e.prototype.setObservableQuery=function(e){var t=this;e!==this.observableQuery&&(this.oqListener&&this.listeners.delete(this.oqListener),this.observableQuery=e,e?(e.queryInfo=this,this.listeners.add(this.oqListener=function(){t.getDiff().fromOptimisticTransaction?e.observe():n4(e)})):delete this.oqListener)},e.prototype.notify=function(){var e=this;r8(this),this.shouldNotify()&&this.listeners.forEach(function(t){return t(e)}),this.dirty=!1},e.prototype.shouldNotify=function(){if(!this.dirty||!this.listeners.size)return!1;if((0,nZ.O)(this.networkStatus)&&this.observableQuery){var e=this.observableQuery.options.fetchPolicy;if("cache-only"!==e&&"cache-and-network"!==e)return!1}return!0},e.prototype.stop=function(){if(!this.stopped){this.stopped=!0,this.reset(),this.cancel(),this.cancel=e.prototype.cancel,this.subscriptions.forEach(function(e){return e.unsubscribe()});var t=this.observableQuery;t&&t.stopPolling()}},e.prototype.cancel=function(){},e.prototype.updateWatch=function(e){var t=this;void 0===e&&(e=this.variables);var n=this.observableQuery;if(!n||"no-cache"!==n.options.fetchPolicy){var r=(0,en.pi)((0,en.pi)({},this.getDiffOptions(e)),{watcher:this,callback:function(e){return t.setDiff(e)}});this.lastWatch&&(0,nm.D)(r,this.lastWatch)||(this.cancel(),this.cancel=this.cache.watch(this.lastWatch=r))}},e.prototype.resetLastWrite=function(){this.lastWrite=void 0},e.prototype.shouldWrite=function(e,t){var n=this.lastWrite;return!(n&&n.dmCount===r6.get(this.cache)&&(0,nm.D)(t,n.variables)&&(0,nm.D)(e.data,n.result.data))},e.prototype.markResult=function(e,t,n,r){var i=this,a=new tB,o=(0,tP.O)(e.errors)?e.errors.slice(0):[];if(this.reset(),"incremental"in e&&(0,tP.O)(e.incremental)){var s=tG(this.getDiff().result,e);e.data=s}else if("hasNext"in e&&e.hasNext){var u=this.getDiff();e.data=a.merge(u.result,e.data)}this.graphQLErrors=o,"no-cache"===n.fetchPolicy?this.updateLastDiff({result:e.data,complete:!0},this.getDiffOptions(n.variables)):0!==r&&(r7(e,n.errorPolicy)?this.cache.performTransaction(function(a){if(i.shouldWrite(e,n.variables))a.writeQuery({query:t,data:e.data,variables:n.variables,overwrite:1===r}),i.lastWrite={result:e,variables:n.variables,dmCount:r6.get(i.cache)};else if(i.lastDiff&&i.lastDiff.diff.complete){e.data=i.lastDiff.diff.result;return}var o=i.getDiffOptions(n.variables),s=a.diff(o);i.stopped||i.updateWatch(n.variables),i.updateLastDiff(s,o),s.complete&&(e.data=s.result)}):this.lastWrite=void 0)},e.prototype.markReady=function(){return this.networkError=null,this.networkStatus=nZ.I.ready},e.prototype.markError=function(e){return this.networkStatus=nZ.I.error,this.lastWrite=void 0,this.reset(),e.graphQLErrors&&(this.graphQLErrors=e.graphQLErrors),e.networkError&&(this.networkError=e.networkError),e},e}();function r7(e,t){void 0===t&&(t="none");var n="ignore"===t||"all"===t,r=!nO(e);return!r&&n&&e.data&&(r=!0),r}var ie=Object.prototype.hasOwnProperty,it=function(){function e(e){var t=e.cache,n=e.link,r=e.defaultOptions,i=e.queryDeduplication,a=void 0!==i&&i,o=e.onBroadcast,s=e.ssrMode,u=void 0!==s&&s,c=e.clientAwareness,l=void 0===c?{}:c,f=e.localState,d=e.assumeImmutableResults;this.clientAwareness={},this.queries=new Map,this.fetchCancelFns=new Map,this.transformCache=new(t_.mr?WeakMap:Map),this.queryIdCounter=1,this.requestIdCounter=1,this.mutationIdCounter=1,this.inFlightLinkObservables=new Map,this.cache=t,this.link=n,this.defaultOptions=r||Object.create(null),this.queryDeduplication=a,this.clientAwareness=l,this.localState=f||new r4({cache:t}),this.ssrMode=u,this.assumeImmutableResults=!!d,(this.onBroadcast=o)&&(this.mutationStore=Object.create(null))}return e.prototype.stop=function(){var e=this;this.queries.forEach(function(t,n){e.stopQueryNoBroadcast(n)}),this.cancelPendingFetches(__DEV__?new Q.ej("QueryManager stopped while query was in flight"):new Q.ej(14))},e.prototype.cancelPendingFetches=function(e){this.fetchCancelFns.forEach(function(t){return t(e)}),this.fetchCancelFns.clear()},e.prototype.mutate=function(e){var t,n,r=e.mutation,i=e.variables,a=e.optimisticResponse,o=e.updateQueries,s=e.refetchQueries,u=void 0===s?[]:s,c=e.awaitRefetchQueries,l=void 0!==c&&c,f=e.update,d=e.onQueryUpdated,h=e.fetchPolicy,p=void 0===h?(null===(t=this.defaultOptions.mutate)||void 0===t?void 0:t.fetchPolicy)||"network-only":h,b=e.errorPolicy,m=void 0===b?(null===(n=this.defaultOptions.mutate)||void 0===n?void 0:n.errorPolicy)||"none":b,g=e.keepRootFields,v=e.context;return(0,en.mG)(this,void 0,void 0,function(){var e,t,n,s,c,h;return(0,en.Jh)(this,function(b){switch(b.label){case 0:if(__DEV__?(0,Q.kG)(r,"mutation option is required. You must specify your GraphQL document in the mutation option."):(0,Q.kG)(r,15),__DEV__?(0,Q.kG)("network-only"===p||"no-cache"===p,"Mutations support only 'network-only' or 'no-cache' fetchPolicy strings. The default `network-only` behavior automatically writes mutation results to the cache. Passing `no-cache` skips the cache write."):(0,Q.kG)("network-only"===p||"no-cache"===p,16),e=this.generateMutationId(),n=(t=this.transform(r)).document,s=t.hasClientExports,r=this.cache.transformForLink(n),i=this.getVariables(r,i),!s)return[3,2];return[4,this.localState.addExportedVariables(r,i,v)];case 1:i=b.sent(),b.label=2;case 2:return c=this.mutationStore&&(this.mutationStore[e]={mutation:r,variables:i,loading:!0,error:null}),a&&this.markMutationOptimistic(a,{mutationId:e,document:r,variables:i,fetchPolicy:p,errorPolicy:m,context:v,updateQueries:o,update:f,keepRootFields:g}),this.broadcastQueries(),h=this,[2,new Promise(function(t,n){return nM(h.getObservableFromLink(r,(0,en.pi)((0,en.pi)({},v),{optimisticResponse:a}),i,!1),function(t){if(nO(t)&&"none"===m)throw new tN.cA({graphQLErrors:nA(t)});c&&(c.loading=!1,c.error=null);var n=(0,en.pi)({},t);return"function"==typeof u&&(u=u(n)),"ignore"===m&&nO(n)&&delete n.errors,h.markMutationResult({mutationId:e,result:n,document:r,variables:i,fetchPolicy:p,errorPolicy:m,context:v,update:f,updateQueries:o,awaitRefetchQueries:l,refetchQueries:u,removeOptimistic:a?e:void 0,onQueryUpdated:d,keepRootFields:g})}).subscribe({next:function(e){h.broadcastQueries(),"hasNext"in e&&!1!==e.hasNext||t(e)},error:function(t){c&&(c.loading=!1,c.error=t),a&&h.cache.removeOptimistic(e),h.broadcastQueries(),n(t instanceof tN.cA?t:new tN.cA({networkError:t}))}})})]}})})},e.prototype.markMutationResult=function(e,t){var n=this;void 0===t&&(t=this.cache);var r=e.result,i=[],a="no-cache"===e.fetchPolicy;if(!a&&r7(r,e.errorPolicy)){if(tU(r)||i.push({result:r.data,dataId:"ROOT_MUTATION",query:e.document,variables:e.variables}),tU(r)&&(0,tP.O)(r.incremental)){var o=t.diff({id:"ROOT_MUTATION",query:this.transform(e.document).asQuery,variables:e.variables,optimistic:!1,returnPartialData:!0}),s=void 0;o.result&&(s=tG(o.result,r)),void 0!==s&&(r.data=s,i.push({result:s,dataId:"ROOT_MUTATION",query:e.document,variables:e.variables}))}var u=e.updateQueries;u&&this.queries.forEach(function(e,a){var o=e.observableQuery,s=o&&o.queryName;if(s&&ie.call(u,s)){var c,l=u[s],f=n.queries.get(a),d=f.document,h=f.variables,p=t.diff({query:d,variables:h,returnPartialData:!0,optimistic:!1}),b=p.result;if(p.complete&&b){var m=l(b,{mutationResult:r,queryName:d&&e3(d)||void 0,queryVariables:h});m&&i.push({result:m,dataId:"ROOT_QUERY",query:d,variables:h})}}})}if(i.length>0||e.refetchQueries||e.update||e.onQueryUpdated||e.removeOptimistic){var c=[];if(this.refetchQueries({updateCache:function(t){a||i.forEach(function(e){return t.write(e)});var o=e.update,s=!t$(r)||tU(r)&&!r.hasNext;if(o){if(!a){var u=t.diff({id:"ROOT_MUTATION",query:n.transform(e.document).asQuery,variables:e.variables,optimistic:!1,returnPartialData:!0});u.complete&&("incremental"in(r=(0,en.pi)((0,en.pi)({},r),{data:u.result}))&&delete r.incremental,"hasNext"in r&&delete r.hasNext)}s&&o(t,r,{context:e.context,variables:e.variables})}a||e.keepRootFields||!s||t.modify({id:"ROOT_MUTATION",fields:function(e,t){var n=t.fieldName,r=t.DELETE;return"__typename"===n?e:r}})},include:e.refetchQueries,optimistic:!1,removeOptimistic:e.removeOptimistic,onQueryUpdated:e.onQueryUpdated||null}).forEach(function(e){return c.push(e)}),e.awaitRefetchQueries||e.onQueryUpdated)return Promise.all(c).then(function(){return r})}return Promise.resolve(r)},e.prototype.markMutationOptimistic=function(e,t){var n=this,r="function"==typeof e?e(t.variables):e;return this.cache.recordOptimisticTransaction(function(e){try{n.markMutationResult((0,en.pi)((0,en.pi)({},t),{result:{data:r}}),e)}catch(i){__DEV__&&Q.kG.error(i)}},t.mutationId)},e.prototype.fetchQuery=function(e,t,n){return this.fetchQueryObservable(e,t,n).promise},e.prototype.getQueryStore=function(){var e=Object.create(null);return this.queries.forEach(function(t,n){e[n]={variables:t.variables,networkStatus:t.networkStatus,networkError:t.networkError,graphQLErrors:t.graphQLErrors}}),e},e.prototype.resetErrors=function(e){var t=this.queries.get(e);t&&(t.networkError=void 0,t.graphQLErrors=[])},e.prototype.transform=function(e){var t=this.transformCache;if(!t.has(e)){var n=this.cache.transformDocument(e),r=nY(n),i=this.localState.clientQuery(n),a=r&&this.localState.serverQuery(r),o={document:n,hasClientExports:tm(n),hasForcedResolvers:this.localState.shouldForceResolvers(n),clientQuery:i,serverQuery:a,defaultVars:e9(e2(n)),asQuery:(0,en.pi)((0,en.pi)({},n),{definitions:n.definitions.map(function(e){return"OperationDefinition"===e.kind&&"query"!==e.operation?(0,en.pi)((0,en.pi)({},e),{operation:"query"}):e})})},s=function(e){e&&!t.has(e)&&t.set(e,o)};s(e),s(n),s(i),s(a)}return t.get(e)},e.prototype.getVariables=function(e,t){return(0,en.pi)((0,en.pi)({},this.transform(e).defaultVars),t)},e.prototype.watchQuery=function(e){void 0===(e=(0,en.pi)((0,en.pi)({},e),{variables:this.getVariables(e.query,e.variables)})).notifyOnNetworkStatusChange&&(e.notifyOnNetworkStatusChange=!1);var t=new r9(this),n=new n3({queryManager:this,queryInfo:t,options:e});return this.queries.set(n.queryId,t),t.init({document:n.query,observableQuery:n,variables:n.variables}),n},e.prototype.query=function(e,t){var n=this;return void 0===t&&(t=this.generateQueryId()),__DEV__?(0,Q.kG)(e.query,"query option is required. You must specify your GraphQL document in the query option."):(0,Q.kG)(e.query,17),__DEV__?(0,Q.kG)("Document"===e.query.kind,'You must wrap the query string in a "gql" tag.'):(0,Q.kG)("Document"===e.query.kind,18),__DEV__?(0,Q.kG)(!e.returnPartialData,"returnPartialData option only supported on watchQuery."):(0,Q.kG)(!e.returnPartialData,19),__DEV__?(0,Q.kG)(!e.pollInterval,"pollInterval option only supported on watchQuery."):(0,Q.kG)(!e.pollInterval,20),this.fetchQuery(t,e).finally(function(){return n.stopQuery(t)})},e.prototype.generateQueryId=function(){return String(this.queryIdCounter++)},e.prototype.generateRequestId=function(){return this.requestIdCounter++},e.prototype.generateMutationId=function(){return String(this.mutationIdCounter++)},e.prototype.stopQueryInStore=function(e){this.stopQueryInStoreNoBroadcast(e),this.broadcastQueries()},e.prototype.stopQueryInStoreNoBroadcast=function(e){var t=this.queries.get(e);t&&t.stop()},e.prototype.clearStore=function(e){return void 0===e&&(e={discardWatches:!0}),this.cancelPendingFetches(__DEV__?new Q.ej("Store reset while query was in flight (not completed in link chain)"):new Q.ej(21)),this.queries.forEach(function(e){e.observableQuery?e.networkStatus=nZ.I.loading:e.stop()}),this.mutationStore&&(this.mutationStore=Object.create(null)),this.cache.reset(e)},e.prototype.getObservableQueries=function(e){var t=this;void 0===e&&(e="active");var n=new Map,r=new Map,i=new Set;return Array.isArray(e)&&e.forEach(function(e){"string"==typeof e?r.set(e,!1):eN(e)?r.set(t.transform(e).document,!1):(0,eO.s)(e)&&e.query&&i.add(e)}),this.queries.forEach(function(t,i){var a=t.observableQuery,o=t.document;if(a){if("all"===e){n.set(i,a);return}var s=a.queryName;if("standby"===a.options.fetchPolicy||"active"===e&&!a.hasObservers())return;("active"===e||s&&r.has(s)||o&&r.has(o))&&(n.set(i,a),s&&r.set(s,!0),o&&r.set(o,!0))}}),i.size&&i.forEach(function(e){var r=nG("legacyOneTimeQuery"),i=t.getQuery(r).init({document:e.query,variables:e.variables}),a=new n3({queryManager:t,queryInfo:i,options:(0,en.pi)((0,en.pi)({},e),{fetchPolicy:"network-only"})});(0,Q.kG)(a.queryId===r),i.setObservableQuery(a),n.set(r,a)}),__DEV__&&r.size&&r.forEach(function(e,t){!e&&__DEV__&&Q.kG.warn("Unknown query ".concat("string"==typeof t?"named ":"").concat(JSON.stringify(t,null,2)," requested in refetchQueries options.include array"))}),n},e.prototype.reFetchObservableQueries=function(e){var t=this;void 0===e&&(e=!1);var n=[];return this.getObservableQueries(e?"all":"active").forEach(function(r,i){var a=r.options.fetchPolicy;r.resetLastResults(),(e||"standby"!==a&&"cache-only"!==a)&&n.push(r.refetch()),t.getQuery(i).setDiff(null)}),this.broadcastQueries(),Promise.all(n)},e.prototype.setObservableQuery=function(e){this.getQuery(e.queryId).setObservableQuery(e)},e.prototype.startGraphQLSubscription=function(e){var t=this,n=e.query,r=e.fetchPolicy,i=e.errorPolicy,a=e.variables,o=e.context,s=void 0===o?{}:o;n=this.transform(n).document,a=this.getVariables(n,a);var u=function(e){return t.getObservableFromLink(n,s,e).map(function(a){"no-cache"!==r&&(r7(a,i)&&t.cache.write({query:n,result:a.data,dataId:"ROOT_SUBSCRIPTION",variables:e}),t.broadcastQueries());var o=nO(a),s=(0,tN.ls)(a);if(o||s){var u={};throw o&&(u.graphQLErrors=a.errors),s&&(u.protocolErrors=a.extensions[tN.YG]),new tN.cA(u)}return a})};if(this.transform(n).hasClientExports){var c=this.localState.addExportedVariables(n,a,s).then(u);return new eT(function(e){var t=null;return c.then(function(n){return t=n.subscribe(e)},e.error),function(){return t&&t.unsubscribe()}})}return u(a)},e.prototype.stopQuery=function(e){this.stopQueryNoBroadcast(e),this.broadcastQueries()},e.prototype.stopQueryNoBroadcast=function(e){this.stopQueryInStoreNoBroadcast(e),this.removeQuery(e)},e.prototype.removeQuery=function(e){this.fetchCancelFns.delete(e),this.queries.has(e)&&(this.getQuery(e).stop(),this.queries.delete(e))},e.prototype.broadcastQueries=function(){this.onBroadcast&&this.onBroadcast(),this.queries.forEach(function(e){return e.notify()})},e.prototype.getLocalState=function(){return this.localState},e.prototype.getObservableFromLink=function(e,t,n,r){var i,a,o=this;void 0===r&&(r=null!==(i=null==t?void 0:t.queryDeduplication)&&void 0!==i?i:this.queryDeduplication);var s=this.transform(e).serverQuery;if(s){var u=this,c=u.inFlightLinkObservables,l=u.link,f={query:s,variables:n,operationName:e3(s)||void 0,context:this.prepareContext((0,en.pi)((0,en.pi)({},t),{forceFetch:!r}))};if(t=f.context,r){var d=c.get(s)||new Map;c.set(s,d);var h=nx(n);if(!(a=d.get(h))){var p=new nq([np(l,f)]);d.set(h,a=p),p.beforeNext(function(){d.delete(h)&&d.size<1&&c.delete(s)})}}else a=new nq([np(l,f)])}else a=new nq([eT.of({data:{}})]),t=this.prepareContext(t);var b=this.transform(e).clientQuery;return b&&(a=nM(a,function(e){return o.localState.runResolvers({document:b,remoteResult:e,context:t,variables:n})})),a},e.prototype.getResultsFromLink=function(e,t,n){var r=e.lastRequestId=this.generateRequestId(),i=this.cache.transformForLink(this.transform(e.document).document);return nM(this.getObservableFromLink(i,n.context,n.variables),function(a){var o=nA(a),s=o.length>0;if(r>=e.lastRequestId){if(s&&"none"===n.errorPolicy)throw e.markError(new tN.cA({graphQLErrors:o}));e.markResult(a,i,n,t),e.markReady()}var u={data:a.data,loading:!1,networkStatus:nZ.I.ready};return s&&"ignore"!==n.errorPolicy&&(u.errors=o,u.networkStatus=nZ.I.error),u},function(t){var n=(0,tN.MS)(t)?t:new tN.cA({networkError:t});throw r>=e.lastRequestId&&e.markError(n),n})},e.prototype.fetchQueryObservable=function(e,t,n){return this.fetchConcastWithInfo(e,t,n).concast},e.prototype.fetchConcastWithInfo=function(e,t,n){var r,i,a=this;void 0===n&&(n=nZ.I.loading);var o=this.transform(t.query).document,s=this.getVariables(o,t.variables),u=this.getQuery(e),c=this.defaultOptions.watchQuery,l=t.fetchPolicy,f=void 0===l?c&&c.fetchPolicy||"cache-first":l,d=t.errorPolicy,h=void 0===d?c&&c.errorPolicy||"none":d,p=t.returnPartialData,b=void 0!==p&&p,m=t.notifyOnNetworkStatusChange,g=void 0!==m&&m,v=t.context,y=void 0===v?{}:v,w=Object.assign({},t,{query:o,variables:s,fetchPolicy:f,errorPolicy:h,returnPartialData:b,notifyOnNetworkStatusChange:g,context:y}),_=function(e){w.variables=e;var r=a.fetchQueryByPolicy(u,w,n);return"standby"!==w.fetchPolicy&&r.sources.length>0&&u.observableQuery&&u.observableQuery.applyNextFetchPolicy("after-fetch",t),r},E=function(){return a.fetchCancelFns.delete(e)};if(this.fetchCancelFns.set(e,function(e){E(),setTimeout(function(){return r.cancel(e)})}),this.transform(w.query).hasClientExports)r=new nq(this.localState.addExportedVariables(w.query,w.variables,w.context).then(_).then(function(e){return e.sources})),i=!0;else{var S=_(w.variables);i=S.fromLink,r=new nq(S.sources)}return r.promise.then(E,E),{concast:r,fromLink:i}},e.prototype.refetchQueries=function(e){var t=this,n=e.updateCache,r=e.include,i=e.optimistic,a=void 0!==i&&i,o=e.removeOptimistic,s=void 0===o?a?nG("refetchQueries"):void 0:o,u=e.onQueryUpdated,c=new Map;r&&this.getObservableQueries(r).forEach(function(e,n){c.set(n,{oq:e,lastDiff:t.getQuery(n).getDiff()})});var l=new Map;return n&&this.cache.batch({update:n,optimistic:a&&s||!1,removeOptimistic:s,onWatchUpdated:function(e,t,n){var r=e.watcher instanceof r9&&e.watcher.observableQuery;if(r){if(u){c.delete(r.queryId);var i=u(r,t,n);return!0===i&&(i=r.refetch()),!1!==i&&l.set(r,i),i}null!==u&&c.set(r.queryId,{oq:r,lastDiff:n,diff:t})}}}),c.size&&c.forEach(function(e,n){var r,i=e.oq,a=e.lastDiff,o=e.diff;if(u){if(!o){var s=i.queryInfo;s.reset(),o=s.getDiff()}r=u(i,o,a)}u&&!0!==r||(r=i.refetch()),!1!==r&&l.set(i,r),n.indexOf("legacyOneTimeQuery")>=0&&t.stopQueryNoBroadcast(n)}),s&&this.cache.removeOptimistic(s),l},e.prototype.fetchQueryByPolicy=function(e,t,n){var r=this,i=t.query,a=t.variables,o=t.fetchPolicy,s=t.refetchWritePolicy,u=t.errorPolicy,c=t.returnPartialData,l=t.context,f=t.notifyOnNetworkStatusChange,d=e.networkStatus;e.init({document:this.transform(i).document,variables:a,networkStatus:n});var h=function(){return e.getDiff(a)},p=function(t,n){void 0===n&&(n=e.networkStatus||nZ.I.loading);var o=t.result;!__DEV__||c||(0,nm.D)(o,{})||n5(t.missing);var s=function(e){return eT.of((0,en.pi)({data:e,loading:(0,nZ.O)(n),networkStatus:n},t.complete?null:{partial:!0}))};return o&&r.transform(i).hasForcedResolvers?r.localState.runResolvers({document:i,remoteResult:{data:o},context:l,variables:a,onlyRunForcedResolvers:!0}).then(function(e){return s(e.data||void 0)}):"none"===u&&n===nZ.I.refetch&&Array.isArray(t.missing)?s(void 0):s(o)},b="no-cache"===o?0:n===nZ.I.refetch&&"merge"!==s?1:2,m=function(){return r.getResultsFromLink(e,b,{variables:a,context:l,fetchPolicy:o,errorPolicy:u})},g=f&&"number"==typeof d&&d!==n&&(0,nZ.O)(n);switch(o){default:case"cache-first":var v=h();if(v.complete)return{fromLink:!1,sources:[p(v,e.markReady())]};if(c||g)return{fromLink:!0,sources:[p(v),m()]};return{fromLink:!0,sources:[m()]};case"cache-and-network":var v=h();if(v.complete||c||g)return{fromLink:!0,sources:[p(v),m()]};return{fromLink:!0,sources:[m()]};case"cache-only":return{fromLink:!1,sources:[p(h(),e.markReady())]};case"network-only":if(g)return{fromLink:!0,sources:[p(h()),m()]};return{fromLink:!0,sources:[m()]};case"no-cache":if(g)return{fromLink:!0,sources:[p(e.getDiff()),m(),]};return{fromLink:!0,sources:[m()]};case"standby":return{fromLink:!1,sources:[]}}},e.prototype.getQuery=function(e){return e&&!this.queries.has(e)&&this.queries.set(e,new r9(this,e)),this.queries.get(e)},e.prototype.prepareContext=function(e){void 0===e&&(e={});var t=this.localState.prepareContext(e);return(0,en.pi)((0,en.pi)({},t),{clientAwareness:this.clientAwareness})},e}(),ir=__webpack_require__(14012),ii=!1,ia=function(){function e(e){var t=this;this.resetStoreCallbacks=[],this.clearStoreCallbacks=[];var n=e.uri,r=e.credentials,i=e.headers,a=e.cache,o=e.ssrMode,s=void 0!==o&&o,u=e.ssrForceFetchDelay,c=void 0===u?0:u,l=e.connectToDevTools,f=void 0===l?"object"==typeof window&&!window.__APOLLO_CLIENT__&&__DEV__:l,d=e.queryDeduplication,h=void 0===d||d,p=e.defaultOptions,b=e.assumeImmutableResults,m=void 0!==b&&b,g=e.resolvers,v=e.typeDefs,y=e.fragmentMatcher,w=e.name,_=e.version,E=e.link;if(E||(E=n?new nh({uri:n,credentials:r,headers:i}):ta.empty()),!a)throw __DEV__?new Q.ej("To initialize Apollo Client, you must specify a 'cache' property in the options object. \nFor more information, please visit: https://go.apollo.dev/c/docs"):new Q.ej(9);if(this.link=E,this.cache=a,this.disableNetworkFetches=s||c>0,this.queryDeduplication=h,this.defaultOptions=p||Object.create(null),this.typeDefs=v,c&&setTimeout(function(){return t.disableNetworkFetches=!1},c),this.watchQuery=this.watchQuery.bind(this),this.query=this.query.bind(this),this.mutate=this.mutate.bind(this),this.resetStore=this.resetStore.bind(this),this.reFetchObservableQueries=this.reFetchObservableQueries.bind(this),f&&"object"==typeof window&&(window.__APOLLO_CLIENT__=this),!ii&&f&&__DEV__&&(ii=!0,"undefined"!=typeof window&&window.document&&window.top===window.self&&!window.__APOLLO_DEVTOOLS_GLOBAL_HOOK__)){var S=window.navigator,k=S&&S.userAgent,x=void 0;"string"==typeof k&&(k.indexOf("Chrome/")>-1?x="https://chrome.google.com/webstore/detail/apollo-client-developer-t/jdkknkkbebbapilgoeccciglkfbmbnfm":k.indexOf("Firefox/")>-1&&(x="https://addons.mozilla.org/en-US/firefox/addon/apollo-developer-tools/")),x&&__DEV__&&Q.kG.log("Download the Apollo DevTools for a better development experience: "+x)}this.version=nb,this.localState=new r4({cache:a,client:this,resolvers:g,fragmentMatcher:y}),this.queryManager=new it({cache:this.cache,link:this.link,defaultOptions:this.defaultOptions,queryDeduplication:h,ssrMode:s,clientAwareness:{name:w,version:_},localState:this.localState,assumeImmutableResults:m,onBroadcast:f?function(){t.devToolsHookCb&&t.devToolsHookCb({action:{},state:{queries:t.queryManager.getQueryStore(),mutations:t.queryManager.mutationStore||{}},dataWithOptimisticResults:t.cache.extract(!0)})}:void 0})}return e.prototype.stop=function(){this.queryManager.stop()},e.prototype.watchQuery=function(e){return this.defaultOptions.watchQuery&&(e=(0,ir.J)(this.defaultOptions.watchQuery,e)),this.disableNetworkFetches&&("network-only"===e.fetchPolicy||"cache-and-network"===e.fetchPolicy)&&(e=(0,en.pi)((0,en.pi)({},e),{fetchPolicy:"cache-first"})),this.queryManager.watchQuery(e)},e.prototype.query=function(e){return this.defaultOptions.query&&(e=(0,ir.J)(this.defaultOptions.query,e)),__DEV__?(0,Q.kG)("cache-and-network"!==e.fetchPolicy,"The cache-and-network fetchPolicy does not work with client.query, because client.query can only return a single result. Please use client.watchQuery to receive multiple results from the cache and the network, or consider using a different fetchPolicy, such as cache-first or network-only."):(0,Q.kG)("cache-and-network"!==e.fetchPolicy,10),this.disableNetworkFetches&&"network-only"===e.fetchPolicy&&(e=(0,en.pi)((0,en.pi)({},e),{fetchPolicy:"cache-first"})),this.queryManager.query(e)},e.prototype.mutate=function(e){return this.defaultOptions.mutate&&(e=(0,ir.J)(this.defaultOptions.mutate,e)),this.queryManager.mutate(e)},e.prototype.subscribe=function(e){return this.queryManager.startGraphQLSubscription(e)},e.prototype.readQuery=function(e,t){return void 0===t&&(t=!1),this.cache.readQuery(e,t)},e.prototype.readFragment=function(e,t){return void 0===t&&(t=!1),this.cache.readFragment(e,t)},e.prototype.writeQuery=function(e){var t=this.cache.writeQuery(e);return!1!==e.broadcast&&this.queryManager.broadcastQueries(),t},e.prototype.writeFragment=function(e){var t=this.cache.writeFragment(e);return!1!==e.broadcast&&this.queryManager.broadcastQueries(),t},e.prototype.__actionHookForDevTools=function(e){this.devToolsHookCb=e},e.prototype.__requestRaw=function(e){return np(this.link,e)},e.prototype.resetStore=function(){var e=this;return Promise.resolve().then(function(){return e.queryManager.clearStore({discardWatches:!1})}).then(function(){return Promise.all(e.resetStoreCallbacks.map(function(e){return e()}))}).then(function(){return e.reFetchObservableQueries()})},e.prototype.clearStore=function(){var e=this;return Promise.resolve().then(function(){return e.queryManager.clearStore({discardWatches:!0})}).then(function(){return Promise.all(e.clearStoreCallbacks.map(function(e){return e()}))})},e.prototype.onResetStore=function(e){var t=this;return this.resetStoreCallbacks.push(e),function(){t.resetStoreCallbacks=t.resetStoreCallbacks.filter(function(t){return t!==e})}},e.prototype.onClearStore=function(e){var t=this;return this.clearStoreCallbacks.push(e),function(){t.clearStoreCallbacks=t.clearStoreCallbacks.filter(function(t){return t!==e})}},e.prototype.reFetchObservableQueries=function(e){return this.queryManager.reFetchObservableQueries(e)},e.prototype.refetchQueries=function(e){var t=this.queryManager.refetchQueries(e),n=[],r=[];t.forEach(function(e,t){n.push(t),r.push(e)});var i=Promise.all(r);return i.queries=n,i.results=r,i.catch(function(e){__DEV__&&Q.kG.debug("In client.refetchQueries, Promise.all promise rejected with error ".concat(e))}),i},e.prototype.getObservableQueries=function(e){return void 0===e&&(e="active"),this.queryManager.getObservableQueries(e)},e.prototype.extract=function(e){return this.cache.extract(e)},e.prototype.restore=function(e){return this.cache.restore(e)},e.prototype.addResolvers=function(e){this.localState.addResolvers(e)},e.prototype.setResolvers=function(e){this.localState.setResolvers(e)},e.prototype.getResolvers=function(){return this.localState.getResolvers()},e.prototype.setLocalStateFragmentMatcher=function(e){this.localState.setFragmentMatcher(e)},e.prototype.setLink=function(e){this.link=this.queryManager.link=e},e}(),io=function(){function e(){this.getFragmentDoc=rZ(eA)}return e.prototype.batch=function(e){var t,n=this,r="string"==typeof e.optimistic?e.optimistic:!1===e.optimistic?null:void 0;return this.performTransaction(function(){return t=e.update(n)},r),t},e.prototype.recordOptimisticTransaction=function(e,t){this.performTransaction(e,t)},e.prototype.transformDocument=function(e){return e},e.prototype.transformForLink=function(e){return e},e.prototype.identify=function(e){},e.prototype.gc=function(){return[]},e.prototype.modify=function(e){return!1},e.prototype.readQuery=function(e,t){return void 0===t&&(t=!!e.optimistic),this.read((0,en.pi)((0,en.pi)({},e),{rootId:e.id||"ROOT_QUERY",optimistic:t}))},e.prototype.readFragment=function(e,t){return void 0===t&&(t=!!e.optimistic),this.read((0,en.pi)((0,en.pi)({},e),{query:this.getFragmentDoc(e.fragment,e.fragmentName),rootId:e.id,optimistic:t}))},e.prototype.writeQuery=function(e){var t=e.id,n=e.data,r=(0,en._T)(e,["id","data"]);return this.write(Object.assign(r,{dataId:t||"ROOT_QUERY",result:n}))},e.prototype.writeFragment=function(e){var t=e.id,n=e.data,r=e.fragment,i=e.fragmentName,a=(0,en._T)(e,["id","data","fragment","fragmentName"]);return this.write(Object.assign(a,{query:this.getFragmentDoc(r,i),dataId:t,result:n}))},e.prototype.updateQuery=function(e,t){return this.batch({update:function(n){var r=n.readQuery(e),i=t(r);return null==i?r:(n.writeQuery((0,en.pi)((0,en.pi)({},e),{data:i})),i)}})},e.prototype.updateFragment=function(e,t){return this.batch({update:function(n){var r=n.readFragment(e),i=t(r);return null==i?r:(n.writeFragment((0,en.pi)((0,en.pi)({},e),{data:i})),i)}})},e}(),is=function(e){function t(n,r,i,a){var o,s=e.call(this,n)||this;if(s.message=n,s.path=r,s.query=i,s.variables=a,Array.isArray(s.path)){s.missing=s.message;for(var u=s.path.length-1;u>=0;--u)s.missing=((o={})[s.path[u]]=s.missing,o)}else s.missing=s.path;return s.__proto__=t.prototype,s}return(0,en.ZT)(t,e),t}(Error),iu=__webpack_require__(10542),ic=Object.prototype.hasOwnProperty;function il(e){return null==e}function id(e,t){var n=e.__typename,r=e.id,i=e._id;if("string"==typeof n&&(t&&(t.keyObject=il(r)?il(i)?void 0:{_id:i}:{id:r}),il(r)&&!il(i)&&(r=i),!il(r)))return"".concat(n,":").concat("number"==typeof r||"string"==typeof r?r:JSON.stringify(r))}var ih={dataIdFromObject:id,addTypename:!0,resultCaching:!0,canonizeResults:!1};function ip(e){return(0,n1.o)(ih,e)}function ib(e){var t=e.canonizeResults;return void 0===t?ih.canonizeResults:t}function im(e,t){return eD(t)?e.get(t.__ref,"__typename"):t&&t.__typename}var ig=/^[_a-z][_0-9a-z]*/i;function iv(e){var t=e.match(ig);return t?t[0]:e}function iy(e,t,n){return!!(0,eO.s)(t)&&((0,tP.k)(t)?t.every(function(t){return iy(e,t,n)}):e.selections.every(function(e){if(eQ(e)&&td(e,n)){var r=eX(e);return ic.call(t,r)&&(!e.selectionSet||iy(e.selectionSet,t[r],n))}return!0}))}function iw(e){return(0,eO.s)(e)&&!eD(e)&&!(0,tP.k)(e)}function i_(){return new tB}function iE(e,t){var n=eL(e4(e));return{fragmentMap:n,lookupFragment:function(e){var r=n[e];return!r&&t&&(r=t.lookup(e)),r||null}}}var iS=Object.create(null),ik=function(){return iS},ix=Object.create(null),iT=function(){function e(e,t){var n=this;this.policies=e,this.group=t,this.data=Object.create(null),this.rootIds=Object.create(null),this.refs=Object.create(null),this.getFieldValue=function(e,t){return(0,iu.J)(eD(e)?n.get(e.__ref,t):e&&e[t])},this.canRead=function(e){return eD(e)?n.has(e.__ref):"object"==typeof e},this.toReference=function(e,t){if("string"==typeof e)return eI(e);if(eD(e))return e;var r=n.policies.identify(e)[0];if(r){var i=eI(r);return t&&n.merge(r,e),i}}}return e.prototype.toObject=function(){return(0,en.pi)({},this.data)},e.prototype.has=function(e){return void 0!==this.lookup(e,!0)},e.prototype.get=function(e,t){if(this.group.depend(e,t),ic.call(this.data,e)){var n=this.data[e];if(n&&ic.call(n,t))return n[t]}return"__typename"===t&&ic.call(this.policies.rootTypenamesById,e)?this.policies.rootTypenamesById[e]:this instanceof iL?this.parent.get(e,t):void 0},e.prototype.lookup=function(e,t){return(t&&this.group.depend(e,"__exists"),ic.call(this.data,e))?this.data[e]:this instanceof iL?this.parent.lookup(e,t):this.policies.rootTypenamesById[e]?Object.create(null):void 0},e.prototype.merge=function(e,t){var n,r=this;eD(e)&&(e=e.__ref),eD(t)&&(t=t.__ref);var i="string"==typeof e?this.lookup(n=e):e,a="string"==typeof t?this.lookup(n=t):t;if(a){__DEV__?(0,Q.kG)("string"==typeof n,"store.merge expects a string ID"):(0,Q.kG)("string"==typeof n,1);var o=new tB(iI).merge(i,a);if(this.data[n]=o,o!==i&&(delete this.refs[n],this.group.caching)){var s=Object.create(null);i||(s.__exists=1),Object.keys(a).forEach(function(e){if(!i||i[e]!==o[e]){s[e]=1;var t=iv(e);t===e||r.policies.hasKeyArgs(o.__typename,t)||(s[t]=1),void 0!==o[e]||r instanceof iL||delete o[e]}}),s.__typename&&!(i&&i.__typename)&&this.policies.rootTypenamesById[n]===o.__typename&&delete s.__typename,Object.keys(s).forEach(function(e){return r.group.dirty(n,e)})}}},e.prototype.modify=function(e,t){var n=this,r=this.lookup(e);if(r){var i=Object.create(null),a=!1,o=!0,s={DELETE:iS,INVALIDATE:ix,isReference:eD,toReference:this.toReference,canRead:this.canRead,readField:function(t,r){return n.policies.readField("string"==typeof t?{fieldName:t,from:r||eI(e)}:t,{store:n})}};if(Object.keys(r).forEach(function(u){var c=iv(u),l=r[u];if(void 0!==l){var f="function"==typeof t?t:t[u]||t[c];if(f){var d=f===ik?iS:f((0,iu.J)(l),(0,en.pi)((0,en.pi)({},s),{fieldName:c,storeFieldName:u,storage:n.getStorage(e,u)}));d===ix?n.group.dirty(e,u):(d===iS&&(d=void 0),d!==l&&(i[u]=d,a=!0,l=d))}void 0!==l&&(o=!1)}}),a)return this.merge(e,i),o&&(this instanceof iL?this.data[e]=void 0:delete this.data[e],this.group.dirty(e,"__exists")),!0}return!1},e.prototype.delete=function(e,t,n){var r,i=this.lookup(e);if(i){var a=this.getFieldValue(i,"__typename"),o=t&&n?this.policies.getStoreFieldName({typename:a,fieldName:t,args:n}):t;return this.modify(e,o?((r={})[o]=ik,r):ik)}return!1},e.prototype.evict=function(e,t){var n=!1;return e.id&&(ic.call(this.data,e.id)&&(n=this.delete(e.id,e.fieldName,e.args)),this instanceof iL&&this!==t&&(n=this.parent.evict(e,t)||n),(e.fieldName||n)&&this.group.dirty(e.id,e.fieldName||"__exists")),n},e.prototype.clear=function(){this.replace(null)},e.prototype.extract=function(){var e=this,t=this.toObject(),n=[];return this.getRootIdSet().forEach(function(t){ic.call(e.policies.rootTypenamesById,t)||n.push(t)}),n.length&&(t.__META={extraRootIds:n.sort()}),t},e.prototype.replace=function(e){var t=this;if(Object.keys(this.data).forEach(function(n){e&&ic.call(e,n)||t.delete(n)}),e){var n=e.__META,r=(0,en._T)(e,["__META"]);Object.keys(r).forEach(function(e){t.merge(e,r[e])}),n&&n.extraRootIds.forEach(this.retain,this)}},e.prototype.retain=function(e){return this.rootIds[e]=(this.rootIds[e]||0)+1},e.prototype.release=function(e){if(this.rootIds[e]>0){var t=--this.rootIds[e];return t||delete this.rootIds[e],t}return 0},e.prototype.getRootIdSet=function(e){return void 0===e&&(e=new Set),Object.keys(this.rootIds).forEach(e.add,e),this instanceof iL?this.parent.getRootIdSet(e):Object.keys(this.policies.rootTypenamesById).forEach(e.add,e),e},e.prototype.gc=function(){var e=this,t=this.getRootIdSet(),n=this.toObject();t.forEach(function(r){ic.call(n,r)&&(Object.keys(e.findChildRefIds(r)).forEach(t.add,t),delete n[r])});var r=Object.keys(n);if(r.length){for(var i=this;i instanceof iL;)i=i.parent;r.forEach(function(e){return i.delete(e)})}return r},e.prototype.findChildRefIds=function(e){if(!ic.call(this.refs,e)){var t=this.refs[e]=Object.create(null),n=this.data[e];if(!n)return t;var r=new Set([n]);r.forEach(function(e){eD(e)&&(t[e.__ref]=!0),(0,eO.s)(e)&&Object.keys(e).forEach(function(t){var n=e[t];(0,eO.s)(n)&&r.add(n)})})}return this.refs[e]},e.prototype.makeCacheKey=function(){return this.group.keyMaker.lookupArray(arguments)},e}(),iM=function(){function e(e,t){void 0===t&&(t=null),this.caching=e,this.parent=t,this.d=null,this.resetCaching()}return e.prototype.resetCaching=function(){this.d=this.caching?rW():null,this.keyMaker=new n_(t_.mr)},e.prototype.depend=function(e,t){if(this.d){this.d(iO(e,t));var n=iv(t);n!==t&&this.d(iO(e,n)),this.parent&&this.parent.depend(e,t)}},e.prototype.dirty=function(e,t){this.d&&this.d.dirty(iO(e,t),"__exists"===t?"forget":"setDirty")},e}();function iO(e,t){return t+"#"+e}function iA(e,t){iD(e)&&e.group.depend(t,"__exists")}!function(e){var t=function(e){function t(t){var n=t.policies,r=t.resultCaching,i=void 0===r||r,a=t.seed,o=e.call(this,n,new iM(i))||this;return o.stump=new iC(o),o.storageTrie=new n_(t_.mr),a&&o.replace(a),o}return(0,en.ZT)(t,e),t.prototype.addLayer=function(e,t){return this.stump.addLayer(e,t)},t.prototype.removeLayer=function(){return this},t.prototype.getStorage=function(){return this.storageTrie.lookupArray(arguments)},t}(e);e.Root=t}(iT||(iT={}));var iL=function(e){function t(t,n,r,i){var a=e.call(this,n.policies,i)||this;return a.id=t,a.parent=n,a.replay=r,a.group=i,r(a),a}return(0,en.ZT)(t,e),t.prototype.addLayer=function(e,n){return new t(e,this,n,this.group)},t.prototype.removeLayer=function(e){var t=this,n=this.parent.removeLayer(e);return e===this.id?(this.group.caching&&Object.keys(this.data).forEach(function(e){var r=t.data[e],i=n.lookup(e);i?r?r!==i&&Object.keys(r).forEach(function(n){(0,nm.D)(r[n],i[n])||t.group.dirty(e,n)}):(t.group.dirty(e,"__exists"),Object.keys(i).forEach(function(n){t.group.dirty(e,n)})):t.delete(e)}),n):n===this.parent?this:n.addLayer(this.id,this.replay)},t.prototype.toObject=function(){return(0,en.pi)((0,en.pi)({},this.parent.toObject()),this.data)},t.prototype.findChildRefIds=function(t){var n=this.parent.findChildRefIds(t);return ic.call(this.data,t)?(0,en.pi)((0,en.pi)({},n),e.prototype.findChildRefIds.call(this,t)):n},t.prototype.getStorage=function(){for(var e=this.parent;e.parent;)e=e.parent;return e.getStorage.apply(e,arguments)},t}(iT),iC=function(e){function t(t){return e.call(this,"EntityStore.Stump",t,function(){},new iM(t.group.caching,t.group))||this}return(0,en.ZT)(t,e),t.prototype.removeLayer=function(){return this},t.prototype.merge=function(){return this.parent.merge.apply(this.parent,arguments)},t}(iL);function iI(e,t,n){var r=e[n],i=t[n];return(0,nm.D)(r,i)?r:i}function iD(e){return!!(e instanceof iT&&e.group.caching)}function iN(e){return[e.selectionSet,e.objectOrReference,e.context,e.context.canonizeResults,]}var iP=function(){function e(e){var t=this;this.knownResults=new(t_.mr?WeakMap:Map),this.config=(0,n1.o)(e,{addTypename:!1!==e.addTypename,canonizeResults:ib(e)}),this.canon=e.canon||new nk,this.executeSelectionSet=rZ(function(e){var n,r=e.context.canonizeResults,i=iN(e);i[3]=!r;var a=(n=t.executeSelectionSet).peek.apply(n,i);return a?r?(0,en.pi)((0,en.pi)({},a),{result:t.canon.admit(a.result)}):a:(iA(e.context.store,e.enclosingRef.__ref),t.execSelectionSetImpl(e))},{max:this.config.resultCacheMaxSize,keyArgs:iN,makeCacheKey:function(e,t,n,r){if(iD(n.store))return n.store.makeCacheKey(e,eD(t)?t.__ref:t,n.varString,r)}}),this.executeSubSelectedArray=rZ(function(e){return iA(e.context.store,e.enclosingRef.__ref),t.execSubSelectedArrayImpl(e)},{max:this.config.resultCacheMaxSize,makeCacheKey:function(e){var t=e.field,n=e.array,r=e.context;if(iD(r.store))return r.store.makeCacheKey(t,n,r.varString)}})}return e.prototype.resetCanon=function(){this.canon=new nk},e.prototype.diffQueryAgainstStore=function(e){var t,n=e.store,r=e.query,i=e.rootId,a=void 0===i?"ROOT_QUERY":i,o=e.variables,s=e.returnPartialData,u=void 0===s||s,c=e.canonizeResults,l=void 0===c?this.config.canonizeResults:c,f=this.config.cache.policies;o=(0,en.pi)((0,en.pi)({},e9(e6(r))),o);var d=eI(a),h=this.executeSelectionSet({selectionSet:e8(r).selectionSet,objectOrReference:d,enclosingRef:d,context:(0,en.pi)({store:n,query:r,policies:f,variables:o,varString:nx(o),canonizeResults:l},iE(r,this.config.fragments))});if(h.missing&&(t=[new is(iR(h.missing),h.missing,r,o)],!u))throw t[0];return{result:h.result,complete:!t,missing:t}},e.prototype.isFresh=function(e,t,n,r){if(iD(r.store)&&this.knownResults.get(e)===n){var i=this.executeSelectionSet.peek(n,t,r,this.canon.isKnown(e));if(i&&e===i.result)return!0}return!1},e.prototype.execSelectionSetImpl=function(e){var t,n=this,r=e.selectionSet,i=e.objectOrReference,a=e.enclosingRef,o=e.context;if(eD(i)&&!o.policies.rootTypenamesById[i.__ref]&&!o.store.has(i.__ref))return{result:this.canon.empty,missing:"Dangling reference to missing ".concat(i.__ref," object")};var s=o.variables,u=o.policies,c=o.store.getFieldValue(i,"__typename"),l=[],f=new tB;function d(e,n){var r;return e.missing&&(t=f.merge(t,((r={})[n]=e.missing,r))),e.result}this.config.addTypename&&"string"==typeof c&&!u.rootIdsByTypename[c]&&l.push({__typename:c});var h=new Set(r.selections);h.forEach(function(e){var r,p;if(td(e,s)){if(eQ(e)){var b=u.readField({fieldName:e.name.value,field:e,variables:o.variables,from:i},o),m=eX(e);void 0===b?nj.added(e)||(t=f.merge(t,((r={})[m]="Can't find field '".concat(e.name.value,"' on ").concat(eD(i)?i.__ref+" object":"object "+JSON.stringify(i,null,2)),r))):(0,tP.k)(b)?b=d(n.executeSubSelectedArray({field:e,array:b,enclosingRef:a,context:o}),m):e.selectionSet?null!=b&&(b=d(n.executeSelectionSet({selectionSet:e.selectionSet,objectOrReference:b,enclosingRef:eD(b)?b:a,context:o}),m)):o.canonizeResults&&(b=n.canon.pass(b)),void 0!==b&&l.push(((p={})[m]=b,p))}else{var g=eC(e,o.lookupFragment);if(!g&&e.kind===nL.h.FRAGMENT_SPREAD)throw __DEV__?new Q.ej("No fragment named ".concat(e.name.value)):new Q.ej(5);g&&u.fragmentMatches(g,c)&&g.selectionSet.selections.forEach(h.add,h)}}});var p={result:tF(l),missing:t},b=o.canonizeResults?this.canon.admit(p):(0,iu.J)(p);return b.result&&this.knownResults.set(b.result,r),b},e.prototype.execSubSelectedArrayImpl=function(e){var t,n=this,r=e.field,i=e.array,a=e.enclosingRef,o=e.context,s=new tB;function u(e,n){var r;return e.missing&&(t=s.merge(t,((r={})[n]=e.missing,r))),e.result}return r.selectionSet&&(i=i.filter(o.store.canRead)),i=i.map(function(e,t){return null===e?null:(0,tP.k)(e)?u(n.executeSubSelectedArray({field:r,array:e,enclosingRef:a,context:o}),t):r.selectionSet?u(n.executeSelectionSet({selectionSet:r.selectionSet,objectOrReference:e,enclosingRef:eD(e)?e:a,context:o}),t):(__DEV__&&ij(o.store,r,e),e)}),{result:o.canonizeResults?this.canon.admit(i):i,missing:t}},e}();function iR(e){try{JSON.stringify(e,function(e,t){if("string"==typeof t)throw t;return t})}catch(t){return t}}function ij(e,t,n){if(!t.selectionSet){var r=new Set([n]);r.forEach(function(n){(0,eO.s)(n)&&(__DEV__?(0,Q.kG)(!eD(n),"Missing selection set for object of type ".concat(im(e,n)," returned for query field ").concat(t.name.value)):(0,Q.kG)(!eD(n),6),Object.values(n).forEach(r.add,r))})}}function iF(e){var t=nG("stringifyForDisplay");return JSON.stringify(e,function(e,n){return void 0===n?t:n}).split(JSON.stringify(t)).join("")}var iY=Object.create(null);function iB(e){var t=JSON.stringify(e);return iY[t]||(iY[t]=Object.create(null))}function iU(e){var t=iB(e);return t.keyFieldsFn||(t.keyFieldsFn=function(t,n){var r=function(e,t){return n.readField(t,e)},i=n.keyObject=i$(e,function(e){var i=iW(n.storeObject,e,r);return void 0===i&&t!==n.storeObject&&ic.call(t,e[0])&&(i=iW(t,e,iG)),__DEV__?(0,Q.kG)(void 0!==i,"Missing field '".concat(e.join("."),"' while extracting keyFields from ").concat(JSON.stringify(t))):(0,Q.kG)(void 0!==i,2),i});return"".concat(n.typename,":").concat(JSON.stringify(i))})}function iH(e){var t=iB(e);return t.keyArgsFn||(t.keyArgsFn=function(t,n){var r=n.field,i=n.variables,a=n.fieldName,o=JSON.stringify(i$(e,function(e){var n=e[0],a=n.charAt(0);if("@"===a){if(r&&(0,tP.O)(r.directives)){var o=n.slice(1),s=r.directives.find(function(e){return e.name.value===o}),u=s&&eZ(s,i);return u&&iW(u,e.slice(1))}return}if("$"===a){var c=n.slice(1);if(i&&ic.call(i,c)){var l=e.slice(0);return l[0]=c,iW(i,l)}return}if(t)return iW(t,e)}));return(t||"{}"!==o)&&(a+=":"+o),a})}function i$(e,t){var n=new tB;return iz(e).reduce(function(e,r){var i,a=t(r);if(void 0!==a){for(var o=r.length-1;o>=0;--o)a=((i={})[r[o]]=a,i);e=n.merge(e,a)}return e},Object.create(null))}function iz(e){var t=iB(e);if(!t.paths){var n=t.paths=[],r=[];e.forEach(function(t,i){(0,tP.k)(t)?(iz(t).forEach(function(e){return n.push(r.concat(e))}),r.length=0):(r.push(t),(0,tP.k)(e[i+1])||(n.push(r.slice(0)),r.length=0))})}return t.paths}function iG(e,t){return e[t]}function iW(e,t,n){return n=n||iG,iK(t.reduce(function e(t,r){return(0,tP.k)(t)?t.map(function(t){return e(t,r)}):t&&n(t,r)},e))}function iK(e){return(0,eO.s)(e)?(0,tP.k)(e)?e.map(iK):i$(Object.keys(e).sort(),function(t){return iW(e,t)}):e}function iV(e){return void 0!==e.args?e.args:e.field?eZ(e.field,e.variables):null}eK.setStringify(nx);var iq=function(){},iZ=function(e,t){return t.fieldName},iX=function(e,t,n){return(0,n.mergeObjects)(e,t)},iJ=function(e,t){return t},iQ=function(){function e(e){this.config=e,this.typePolicies=Object.create(null),this.toBeAdded=Object.create(null),this.supertypeMap=new Map,this.fuzzySubtypes=new Map,this.rootIdsByTypename=Object.create(null),this.rootTypenamesById=Object.create(null),this.usingPossibleTypes=!1,this.config=(0,en.pi)({dataIdFromObject:id},e),this.cache=this.config.cache,this.setRootTypename("Query"),this.setRootTypename("Mutation"),this.setRootTypename("Subscription"),e.possibleTypes&&this.addPossibleTypes(e.possibleTypes),e.typePolicies&&this.addTypePolicies(e.typePolicies)}return e.prototype.identify=function(e,t){var n,r,i=this,a=t&&(t.typename||(null===(n=t.storeObject)||void 0===n?void 0:n.__typename))||e.__typename;if(a===this.rootTypenamesById.ROOT_QUERY)return["ROOT_QUERY"];for(var o=t&&t.storeObject||e,s=(0,en.pi)((0,en.pi)({},t),{typename:a,storeObject:o,readField:t&&t.readField||function(){var e=i0(arguments,o);return i.readField(e,{store:i.cache.data,variables:e.variables})}}),u=a&&this.getTypePolicy(a),c=u&&u.keyFn||this.config.dataIdFromObject;c;){var l=c((0,en.pi)((0,en.pi)({},e),o),s);if((0,tP.k)(l))c=iU(l);else{r=l;break}}return r=r?String(r):void 0,s.keyObject?[r,s.keyObject]:[r]},e.prototype.addTypePolicies=function(e){var t=this;Object.keys(e).forEach(function(n){var r=e[n],i=r.queryType,a=r.mutationType,o=r.subscriptionType,s=(0,en._T)(r,["queryType","mutationType","subscriptionType"]);i&&t.setRootTypename("Query",n),a&&t.setRootTypename("Mutation",n),o&&t.setRootTypename("Subscription",n),ic.call(t.toBeAdded,n)?t.toBeAdded[n].push(s):t.toBeAdded[n]=[s]})},e.prototype.updateTypePolicy=function(e,t){var n=this,r=this.getTypePolicy(e),i=t.keyFields,a=t.fields;function o(e,t){e.merge="function"==typeof t?t:!0===t?iX:!1===t?iJ:e.merge}o(r,t.merge),r.keyFn=!1===i?iq:(0,tP.k)(i)?iU(i):"function"==typeof i?i:r.keyFn,a&&Object.keys(a).forEach(function(t){var r=n.getFieldPolicy(e,t,!0),i=a[t];if("function"==typeof i)r.read=i;else{var s=i.keyArgs,u=i.read,c=i.merge;r.keyFn=!1===s?iZ:(0,tP.k)(s)?iH(s):"function"==typeof s?s:r.keyFn,"function"==typeof u&&(r.read=u),o(r,c)}r.read&&r.merge&&(r.keyFn=r.keyFn||iZ)})},e.prototype.setRootTypename=function(e,t){void 0===t&&(t=e);var n="ROOT_"+e.toUpperCase(),r=this.rootTypenamesById[n];t!==r&&(__DEV__?(0,Q.kG)(!r||r===e,"Cannot change root ".concat(e," __typename more than once")):(0,Q.kG)(!r||r===e,3),r&&delete this.rootIdsByTypename[r],this.rootIdsByTypename[t]=n,this.rootTypenamesById[n]=t)},e.prototype.addPossibleTypes=function(e){var t=this;this.usingPossibleTypes=!0,Object.keys(e).forEach(function(n){t.getSupertypeSet(n,!0),e[n].forEach(function(e){t.getSupertypeSet(e,!0).add(n);var r=e.match(ig);r&&r[0]===e||t.fuzzySubtypes.set(e,RegExp(e))})})},e.prototype.getTypePolicy=function(e){var t=this;if(!ic.call(this.typePolicies,e)){var n=this.typePolicies[e]=Object.create(null);n.fields=Object.create(null);var r=this.supertypeMap.get(e);r&&r.size&&r.forEach(function(e){var r=t.getTypePolicy(e),i=r.fields;Object.assign(n,(0,en._T)(r,["fields"])),Object.assign(n.fields,i)})}var i=this.toBeAdded[e];return i&&i.length&&i.splice(0).forEach(function(n){t.updateTypePolicy(e,n)}),this.typePolicies[e]},e.prototype.getFieldPolicy=function(e,t,n){if(e){var r=this.getTypePolicy(e).fields;return r[t]||n&&(r[t]=Object.create(null))}},e.prototype.getSupertypeSet=function(e,t){var n=this.supertypeMap.get(e);return!n&&t&&this.supertypeMap.set(e,n=new Set),n},e.prototype.fragmentMatches=function(e,t,n,r){var i=this;if(!e.typeCondition)return!0;if(!t)return!1;var a=e.typeCondition.name.value;if(t===a)return!0;if(this.usingPossibleTypes&&this.supertypeMap.has(a))for(var o=this.getSupertypeSet(t,!0),s=[o],u=function(e){var t=i.getSupertypeSet(e,!1);t&&t.size&&0>s.indexOf(t)&&s.push(t)},c=!!(n&&this.fuzzySubtypes.size),l=!1,f=0;f1?a:t}:(r=(0,en.pi)({},i),ic.call(r,"from")||(r.from=t)),__DEV__&&void 0===r.from&&__DEV__&&Q.kG.warn("Undefined 'from' passed to readField with arguments ".concat(iF(Array.from(e)))),void 0===r.variables&&(r.variables=n),r}function i2(e){return function(t,n){if((0,tP.k)(t)||(0,tP.k)(n))throw __DEV__?new Q.ej("Cannot automatically merge arrays"):new Q.ej(4);if((0,eO.s)(t)&&(0,eO.s)(n)){var r=e.getFieldValue(t,"__typename"),i=e.getFieldValue(n,"__typename");if(r&&i&&r!==i)return n;if(eD(t)&&iw(n))return e.merge(t.__ref,n),t;if(iw(t)&&eD(n))return e.merge(t,n.__ref),n;if(iw(t)&&iw(n))return(0,en.pi)((0,en.pi)({},t),n)}return n}}function i3(e,t,n){var r="".concat(t).concat(n),i=e.flavors.get(r);return i||e.flavors.set(r,i=e.clientOnly===t&&e.deferred===n?e:(0,en.pi)((0,en.pi)({},e),{clientOnly:t,deferred:n})),i}var i4=function(){function e(e,t,n){this.cache=e,this.reader=t,this.fragments=n}return e.prototype.writeToStore=function(e,t){var n=this,r=t.query,i=t.result,a=t.dataId,o=t.variables,s=t.overwrite,u=e2(r),c=i_();o=(0,en.pi)((0,en.pi)({},e9(u)),o);var l=(0,en.pi)((0,en.pi)({store:e,written:Object.create(null),merge:function(e,t){return c.merge(e,t)},variables:o,varString:nx(o)},iE(r,this.fragments)),{overwrite:!!s,incomingById:new Map,clientOnly:!1,deferred:!1,flavors:new Map}),f=this.processSelectionSet({result:i||Object.create(null),dataId:a,selectionSet:u.selectionSet,mergeTree:{map:new Map},context:l});if(!eD(f))throw __DEV__?new Q.ej("Could not identify object ".concat(JSON.stringify(i))):new Q.ej(7);return l.incomingById.forEach(function(t,r){var i=t.storeObject,a=t.mergeTree,o=t.fieldNodeSet,s=eI(r);if(a&&a.map.size){var u=n.applyMerges(a,s,i,l);if(eD(u))return;i=u}if(__DEV__&&!l.overwrite){var c=Object.create(null);o.forEach(function(e){e.selectionSet&&(c[e.name.value]=!0)});var f=function(e){return!0===c[iv(e)]},d=function(e){var t=a&&a.map.get(e);return Boolean(t&&t.info&&t.info.merge)};Object.keys(i).forEach(function(e){f(e)&&!d(e)&&at(s,i,e,l.store)})}e.merge(r,i)}),e.retain(f.__ref),f},e.prototype.processSelectionSet=function(e){var t=this,n=e.dataId,r=e.result,i=e.selectionSet,a=e.context,o=e.mergeTree,s=this.cache.policies,u=Object.create(null),c=n&&s.rootTypenamesById[n]||eJ(r,i,a.fragmentMap)||n&&a.store.get(n,"__typename");"string"==typeof c&&(u.__typename=c);var l=function(){var e=i0(arguments,u,a.variables);if(eD(e.from)){var t=a.incomingById.get(e.from.__ref);if(t){var n=s.readField((0,en.pi)((0,en.pi)({},e),{from:t.storeObject}),a);if(void 0!==n)return n}}return s.readField(e,a)},f=new Set;this.flattenFields(i,r,a,c).forEach(function(e,n){var i,a=r[eX(n)];if(f.add(n),void 0!==a){var d=s.getStoreFieldName({typename:c,fieldName:n.name.value,field:n,variables:e.variables}),h=i5(o,d),p=t.processFieldValue(a,n,n.selectionSet?i3(e,!1,!1):e,h),b=void 0;n.selectionSet&&(eD(p)||iw(p))&&(b=l("__typename",p));var m=s.getMergeFunction(c,n.name.value,b);m?h.info={field:n,typename:c,merge:m}:i7(o,d),u=e.merge(u,((i={})[d]=p,i))}else __DEV__&&!e.clientOnly&&!e.deferred&&!nj.added(n)&&!s.getReadFunction(c,n.name.value)&&__DEV__&&Q.kG.error("Missing field '".concat(eX(n),"' while writing result ").concat(JSON.stringify(r,null,2)).substring(0,1e3))});try{var d=s.identify(r,{typename:c,selectionSet:i,fragmentMap:a.fragmentMap,storeObject:u,readField:l}),h=d[0],p=d[1];n=n||h,p&&(u=a.merge(u,p))}catch(b){if(!n)throw b}if("string"==typeof n){var m=eI(n),g=a.written[n]||(a.written[n]=[]);if(g.indexOf(i)>=0||(g.push(i),this.reader&&this.reader.isFresh(r,m,i,a)))return m;var v=a.incomingById.get(n);return v?(v.storeObject=a.merge(v.storeObject,u),v.mergeTree=i8(v.mergeTree,o),f.forEach(function(e){return v.fieldNodeSet.add(e)})):a.incomingById.set(n,{storeObject:u,mergeTree:i9(o)?void 0:o,fieldNodeSet:f}),m}return u},e.prototype.processFieldValue=function(e,t,n,r){var i=this;return t.selectionSet&&null!==e?(0,tP.k)(e)?e.map(function(e,a){var o=i.processFieldValue(e,t,n,i5(r,a));return i7(r,a),o}):this.processSelectionSet({result:e,selectionSet:t.selectionSet,context:n,mergeTree:r}):__DEV__?nJ(e):e},e.prototype.flattenFields=function(e,t,n,r){void 0===r&&(r=eJ(t,e,n.fragmentMap));var i=new Map,a=this.cache.policies,o=new n_(!1);return function e(s,u){var c=o.lookup(s,u.clientOnly,u.deferred);c.visited||(c.visited=!0,s.selections.forEach(function(o){if(td(o,n.variables)){var s=u.clientOnly,c=u.deferred;if(!(s&&c)&&(0,tP.O)(o.directives)&&o.directives.forEach(function(e){var t=e.name.value;if("client"===t&&(s=!0),"defer"===t){var r=eZ(e,n.variables);r&&!1===r.if||(c=!0)}}),eQ(o)){var l=i.get(o);l&&(s=s&&l.clientOnly,c=c&&l.deferred),i.set(o,i3(n,s,c))}else{var f=eC(o,n.lookupFragment);if(!f&&o.kind===nL.h.FRAGMENT_SPREAD)throw __DEV__?new Q.ej("No fragment named ".concat(o.name.value)):new Q.ej(8);f&&a.fragmentMatches(f,r,t,n.variables)&&e(f.selectionSet,i3(n,s,c))}}}))}(e,n),i},e.prototype.applyMerges=function(e,t,n,r,i){var a=this;if(e.map.size&&!eD(n)){var o,s,u=!(0,tP.k)(n)&&(eD(t)||iw(t))?t:void 0,c=n;u&&!i&&(i=[eD(u)?u.__ref:u]);var l=function(e,t){return(0,tP.k)(e)?"number"==typeof t?e[t]:void 0:r.store.getFieldValue(e,String(t))};e.map.forEach(function(e,t){var n=l(u,t),o=l(c,t);if(void 0!==o){i&&i.push(t);var f=a.applyMerges(e,n,o,r,i);f!==o&&(s=s||new Map).set(t,f),i&&(0,Q.kG)(i.pop()===t)}}),s&&(n=(0,tP.k)(c)?c.slice(0):(0,en.pi)({},c),s.forEach(function(e,t){n[t]=e}))}return e.info?this.cache.policies.runMergeFunction(t,n,e.info,r,i&&(o=r.store).getStorage.apply(o,i)):n},e}(),i6=[];function i5(e,t){var n=e.map;return n.has(t)||n.set(t,i6.pop()||{map:new Map}),n.get(t)}function i8(e,t){if(e===t||!t||i9(t))return e;if(!e||i9(e))return t;var n=e.info&&t.info?(0,en.pi)((0,en.pi)({},e.info),t.info):e.info||t.info,r=e.map.size&&t.map.size,i=r?new Map:e.map.size?e.map:t.map,a={info:n,map:i};if(r){var o=new Set(t.map.keys());e.map.forEach(function(e,n){a.map.set(n,i8(e,t.map.get(n))),o.delete(n)}),o.forEach(function(n){a.map.set(n,i8(t.map.get(n),e.map.get(n)))})}return a}function i9(e){return!e||!(e.info||e.map.size)}function i7(e,t){var n=e.map,r=n.get(t);r&&i9(r)&&(i6.push(r),n.delete(t))}var ae=new Set;function at(e,t,n,r){var i=function(e){var t=r.getFieldValue(e,n);return"object"==typeof t&&t},a=i(e);if(a){var o=i(t);if(!(!o||eD(a)||(0,nm.D)(a,o)||Object.keys(a).every(function(e){return void 0!==r.getFieldValue(o,e)}))){var s=r.getFieldValue(e,"__typename")||r.getFieldValue(t,"__typename"),u=iv(n),c="".concat(s,".").concat(u);if(!ae.has(c)){ae.add(c);var l=[];(0,tP.k)(a)||(0,tP.k)(o)||[a,o].forEach(function(e){var t=r.getFieldValue(e,"__typename");"string"!=typeof t||l.includes(t)||l.push(t)}),__DEV__&&Q.kG.warn("Cache data may be lost when replacing the ".concat(u," field of a ").concat(s," object.\n\nThis could cause additional (usually avoidable) network requests to fetch data that were otherwise cached.\n\nTo address this problem (which is not a bug in Apollo Client), ").concat(l.length?"either ensure all objects of type "+l.join(" and ")+" have an ID or a custom merge function, or ":"","define a custom merge function for the ").concat(c," field, so InMemoryCache can safely merge these objects:\n\n existing: ").concat(JSON.stringify(a).slice(0,1e3),"\n incoming: ").concat(JSON.stringify(o).slice(0,1e3),"\n\nFor more information about these options, please refer to the documentation:\n\n * Ensuring entity objects have IDs: https://go.apollo.dev/c/generating-unique-identifiers\n * Defining custom merge functions: https://go.apollo.dev/c/merging-non-normalized-objects\n"))}}}}var an=function(e){function t(t){void 0===t&&(t={});var n=e.call(this)||this;return n.watches=new Set,n.typenameDocumentCache=new Map,n.makeVar=r2,n.txCount=0,n.config=ip(t),n.addTypename=!!n.config.addTypename,n.policies=new iQ({cache:n,dataIdFromObject:n.config.dataIdFromObject,possibleTypes:n.config.possibleTypes,typePolicies:n.config.typePolicies}),n.init(),n}return(0,en.ZT)(t,e),t.prototype.init=function(){var e=this.data=new iT.Root({policies:this.policies,resultCaching:this.config.resultCaching});this.optimisticData=e.stump,this.resetResultCache()},t.prototype.resetResultCache=function(e){var t=this,n=this.storeReader,r=this.config.fragments;this.storeWriter=new i4(this,this.storeReader=new iP({cache:this,addTypename:this.addTypename,resultCacheMaxSize:this.config.resultCacheMaxSize,canonizeResults:ib(this.config),canon:e?void 0:n&&n.canon,fragments:r}),r),this.maybeBroadcastWatch=rZ(function(e,n){return t.broadcastWatch(e,n)},{max:this.config.resultCacheMaxSize,makeCacheKey:function(e){var n=e.optimistic?t.optimisticData:t.data;if(iD(n)){var r=e.optimistic,i=e.id,a=e.variables;return n.makeCacheKey(e.query,e.callback,nx({optimistic:r,id:i,variables:a}))}}}),new Set([this.data.group,this.optimisticData.group,]).forEach(function(e){return e.resetCaching()})},t.prototype.restore=function(e){return this.init(),e&&this.data.replace(e),this},t.prototype.extract=function(e){return void 0===e&&(e=!1),(e?this.optimisticData:this.data).extract()},t.prototype.read=function(e){var t=e.returnPartialData,n=void 0!==t&&t;try{return this.storeReader.diffQueryAgainstStore((0,en.pi)((0,en.pi)({},e),{store:e.optimistic?this.optimisticData:this.data,config:this.config,returnPartialData:n})).result||null}catch(r){if(r instanceof is)return null;throw r}},t.prototype.write=function(e){try{return++this.txCount,this.storeWriter.writeToStore(this.data,e)}finally{--this.txCount||!1===e.broadcast||this.broadcastWatches()}},t.prototype.modify=function(e){if(ic.call(e,"id")&&!e.id)return!1;var t=e.optimistic?this.optimisticData:this.data;try{return++this.txCount,t.modify(e.id||"ROOT_QUERY",e.fields)}finally{--this.txCount||!1===e.broadcast||this.broadcastWatches()}},t.prototype.diff=function(e){return this.storeReader.diffQueryAgainstStore((0,en.pi)((0,en.pi)({},e),{store:e.optimistic?this.optimisticData:this.data,rootId:e.id||"ROOT_QUERY",config:this.config}))},t.prototype.watch=function(e){var t=this;return this.watches.size||r0(this),this.watches.add(e),e.immediate&&this.maybeBroadcastWatch(e),function(){t.watches.delete(e)&&!t.watches.size&&r1(t),t.maybeBroadcastWatch.forget(e)}},t.prototype.gc=function(e){nx.reset();var t=this.optimisticData.gc();return e&&!this.txCount&&(e.resetResultCache?this.resetResultCache(e.resetResultIdentities):e.resetResultIdentities&&this.storeReader.resetCanon()),t},t.prototype.retain=function(e,t){return(t?this.optimisticData:this.data).retain(e)},t.prototype.release=function(e,t){return(t?this.optimisticData:this.data).release(e)},t.prototype.identify=function(e){if(eD(e))return e.__ref;try{return this.policies.identify(e)[0]}catch(t){__DEV__&&Q.kG.warn(t)}},t.prototype.evict=function(e){if(!e.id){if(ic.call(e,"id"))return!1;e=(0,en.pi)((0,en.pi)({},e),{id:"ROOT_QUERY"})}try{return++this.txCount,this.optimisticData.evict(e,this.data)}finally{--this.txCount||!1===e.broadcast||this.broadcastWatches()}},t.prototype.reset=function(e){var t=this;return this.init(),nx.reset(),e&&e.discardWatches?(this.watches.forEach(function(e){return t.maybeBroadcastWatch.forget(e)}),this.watches.clear(),r1(this)):this.broadcastWatches(),Promise.resolve()},t.prototype.removeOptimistic=function(e){var t=this.optimisticData.removeLayer(e);t!==this.optimisticData&&(this.optimisticData=t,this.broadcastWatches())},t.prototype.batch=function(e){var t,n=this,r=e.update,i=e.optimistic,a=void 0===i||i,o=e.removeOptimistic,s=e.onWatchUpdated,u=function(e){var i=n,a=i.data,o=i.optimisticData;++n.txCount,e&&(n.data=n.optimisticData=e);try{return t=r(n)}finally{--n.txCount,n.data=a,n.optimisticData=o}},c=new Set;return s&&!this.txCount&&this.broadcastWatches((0,en.pi)((0,en.pi)({},e),{onWatchUpdated:function(e){return c.add(e),!1}})),"string"==typeof a?this.optimisticData=this.optimisticData.addLayer(a,u):!1===a?u(this.data):u(),"string"==typeof o&&(this.optimisticData=this.optimisticData.removeLayer(o)),s&&c.size?(this.broadcastWatches((0,en.pi)((0,en.pi)({},e),{onWatchUpdated:function(e,t){var n=s.call(this,e,t);return!1!==n&&c.delete(e),n}})),c.size&&c.forEach(function(e){return n.maybeBroadcastWatch.dirty(e)})):this.broadcastWatches(e),t},t.prototype.performTransaction=function(e,t){return this.batch({update:e,optimistic:t||null!==t})},t.prototype.transformDocument=function(e){if(this.addTypename){var t=this.typenameDocumentCache.get(e);return t||(t=nj(e),this.typenameDocumentCache.set(e,t),this.typenameDocumentCache.set(t,t)),t}return e},t.prototype.transformForLink=function(e){var t=this.config.fragments;return t?t.transform(e):e},t.prototype.broadcastWatches=function(e){var t=this;this.txCount||this.watches.forEach(function(n){return t.maybeBroadcastWatch(n,e)})},t.prototype.broadcastWatch=function(e,t){var n=e.lastDiff,r=this.diff(e);(!t||(e.optimistic&&"string"==typeof t.optimistic&&(r.fromOptimisticTransaction=!0),!t.onWatchUpdated||!1!==t.onWatchUpdated.call(this,e,r,n)))&&(n&&(0,nm.D)(n.result,r.result)||e.callback(e.lastDiff=r,n))},t}(io),ar={possibleTypes:{ApproveJobProposalSpecPayload:["ApproveJobProposalSpecSuccess","JobAlreadyExistsError","NotFoundError"],BridgePayload:["Bridge","NotFoundError"],CancelJobProposalSpecPayload:["CancelJobProposalSpecSuccess","NotFoundError"],ChainPayload:["Chain","NotFoundError"],CreateAPITokenPayload:["CreateAPITokenSuccess","InputErrors"],CreateBridgePayload:["CreateBridgeSuccess"],CreateCSAKeyPayload:["CSAKeyExistsError","CreateCSAKeySuccess"],CreateFeedsManagerChainConfigPayload:["CreateFeedsManagerChainConfigSuccess","InputErrors","NotFoundError"],CreateFeedsManagerPayload:["CreateFeedsManagerSuccess","InputErrors","NotFoundError","SingleFeedsManagerError"],CreateJobPayload:["CreateJobSuccess","InputErrors"],CreateOCR2KeyBundlePayload:["CreateOCR2KeyBundleSuccess"],CreateOCRKeyBundlePayload:["CreateOCRKeyBundleSuccess"],CreateP2PKeyPayload:["CreateP2PKeySuccess"],DeleteAPITokenPayload:["DeleteAPITokenSuccess","InputErrors"],DeleteBridgePayload:["DeleteBridgeConflictError","DeleteBridgeInvalidNameError","DeleteBridgeSuccess","NotFoundError"],DeleteCSAKeyPayload:["DeleteCSAKeySuccess","NotFoundError"],DeleteFeedsManagerChainConfigPayload:["DeleteFeedsManagerChainConfigSuccess","NotFoundError"],DeleteJobPayload:["DeleteJobSuccess","NotFoundError"],DeleteOCR2KeyBundlePayload:["DeleteOCR2KeyBundleSuccess","NotFoundError"],DeleteOCRKeyBundlePayload:["DeleteOCRKeyBundleSuccess","NotFoundError"],DeleteP2PKeyPayload:["DeleteP2PKeySuccess","NotFoundError"],DeleteVRFKeyPayload:["DeleteVRFKeySuccess","NotFoundError"],DismissJobErrorPayload:["DismissJobErrorSuccess","NotFoundError"],Error:["CSAKeyExistsError","DeleteBridgeConflictError","DeleteBridgeInvalidNameError","InputError","JobAlreadyExistsError","NotFoundError","RunJobCannotRunError","SingleFeedsManagerError"],EthTransactionPayload:["EthTransaction","NotFoundError"],FeaturesPayload:["Features"],FeedsManagerPayload:["FeedsManager","NotFoundError"],GetSQLLoggingPayload:["SQLLogging"],GlobalLogLevelPayload:["GlobalLogLevel"],JobPayload:["Job","NotFoundError"],JobProposalPayload:["JobProposal","NotFoundError"],JobRunPayload:["JobRun","NotFoundError"],JobSpec:["BlockHeaderFeederSpec","BlockhashStoreSpec","BootstrapSpec","CronSpec","DirectRequestSpec","FluxMonitorSpec","GatewaySpec","KeeperSpec","OCR2Spec","OCRSpec","StandardCapabilitiesSpec","VRFSpec","WebhookSpec","WorkflowSpec"],NodePayload:["Node","NotFoundError"],PaginatedPayload:["BridgesPayload","ChainsPayload","EthTransactionAttemptsPayload","EthTransactionsPayload","JobRunsPayload","JobsPayload","NodesPayload"],RejectJobProposalSpecPayload:["NotFoundError","RejectJobProposalSpecSuccess"],RunJobPayload:["NotFoundError","RunJobCannotRunError","RunJobSuccess"],SetGlobalLogLevelPayload:["InputErrors","SetGlobalLogLevelSuccess"],SetSQLLoggingPayload:["SetSQLLoggingSuccess"],SetServicesLogLevelsPayload:["InputErrors","SetServicesLogLevelsSuccess"],UpdateBridgePayload:["NotFoundError","UpdateBridgeSuccess"],UpdateFeedsManagerChainConfigPayload:["InputErrors","NotFoundError","UpdateFeedsManagerChainConfigSuccess"],UpdateFeedsManagerPayload:["InputErrors","NotFoundError","UpdateFeedsManagerSuccess"],UpdateJobProposalSpecDefinitionPayload:["NotFoundError","UpdateJobProposalSpecDefinitionSuccess"],UpdatePasswordPayload:["InputErrors","UpdatePasswordSuccess"],VRFKeyPayload:["NotFoundError","VRFKeySuccess"]}};let ai=ar;var aa=(r=void 0,location.origin),ao=new nh({uri:"".concat(aa,"/query"),credentials:"include"}),as=new ia({cache:new an({possibleTypes:ai.possibleTypes}),link:ao});if(a.Z.locale(o),u().defaultFormat="YYYY-MM-DD h:mm:ss A","undefined"!=typeof document){var au,ac,al=f().hydrate;ac=X,al(c.createElement(et,{client:as},c.createElement(d.zj,null,c.createElement(i.MuiThemeProvider,{theme:J.r},c.createElement(ac,null)))),document.getElementById("root"))}})()})(); \ No newline at end of file diff --git a/core/web/assets/main.026013f04da39527a75d.js.gz b/core/web/assets/main.6f07a88cfc748f57e21d.js.gz similarity index 94% rename from core/web/assets/main.026013f04da39527a75d.js.gz rename to core/web/assets/main.6f07a88cfc748f57e21d.js.gz index 8524ee8d01..1a83398229 100644 Binary files a/core/web/assets/main.026013f04da39527a75d.js.gz and b/core/web/assets/main.6f07a88cfc748f57e21d.js.gz differ diff --git a/core/web/auth/auth_test.go b/core/web/auth/auth_test.go index 9896a2e359..632527ac76 100644 --- a/core/web/auth/auth_test.go +++ b/core/web/auth/auth_test.go @@ -274,18 +274,28 @@ var routesRolesMap = [...]routeRules{ {"POST", "/v2/keys/p2p/export/MOCK", false, false, false}, {"GET", "/v2/keys/solana", true, true, true}, {"GET", "/v2/keys/cosmos", true, true, true}, + {"GET", "/v2/keys/starknet", true, true, true}, + {"GET", "/v2/keys/aptos", true, true, true}, {"GET", "/v2/keys/dkgsign", 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/dkgsign", 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/dkgsign/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}, + {"POST", "/v2/keys/aptos/import", false, false, false}, {"POST", "/v2/keys/dkgsign/import", false, false, false}, {"POST", "/v2/keys/solana/export/MOCK", false, false, false}, {"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/dkgsign/export/MOCK", false, false, false}, {"GET", "/v2/keys/vrf", true, true, true}, {"POST", "/v2/keys/vrf", false, false, true}, diff --git a/core/web/jobs_controller_test.go b/core/web/jobs_controller_test.go index 0401dbdb8c..60abe61537 100644 --- a/core/web/jobs_controller_test.go +++ b/core/web/jobs_controller_test.go @@ -28,6 +28,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/utils" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" @@ -270,6 +271,25 @@ func TestJobController_Create_HappyPath(t *testing.T) { require.Equal(t, "CRON_TZ=UTC * 0 0 1 1 *", jb.CronSpec.CronSchedule) }, }, + { + name: "cron-evm-chain-id", + tomlTemplate: func(nameAndExternalJobID string) string { + return fmt.Sprintf(testspecs.CronSpecEVMChainIDTemplate, nameAndExternalJobID) + }, + assertion: func(t *testing.T, nameAndExternalJobID string, r *http.Response) { + require.Equal(t, http.StatusOK, r.StatusCode) + resource := presenters.JobResource{} + err := web.ParseJSONAPIResponse(cltest.ParseResponseBody(t, r), &resource) + assert.NoError(t, err) + + jb, err := jorm.FindJob(testutils.Context(t), mustInt32FromString(t, resource.ID)) + require.NoError(t, err) + require.NotNil(t, jb.CronSpec) + + assert.NotNil(t, resource.PipelineSpec.DotDAGSource) + require.Equal(t, ubig.NewI(42), jb.CronSpec.EVMChainID) + }, + }, { name: "directrequest", tomlTemplate: func(nameAndExternalJobID string) string { @@ -464,6 +484,7 @@ targets: t.Run(c.name, func(t *testing.T) { nameAndExternalJobID := uuid.New().String() toml := c.tomlTemplate(nameAndExternalJobID) + t.Log("Job toml:", toml) body, err := json.Marshal(web.CreateJobRequest{ TOML: toml, }) diff --git a/core/web/presenters/aptos_key.go b/core/web/presenters/aptos_key.go new file mode 100644 index 0000000000..6460c325f9 --- /dev/null +++ b/core/web/presenters/aptos_key.go @@ -0,0 +1,32 @@ +package presenters + +import "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/aptoskey" + +// AptosKeyResource represents a Aptos key JSONAPI resource. +type AptosKeyResource struct { + JAID + PubKey string `json:"publicKey"` +} + +// GetName implements the api2go EntityNamer interface +func (AptosKeyResource) GetName() string { + return "encryptedAptosKeys" +} + +func NewAptosKeyResource(key aptoskey.Key) *AptosKeyResource { + r := &AptosKeyResource{ + JAID: JAID{ID: key.ID()}, + PubKey: key.PublicKeyStr(), + } + + return r +} + +func NewAptosKeyResources(keys []aptoskey.Key) []AptosKeyResource { + rs := []AptosKeyResource{} + for _, key := range keys { + rs = append(rs, *NewAptosKeyResource(key)) + } + + return rs +} diff --git a/core/web/presenters/job.go b/core/web/presenters/job.go index 8d7dab626c..ad6bf617a8 100644 --- a/core/web/presenters/job.go +++ b/core/web/presenters/job.go @@ -252,9 +252,10 @@ func NewWebhookSpec(spec *job.WebhookSpec) *WebhookSpec { // CronSpec defines the spec details of a Cron Job type CronSpec struct { - CronSchedule string `json:"schedule" tom:"schedule"` + CronSchedule string `json:"schedule"` CreatedAt time.Time `json:"createdAt"` UpdatedAt time.Time `json:"updatedAt"` + EVMChainID *big.Big `json:"evmChainID"` } // NewCronSpec generates a new CronSpec from a job.CronSpec @@ -263,6 +264,7 @@ func NewCronSpec(spec *job.CronSpec) *CronSpec { CronSchedule: spec.CronSchedule, CreatedAt: spec.CreatedAt, UpdatedAt: spec.UpdatedAt, + EVMChainID: spec.EVMChainID, } } diff --git a/core/web/presenters/job_test.go b/core/web/presenters/job_test.go index 80856daa8e..5de71f918e 100644 --- a/core/web/presenters/job_test.go +++ b/core/web/presenters/job_test.go @@ -374,6 +374,7 @@ func TestJob(t *testing.T) { CronSchedule: cronSchedule, CreatedAt: timestamp, UpdatedAt: timestamp, + EVMChainID: evmChainID, }, ExternalJobID: uuid.MustParse("0EEC7E1D-D0D2-476C-A1A8-72DFB6633F46"), PipelineSpec: &pipeline.Spec{ @@ -404,7 +405,8 @@ func TestJob(t *testing.T) { "cronSpec": { "schedule": "%s", "createdAt":"2000-01-01T00:00:00Z", - "updatedAt":"2000-01-01T00:00:00Z" + "updatedAt":"2000-01-01T00:00:00Z", + "evmChainID":"42" }, "fluxMonitorSpec": null, "gasLimit": null, diff --git a/core/web/resolver/ocr2_keys.go b/core/web/resolver/ocr2_keys.go index df18410933..d04ebbd0e2 100644 --- a/core/web/resolver/ocr2_keys.go +++ b/core/web/resolver/ocr2_keys.go @@ -25,6 +25,8 @@ const ( OCR2ChainTypeSolana = "SOLANA" // OCR2ChainTypeStarkNet defines OCR2 StarkNet Chain Type OCR2ChainTypeStarkNet = "STARKNET" + // OCRChainTypeAptos defines OCR Aptos Chain Type + OCRChainTypeAptos = "APTOS" ) // ToOCR2ChainType turns a valid string into a OCR2ChainType @@ -38,6 +40,8 @@ func ToOCR2ChainType(s string) (OCR2ChainType, error) { return OCR2ChainTypeSolana, nil case string(chaintype.StarkNet): return OCR2ChainTypeStarkNet, nil + case string(chaintype.Aptos): + return OCRChainTypeAptos, nil default: return "", errors.New("unknown ocr2 chain type") } @@ -54,6 +58,8 @@ func FromOCR2ChainType(ct OCR2ChainType) string { return string(chaintype.Solana) case OCR2ChainTypeStarkNet: return string(chaintype.StarkNet) + case OCRChainTypeAptos: + return string(chaintype.Aptos) 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 2269149bc3..033d22799b 100644 --- a/core/web/resolver/ocr2_keys_test.go +++ b/core/web/resolver/ocr2_keys_test.go @@ -41,6 +41,7 @@ func TestResolver_GetOCR2KeyBundles(t *testing.T) { ocr2key.MustNewInsecure(keystest.NewRandReaderFromSeed(1), "cosmos"), ocr2key.MustNewInsecure(keystest.NewRandReaderFromSeed(1), "solana"), ocr2key.MustNewInsecure(keystest.NewRandReaderFromSeed(1), "starknet"), + ocr2key.MustNewInsecure(keystest.NewRandReaderFromSeed(1), "aptos"), } expectedBundles := []map[string]interface{}{} for _, k := range fakeKeys { diff --git a/core/web/resolver/spec.go b/core/web/resolver/spec.go index b3c22b3b34..00b2442aca 100644 --- a/core/web/resolver/spec.go +++ b/core/web/resolver/spec.go @@ -141,6 +141,17 @@ func (r *CronSpecResolver) Schedule() string { return r.spec.CronSchedule } +// EVMChainID resolves the spec's evm chain id. +func (r *CronSpecResolver) EVMChainID() *string { + if r.spec.EVMChainID == nil { + return nil + } + + chainID := r.spec.EVMChainID.String() + + return &chainID +} + // CreatedAt resolves the spec's created at timestamp. func (r *CronSpecResolver) CreatedAt() graphql.Time { return graphql.Time{Time: r.spec.CreatedAt} diff --git a/core/web/resolver/spec_test.go b/core/web/resolver/spec_test.go index 2d5dcc71d1..69d6a56509 100644 --- a/core/web/resolver/spec_test.go +++ b/core/web/resolver/spec_test.go @@ -42,6 +42,7 @@ func TestResolver_CronSpec(t *testing.T) { Type: job.Cron, CronSpec: &job.CronSpec{ CronSchedule: "CRON_TZ=UTC 0 0 1 1 *", + EVMChainID: ubig.NewI(42), CreatedAt: f.Timestamp(), }, }, nil) @@ -54,6 +55,7 @@ func TestResolver_CronSpec(t *testing.T) { __typename ... on CronSpec { schedule + evmChainID createdAt } } @@ -67,6 +69,7 @@ func TestResolver_CronSpec(t *testing.T) { "spec": { "__typename": "CronSpec", "schedule": "CRON_TZ=UTC 0 0 1 1 *", + "evmChainID": "42", "createdAt": "2021-01-01T00:00:00Z" } } diff --git a/core/web/resolver/testdata/config-full.toml b/core/web/resolver/testdata/config-full.toml index ec2a99812a..79ad7bb8b1 100644 --- a/core/web/resolver/testdata/config-full.toml +++ b/core/web/resolver/testdata/config-full.toml @@ -288,6 +288,7 @@ NoNewHeadsThreshold = '1m0s' OperatorFactoryAddress = '0xa5B85635Be42F21f94F28034B7DA440EeFF0F418' RPCDefaultBatchSize = 17 RPCBlockQueryDelay = 10 +FinalizedBlockOffset = 0 [EVM.Transactions] ForwardersEnabled = true @@ -357,6 +358,8 @@ SyncThreshold = 13 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [EVM.NodePool.Errors] NonceTooLow = '(: |^)nonce too low' diff --git a/core/web/resolver/testdata/config-multi-chain-effective.toml b/core/web/resolver/testdata/config-multi-chain-effective.toml index 7ca4311764..5d783fb23a 100644 --- a/core/web/resolver/testdata/config-multi-chain-effective.toml +++ b/core/web/resolver/testdata/config-multi-chain-effective.toml @@ -275,6 +275,7 @@ NoNewHeadsThreshold = '3m0s' OperatorFactoryAddress = '0x3E64Cd889482443324F91bFA9c84fE72A511f48A' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [EVM.Transactions] ForwardersEnabled = false @@ -329,6 +330,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [EVM.OCR] ContractConfirmations = 4 @@ -371,6 +374,7 @@ NoNewHeadsThreshold = '3m0s' OperatorFactoryAddress = '0x8007e24251b1D2Fc518Eb843A701d9cD21fe0aA3' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [EVM.Transactions] ForwardersEnabled = false @@ -425,6 +429,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [EVM.OCR] ContractConfirmations = 4 @@ -461,6 +467,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '30s' RPCDefaultBatchSize = 100 RPCBlockQueryDelay = 10 +FinalizedBlockOffset = 0 [EVM.Transactions] ForwardersEnabled = false @@ -515,6 +522,8 @@ SyncThreshold = 10 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [EVM.OCR] ContractConfirmations = 4 diff --git a/core/web/resolver/testdata/config-multi-chain.toml b/core/web/resolver/testdata/config-multi-chain.toml index 3598e92cdc..9abb171940 100644 --- a/core/web/resolver/testdata/config-multi-chain.toml +++ b/core/web/resolver/testdata/config-multi-chain.toml @@ -39,6 +39,7 @@ CPUProfileRate = 7 ChainID = '1' FinalityDepth = 26 FinalityTagEnabled = false +FinalizedBlockOffset = 0 [EVM.OCR2] [EVM.OCR2.Automation] diff --git a/core/web/router.go b/core/web/router.go index 5e90cea237..a220df220c 100644 --- a/core/web/router.go +++ b/core/web/router.go @@ -350,6 +350,7 @@ func v2Routes(app chainlink.Application, r *gin.RouterGroup) { {"solana", NewSolanaKeysController(app)}, {"cosmos", NewCosmosKeysController(app)}, {"starknet", NewStarkNetKeysController(app)}, + {"aptos", NewAptosKeysController(app)}, {"dkgsign", NewDKGSignKeysController(app)}, {"dkgencrypt", NewDKGEncryptKeysController(app)}, } { diff --git a/core/web/schema/type/ocr2_keys.graphql b/core/web/schema/type/ocr2_keys.graphql index 95da9acf71..c25148c686 100644 --- a/core/web/schema/type/ocr2_keys.graphql +++ b/core/web/schema/type/ocr2_keys.graphql @@ -3,6 +3,7 @@ enum OCR2ChainType { COSMOS SOLANA STARKNET + APTOS } type OCR2KeyBundle { diff --git a/core/web/schema/type/spec.graphql b/core/web/schema/type/spec.graphql index db33dd14ee..5a803e2f8e 100644 --- a/core/web/schema/type/spec.graphql +++ b/core/web/schema/type/spec.graphql @@ -16,6 +16,7 @@ union JobSpec = type CronSpec { schedule: String! + evmChainID: String createdAt: Time! } diff --git a/docs/CONFIG.md b/docs/CONFIG.md index 3de0cc64dd..e38fbdd6de 100644 --- a/docs/CONFIG.md +++ b/docs/CONFIG.md @@ -1783,6 +1783,7 @@ NoNewHeadsThreshold = '3m0s' OperatorFactoryAddress = '0x3E64Cd889482443324F91bFA9c84fE72A511f48A' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -1837,6 +1838,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -1873,6 +1876,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '3m0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -1927,6 +1931,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -1963,6 +1969,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '3m0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -2017,6 +2024,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -2053,6 +2062,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '3m0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -2107,6 +2117,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -2144,6 +2156,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '40s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -2198,6 +2211,8 @@ SyncThreshold = 10 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -2234,6 +2249,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '3m0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -2288,6 +2304,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -2324,6 +2342,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '3m0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -2378,6 +2397,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -2415,6 +2436,7 @@ NoNewHeadsThreshold = '3m0s' OperatorFactoryAddress = '0x8007e24251b1D2Fc518Eb843A701d9cD21fe0aA3' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -2469,6 +2491,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -2505,6 +2529,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '30s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 2 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -2559,6 +2584,8 @@ SyncThreshold = 10 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -2594,6 +2621,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '3m0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -2648,6 +2676,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -2683,6 +2713,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '3m0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -2737,6 +2768,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -2773,6 +2806,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '30s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 2 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -2827,6 +2861,8 @@ SyncThreshold = 10 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -2864,6 +2900,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '3m0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -2918,6 +2955,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -2954,6 +2993,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '30s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 2 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -3008,6 +3048,8 @@ SyncThreshold = 10 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -3044,6 +3086,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '30s' RPCDefaultBatchSize = 100 RPCBlockQueryDelay = 10 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -3098,6 +3141,8 @@ SyncThreshold = 10 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -3134,6 +3179,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '12m0s' RPCDefaultBatchSize = 100 RPCBlockQueryDelay = 15 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -3188,6 +3234,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -3224,6 +3272,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '6m0s' RPCDefaultBatchSize = 100 RPCBlockQueryDelay = 15 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -3278,6 +3327,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -3314,6 +3365,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '30s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 2 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -3368,6 +3420,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -3404,6 +3458,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '40s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -3458,6 +3513,8 @@ SyncThreshold = 10 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -3494,6 +3551,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '1m0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -3548,6 +3606,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -3584,6 +3644,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '1m0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -3638,6 +3699,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -3674,6 +3737,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '1m0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -3728,6 +3792,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -3765,6 +3831,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '40s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -3819,6 +3886,8 @@ SyncThreshold = 10 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -3855,6 +3924,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -3909,6 +3979,8 @@ SyncThreshold = 10 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -3945,6 +4017,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '3m0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -4000,6 +4073,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -4035,6 +4110,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '30s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -4089,6 +4165,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -4125,6 +4203,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -4179,6 +4258,8 @@ SyncThreshold = 10 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -4215,6 +4296,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '6m0s' RPCDefaultBatchSize = 100 RPCBlockQueryDelay = 15 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -4269,6 +4351,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -4305,6 +4389,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '30s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -4359,6 +4444,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -4395,6 +4482,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '30s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -4449,6 +4537,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -4484,6 +4574,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -4538,6 +4629,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -4574,6 +4667,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '12m0s' RPCDefaultBatchSize = 100 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -4628,6 +4722,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -4664,6 +4760,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '40s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -4718,6 +4815,8 @@ SyncThreshold = 10 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -4754,6 +4853,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '12m0s' RPCDefaultBatchSize = 100 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -4808,6 +4908,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -4844,6 +4946,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 2 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -4898,6 +5001,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -4934,6 +5039,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -4989,6 +5095,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -5024,6 +5132,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '30s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -5078,6 +5187,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -5114,6 +5225,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '40s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -5168,6 +5280,8 @@ SyncThreshold = 10 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -5204,6 +5318,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '3m0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -5258,6 +5373,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -5294,6 +5411,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '3m0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -5349,6 +5467,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -5386,6 +5506,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -5440,6 +5561,8 @@ SyncThreshold = 10 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -5476,6 +5599,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '1m0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -5530,6 +5654,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -5566,6 +5692,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '30s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 2 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -5620,6 +5747,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -5656,6 +5785,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '30s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 2 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -5710,6 +5840,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -5746,6 +5878,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '1m0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -5800,6 +5933,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -5835,6 +5970,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -5889,6 +6025,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -5924,6 +6062,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -5978,6 +6117,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -6013,6 +6154,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -6067,6 +6209,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -6103,6 +6247,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -6157,6 +6302,8 @@ SyncThreshold = 10 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -6193,6 +6340,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 2 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -6247,6 +6395,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -6283,6 +6433,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '30s' RPCDefaultBatchSize = 100 RPCBlockQueryDelay = 10 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -6337,6 +6488,8 @@ SyncThreshold = 10 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -6372,6 +6525,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '30s' RPCDefaultBatchSize = 100 RPCBlockQueryDelay = 10 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -6426,6 +6580,8 @@ SyncThreshold = 10 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -6462,6 +6618,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '3m0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -6517,6 +6674,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -6553,6 +6712,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '40s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -6607,6 +6767,8 @@ SyncThreshold = 10 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -6644,6 +6806,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '40s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -6698,6 +6861,8 @@ SyncThreshold = 10 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -6735,6 +6900,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -6789,6 +6955,8 @@ SyncThreshold = 10 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -6826,6 +6994,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -6880,6 +7049,8 @@ SyncThreshold = 10 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -6917,6 +7088,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -6971,6 +7143,8 @@ SyncThreshold = 10 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -7007,6 +7181,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -7061,6 +7236,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -7097,6 +7274,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -7151,6 +7329,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -7187,6 +7367,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '3m0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -7241,6 +7422,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -7277,6 +7460,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '40s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -7331,6 +7515,8 @@ SyncThreshold = 10 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -7367,6 +7553,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '3m0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -7422,6 +7609,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -7458,6 +7647,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '30s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -7512,6 +7702,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -7548,6 +7740,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '30s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -7602,6 +7795,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -7791,6 +7986,22 @@ block, but it is possible to receive a head BEFORE that block is actually available from the connected node via RPC, due to race conditions in the code of the remote ETH node. In this case you will get false "zero" blocks that are missing transactions. +### FinalizedBlockOffset +```toml +FinalizedBlockOffset = 0 # Default +``` +FinalizedBlockOffset defines the number of blocks by which the latest finalized block will be shifted/delayed. +For example, suppose RPC returns block 100 as the latest finalized. In that case, the CL Node will treat block `100 - FinalizedBlockOffset` as the latest finalized block and `latest - FinalityDepth - FinalizedBlockOffset` in case of `FinalityTagEnabled = false.` +With `EnforceRepeatableRead = true,` RPC is considered healthy only if its most recent finalized block is larger or equal to the highest finalized block observed by the CL Node minus `FinalizedBlockOffset.` +Higher values of `FinalizedBlockOffset` with `EnforceRepeatableRead = true` reduce the number of false `FinalizedBlockOutOfSync` declarations on healthy RPCs that are slightly lagging behind due to network delays. +This may increase the number of healthy RPCs and reduce the probability that the CL Node will not have any healthy alternatives to the active RPC. +CAUTION: Setting this to values higher than 0 may delay transaction creation in products (e.g., CCIP, Automation) that base their decision on finalized on-chain events. +PoS chains with `FinalityTagEnabled=true` and batched (epochs) blocks finalization (e.g., Ethereum Mainnet) must be treated with special care as a minor increase in the `FinalizedBlockOffset` may lead to significant delays. +For example, let's say that `FinalizedBlockOffset = 1` and blocks are finalized in batches of 32. +The latest finalized block on chain is 64, so block 63 is the latest finalized for CL Node. +Block 64 will be treated as finalized by CL Node only when chain's latest finalized block is 65. As chain finalizes blocks in batches of 32, +CL Node has to wait for a whole new batch to be finalized to treat block 64 as finalized. + ## EVM.Transactions ```toml [EVM.Transactions] @@ -8298,6 +8509,8 @@ SyncThreshold = 5 # Default LeaseDuration = '0s' # Default NodeIsSyncingEnabled = false # Default FinalizedBlockPollInterval = '5s' # Default +EnforceRepeatableRead = false # Default +DeathDeclarationDelay = '10s' # Default ``` The node pool manages multiple RPC endpoints. @@ -8371,6 +8584,25 @@ reported based on latest block and finality depth. Set to 0 to disable. +### EnforceRepeatableRead +```toml +EnforceRepeatableRead = false # Default +``` +EnforceRepeatableRead defines if Core should only use RPCs whose most recently finalized block is greater or equal to +`highest finalized block - FinalizedBlockOffset`. In other words, exclude RPCs lagging on latest finalized +block. + +Set false to disable + +### DeathDeclarationDelay +```toml +DeathDeclarationDelay = '10s' # Default +``` +DeathDeclarationDelay defines the minimum duration an RPC must be in unhealthy state before producing an error log message. +Larger values might be helpful to reduce the noisiness of health checks like `EnforceRepeatableRead = true', which might be falsely +trigger declaration of `FinalizedBlockOutOfSync` due to insignificant network delays in broadcasting of the finalized state among RPCs. +RPC will not be picked to handle a request even if this option is set to a nonzero value. + ## EVM.NodePool.Errors :warning: **_ADVANCED_**: _Do not change these settings unless you know what you are doing._ ```toml diff --git a/go.mod b/go.mod index ee07ef8922..77ae271f32 100644 --- a/go.mod +++ b/go.mod @@ -75,17 +75,17 @@ require ( github.com/smartcontractkit/chain-selectors v1.0.17 github.com/smartcontractkit/chainlink-automation v1.0.4 github.com/smartcontractkit/chainlink-ccip v0.0.0-20240702134133-a6d2f429671f - github.com/smartcontractkit/chainlink-common v0.1.7-0.20240625144005-556a7ba7b11c + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240702120320-563bf07487fe github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240621143432-85370a54b141 - github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 - github.com/smartcontractkit/chainlink-feeds v0.0.0-20240523182126-1784bd4f3294 - github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240621151551-885054fb0f0e - github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240621143321-7e5949418911 + github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240702144807-761f63e7b527 + github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917 + github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240701154249-032706dcb7c8 + github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240625074951-06ab5e670dba github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 - github.com/smartcontractkit/libocr v0.0.0-20240419185742-fd3cab206b2c + github.com/smartcontractkit/libocr v0.0.0-20240702141926-063ceef8c42e github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 - github.com/smartcontractkit/wsrpc v0.8.1 + github.com/smartcontractkit/wsrpc v0.7.3 github.com/spf13/cast v1.6.0 github.com/stretchr/testify v1.9.0 github.com/test-go/testify v1.1.4 @@ -353,6 +353,10 @@ replace ( // until merged upstream: https://github.com/hashicorp/go-plugin/pull/257 github.com/hashicorp/go-plugin => github.com/smartcontractkit/go-plugin v0.0.0-20240208201424-b3b91517de16 + // until nolag updates merged upstream + github.com/mitchellh/mapstructure => github.com/nolag/mapstructure v1.5.2-0.20240625151721-90ea83a3f479 + // until merged upstream: https://github.com/mwitkow/grpc-proxy/pull/69 github.com/mwitkow/grpc-proxy => github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f + ) diff --git a/go.sum b/go.sum index ea0257c953..c339602e34 100644 --- a/go.sum +++ b/go.sum @@ -569,6 +569,7 @@ github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3 github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -886,10 +887,6 @@ github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJ github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= -github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= @@ -917,6 +914,8 @@ github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ib github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 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/nolag/mapstructure v1.5.2-0.20240625151721-90ea83a3f479 h1:1jCGDLFXDOHF2sdeTJYKrIuSLGMpQZpgXXHNGXR5Ouk= +github.com/nolag/mapstructure v1.5.2-0.20240625151721-90ea83a3f479/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/nsf/jsondiff v0.0.0-20210926074059-1e845ec5d249 h1:NHrXEjTNQY7P0Zfx1aMrNhpgxHmow66XQtm0aQLY0AE= github.com/nsf/jsondiff v0.0.0-20210926074059-1e845ec5d249/go.mod h1:mpRZBD8SJ55OIICQ3iWH0Yz3cjzA61JdqMLoWXeB2+8= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= @@ -1086,32 +1085,32 @@ github.com/smartcontractkit/chainlink-automation v1.0.4 h1:iyW181JjKHLNMnDleI8um github.com/smartcontractkit/chainlink-automation v1.0.4/go.mod h1:u4NbPZKJ5XiayfKHD/v3z3iflQWqvtdhj13jVZXj/cM= github.com/smartcontractkit/chainlink-ccip v0.0.0-20240702134133-a6d2f429671f h1:ohB9VgjJ+cvfOSzVEykZqpB/wGlzhzy3IjtdG3ZiO9E= github.com/smartcontractkit/chainlink-ccip v0.0.0-20240702134133-a6d2f429671f/go.mod h1:vy2vEF1K61khqGcbOCrHBKFPvSQW0O1eg19Sv74Xq/4= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240625144005-556a7ba7b11c h1:dR2y3uzuZ8cJOaMSij8LQIdySuImwxkiZFjZ62ML5S0= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240625144005-556a7ba7b11c/go.mod h1:L32xvCpk84Nglit64OhySPMP1tM3TTBK7Tw0qZl7Sd4= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240702120320-563bf07487fe h1:lYyWmjheglMu0y3JmfSqs9Dm4tZO34RmbUTOtQ4CadE= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240702120320-563bf07487fe/go.mod h1:EWvSuqIJUYXZLEHewC7WCaPylM2jyjF3Q36BZPS4MoI= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240621143432-85370a54b141 h1:TMOoYaeSDkkI3jkCH7lKHOZaLkeDuxFTNC+XblD6M0M= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240621143432-85370a54b141/go.mod h1:0UNuO3nDt9MFsZPaHJBEUolxVkN0iC69j1ccDp95e8k= -github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 h1:xFSv8561jsLtF6gYZr/zW2z5qUUAkcFkApin2mnbYTo= -github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540/go.mod h1:sjAmX8K2kbQhvDarZE1ZZgDgmHJ50s0BBc/66vKY2ek= -github.com/smartcontractkit/chainlink-feeds v0.0.0-20240523182126-1784bd4f3294 h1:4L1878goh1mqJx6JPv/TEeMAgYvbx7+D14QFSSu/DPs= -github.com/smartcontractkit/chainlink-feeds v0.0.0-20240523182126-1784bd4f3294/go.mod h1:jwVxhctE6BgLOSSsVq9wbREpZ8Ev34H+UBxeUhESZRs= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240621151551-885054fb0f0e h1:4dwcbcVrMne6wtOX/4yWdYJwk8iSm0INVNva/EhF35E= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240621151551-885054fb0f0e/go.mod h1:rJ4+TwNMciU2pvx6dsnUNnh4sPI5bUqgmYfhSrYhQ8Y= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240621143321-7e5949418911 h1:TSgCT8Uy+R4o3GwoWUqRCGNKeciub7r7+Aa3ylmg66c= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240621143321-7e5949418911/go.mod h1:OiWUTrrpSLLTMh7FINWjEh6mmDJCVPaC4yEsDCVaWdU= +github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240702144807-761f63e7b527 h1:Vs6myS+bpPwb8chUY7XxveJyhvejknhOmhDTddgsK5I= +github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240702144807-761f63e7b527/go.mod h1:KRK7KlAEpmORi+nJgT0vxQVWvlLEBQ6zgzXziZuKvUM= +github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917 h1:MD80ZRCTvxxJ8PBmhtrKoTnky8cVNYrCrIBLVRbrOM0= +github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917/go.mod h1:jwVxhctE6BgLOSSsVq9wbREpZ8Ev34H+UBxeUhESZRs= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240701154249-032706dcb7c8 h1:JkBap2v5AmU4H9LWVDGr6XKnnDwU0OzX4W7u9aq5PQg= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240701154249-032706dcb7c8/go.mod h1:NbXXQaNFskVMYRut0MvBlcHu/vDgipGMwYjamvjVB9Y= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240625074951-06ab5e670dba h1:YNSlhK5mobyAaw02LPGgIEuS3lXyCTXcc6oaV2L6uUI= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240625074951-06ab5e670dba/go.mod h1:UVFRacRkP7O7TQAzFmR52v5mUlxf+G1ovMlCQAB/cHU= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 h1:FFdvEzlYwcuVHkdZ8YnZR/XomeMGbz5E2F2HZI3I3w8= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868/go.mod h1:Kn1Hape05UzFZ7bOUnm3GVsHzP0TNrVmpfXYNHdqGGs= github.com/smartcontractkit/go-plugin v0.0.0-20240208201424-b3b91517de16 h1:TFe+FvzxClblt6qRfqEhUfa4kFQx5UobuoFGO2W4mMo= github.com/smartcontractkit/go-plugin v0.0.0-20240208201424-b3b91517de16/go.mod h1:lBS5MtSSBZk0SHc66KACcjjlU6WzEVP/8pwz68aMkCI= github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f h1:hgJif132UCdjo8u43i7iPN1/MFnu49hv7lFGFftCHKU= github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f/go.mod h1:MvMXoufZAtqExNexqi4cjrNYE9MefKddKylxjS+//n0= -github.com/smartcontractkit/libocr v0.0.0-20240419185742-fd3cab206b2c h1:lIyMbTaF2H0Q71vkwZHX/Ew4KF2BxiKhqEXwF8rn+KI= -github.com/smartcontractkit/libocr v0.0.0-20240419185742-fd3cab206b2c/go.mod h1:fb1ZDVXACvu4frX3APHZaEBp0xi1DIm34DcA0CwTsZM= +github.com/smartcontractkit/libocr v0.0.0-20240702141926-063ceef8c42e h1:9ypZ/8aW8Vm497i1gXHcT96oNLiu88jbg9QdX+IUE3E= +github.com/smartcontractkit/libocr v0.0.0-20240702141926-063ceef8c42e/go.mod h1:fb1ZDVXACvu4frX3APHZaEBp0xi1DIm34DcA0CwTsZM= github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 h1:yiKnypAqP8l0OX0P3klzZ7SCcBUxy5KqTAKZmQOvSQE= github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1/go.mod h1:q6f4fe39oZPdsh1i57WznEZgxd8siidMaSFq3wdPmVg= github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 h1:Dai1bn+Q5cpeGMQwRdjOdVjG8mmFFROVkSKuUgBErRQ= github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1/go.mod h1:G5Sd/yzHWf26rQ+X0nG9E0buKPqRGPMJAfk2gwCzOOw= -github.com/smartcontractkit/wsrpc v0.8.1 h1:kk0SXLqWrWaZ3J6c7n8D0NZ2uTMBBBpG5dZZXZX8UGE= -github.com/smartcontractkit/wsrpc v0.8.1/go.mod h1:yfg8v8fPLXkb6Mcnx6Pm/snP6jJ0r5Kf762Yd1a/KpA= +github.com/smartcontractkit/wsrpc v0.7.3 h1:CKYZfawZShZGfvsQep1F9oBansnFk9ByZPCdTMpLphw= +github.com/smartcontractkit/wsrpc v0.7.3/go.mod h1:sj7QX2NQibhkhxTfs3KOhAj/5xwgqMipTvJVSssT9i0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= @@ -1337,6 +1336,7 @@ 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.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= +go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= diff --git a/integration-tests/README.md b/integration-tests/README.md index d34b4426fd..fcfefe97a7 100644 --- a/integration-tests/README.md +++ b/integration-tests/README.md @@ -127,4 +127,4 @@ Run soak/ocr_test.go with RPC network chaos by bringing down network to RPC node ```bash make test_soak_ocr_rpc_down_half_cl_nodes -``` \ No newline at end of file +``` diff --git a/integration-tests/actions/automation_ocr_helpers.go b/integration-tests/actions/automation_ocr_helpers.go index 22220af9d6..9aa9604025 100644 --- a/integration-tests/actions/automation_ocr_helpers.go +++ b/integration-tests/actions/automation_ocr_helpers.go @@ -274,9 +274,12 @@ func DeployAutoOCRRegistryAndRegistrar( // DeployConsumers deploys and registers keeper consumers. If ephemeral addresses are enabled, it will deploy and register the consumers from ephemeral addresses, but each upkpeep will be registered with root key address as the admin. Which means // that functions like setting upkeep configuration, pausing, unpausing, etc. will be done by the root key address. It deploys multicall contract and sends link funds to each deployment address. -func DeployConsumers(t *testing.T, chainClient *seth.Client, registry contracts.KeeperRegistry, registrar contracts.KeeperRegistrar, linkToken contracts.LinkToken, numberOfUpkeeps int, linkFundsForEachUpkeep *big.Int, upkeepGasLimit uint32, isLogTrigger bool, isMercury bool) ([]contracts.KeeperConsumer, []*big.Int) { - err := DeployMultiCallAndFundDeploymentAddresses(chainClient, linkToken, numberOfUpkeeps, linkFundsForEachUpkeep) - require.NoError(t, err, "Sending link funds to deployment addresses shouldn't fail") +func DeployConsumers(t *testing.T, chainClient *seth.Client, registry contracts.KeeperRegistry, registrar contracts.KeeperRegistrar, linkToken contracts.LinkToken, numberOfUpkeeps int, linkFundsForEachUpkeep *big.Int, upkeepGasLimit uint32, isLogTrigger bool, isMercury bool, isBillingTokenNative bool, wethToken contracts.WETHToken) ([]contracts.KeeperConsumer, []*big.Int) { + // Fund deployers with LINK, no need to do this for Native token + if !isBillingTokenNative { + err := DeployMultiCallAndFundDeploymentAddresses(chainClient, linkToken, numberOfUpkeeps, linkFundsForEachUpkeep) + require.NoError(t, err, "Sending link funds to deployment addresses shouldn't fail") + } upkeeps := DeployKeeperConsumers(t, chainClient, numberOfUpkeeps, isLogTrigger, isMercury) require.Equal(t, numberOfUpkeeps, len(upkeeps), "Number of upkeeps should match") @@ -285,7 +288,7 @@ func DeployConsumers(t *testing.T, chainClient *seth.Client, registry contracts. upkeepsAddresses = append(upkeepsAddresses, upkeep.Address()) } upkeepIds := RegisterUpkeepContracts( - t, chainClient, linkToken, linkFundsForEachUpkeep, upkeepGasLimit, registry, registrar, numberOfUpkeeps, upkeepsAddresses, isLogTrigger, isMercury, + t, chainClient, linkToken, linkFundsForEachUpkeep, upkeepGasLimit, registry, registrar, numberOfUpkeeps, upkeepsAddresses, isLogTrigger, isMercury, isBillingTokenNative, wethToken, ) require.Equal(t, numberOfUpkeeps, len(upkeepIds), "Number of upkeepIds should match") return upkeeps, upkeepIds @@ -318,7 +321,7 @@ func DeployPerformanceConsumers( for _, upkeep := range upkeeps { upkeepsAddresses = append(upkeepsAddresses, upkeep.Address()) } - upkeepIds := RegisterUpkeepContracts(t, chainClient, linkToken, linkFundsForEachUpkeep, upkeepGasLimit, registry, registrar, numberOfUpkeeps, upkeepsAddresses, false, false) + upkeepIds := RegisterUpkeepContracts(t, chainClient, linkToken, linkFundsForEachUpkeep, upkeepGasLimit, registry, registrar, numberOfUpkeeps, upkeepsAddresses, false, false, false, nil) return upkeeps, upkeepIds } @@ -344,7 +347,7 @@ func DeployPerformDataCheckerConsumers( for _, upkeep := range upkeeps { upkeepsAddresses = append(upkeepsAddresses, upkeep.Address()) } - upkeepIds := RegisterUpkeepContracts(t, chainClient, linkToken, linkFundsForEachUpkeep, upkeepGasLimit, registry, registrar, numberOfUpkeeps, upkeepsAddresses, false, false) + upkeepIds := RegisterUpkeepContracts(t, chainClient, linkToken, linkFundsForEachUpkeep, upkeepGasLimit, registry, registrar, numberOfUpkeeps, upkeepsAddresses, false, false, false, nil) return upkeeps, upkeepIds } diff --git a/integration-tests/actions/automationv2/actions.go b/integration-tests/actions/automationv2/actions.go index 1ed4bef141..f7d495bda5 100644 --- a/integration-tests/actions/automationv2/actions.go +++ b/integration-tests/actions/automationv2/actions.go @@ -565,7 +565,8 @@ func (a *AutomationTest) SetConfigOnRegistry() error { } else if a.RegistrySettings.RegistryVersion == ethereum.RegistryVersion_2_3 { ocrConfig.TypedOnchainConfig23 = a.RegistrySettings.Create23OnchainConfig(a.Registrar.Address(), a.UpkeepPrivilegeManager, a.Registry.ChainModuleAddress(), a.Registry.ReorgProtectionEnabled()) ocrConfig.BillingTokens = []common.Address{ - common.HexToAddress(a.LinkToken.Address()), // TODO add more billing tokens + common.HexToAddress(a.LinkToken.Address()), + common.HexToAddress(a.WETHToken.Address()), } ocrConfig.BillingConfigs = []i_automation_registry_master_wrapper_2_3.AutomationRegistryBase23BillingConfig{ @@ -577,6 +578,14 @@ func (a *AutomationTest) SetConfigOnRegistry() error { FallbackPrice: big.NewInt(1000), MinSpend: big.NewInt(200), }, + { + GasFeePPB: 100, + FlatFeeMilliCents: big.NewInt(500), + PriceFeed: common.HexToAddress(a.EthUSDFeed.Address()), // ETH/USD feed and LINK/USD feed are the same + Decimals: 18, + FallbackPrice: big.NewInt(1000), + MinSpend: big.NewInt(200), + }, } } err = a.Registry.SetConfigTypeSafe(ocrConfig) diff --git a/integration-tests/actions/keeper_helpers.go b/integration-tests/actions/keeper_helpers.go index 7f02f54c75..ee1662cc18 100644 --- a/integration-tests/actions/keeper_helpers.go +++ b/integration-tests/actions/keeper_helpers.go @@ -8,6 +8,7 @@ import ( "strconv" "testing" + "github.com/ethereum/go-ethereum/core/types" "github.com/google/uuid" "github.com/pkg/errors" "github.com/smartcontractkit/seth" @@ -115,7 +116,7 @@ func DeployKeeperContracts( } registrar := DeployKeeperRegistrar(t, client, registryVersion, linkToken, registrarSettings, registry) - upkeeps, upkeepIds := DeployConsumers(t, client, registry, registrar, linkToken, numberOfUpkeeps, linkFundsForEachUpkeep, upkeepGasLimit, false, false) + upkeeps, upkeepIds := DeployConsumers(t, client, registry, registrar, linkToken, numberOfUpkeeps, linkFundsForEachUpkeep, upkeepGasLimit, false, false, false, nil) return registry, registrar, upkeeps, upkeepIds } @@ -178,7 +179,7 @@ func DeployPerformanceKeeperContracts( upkeepsAddresses = append(upkeepsAddresses, upkeep.Address()) } - upkeepIds := RegisterUpkeepContracts(t, chainClient, linkToken, linkFundsForEachUpkeep, upkeepGasLimit, registry, registrar, numberOfContracts, upkeepsAddresses, false, false) + upkeepIds := RegisterUpkeepContracts(t, chainClient, linkToken, linkFundsForEachUpkeep, upkeepGasLimit, registry, registrar, numberOfContracts, upkeepsAddresses, false, false, false, nil) return registry, registrar, upkeeps, upkeepIds } @@ -236,7 +237,7 @@ func DeployPerformDataCheckerContracts( upkeepsAddresses = append(upkeepsAddresses, upkeep.Address()) } - upkeepIds := RegisterUpkeepContracts(t, chainClient, linkToken, linkFundsForEachUpkeep, upkeepGasLimit, registry, registrar, numberOfContracts, upkeepsAddresses, false, false) + upkeepIds := RegisterUpkeepContracts(t, chainClient, linkToken, linkFundsForEachUpkeep, upkeepGasLimit, registry, registrar, numberOfContracts, upkeepsAddresses, false, false, false, nil) return registry, registrar, upkeeps, upkeepIds } @@ -259,14 +260,14 @@ func DeployKeeperRegistrar( return registrar } -func RegisterUpkeepContracts(t *testing.T, client *seth.Client, linkToken contracts.LinkToken, linkFunds *big.Int, upkeepGasLimit uint32, registry contracts.KeeperRegistry, registrar contracts.KeeperRegistrar, numberOfContracts int, upkeepAddresses []string, isLogTrigger bool, isMercury bool) []*big.Int { +func RegisterUpkeepContracts(t *testing.T, client *seth.Client, linkToken contracts.LinkToken, fundsForEachUpkeep *big.Int, upkeepGasLimit uint32, registry contracts.KeeperRegistry, registrar contracts.KeeperRegistrar, numberOfContracts int, upkeepAddresses []string, isLogTrigger bool, isMercury bool, isBillingTokenNative bool, wethToken contracts.WETHToken) []*big.Int { checkData := make([][]byte, 0) for i := 0; i < numberOfContracts; i++ { checkData = append(checkData, []byte("0")) } return RegisterUpkeepContractsWithCheckData( - t, client, linkToken, linkFunds, upkeepGasLimit, registry, registrar, - numberOfContracts, upkeepAddresses, checkData, isLogTrigger, isMercury) + t, client, linkToken, fundsForEachUpkeep, upkeepGasLimit, registry, registrar, + numberOfContracts, upkeepAddresses, checkData, isLogTrigger, isMercury, isBillingTokenNative, wethToken) } type upkeepRegistrationResult struct { @@ -284,7 +285,7 @@ type upkeepConfig struct { type UpkeepId = *big.Int -func RegisterUpkeepContractsWithCheckData(t *testing.T, client *seth.Client, linkToken contracts.LinkToken, linkFunds *big.Int, upkeepGasLimit uint32, registry contracts.KeeperRegistry, registrar contracts.KeeperRegistrar, numberOfContracts int, upkeepAddresses []string, checkData [][]byte, isLogTrigger bool, isMercury bool) []*big.Int { +func RegisterUpkeepContractsWithCheckData(t *testing.T, client *seth.Client, linkToken contracts.LinkToken, fundsForEachUpkeep *big.Int, upkeepGasLimit uint32, registry contracts.KeeperRegistry, registrar contracts.KeeperRegistrar, numberOfContracts int, upkeepAddresses []string, checkData [][]byte, isLogTrigger bool, isMercury bool, isBillingTokenNative bool, wethToken contracts.WETHToken) []*big.Int { l := logging.GetTestLogger(t) concurrency, err := GetAndAssertCorrectConcurrency(client, 1) @@ -300,45 +301,69 @@ func RegisterUpkeepContractsWithCheckData(t *testing.T, client *seth.Client, lin var registerUpkeepFn = func(resultCh chan upkeepRegistrationResult, errorCh chan error, executorNum int, config upkeepConfig) { id := uuid.New().String() keyNum := executorNum + 1 // key 0 is the root key + var tx *types.Transaction + + if isBillingTokenNative { + // register upkeep with native token + tx, err = registrar.RegisterUpkeepFromKey( + keyNum, + fmt.Sprintf("upkeep_%s", id), + []byte("test@mail.com"), + config.address, + upkeepGasLimit, + client.MustGetRootKeyAddress().Hex(), // upkeep Admin + config.data, + fundsForEachUpkeep, + wethToken.Address(), + isLogTrigger, + isMercury, + ) + if err != nil { + errorCh <- errors.Wrapf(err, "[id: %s] Failed to register upkeep at %s", id, config.address) + return + } + } else { + // register upkeep with LINK + req, err := registrar.EncodeRegisterRequest( + fmt.Sprintf("upkeep_%s", id), + []byte("test@mail.com"), + config.address, + upkeepGasLimit, + client.MustGetRootKeyAddress().Hex(), // upkeep Admin + config.data, + fundsForEachUpkeep, + 0, + client.Addresses[keyNum].Hex(), + isLogTrigger, + isMercury, + linkToken.Address(), + ) + + if err != nil { + errorCh <- errors.Wrapf(err, "[id: %s] Failed to encode register request for upkeep at %s", id, config.address) + return + } - req, err := registrar.EncodeRegisterRequest( - fmt.Sprintf("upkeep_%s", id), - []byte("test@mail.com"), - config.address, - upkeepGasLimit, - client.MustGetRootKeyAddress().Hex(), // upkeep Admin - config.data, - linkFunds, - 0, - client.Addresses[keyNum].Hex(), - isLogTrigger, - isMercury, - linkToken.Address(), - ) - - if err != nil { - errorCh <- errors.Wrapf(err, "[id: %s] Failed to encode register request for upkeep at %s", id, config.address) - return - } - - balance, err := linkToken.BalanceOf(context.Background(), client.Addresses[keyNum].Hex()) - if err != nil { - errorCh <- errors.Wrapf(err, "[id: %s]Failed to get LINK balance of %s", id, client.Addresses[keyNum].Hex()) - return - } + balance, err := linkToken.BalanceOf(context.Background(), client.Addresses[keyNum].Hex()) + if err != nil { + errorCh <- errors.Wrapf(err, "[id: %s]Failed to get LINK balance of %s", id, client.Addresses[keyNum].Hex()) + return + } - // not stricly necessary, but helps us to avoid an errorless revert if there is not enough LINK - if balance.Cmp(linkFunds) < 0 { - errorCh <- fmt.Errorf("[id: %s] Not enough LINK balance for %s. Has: %s. Needs: %s", id, client.Addresses[keyNum].Hex(), balance.String(), linkFunds.String()) - return - } + // not strictly necessary, but helps us to avoid an errorless revert if there is not enough LINK + if balance.Cmp(fundsForEachUpkeep) < 0 { + errorCh <- fmt.Errorf("[id: %s] Not enough LINK balance for %s. Has: %s. Needs: %s", id, client.Addresses[keyNum].Hex(), balance.String(), fundsForEachUpkeep.String()) + return + } - tx, err := linkToken.TransferAndCallFromKey(registrar.Address(), linkFunds, req, keyNum) - if err != nil { - errorCh <- errors.Wrapf(err, "[id: %s] Failed to register upkeep at %s", id, config.address) - return + tx, err = linkToken.TransferAndCallFromKey(registrar.Address(), fundsForEachUpkeep, req, keyNum) + if err != nil { + errorCh <- errors.Wrapf(err, "[id: %s] Failed to register upkeep at %s", id, config.address) + return + } } + // parse txn to get upkeep ID receipt, err := client.Client.TransactionReceipt(context.Background(), tx.Hash()) if err != nil { errorCh <- errors.Wrapf(err, "[id: %s] Failed to get receipt for upkeep at %s and tx hash %s", id, config.address, tx.Hash()) @@ -405,10 +430,10 @@ func DeployKeeperConsumers(t *testing.T, client *seth.Client, numberOfContracts // v2.1 only: Conditional based contract with Mercury enabled keeperConsumerInstance, err = contracts.DeployAutomationStreamsLookupUpkeepConsumerFromKey(client, keyNum, big.NewInt(1000), big.NewInt(5), false, true, false) // 1000 block test range } else if isLogTrigger { - // v2.1 only: Log triggered based contract without Mercury + // v2.1+: Log triggered based contract without Mercury keeperConsumerInstance, err = contracts.DeployAutomationLogTriggerConsumerFromKey(client, keyNum, big.NewInt(1000)) // 1000 block test range } else { - // v2.0 and v2.1: Conditional based contract without Mercury + // v2.0+: Conditional based contract without Mercury keeperConsumerInstance, err = contracts.DeployUpkeepCounterFromKey(client, keyNum, big.NewInt(999999), big.NewInt(5)) } @@ -580,7 +605,7 @@ func RegisterNewUpkeeps( err = SendLinkFundsToDeploymentAddresses(chainClient, concurrency, numberOfNewUpkeeps, operationsPerAddress, multicallAddress, linkFundsForEachUpkeep, linkToken) require.NoError(t, err, "Sending link funds to deployment addresses shouldn't fail") - newUpkeepIDs := RegisterUpkeepContracts(t, chainClient, linkToken, linkFundsForEachUpkeep, upkeepGasLimit, registry, registrar, numberOfNewUpkeeps, addressesOfNewUpkeeps, false, false) + newUpkeepIDs := RegisterUpkeepContracts(t, chainClient, linkToken, linkFundsForEachUpkeep, upkeepGasLimit, registry, registrar, numberOfNewUpkeeps, addressesOfNewUpkeeps, false, false, false, nil) return newlyDeployedUpkeeps, newUpkeepIDs } diff --git a/integration-tests/benchmark/keeper_test.go b/integration-tests/benchmark/keeper_test.go index 8c1d5bdac3..177b352101 100644 --- a/integration-tests/benchmark/keeper_test.go +++ b/integration-tests/benchmark/keeper_test.go @@ -102,7 +102,7 @@ func TestAutomationBenchmark(t *testing.T) { testType, err := tc.GetConfigurationNameFromEnv() require.NoError(t, err, "Error getting test type") - config, err := tc.GetConfig(testType, tc.Keeper) + config, err := tc.GetConfig([]string{testType}, tc.Keeper) require.NoError(t, err, "Error getting test config") testEnvironment, benchmarkNetwork := SetupAutomationBenchmarkEnv(t, &config) @@ -308,7 +308,7 @@ func SetupAutomationBenchmarkEnv(t *testing.T, keeperTestConfig types.KeeperBenc TTL: time.Hour * 720, // 30 days, NamespacePrefix: fmt.Sprintf( "automation-%s-%s-%s", - strings.ToLower(keeperTestConfig.GetConfigurationName()), + strings.ToLower(strings.Join(keeperTestConfig.GetConfigurationNames(), "")), strings.ReplaceAll(strings.ToLower(testNetwork.Name), " ", "-"), strings.ReplaceAll(strings.ToLower(*keeperTestConfig.GetKeeperConfig().Common.RegistryToTest), "_", "-"), ), @@ -318,7 +318,7 @@ func SetupAutomationBenchmarkEnv(t *testing.T, keeperTestConfig types.KeeperBenc dbResources := performanceDbResources chainlinkResources := performanceChainlinkResources - if strings.ToLower(keeperTestConfig.GetConfigurationName()) == "soak" { + if strings.Contains(strings.ToLower(strings.Join(keeperTestConfig.GetConfigurationNames(), ",")), "soak") { chainlinkResources = soakChainlinkResources dbResources = soakDbResources } diff --git a/integration-tests/ccip-tests/actions/ccip_helpers.go b/integration-tests/ccip-tests/actions/ccip_helpers.go index d796970e83..62ce2996c9 100644 --- a/integration-tests/ccip-tests/actions/ccip_helpers.go +++ b/integration-tests/ccip-tests/actions/ccip_helpers.go @@ -1017,24 +1017,26 @@ func (ccipModule *CCIPCommon) DeployContracts( } // no need to have price registry for existing deployment, we consider that it's already deployed - if ccipModule.PriceRegistry == nil && !ccipModule.ExistingDeployment { - // we will update the price updates later based on source and dest PriceUpdates - ccipModule.PriceRegistry, err = cd.DeployPriceRegistry( - []common.Address{ - common.HexToAddress(ccipModule.FeeToken.Address()), - common.HexToAddress(ccipModule.WrappedNative.Hex()), - }) - if err != nil { - return fmt.Errorf("deploying PriceRegistry shouldn't fail %w", err) - } - err = ccipModule.ChainClient.WaitForEvents() - if err != nil { - return fmt.Errorf("error in waiting for PriceRegistry deployment %w", err) - } - } else { - ccipModule.PriceRegistry, err = cd.NewPriceRegistry(ccipModule.PriceRegistry.EthAddress) - if err != nil { - return fmt.Errorf("getting new PriceRegistry contract shouldn't fail %w", err) + if !ccipModule.ExistingDeployment { + if ccipModule.PriceRegistry == nil { + // we will update the price updates later based on source and dest PriceUpdates + ccipModule.PriceRegistry, err = cd.DeployPriceRegistry( + []common.Address{ + common.HexToAddress(ccipModule.FeeToken.Address()), + common.HexToAddress(ccipModule.WrappedNative.Hex()), + }) + if err != nil { + return fmt.Errorf("deploying PriceRegistry shouldn't fail %w", err) + } + err = ccipModule.ChainClient.WaitForEvents() + if err != nil { + return fmt.Errorf("error in waiting for PriceRegistry deployment %w", err) + } + } else { + ccipModule.PriceRegistry, err = cd.NewPriceRegistry(ccipModule.PriceRegistry.EthAddress) + if err != nil { + return fmt.Errorf("getting new PriceRegistry contract shouldn't fail %w", err) + } } } if ccipModule.MulticallContract == (common.Address{}) && ccipModule.MulticallEnabled { @@ -1227,9 +1229,11 @@ func NewCCIPCommonFromConfig( if err != nil { return nil, err } - newCCIPModule.PriceRegistry, err = newCCIPModule.Deployer.NewPriceRegistry(common.HexToAddress(newCCIPModule.PriceRegistry.Address())) - if err != nil { - return nil, err + if newCCIPModule.PriceRegistry != nil { + newCCIPModule.PriceRegistry, err = newCCIPModule.Deployer.NewPriceRegistry(common.HexToAddress(newCCIPModule.PriceRegistry.Address())) + if err != nil { + return nil, err + } } newCCIPModule.Router, err = newCCIPModule.Deployer.NewRouter(common.HexToAddress(newCCIPModule.Router.Address())) if err != nil { diff --git a/integration-tests/ccip-tests/testsetups/ccip.go b/integration-tests/ccip-tests/testsetups/ccip.go index ad2efe10ce..10cd60277d 100644 --- a/integration-tests/ccip-tests/testsetups/ccip.go +++ b/integration-tests/ccip-tests/testsetups/ccip.go @@ -1359,9 +1359,9 @@ func (o *CCIPTestSetUpOutputs) CreateEnvironment( func createEnvironmentConfig(t *testing.T, envName string, testConfig *CCIPTestConfig, reportPath string) *environment.Config { envConfig := &environment.Config{ - NamespacePrefix: envName, - Test: t, - PreventPodEviction: true, + NamespacePrefix: envName, + Test: t, + // PreventPodEviction: true, //TODO: enable this once we have a way to handle pod eviction } if pointer.GetBool(testConfig.TestGroupInput.StoreLaneConfig) { envConfig.ReportPath = reportPath diff --git a/integration-tests/chaos/automation_chaos_test.go b/integration-tests/chaos/automation_chaos_test.go index a6267138d2..e14c35ed17 100644 --- a/integration-tests/chaos/automation_chaos_test.go +++ b/integration-tests/chaos/automation_chaos_test.go @@ -134,7 +134,7 @@ func TestAutomationChaos(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() - config, err := tc.GetConfig("Chaos", tc.Automation) + config, err := tc.GetConfig([]string{"Chaos"}, tc.Automation) if err != nil { t.Fatal(err) } @@ -286,8 +286,8 @@ func TestAutomationChaos(t *testing.T) { } require.NoError(t, err, "Error setting OCR config") - consumersConditional, upkeepidsConditional := actions.DeployConsumers(t, chainClient, registry, registrar, linkToken, numberOfUpkeeps, big.NewInt(defaultLinkFunds), defaultUpkeepGasLimit, false, false) - consumersLogtrigger, upkeepidsLogtrigger := actions.DeployConsumers(t, chainClient, registry, registrar, linkToken, numberOfUpkeeps, big.NewInt(defaultLinkFunds), defaultUpkeepGasLimit, true, false) + consumersConditional, upkeepidsConditional := actions.DeployConsumers(t, chainClient, registry, registrar, linkToken, numberOfUpkeeps, big.NewInt(defaultLinkFunds), defaultUpkeepGasLimit, false, false, false, nil) + consumersLogtrigger, upkeepidsLogtrigger := actions.DeployConsumers(t, chainClient, registry, registrar, linkToken, numberOfUpkeeps, big.NewInt(defaultLinkFunds), defaultUpkeepGasLimit, true, false, false, nil) consumers := append(consumersConditional, consumersLogtrigger...) upkeepIDs := append(upkeepidsConditional, upkeepidsLogtrigger...) diff --git a/integration-tests/chaos/ocr2vrf_chaos_test.go b/integration-tests/chaos/ocr2vrf_chaos_test.go index 95b8373dc2..2d1bb5b10e 100644 --- a/integration-tests/chaos/ocr2vrf_chaos_test.go +++ b/integration-tests/chaos/ocr2vrf_chaos_test.go @@ -32,7 +32,7 @@ import ( func TestOCR2VRFChaos(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - testConfig, err := tc.GetConfig("Chaos", tc.OCR2VRF) + testConfig, err := tc.GetConfig([]string{"Chaos"}, tc.OCR2VRF) if err != nil { t.Fatal(err) } diff --git a/integration-tests/chaos/ocr_chaos_test.go b/integration-tests/chaos/ocr_chaos_test.go index 3b7eeac888..54a02cf64f 100644 --- a/integration-tests/chaos/ocr_chaos_test.go +++ b/integration-tests/chaos/ocr_chaos_test.go @@ -54,7 +54,7 @@ var ( func TestOCRChaos(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Chaos", tc.OCR) + config, err := tc.GetConfig([]string{"Chaos"}, tc.OCR) require.NoError(t, err, "Error getting config") var overrideFn = func(_ interface{}, target interface{}) { diff --git a/integration-tests/contracts/ethereum_contracts_automation.go b/integration-tests/contracts/ethereum_contracts_automation.go index c2c7576e0f..bd0e1aafc8 100644 --- a/integration-tests/contracts/ethereum_contracts_automation.go +++ b/integration-tests/contracts/ethereum_contracts_automation.go @@ -1799,6 +1799,87 @@ func (v *EthereumKeeperRegistrar) Fund(_ *big.Float) error { panic("do not use this function, use actions.SendFunds instead") } +// register Upkeep with native token, only available from v2.3 +func (v *EthereumKeeperRegistrar) RegisterUpkeepFromKey(keyNum int, name string, email []byte, upkeepAddr string, gasLimit uint32, adminAddr string, checkData []byte, amount *big.Int, wethTokenAddr string, isLogTrigger bool, isMercury bool) (*types.Transaction, error) { + if v.registrar23 == nil { + return nil, fmt.Errorf("RegisterUpkeepFromKey with native token is only supported in registrar version v2.3") + } + + registrarABI = cltypes.MustGetABI(registrar23.AutomationRegistrarABI) + txOpts := v.client.NewTXKeyOpts(keyNum, seth.WithValue(amount)) + + if isLogTrigger { + var topic0InBytes [32]byte + // bytes representation of 0x0000000000000000000000000000000000000000000000000000000000000000 + bytes0 := [32]byte{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + } + if isMercury { + // bytes representation of 0xd1ffe9e45581c11d7d9f2ed5f75217cd4be9f8b7eee6af0f6d03f46de53956cd + topic0InBytes = [32]byte{209, 255, 233, 228, 85, 129, 193, 29, 125, 159, 46, 213, 247, 82, 23, 205, 75, 233, 248, 183, 238, 230, 175, 15, 109, 3, 244, 109, 229, 57, 86, 205} + } else { + // bytes representation of 0x3d53a39550e04688065827f3bb86584cb007ab9ebca7ebd528e7301c9c31eb5d + topic0InBytes = [32]byte{ + 61, 83, 163, 149, 80, 224, 70, 136, + 6, 88, 39, 243, 187, 134, 88, 76, + 176, 7, 171, 158, 188, 167, 235, + 213, 40, 231, 48, 28, 156, 49, 235, 93, + } + } + + logTriggerConfigStruct := acutils.IAutomationV21PlusCommonLogTriggerConfig{ + ContractAddress: common.HexToAddress(upkeepAddr), + FilterSelector: 0, + Topic0: topic0InBytes, + Topic1: bytes0, + Topic2: bytes0, + Topic3: bytes0, + } + encodedLogTriggerConfig, err := compatibleUtils.Methods["_logTriggerConfig"].Inputs.Pack(&logTriggerConfigStruct) + if err != nil { + return nil, err + } + + params := registrar23.AutomationRegistrar23RegistrationParams{ + UpkeepContract: common.HexToAddress(upkeepAddr), + Amount: amount, + AdminAddress: common.HexToAddress(adminAddr), + GasLimit: gasLimit, + TriggerType: uint8(1), // trigger type + BillingToken: common.HexToAddress(wethTokenAddr), // native + Name: name, + EncryptedEmail: email, + CheckData: checkData, + TriggerConfig: encodedLogTriggerConfig, // log trigger upkeep + OffchainConfig: []byte{}, + } + + decodedTx, err := v.client.Decode(v.registrar23.RegisterUpkeep(txOpts, + params, + )) + return decodedTx.Transaction, err + } + + params := registrar23.AutomationRegistrar23RegistrationParams{ + UpkeepContract: common.HexToAddress(upkeepAddr), + Amount: amount, + AdminAddress: common.HexToAddress(adminAddr), + GasLimit: gasLimit, + TriggerType: uint8(0), // trigger type + BillingToken: common.HexToAddress(wethTokenAddr), // native + Name: name, + EncryptedEmail: email, + CheckData: checkData, + TriggerConfig: []byte{}, // conditional upkeep + OffchainConfig: []byte{}, + } + + decodedTx, err := v.client.Decode(v.registrar23.RegisterUpkeep(txOpts, + params, + )) + return decodedTx.Transaction, err +} + // EncodeRegisterRequest encodes register request to call it through link token TransferAndCall func (v *EthereumKeeperRegistrar) EncodeRegisterRequest(name string, email []byte, upkeepAddr string, gasLimit uint32, adminAddr string, checkData []byte, amount *big.Int, source uint8, senderAddr string, isLogTrigger bool, isMercury bool, linkTokenAddr string) ([]byte, error) { if v.registrar20 != nil { @@ -2056,9 +2137,11 @@ func DeployKeeperRegistrar(client *seth.Client, registryVersion eth_contracts.Ke billingTokens := []common.Address{ common.HexToAddress(linkAddr), + common.HexToAddress(registrarSettings.WETHTokenAddr), } minRegistrationFees := []*big.Int{ big.NewInt(10), + big.NewInt(10), } data, err := client.DeployContract(client.NewTXOpts(), "KeeperRegistrar2_3", *abi, common.FromHex(registrar23.AutomationRegistrarMetaData.Bin), diff --git a/integration-tests/contracts/ethereum_keeper_contracts.go b/integration-tests/contracts/ethereum_keeper_contracts.go index 84543627d4..34c78b3fc6 100644 --- a/integration-tests/contracts/ethereum_keeper_contracts.go +++ b/integration-tests/contracts/ethereum_keeper_contracts.go @@ -27,6 +27,8 @@ type KeeperRegistrar interface { EncodeRegisterRequest(name string, email []byte, upkeepAddr string, gasLimit uint32, adminAddr string, checkData []byte, amount *big.Int, source uint8, senderAddr string, isLogTrigger bool, isMercury bool, linkTokenAddr string) ([]byte, error) Fund(ethAmount *big.Float) error + + RegisterUpkeepFromKey(keyNum int, name string, email []byte, upkeepAddr string, gasLimit uint32, adminAddr string, checkData []byte, amount *big.Int, wethTokenAddr string, isLogTrigger bool, isMercury bool) (*types.Transaction, error) } type UpkeepTranscoder interface { diff --git a/integration-tests/docker/cmd/internal/commands.go b/integration-tests/docker/cmd/internal/commands.go index e05e5d89fa..9939765f7f 100644 --- a/integration-tests/docker/cmd/internal/commands.go +++ b/integration-tests/docker/cmd/internal/commands.go @@ -28,7 +28,7 @@ var StartNodesCmd = &cobra.Command{ log.Logger = logging.GetLogger(nil, "CORE_DOCKER_ENV_LOG_LEVEL") log.Info().Msg("Starting docker test env with Chainlink nodes..") - config, err := testconfig.GetConfig("Smoke", testconfig.OCR2) + config, err := testconfig.GetConfig([]string{"Smoke"}, testconfig.OCR2) if err != nil { return err } diff --git a/integration-tests/experiments/gas_test.go b/integration-tests/experiments/gas_test.go index 88bfbcf192..4d1f459711 100644 --- a/integration-tests/experiments/gas_test.go +++ b/integration-tests/experiments/gas_test.go @@ -18,7 +18,7 @@ import ( func TestGasExperiment(t *testing.T) { l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Soak", tc.OCR) + config, err := tc.GetConfig([]string{"Soak"}, tc.OCR) require.NoError(t, err, "Error getting config") network := networks.MustGetSelectedNetworkConfig(config.GetNetworkConfig())[0] diff --git a/integration-tests/go.mod b/integration-tests/go.mod index fa92bce013..e73850580a 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -33,14 +33,14 @@ require ( github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chain-selectors v1.0.17 github.com/smartcontractkit/chainlink-automation v1.0.4 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20240625144005-556a7ba7b11c - github.com/smartcontractkit/chainlink-testing-framework v1.31.6 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240702120320-563bf07487fe + github.com/smartcontractkit/chainlink-testing-framework v1.31.7 github.com/smartcontractkit/chainlink-testing-framework/grafana v0.0.0-20240405215812-5a72bc9af239 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/integration-tests v0.0.0-00010101000000-000000000000 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/havoc/k8schaos v0.0.0-20240409145249-e78d20847e37 - github.com/smartcontractkit/libocr v0.0.0-20240419185742-fd3cab206b2c + github.com/smartcontractkit/libocr v0.0.0-20240702141926-063ceef8c42e github.com/smartcontractkit/seth v1.0.12 github.com/smartcontractkit/wasp v0.4.7 github.com/spf13/cobra v1.8.0 @@ -386,13 +386,13 @@ require ( github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240621143432-85370a54b141 // indirect - github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 // indirect - github.com/smartcontractkit/chainlink-feeds v0.0.0-20240523182126-1784bd4f3294 // indirect - github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240621151551-885054fb0f0e // indirect - github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240621143321-7e5949418911 // indirect + github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240702144807-761f63e7b527 // indirect + github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917 // indirect + github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240701154249-032706dcb7c8 // indirect + github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240625074951-06ab5e670dba // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 // indirect - github.com/smartcontractkit/wsrpc v0.8.1 // indirect + github.com/smartcontractkit/wsrpc v0.7.3 // indirect github.com/soheilhy/cmux v0.1.5 // indirect github.com/sony/gobreaker v0.5.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 46bb8bcab3..1a563cb1e5 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -768,6 +768,7 @@ github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3 github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -1425,20 +1426,20 @@ github.com/smartcontractkit/chain-selectors v1.0.17 h1:otOlYUnutS8oQBEAi9RLQICqZ github.com/smartcontractkit/chain-selectors v1.0.17/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v1.0.4 h1:iyW181JjKHLNMnDleI8umfIfVVlwC7+n5izbLSFgjw8= github.com/smartcontractkit/chainlink-automation v1.0.4/go.mod h1:u4NbPZKJ5XiayfKHD/v3z3iflQWqvtdhj13jVZXj/cM= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240625144005-556a7ba7b11c h1:dR2y3uzuZ8cJOaMSij8LQIdySuImwxkiZFjZ62ML5S0= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240625144005-556a7ba7b11c/go.mod h1:L32xvCpk84Nglit64OhySPMP1tM3TTBK7Tw0qZl7Sd4= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240702120320-563bf07487fe h1:lYyWmjheglMu0y3JmfSqs9Dm4tZO34RmbUTOtQ4CadE= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240702120320-563bf07487fe/go.mod h1:EWvSuqIJUYXZLEHewC7WCaPylM2jyjF3Q36BZPS4MoI= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240621143432-85370a54b141 h1:TMOoYaeSDkkI3jkCH7lKHOZaLkeDuxFTNC+XblD6M0M= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240621143432-85370a54b141/go.mod h1:0UNuO3nDt9MFsZPaHJBEUolxVkN0iC69j1ccDp95e8k= -github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 h1:xFSv8561jsLtF6gYZr/zW2z5qUUAkcFkApin2mnbYTo= -github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540/go.mod h1:sjAmX8K2kbQhvDarZE1ZZgDgmHJ50s0BBc/66vKY2ek= -github.com/smartcontractkit/chainlink-feeds v0.0.0-20240523182126-1784bd4f3294 h1:4L1878goh1mqJx6JPv/TEeMAgYvbx7+D14QFSSu/DPs= -github.com/smartcontractkit/chainlink-feeds v0.0.0-20240523182126-1784bd4f3294/go.mod h1:jwVxhctE6BgLOSSsVq9wbREpZ8Ev34H+UBxeUhESZRs= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240621151551-885054fb0f0e h1:4dwcbcVrMne6wtOX/4yWdYJwk8iSm0INVNva/EhF35E= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240621151551-885054fb0f0e/go.mod h1:rJ4+TwNMciU2pvx6dsnUNnh4sPI5bUqgmYfhSrYhQ8Y= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240621143321-7e5949418911 h1:TSgCT8Uy+R4o3GwoWUqRCGNKeciub7r7+Aa3ylmg66c= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240621143321-7e5949418911/go.mod h1:OiWUTrrpSLLTMh7FINWjEh6mmDJCVPaC4yEsDCVaWdU= -github.com/smartcontractkit/chainlink-testing-framework v1.31.6 h1:NL91Se5alOyJCzN9cR+osZJJ0nYYgyxMVSK0cSha9wU= -github.com/smartcontractkit/chainlink-testing-framework v1.31.6/go.mod h1:E6uNEZhZZid9PHv6/Kq5Vn63GlO61ZcKB+/f0DKo3Q4= +github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240702144807-761f63e7b527 h1:Vs6myS+bpPwb8chUY7XxveJyhvejknhOmhDTddgsK5I= +github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240702144807-761f63e7b527/go.mod h1:KRK7KlAEpmORi+nJgT0vxQVWvlLEBQ6zgzXziZuKvUM= +github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917 h1:MD80ZRCTvxxJ8PBmhtrKoTnky8cVNYrCrIBLVRbrOM0= +github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917/go.mod h1:jwVxhctE6BgLOSSsVq9wbREpZ8Ev34H+UBxeUhESZRs= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240701154249-032706dcb7c8 h1:JkBap2v5AmU4H9LWVDGr6XKnnDwU0OzX4W7u9aq5PQg= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240701154249-032706dcb7c8/go.mod h1:NbXXQaNFskVMYRut0MvBlcHu/vDgipGMwYjamvjVB9Y= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240625074951-06ab5e670dba h1:YNSlhK5mobyAaw02LPGgIEuS3lXyCTXcc6oaV2L6uUI= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240625074951-06ab5e670dba/go.mod h1:UVFRacRkP7O7TQAzFmR52v5mUlxf+G1ovMlCQAB/cHU= +github.com/smartcontractkit/chainlink-testing-framework v1.31.7 h1:Vy4ah8VAfj+Y7vVmhjvwyAO6wG+Fp2vzdkSJwJPMQO4= +github.com/smartcontractkit/chainlink-testing-framework v1.31.7/go.mod h1:E6uNEZhZZid9PHv6/Kq5Vn63GlO61ZcKB+/f0DKo3Q4= github.com/smartcontractkit/chainlink-testing-framework/grafana v0.0.0-20240405215812-5a72bc9af239 h1:Kk5OVlx/5g9q3Z3lhxytZS4/f8ds1MiNM8yaHgK3Oe8= github.com/smartcontractkit/chainlink-testing-framework/grafana v0.0.0-20240405215812-5a72bc9af239/go.mod h1:DC8sQMyTlI/44UCTL8QWFwb0bYNoXCfjwCv2hMivYZU= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 h1:FFdvEzlYwcuVHkdZ8YnZR/XomeMGbz5E2F2HZI3I3w8= @@ -1449,8 +1450,8 @@ github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f h1:hgJ github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f/go.mod h1:MvMXoufZAtqExNexqi4cjrNYE9MefKddKylxjS+//n0= github.com/smartcontractkit/havoc/k8schaos v0.0.0-20240409145249-e78d20847e37 h1:ZEhn2Yo1jY4hqy8nasDL4k4pNtopT3rS3Ap1GDb7ODc= github.com/smartcontractkit/havoc/k8schaos v0.0.0-20240409145249-e78d20847e37/go.mod h1:/kFr0D7SI/vueXl1N03uzOun4nViGPFRyA5X6eL3jXw= -github.com/smartcontractkit/libocr v0.0.0-20240419185742-fd3cab206b2c h1:lIyMbTaF2H0Q71vkwZHX/Ew4KF2BxiKhqEXwF8rn+KI= -github.com/smartcontractkit/libocr v0.0.0-20240419185742-fd3cab206b2c/go.mod h1:fb1ZDVXACvu4frX3APHZaEBp0xi1DIm34DcA0CwTsZM= +github.com/smartcontractkit/libocr v0.0.0-20240702141926-063ceef8c42e h1:9ypZ/8aW8Vm497i1gXHcT96oNLiu88jbg9QdX+IUE3E= +github.com/smartcontractkit/libocr v0.0.0-20240702141926-063ceef8c42e/go.mod h1:fb1ZDVXACvu4frX3APHZaEBp0xi1DIm34DcA0CwTsZM= github.com/smartcontractkit/seth v1.0.12 h1:iVdgMx42XWanPPnBaM5StR4c1XsTr/0/B/kKRZL5BsY= github.com/smartcontractkit/seth v1.0.12/go.mod h1:thWtbLyW4nRHJGzC5heknQDORoJPErE15sF34LHkorg= github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 h1:yiKnypAqP8l0OX0P3klzZ7SCcBUxy5KqTAKZmQOvSQE= @@ -1459,8 +1460,8 @@ github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 h1:D github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1/go.mod h1:G5Sd/yzHWf26rQ+X0nG9E0buKPqRGPMJAfk2gwCzOOw= github.com/smartcontractkit/wasp v0.4.7 h1:7mKJfwzFbuE8xVLUYtLt7Bjw8q/bmVZRW6Ks8kc1LVM= github.com/smartcontractkit/wasp v0.4.7/go.mod h1:jeabvyXikb2aNoLQwcZGqaz17efrR8NJhpq4seAmdgs= -github.com/smartcontractkit/wsrpc v0.8.1 h1:kk0SXLqWrWaZ3J6c7n8D0NZ2uTMBBBpG5dZZXZX8UGE= -github.com/smartcontractkit/wsrpc v0.8.1/go.mod h1:yfg8v8fPLXkb6Mcnx6Pm/snP6jJ0r5Kf762Yd1a/KpA= +github.com/smartcontractkit/wsrpc v0.7.3 h1:CKYZfawZShZGfvsQep1F9oBansnFk9ByZPCdTMpLphw= +github.com/smartcontractkit/wsrpc v0.7.3/go.mod h1:sj7QX2NQibhkhxTfs3KOhAj/5xwgqMipTvJVSssT9i0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= @@ -1716,6 +1717,7 @@ 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.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= +go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= go4.org/netipx v0.0.0-20230125063823-8449b0a6169f h1:ketMxHg+vWm3yccyYiq+uK8D3fRmna2Fcj+awpQp84s= diff --git a/integration-tests/load/automationv2_1/automationv2_1_test.go b/integration-tests/load/automationv2_1/automationv2_1_test.go index c1b6a6d4a8..196e8d230f 100644 --- a/integration-tests/load/automationv2_1/automationv2_1_test.go +++ b/integration-tests/load/automationv2_1/automationv2_1_test.go @@ -153,7 +153,7 @@ func TestLogTrigger(t *testing.T) { ctx := tests.Context(t) l := logging.GetTestLogger(t) - loadedTestConfig, err := tc.GetConfig("Load", tc.Automation) + loadedTestConfig, err := tc.GetConfig([]string{"Load"}, tc.Automation) if err != nil { t.Fatal(err) } diff --git a/integration-tests/load/functions/functions_test.go b/integration-tests/load/functions/functions_test.go index 49102bcaa6..374b3df705 100644 --- a/integration-tests/load/functions/functions_test.go +++ b/integration-tests/load/functions/functions_test.go @@ -11,7 +11,7 @@ import ( ) func TestFunctionsLoad(t *testing.T) { - generalConfig, err := tc.GetConfig(tc.NoKey, tc.Functions) + generalConfig, err := tc.GetConfig([]string{""}, tc.Functions) require.NoError(t, err, "failed to get config") ft, err := SetupLocalLoadTestEnv(&generalConfig, &generalConfig) @@ -25,7 +25,7 @@ func TestFunctionsLoad(t *testing.T) { MonitorLoadStats(t, ft, labels, &generalConfig) t.Run("mumbai functions soak test http", func(t *testing.T) { - config, err := tc.GetConfig("Soak", tc.Functions) + config, err := tc.GetConfig([]string{"Soak"}, tc.Functions) require.NoError(t, err, "failed to get config") cfg := config.Functions cfgl := config.Logging.Loki @@ -59,7 +59,7 @@ func TestFunctionsLoad(t *testing.T) { }) t.Run("mumbai functions stress test http", func(t *testing.T) { - config, err := tc.GetConfig("Stress", tc.Functions) + config, err := tc.GetConfig([]string{"Stress"}, tc.Functions) require.NoError(t, err, "failed to get config") cfg := config.Functions cfgl := config.Logging.Loki @@ -93,7 +93,7 @@ func TestFunctionsLoad(t *testing.T) { }) t.Run("mumbai functions soak test only secrets", func(t *testing.T) { - config, err := tc.GetConfig("SecretsSoak", tc.Functions) + config, err := tc.GetConfig([]string{"SecretsSoak"}, tc.Functions) require.NoError(t, err, "failed to get config") cfg := config.Functions cfgl := config.Logging.Loki @@ -127,7 +127,7 @@ func TestFunctionsLoad(t *testing.T) { }) t.Run("mumbai functions stress test only secrets", func(t *testing.T) { - config, err := tc.GetConfig("SecretsStress", tc.Functions) + config, err := tc.GetConfig([]string{"SecretsStress"}, tc.Functions) require.NoError(t, err, "failed to get config") cfg := config.Functions cfgl := config.Logging.Loki @@ -161,7 +161,7 @@ func TestFunctionsLoad(t *testing.T) { }) t.Run("mumbai functions soak test real", func(t *testing.T) { - config, err := tc.GetConfig("RealSoak", tc.Functions) + config, err := tc.GetConfig([]string{"RealSoak"}, tc.Functions) require.NoError(t, err, "failed to get config") cfg := config.Functions cfgl := config.Logging.Loki @@ -195,7 +195,7 @@ func TestFunctionsLoad(t *testing.T) { }) t.Run("mumbai functions stress test real", func(t *testing.T) { - config, err := tc.GetConfig("RealStress", tc.Functions) + config, err := tc.GetConfig([]string{"RealStress"}, tc.Functions) require.NoError(t, err, "failed to get config") cfg := config.Functions cfgl := config.Logging.Loki diff --git a/integration-tests/load/functions/gateway_test.go b/integration-tests/load/functions/gateway_test.go index c2d5bd7c2c..dd1092a595 100644 --- a/integration-tests/load/functions/gateway_test.go +++ b/integration-tests/load/functions/gateway_test.go @@ -11,7 +11,7 @@ import ( ) func TestGatewayLoad(t *testing.T) { - listConfig, err := tc.GetConfig("GatewayList", tc.Functions) + listConfig, err := tc.GetConfig([]string{"GatewayList"}, tc.Functions) require.NoError(t, err) cfgl := listConfig.Logging.Loki @@ -42,7 +42,7 @@ func TestGatewayLoad(t *testing.T) { LokiConfig: wasp.NewLokiConfig(cfgl.Endpoint, cfgl.TenantId, cfgl.BasicAuth, cfgl.BearerToken), } - setConfig, err := tc.GetConfig("GatewaySet", tc.Functions) + setConfig, err := tc.GetConfig([]string{"GatewaySet"}, tc.Functions) require.NoError(t, err) secretsSetCfg := &wasp.Config{ diff --git a/integration-tests/load/go.mod b/integration-tests/load/go.mod index 44d38aee55..c80766d23a 100644 --- a/integration-tests/load/go.mod +++ b/integration-tests/load/go.mod @@ -16,11 +16,11 @@ require ( github.com/rs/zerolog v1.32.0 github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-automation v1.0.4 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20240625144005-556a7ba7b11c - github.com/smartcontractkit/chainlink-testing-framework v1.31.6 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240702120320-563bf07487fe + github.com/smartcontractkit/chainlink-testing-framework v1.31.7 github.com/smartcontractkit/chainlink/integration-tests v0.0.0-20240214231432-4ad5eb95178c github.com/smartcontractkit/chainlink/v2 v2.9.0-beta0.0.20240216210048-da02459ddad8 - github.com/smartcontractkit/libocr v0.0.0-20240419185742-fd3cab206b2c + github.com/smartcontractkit/libocr v0.0.0-20240702141926-063ceef8c42e github.com/smartcontractkit/seth v1.0.12 github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 github.com/smartcontractkit/wasp v0.4.7 @@ -368,14 +368,14 @@ require ( github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/chain-selectors v1.0.17 // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240621143432-85370a54b141 // indirect - github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 // indirect - github.com/smartcontractkit/chainlink-feeds v0.0.0-20240523182126-1784bd4f3294 // indirect - github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240621151551-885054fb0f0e // indirect - github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240621143321-7e5949418911 // indirect + github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240702144807-761f63e7b527 // indirect + github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917 // indirect + github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240701154249-032706dcb7c8 // indirect + github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240625074951-06ab5e670dba // indirect github.com/smartcontractkit/chainlink-testing-framework/grafana v0.0.0-20240405215812-5a72bc9af239 // indirect github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect - github.com/smartcontractkit/wsrpc v0.8.1 // indirect + github.com/smartcontractkit/wsrpc v0.7.3 // indirect github.com/soheilhy/cmux v0.1.5 // indirect github.com/sony/gobreaker v0.5.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect diff --git a/integration-tests/load/go.sum b/integration-tests/load/go.sum index 967a7e9b88..8363c44371 100644 --- a/integration-tests/load/go.sum +++ b/integration-tests/load/go.sum @@ -762,6 +762,7 @@ github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3 github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -1415,20 +1416,20 @@ github.com/smartcontractkit/chain-selectors v1.0.17 h1:otOlYUnutS8oQBEAi9RLQICqZ github.com/smartcontractkit/chain-selectors v1.0.17/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v1.0.4 h1:iyW181JjKHLNMnDleI8umfIfVVlwC7+n5izbLSFgjw8= github.com/smartcontractkit/chainlink-automation v1.0.4/go.mod h1:u4NbPZKJ5XiayfKHD/v3z3iflQWqvtdhj13jVZXj/cM= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240625144005-556a7ba7b11c h1:dR2y3uzuZ8cJOaMSij8LQIdySuImwxkiZFjZ62ML5S0= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240625144005-556a7ba7b11c/go.mod h1:L32xvCpk84Nglit64OhySPMP1tM3TTBK7Tw0qZl7Sd4= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240702120320-563bf07487fe h1:lYyWmjheglMu0y3JmfSqs9Dm4tZO34RmbUTOtQ4CadE= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240702120320-563bf07487fe/go.mod h1:EWvSuqIJUYXZLEHewC7WCaPylM2jyjF3Q36BZPS4MoI= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240621143432-85370a54b141 h1:TMOoYaeSDkkI3jkCH7lKHOZaLkeDuxFTNC+XblD6M0M= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240621143432-85370a54b141/go.mod h1:0UNuO3nDt9MFsZPaHJBEUolxVkN0iC69j1ccDp95e8k= -github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 h1:xFSv8561jsLtF6gYZr/zW2z5qUUAkcFkApin2mnbYTo= -github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540/go.mod h1:sjAmX8K2kbQhvDarZE1ZZgDgmHJ50s0BBc/66vKY2ek= -github.com/smartcontractkit/chainlink-feeds v0.0.0-20240523182126-1784bd4f3294 h1:4L1878goh1mqJx6JPv/TEeMAgYvbx7+D14QFSSu/DPs= -github.com/smartcontractkit/chainlink-feeds v0.0.0-20240523182126-1784bd4f3294/go.mod h1:jwVxhctE6BgLOSSsVq9wbREpZ8Ev34H+UBxeUhESZRs= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240621151551-885054fb0f0e h1:4dwcbcVrMne6wtOX/4yWdYJwk8iSm0INVNva/EhF35E= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240621151551-885054fb0f0e/go.mod h1:rJ4+TwNMciU2pvx6dsnUNnh4sPI5bUqgmYfhSrYhQ8Y= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240621143321-7e5949418911 h1:TSgCT8Uy+R4o3GwoWUqRCGNKeciub7r7+Aa3ylmg66c= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240621143321-7e5949418911/go.mod h1:OiWUTrrpSLLTMh7FINWjEh6mmDJCVPaC4yEsDCVaWdU= -github.com/smartcontractkit/chainlink-testing-framework v1.31.6 h1:NL91Se5alOyJCzN9cR+osZJJ0nYYgyxMVSK0cSha9wU= -github.com/smartcontractkit/chainlink-testing-framework v1.31.6/go.mod h1:E6uNEZhZZid9PHv6/Kq5Vn63GlO61ZcKB+/f0DKo3Q4= +github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240702144807-761f63e7b527 h1:Vs6myS+bpPwb8chUY7XxveJyhvejknhOmhDTddgsK5I= +github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240702144807-761f63e7b527/go.mod h1:KRK7KlAEpmORi+nJgT0vxQVWvlLEBQ6zgzXziZuKvUM= +github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917 h1:MD80ZRCTvxxJ8PBmhtrKoTnky8cVNYrCrIBLVRbrOM0= +github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917/go.mod h1:jwVxhctE6BgLOSSsVq9wbREpZ8Ev34H+UBxeUhESZRs= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240701154249-032706dcb7c8 h1:JkBap2v5AmU4H9LWVDGr6XKnnDwU0OzX4W7u9aq5PQg= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240701154249-032706dcb7c8/go.mod h1:NbXXQaNFskVMYRut0MvBlcHu/vDgipGMwYjamvjVB9Y= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240625074951-06ab5e670dba h1:YNSlhK5mobyAaw02LPGgIEuS3lXyCTXcc6oaV2L6uUI= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240625074951-06ab5e670dba/go.mod h1:UVFRacRkP7O7TQAzFmR52v5mUlxf+G1ovMlCQAB/cHU= +github.com/smartcontractkit/chainlink-testing-framework v1.31.7 h1:Vy4ah8VAfj+Y7vVmhjvwyAO6wG+Fp2vzdkSJwJPMQO4= +github.com/smartcontractkit/chainlink-testing-framework v1.31.7/go.mod h1:E6uNEZhZZid9PHv6/Kq5Vn63GlO61ZcKB+/f0DKo3Q4= github.com/smartcontractkit/chainlink-testing-framework/grafana v0.0.0-20240405215812-5a72bc9af239 h1:Kk5OVlx/5g9q3Z3lhxytZS4/f8ds1MiNM8yaHgK3Oe8= github.com/smartcontractkit/chainlink-testing-framework/grafana v0.0.0-20240405215812-5a72bc9af239/go.mod h1:DC8sQMyTlI/44UCTL8QWFwb0bYNoXCfjwCv2hMivYZU= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 h1:FFdvEzlYwcuVHkdZ8YnZR/XomeMGbz5E2F2HZI3I3w8= @@ -1437,8 +1438,8 @@ github.com/smartcontractkit/go-plugin v0.0.0-20240208201424-b3b91517de16 h1:TFe+ github.com/smartcontractkit/go-plugin v0.0.0-20240208201424-b3b91517de16/go.mod h1:lBS5MtSSBZk0SHc66KACcjjlU6WzEVP/8pwz68aMkCI= github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f h1:hgJif132UCdjo8u43i7iPN1/MFnu49hv7lFGFftCHKU= github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f/go.mod h1:MvMXoufZAtqExNexqi4cjrNYE9MefKddKylxjS+//n0= -github.com/smartcontractkit/libocr v0.0.0-20240419185742-fd3cab206b2c h1:lIyMbTaF2H0Q71vkwZHX/Ew4KF2BxiKhqEXwF8rn+KI= -github.com/smartcontractkit/libocr v0.0.0-20240419185742-fd3cab206b2c/go.mod h1:fb1ZDVXACvu4frX3APHZaEBp0xi1DIm34DcA0CwTsZM= +github.com/smartcontractkit/libocr v0.0.0-20240702141926-063ceef8c42e h1:9ypZ/8aW8Vm497i1gXHcT96oNLiu88jbg9QdX+IUE3E= +github.com/smartcontractkit/libocr v0.0.0-20240702141926-063ceef8c42e/go.mod h1:fb1ZDVXACvu4frX3APHZaEBp0xi1DIm34DcA0CwTsZM= github.com/smartcontractkit/seth v1.0.12 h1:iVdgMx42XWanPPnBaM5StR4c1XsTr/0/B/kKRZL5BsY= github.com/smartcontractkit/seth v1.0.12/go.mod h1:thWtbLyW4nRHJGzC5heknQDORoJPErE15sF34LHkorg= github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 h1:yiKnypAqP8l0OX0P3klzZ7SCcBUxy5KqTAKZmQOvSQE= @@ -1447,8 +1448,8 @@ github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 h1:D github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1/go.mod h1:G5Sd/yzHWf26rQ+X0nG9E0buKPqRGPMJAfk2gwCzOOw= github.com/smartcontractkit/wasp v0.4.7 h1:7mKJfwzFbuE8xVLUYtLt7Bjw8q/bmVZRW6Ks8kc1LVM= github.com/smartcontractkit/wasp v0.4.7/go.mod h1:jeabvyXikb2aNoLQwcZGqaz17efrR8NJhpq4seAmdgs= -github.com/smartcontractkit/wsrpc v0.8.1 h1:kk0SXLqWrWaZ3J6c7n8D0NZ2uTMBBBpG5dZZXZX8UGE= -github.com/smartcontractkit/wsrpc v0.8.1/go.mod h1:yfg8v8fPLXkb6Mcnx6Pm/snP6jJ0r5Kf762Yd1a/KpA= +github.com/smartcontractkit/wsrpc v0.7.3 h1:CKYZfawZShZGfvsQep1F9oBansnFk9ByZPCdTMpLphw= +github.com/smartcontractkit/wsrpc v0.7.3/go.mod h1:sj7QX2NQibhkhxTfs3KOhAj/5xwgqMipTvJVSssT9i0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= @@ -1704,6 +1705,7 @@ 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.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= +go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= go4.org/netipx v0.0.0-20230125063823-8449b0a6169f h1:ketMxHg+vWm3yccyYiq+uK8D3fRmna2Fcj+awpQp84s= diff --git a/integration-tests/load/ocr/ocr_test.go b/integration-tests/load/ocr/ocr_test.go index 15f52acbff..db90f67b3c 100644 --- a/integration-tests/load/ocr/ocr_test.go +++ b/integration-tests/load/ocr/ocr_test.go @@ -25,7 +25,7 @@ var ( func TestOCRLoad(t *testing.T) { l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Load", tc.OCR) + config, err := tc.GetConfig([]string{"Load"}, tc.OCR) require.NoError(t, err) evmNetwork, msClient, bootstrapNode, workerNodes, err := k8s.ConnectRemote() @@ -61,7 +61,7 @@ func TestOCRLoad(t *testing.T) { func TestOCRVolume(t *testing.T) { l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Volume", tc.OCR) + config, err := tc.GetConfig([]string{"Volume"}, tc.OCR) require.NoError(t, err) evmNetwork, msClient, bootstrapNode, workerNodes, err := k8s.ConnectRemote() diff --git a/integration-tests/load/vrfv2/vrfv2_test.go b/integration-tests/load/vrfv2/vrfv2_test.go index d5612e885e..3338ed3350 100644 --- a/integration-tests/load/vrfv2/vrfv2_test.go +++ b/integration-tests/load/vrfv2/vrfv2_test.go @@ -45,7 +45,7 @@ func TestVRFV2Performance(t *testing.T) { l := logging.GetTestLogger(t) testType, err := tc.GetConfigurationNameFromEnv() require.NoError(t, err) - testConfig, err := tc.GetConfig(testType, tc.VRFv2) + testConfig, err := tc.GetChainAndTestTypeSpecificConfig(testType, tc.VRFv2) require.NoError(t, err) cfgl := testConfig.Logging.Loki @@ -187,7 +187,7 @@ func TestVRFV2BHSPerformance(t *testing.T) { testType, err := tc.GetConfigurationNameFromEnv() require.NoError(t, err) - testConfig, err := tc.GetConfig(testType, tc.VRFv2) + testConfig, err := tc.GetChainAndTestTypeSpecificConfig(testType, tc.VRFv2) require.NoError(t, err) vrfv2Config := testConfig.VRFv2 testReporter := &testreporters.VRFV2TestReporter{} diff --git a/integration-tests/load/vrfv2plus/vrfv2plus_test.go b/integration-tests/load/vrfv2plus/vrfv2plus_test.go index 9ab1e8d745..f9df31613c 100644 --- a/integration-tests/load/vrfv2plus/vrfv2plus_test.go +++ b/integration-tests/load/vrfv2plus/vrfv2plus_test.go @@ -43,7 +43,7 @@ func TestVRFV2PlusPerformance(t *testing.T) { l := logging.GetTestLogger(t) testType, err := tc.GetConfigurationNameFromEnv() require.NoError(t, err) - testConfig, err := tc.GetConfig(testType, tc.VRFv2Plus) + testConfig, err := tc.GetChainAndTestTypeSpecificConfig(testType, tc.VRFv2Plus) require.NoError(t, err) cfgl := testConfig.Logging.Loki @@ -186,7 +186,7 @@ func TestVRFV2PlusBHSPerformance(t *testing.T) { testType, err := tc.GetConfigurationNameFromEnv() require.NoError(t, err) - testConfig, err := tc.GetConfig(testType, tc.VRFv2Plus) + testConfig, err := tc.GetChainAndTestTypeSpecificConfig(testType, tc.VRFv2Plus) require.NoError(t, err) vrfv2PlusConfig := testConfig.VRFv2Plus testReporter := &testreporters.VRFV2PlusTestReporter{} diff --git a/integration-tests/load/zcluster/cluster_entrypoint_test.go b/integration-tests/load/zcluster/cluster_entrypoint_test.go index 35b3ee422c..aace97b082 100644 --- a/integration-tests/load/zcluster/cluster_entrypoint_test.go +++ b/integration-tests/load/zcluster/cluster_entrypoint_test.go @@ -10,7 +10,7 @@ import ( ) func TestClusterEntrypoint(t *testing.T) { - config, err := tc.GetConfig("Load", tc.OCR) + config, err := tc.GetConfig([]string{"Load"}, tc.OCR) require.NoError(t, err) cfgBase64, err := config.AsBase64() require.NoError(t, err) diff --git a/integration-tests/migration/upgrade_version_test.go b/integration-tests/migration/upgrade_version_test.go index 4c7b275e29..a17f00e179 100644 --- a/integration-tests/migration/upgrade_version_test.go +++ b/integration-tests/migration/upgrade_version_test.go @@ -17,7 +17,7 @@ func TestVersionUpgrade(t *testing.T) { l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Migration", tc.Node) + config, err := tc.GetConfig([]string{"Migration"}, tc.Node) require.NoError(t, err, "Error getting config") err = config.ChainlinkUpgradeImage.Validate() diff --git a/integration-tests/reorg/automation_reorg_test.go b/integration-tests/reorg/automation_reorg_test.go index 958923327a..2a2350e195 100644 --- a/integration-tests/reorg/automation_reorg_test.go +++ b/integration-tests/reorg/automation_reorg_test.go @@ -84,7 +84,7 @@ var ( * Upkeeps are expected to be performed during the reorg. */ func TestAutomationReorg(t *testing.T) { - c, err := tc.GetConfig("Reorg", tc.Automation) + c, err := tc.GetConfig([]string{"Reorg"}, tc.Automation) require.NoError(t, err, "Error getting config") findIntValue := func(text string, substring string) (int, error) { @@ -126,7 +126,7 @@ func TestAutomationReorg(t *testing.T) { registryVersion := rv t.Run(name, func(t *testing.T) { t.Parallel() - config, err := tc.GetConfig("Reorg", tc.Automation) + config, err := tc.GetConfig([]string{"Reorg"}, tc.Automation) if err != nil { t.Fatal(err) } @@ -229,6 +229,8 @@ func TestAutomationReorg(t *testing.T) { defaultUpkeepGasLimit, isLogTrigger, false, + false, + nil, ) if isLogTrigger { diff --git a/integration-tests/smoke/automation_test.go b/integration-tests/smoke/automation_test.go index 46abccae56..62462d641a 100644 --- a/integration-tests/smoke/automation_test.go +++ b/integration-tests/smoke/automation_test.go @@ -8,6 +8,7 @@ import ( "net/http" "os" "strconv" + "strings" "testing" "time" @@ -98,19 +99,25 @@ func TestAutomationBasic(t *testing.T) { func SetupAutomationBasic(t *testing.T, nodeUpgrade bool) { t.Parallel() + // native, mercury_v02, mercury_v03 and logtrigger are reserved keywords, use them with caution registryVersions := map[string]ethereum.KeeperRegistryVersion{ - "registry_2_0": ethereum.RegistryVersion_2_0, - "registry_2_1_conditional": ethereum.RegistryVersion_2_1, - "registry_2_1_logtrigger": ethereum.RegistryVersion_2_1, - "registry_2_1_with_mercury_v02": ethereum.RegistryVersion_2_1, - "registry_2_1_with_mercury_v03": ethereum.RegistryVersion_2_1, - "registry_2_1_with_logtrigger_and_mercury_v02": ethereum.RegistryVersion_2_1, - "registry_2_2_conditional": ethereum.RegistryVersion_2_2, - "registry_2_2_logtrigger": ethereum.RegistryVersion_2_2, - "registry_2_2_with_mercury_v02": ethereum.RegistryVersion_2_2, - "registry_2_2_with_mercury_v03": ethereum.RegistryVersion_2_2, - "registry_2_2_with_logtrigger_and_mercury_v02": ethereum.RegistryVersion_2_2, - "registry_2_3_conditional": ethereum.RegistryVersion_2_3, + "registry_2_0": ethereum.RegistryVersion_2_0, + "registry_2_1_conditional": ethereum.RegistryVersion_2_1, + "registry_2_1_logtrigger": ethereum.RegistryVersion_2_1, + "registry_2_1_with_mercury_v02": ethereum.RegistryVersion_2_1, + "registry_2_1_with_mercury_v03": ethereum.RegistryVersion_2_1, + "registry_2_1_with_logtrigger_and_mercury_v02": ethereum.RegistryVersion_2_1, + "registry_2_2_conditional": ethereum.RegistryVersion_2_2, + "registry_2_2_logtrigger": ethereum.RegistryVersion_2_2, + "registry_2_2_with_mercury_v02": ethereum.RegistryVersion_2_2, + "registry_2_2_with_mercury_v03": ethereum.RegistryVersion_2_2, + "registry_2_2_with_logtrigger_and_mercury_v02": ethereum.RegistryVersion_2_2, + "registry_2_3_conditional_native": ethereum.RegistryVersion_2_3, + "registry_2_3_conditional_link": ethereum.RegistryVersion_2_3, + "registry_2_3_logtrigger_native": ethereum.RegistryVersion_2_3, + "registry_2_3_logtrigger_link": ethereum.RegistryVersion_2_3, + "registry_2_3_with_mercury_v03_link": ethereum.RegistryVersion_2_3, + "registry_2_3_with_logtrigger_and_mercury_v02_link": ethereum.RegistryVersion_2_3, } for n, rv := range registryVersions { @@ -120,7 +127,7 @@ func SetupAutomationBasic(t *testing.T, nodeUpgrade bool) { t.Parallel() l := logging.GetTestLogger(t) - cfg, err := tc.GetConfig("Smoke", tc.Automation) + cfg, err := tc.GetConfig([]string{"Smoke"}, tc.Automation) require.NoError(t, err, "Failed to get config") if nodeUpgrade { @@ -129,10 +136,11 @@ func SetupAutomationBasic(t *testing.T, nodeUpgrade bool) { } } - // Use the name to determine if this is a log trigger or mercury - isLogTrigger := name == "registry_2_1_logtrigger" || name == "registry_2_1_with_logtrigger_and_mercury_v02" || name == "registry_2_2_logtrigger" || name == "registry_2_2_with_logtrigger_and_mercury_v02" - isMercuryV02 := name == "registry_2_1_with_mercury_v02" || name == "registry_2_1_with_logtrigger_and_mercury_v02" || name == "registry_2_2_with_mercury_v02" || name == "registry_2_2_with_logtrigger_and_mercury_v02" - isMercuryV03 := name == "registry_2_1_with_mercury_v03" || name == "registry_2_2_with_mercury_v03" + // Use the name to determine if this is a log trigger or mercury or billing token is native + isBillingTokenNative := strings.Contains(name, "native") + isLogTrigger := strings.Contains(name, "logtrigger") + isMercuryV02 := strings.Contains(name, "mercury_v02") + isMercuryV03 := strings.Contains(name, "mercury_v03") isMercury := isMercuryV02 || isMercuryV03 a := setupAutomationTestDocker( @@ -153,6 +161,8 @@ func SetupAutomationBasic(t *testing.T, nodeUpgrade bool) { automationDefaultUpkeepGasLimit, isLogTrigger, isMercury, + isBillingTokenNative, + a.WETHToken, ) // Do it in two separate loops, so we don't end up setting up one upkeep, but starting the consumer for another one @@ -189,8 +199,6 @@ func SetupAutomationBasic(t *testing.T, nodeUpgrade bool) { actions.GetStalenessReportCleanupFn(t, a.Logger, a.ChainClient, sb, a.Registry, registryVersion)() }) - // TODO Tune this timeout window after stress testing - l.Info().Msg("Waiting 10m for all upkeeps to perform at least 1 upkeep") gom.Eventually(func(g gomega.Gomega) { // Check if the upkeeps are performing multiple times by analyzing their counters for i := 0; i < len(upkeepIDs); i++ { @@ -271,7 +279,7 @@ func TestSetUpkeepTriggerConfig(t *testing.T) { registryVersion := rv t.Run(name, func(t *testing.T) { t.Parallel() - config, err := tc.GetConfig("Smoke", tc.Automation) + config, err := tc.GetConfig([]string{"Smoke"}, tc.Automation) require.NoError(t, err, "Failed to get config") a := setupAutomationTestDocker( @@ -292,6 +300,8 @@ func TestSetUpkeepTriggerConfig(t *testing.T) { automationDefaultUpkeepGasLimit, true, false, + false, + nil, ) // Start log trigger based upkeeps for all consumers @@ -453,7 +463,7 @@ func TestAutomationAddFunds(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Smoke", tc.Automation) + config, err := tc.GetConfig([]string{"Smoke"}, tc.Automation) require.NoError(t, err, "Failed to get config") a := setupAutomationTestDocker( t, registryVersion, automationDefaultRegistryConfig(config), false, false, &config, @@ -473,6 +483,8 @@ func TestAutomationAddFunds(t *testing.T) { automationDefaultUpkeepGasLimit, false, false, + false, + nil, ) t.Cleanup(func() { @@ -530,7 +542,7 @@ func TestAutomationPauseUnPause(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Smoke", tc.Automation) + config, err := tc.GetConfig([]string{"Smoke"}, tc.Automation) require.NoError(t, err, "Failed to get config") a := setupAutomationTestDocker( @@ -551,6 +563,8 @@ func TestAutomationPauseUnPause(t *testing.T) { automationDefaultUpkeepGasLimit, false, false, + false, + nil, ) t.Cleanup(func() { @@ -629,7 +643,7 @@ func TestAutomationRegisterUpkeep(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Smoke", tc.Automation) + config, err := tc.GetConfig([]string{"Smoke"}, tc.Automation) require.NoError(t, err, "Failed to get config") a := setupAutomationTestDocker( @@ -650,6 +664,8 @@ func TestAutomationRegisterUpkeep(t *testing.T) { automationDefaultUpkeepGasLimit, false, false, + false, + nil, ) t.Cleanup(func() { @@ -723,7 +739,7 @@ func TestAutomationPauseRegistry(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() - config, err := tc.GetConfig("Smoke", tc.Automation) + config, err := tc.GetConfig([]string{"Smoke"}, tc.Automation) require.NoError(t, err, "Failed to get config") a := setupAutomationTestDocker( @@ -744,6 +760,8 @@ func TestAutomationPauseRegistry(t *testing.T) { automationDefaultUpkeepGasLimit, false, false, + false, + nil, ) t.Cleanup(func() { @@ -801,7 +819,7 @@ func TestAutomationKeeperNodesDown(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Smoke", tc.Automation) + config, err := tc.GetConfig([]string{"Smoke"}, tc.Automation) require.NoError(t, err, "Failed to get config") a := setupAutomationTestDocker( @@ -822,6 +840,8 @@ func TestAutomationKeeperNodesDown(t *testing.T) { automationDefaultUpkeepGasLimit, false, false, + false, + nil, ) t.Cleanup(func() { @@ -907,7 +927,7 @@ func TestAutomationPerformSimulation(t *testing.T) { registryVersion := rv t.Run(name, func(t *testing.T) { t.Parallel() - config, err := tc.GetConfig("Smoke", tc.Automation) + config, err := tc.GetConfig([]string{"Smoke"}, tc.Automation) require.NoError(t, err, "Failed to get config") a := setupAutomationTestDocker( @@ -979,7 +999,7 @@ func TestAutomationCheckPerformGasLimit(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Smoke", tc.Automation) + config, err := tc.GetConfig([]string{"Smoke"}, tc.Automation) require.NoError(t, err, "Failed to get config") a := setupAutomationTestDocker( t, registryVersion, automationDefaultRegistryConfig(config), false, false, &config, @@ -1133,7 +1153,7 @@ func TestUpdateCheckData(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Smoke", tc.Automation) + config, err := tc.GetConfig([]string{"Smoke"}, tc.Automation) require.NoError(t, err, "Failed to get config") a := setupAutomationTestDocker( @@ -1213,7 +1233,7 @@ func TestSetOffchainConfigWithMaxGasPrice(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Smoke", tc.Automation) + config, err := tc.GetConfig([]string{"Smoke"}, tc.Automation) if err != nil { t.Fatal(err) } @@ -1235,6 +1255,8 @@ func TestSetOffchainConfigWithMaxGasPrice(t *testing.T) { automationDefaultUpkeepGasLimit, false, false, + false, + nil, ) t.Cleanup(func() { diff --git a/integration-tests/smoke/automation_test.go_test_list.json b/integration-tests/smoke/automation_test.go_test_list.json index f0b8a1cb60..1b8477ca28 100644 --- a/integration-tests/smoke/automation_test.go_test_list.json +++ b/integration-tests/smoke/automation_test.go_test_list.json @@ -37,9 +37,26 @@ }, { "name": "TestAutomationBasic", - "nodes": 1, + "nodes": 2, + "run":[ + {"name":"registry_2_3_conditional_native"}, + {"name":"registry_2_3_conditional_link"} + ] + }, + { + "name": "TestAutomationBasic", + "nodes": 2, + "run":[ + {"name":"registry_2_3_logtrigger_native"}, + {"name":"registry_2_3_logtrigger_link"} + ] + }, + { + "name": "TestAutomationBasic", + "nodes": 2, "run":[ - {"name":"registry_2_3_conditional"} + {"name":"registry_2_3_with_mercury_v03_link"}, + {"name":"registry_2_3_with_logtrigger_and_mercury_v02_link"} ] }, { diff --git a/integration-tests/smoke/cron_test.go b/integration-tests/smoke/cron_test.go index 6f864b2e49..98c1fe0caf 100644 --- a/integration-tests/smoke/cron_test.go +++ b/integration-tests/smoke/cron_test.go @@ -21,7 +21,7 @@ func TestCronBasic(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Smoke", tc.Cron) + config, err := tc.GetConfig([]string{"Smoke"}, tc.Cron) if err != nil { t.Fatal(err) } @@ -76,7 +76,7 @@ func TestCronJobReplacement(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Smoke", tc.Cron) + config, err := tc.GetConfig([]string{"Smoke"}, tc.Cron) if err != nil { t.Fatal(err) } diff --git a/integration-tests/smoke/flux_test.go b/integration-tests/smoke/flux_test.go index 05b75da223..d8773690b2 100644 --- a/integration-tests/smoke/flux_test.go +++ b/integration-tests/smoke/flux_test.go @@ -27,7 +27,7 @@ func TestFluxBasic(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Smoke", tc.Flux) + config, err := tc.GetConfig([]string{"Smoke"}, tc.Flux) require.NoError(t, err, "Error getting config") privateNetwork, err := actions.EthereumNetworkConfigFromConfig(l, &config) diff --git a/integration-tests/smoke/forwarder_ocr_test.go b/integration-tests/smoke/forwarder_ocr_test.go index 0377553191..a249775dc6 100644 --- a/integration-tests/smoke/forwarder_ocr_test.go +++ b/integration-tests/smoke/forwarder_ocr_test.go @@ -24,7 +24,7 @@ func TestForwarderOCRBasic(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Smoke", tc.ForwarderOcr) + config, err := tc.GetConfig([]string{"Smoke"}, tc.ForwarderOcr) require.NoError(t, err, "Error getting config") privateNetwork, err := actions.EthereumNetworkConfigFromConfig(l, &config) diff --git a/integration-tests/smoke/forwarders_ocr2_test.go b/integration-tests/smoke/forwarders_ocr2_test.go index 3970a0b43b..863b36e4ed 100644 --- a/integration-tests/smoke/forwarders_ocr2_test.go +++ b/integration-tests/smoke/forwarders_ocr2_test.go @@ -25,7 +25,7 @@ func TestForwarderOCR2Basic(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Smoke", tc.ForwarderOcr2) + config, err := tc.GetConfig([]string{"Smoke"}, tc.ForwarderOcr2) require.NoError(t, err, "Error getting config") privateNetwork, err := actions.EthereumNetworkConfigFromConfig(l, &config) diff --git a/integration-tests/smoke/keeper_test.go b/integration-tests/smoke/keeper_test.go index 21e1aa6e08..4ff1c90bd1 100644 --- a/integration-tests/smoke/keeper_test.go +++ b/integration-tests/smoke/keeper_test.go @@ -89,7 +89,7 @@ func TestKeeperBasicSmoke(t *testing.T) { t.Run(fmt.Sprintf("registry_1_%d", registryVersion), func(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Smoke", tc.Keeper) + config, err := tc.GetConfig([]string{"Smoke"}, tc.Keeper) require.NoError(t, err, "Failed to get config") chainClient, chainlinkNodes, linkToken, _ := setupKeeperTest(l, t, &config) @@ -170,7 +170,7 @@ func TestKeeperBlockCountPerTurn(t *testing.T) { t.Run(fmt.Sprintf("registry_1_%d", registryVersion), func(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Smoke", tc.Keeper) + config, err := tc.GetConfig([]string{"Smoke"}, tc.Keeper) require.NoError(t, err, "Failed to get config") chainClient, chainlinkNodes, linkToken, _ := setupKeeperTest(l, t, &config) @@ -314,7 +314,7 @@ func TestKeeperSimulation(t *testing.T) { t.Run(fmt.Sprintf("registry_1_%d", registryVersion), func(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Smoke", tc.Keeper) + config, err := tc.GetConfig([]string{"Smoke"}, tc.Keeper) require.NoError(t, err, "Failed to get config") chainClient, chainlinkNodes, linkToken, _ := setupKeeperTest(l, t, &config) @@ -393,7 +393,7 @@ func TestKeeperCheckPerformGasLimit(t *testing.T) { t.Run(fmt.Sprintf("registry_1_%d", registryVersion), func(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Smoke", tc.Keeper) + config, err := tc.GetConfig([]string{"Smoke"}, tc.Keeper) require.NoError(t, err, "Failed to get config") chainClient, chainlinkNodes, linkToken, _ := setupKeeperTest(l, t, &config) @@ -530,7 +530,7 @@ func TestKeeperRegisterUpkeep(t *testing.T) { t.Run(fmt.Sprintf("registry_1_%d", registryVersion), func(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Smoke", tc.Keeper) + config, err := tc.GetConfig([]string{"Smoke"}, tc.Keeper) require.NoError(t, err, "Failed to get config") chainClient, chainlinkNodes, linkToken, _ := setupKeeperTest(l, t, &config) @@ -626,7 +626,7 @@ func TestKeeperAddFunds(t *testing.T) { t.Run(fmt.Sprintf("registry_1_%d", registryVersion), func(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Smoke", tc.Keeper) + config, err := tc.GetConfig([]string{"Smoke"}, tc.Keeper) require.NoError(t, err, "Failed to get config") chainClient, chainlinkNodes, linkToken, _ := setupKeeperTest(l, t, &config) @@ -701,7 +701,7 @@ func TestKeeperRemove(t *testing.T) { t.Run(fmt.Sprintf("registry_1_%d", registryVersion), func(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Smoke", tc.Keeper) + config, err := tc.GetConfig([]string{"Smoke"}, tc.Keeper) require.NoError(t, err, "Failed to get config") chainClient, chainlinkNodes, linkToken, _ := setupKeeperTest(l, t, &config) @@ -786,7 +786,7 @@ func TestKeeperPauseRegistry(t *testing.T) { t.Run(fmt.Sprintf("registry_1_%d", registryVersion), func(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Smoke", tc.Keeper) + config, err := tc.GetConfig([]string{"Smoke"}, tc.Keeper) require.NoError(t, err, "Failed to get config") chainClient, chainlinkNodes, linkToken, _ := setupKeeperTest(l, t, &config) @@ -853,7 +853,7 @@ func TestKeeperPauseRegistry(t *testing.T) { func TestKeeperMigrateRegistry(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Smoke", tc.Keeper) + config, err := tc.GetConfig([]string{"Smoke"}, tc.Keeper) require.NoError(t, err, "Error getting config") chainClient, chainlinkNodes, linkToken, _ := setupKeeperTest(l, t, &config) @@ -956,7 +956,7 @@ func TestKeeperNodeDown(t *testing.T) { t.Run(fmt.Sprintf("registry_1_%d", registryVersion), func(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Smoke", tc.Keeper) + config, err := tc.GetConfig([]string{"Smoke"}, tc.Keeper) require.NoError(t, err, "Failed to get config") chainClient, chainlinkNodes, linkToken, _ := setupKeeperTest(l, t, &config) @@ -1066,7 +1066,7 @@ type nodeAndJob struct { func TestKeeperPauseUnPauseUpkeep(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Smoke", tc.Keeper) + config, err := tc.GetConfig([]string{"Smoke"}, tc.Keeper) require.NoError(t, err, "Failed to get config") chainClient, chainlinkNodes, linkToken, _ := setupKeeperTest(l, t, &config) @@ -1158,7 +1158,7 @@ func TestKeeperPauseUnPauseUpkeep(t *testing.T) { func TestKeeperUpdateCheckData(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Smoke", tc.Keeper) + config, err := tc.GetConfig([]string{"Smoke"}, tc.Keeper) require.NoError(t, err, "Failed to get config") chainClient, chainlinkNodes, linkToken, _ := setupKeeperTest(l, t, &config) @@ -1264,7 +1264,7 @@ func TestKeeperJobReplacement(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) registryVersion := ethereum.RegistryVersion_1_3 - config, err := tc.GetConfig("Smoke", tc.Keeper) + config, err := tc.GetConfig([]string{"Smoke"}, tc.Keeper) require.NoError(t, err, "Failed to get config") chainClient, chainlinkNodes, linkToken, _ := setupKeeperTest(l, t, &config) diff --git a/integration-tests/smoke/log_poller_test.go b/integration-tests/smoke/log_poller_test.go index 05be29a90b..f1a257552a 100644 --- a/integration-tests/smoke/log_poller_test.go +++ b/integration-tests/smoke/log_poller_test.go @@ -91,7 +91,7 @@ func TestLogPollerReplayFinalityTag(t *testing.T) { // HELPER FUNCTIONS func executeBasicLogPollerTest(t *testing.T, logScannerSettings test_env.ChainlinkNodeLogScannerSettings) { - testConfig, err := tc.GetConfig(t.Name(), tc.LogPoller) + testConfig, err := tc.GetConfig([]string{t.Name()}, tc.LogPoller) require.NoError(t, err, "Error getting config") overrideEphemeralAddressesCount(&testConfig) @@ -174,7 +174,7 @@ func executeBasicLogPollerTest(t *testing.T, logScannerSettings test_env.Chainli } func executeLogPollerReplay(t *testing.T, consistencyTimeout string) { - testConfig, err := tc.GetConfig(t.Name(), tc.LogPoller) + testConfig, err := tc.GetConfig([]string{t.Name()}, tc.LogPoller) require.NoError(t, err, "Error getting config") overrideEphemeralAddressesCount(&testConfig) @@ -317,6 +317,8 @@ func prepareEnvironment(l zerolog.Logger, t *testing.T, testConfig *tc.TestConfi uint32(2500000), true, false, + false, + nil, ) err = logpoller.AssertUpkeepIdsUniqueness(upkeepIDs) diff --git a/integration-tests/smoke/ocr2_test.go b/integration-tests/smoke/ocr2_test.go index 09a9a22249..56a95c50bd 100644 --- a/integration-tests/smoke/ocr2_test.go +++ b/integration-tests/smoke/ocr2_test.go @@ -139,7 +139,7 @@ func TestOCRv2JobReplacement(t *testing.T) { } func prepareORCv2SmokeTestEnv(t *testing.T, testData ocr2test, l zerolog.Logger, firstRoundResult int) (*test_env.CLClusterTestEnv, []contracts.OffchainAggregatorV2, *seth.Client) { - config, err := tc.GetConfig("Smoke", tc.OCR2) + config, err := tc.GetConfig([]string{"Smoke"}, tc.OCR2) if err != nil { t.Fatal(err) } diff --git a/integration-tests/smoke/ocr2vrf_test.go b/integration-tests/smoke/ocr2vrf_test.go index 5b263dc144..bf81e22b40 100644 --- a/integration-tests/smoke/ocr2vrf_test.go +++ b/integration-tests/smoke/ocr2vrf_test.go @@ -34,7 +34,7 @@ func TestOCR2VRFRedeemModel(t *testing.T) { // remember to add TOML testConfig for Chainlink node before trying to run this test in future t.Skip("VRFv3 is on pause, skipping") l := logging.GetTestLogger(t) - testConfig, err := testconfig.GetConfig("Smoke", testconfig.OCR2VRF) + testConfig, err := testconfig.GetConfig([]string{"Smoke"}, testconfig.OCR2VRF) require.NoError(t, err, "Error getting config") testEnvironment, testNetwork := setupOCR2VRFEnvironment(t) @@ -95,7 +95,7 @@ func TestOCR2VRFFulfillmentModel(t *testing.T) { t.Parallel() t.Skip("VRFv3 is on pause, skipping") l := logging.GetTestLogger(t) - testConfig, err := testconfig.GetConfig("Smoke", testconfig.OCR2VRF) + testConfig, err := testconfig.GetConfig([]string{"Smoke"}, testconfig.OCR2VRF) require.NoError(t, err, "Error getting config") testEnvironment, testNetwork := setupOCR2VRFEnvironment(t) @@ -153,7 +153,7 @@ func TestOCR2VRFFulfillmentModel(t *testing.T) { func setupOCR2VRFEnvironment(t *testing.T) (testEnvironment *environment.Environment, testNetwork blockchain.EVMNetwork) { if ocr2vrfSmokeConfig == nil { - c, err := testconfig.GetConfig("Smoke", testconfig.OCR2VRF) + c, err := testconfig.GetConfig([]string{"Smoke"}, testconfig.OCR2VRF) require.NoError(t, err, "Error getting config") ocr2vrfSmokeConfig = &c } diff --git a/integration-tests/smoke/ocr_test.go b/integration-tests/smoke/ocr_test.go index d989517278..67f8c44f8a 100644 --- a/integration-tests/smoke/ocr_test.go +++ b/integration-tests/smoke/ocr_test.go @@ -80,7 +80,7 @@ func TestOCRJobReplacement(t *testing.T) { } func prepareORCv1SmokeTestEnv(t *testing.T, l zerolog.Logger, firstRoundResult int64) (*test_env.CLClusterTestEnv, []contracts.OffchainAggregator, *seth.Client) { - config, err := tc.GetConfig("Smoke", tc.OCR) + config, err := tc.GetConfig([]string{"Smoke"}, tc.OCR) if err != nil { t.Fatal(err) } diff --git a/integration-tests/smoke/reorg_above_finality_test.go b/integration-tests/smoke/reorg_above_finality_test.go index 829aa66fdc..e7b9e4a218 100644 --- a/integration-tests/smoke/reorg_above_finality_test.go +++ b/integration-tests/smoke/reorg_above_finality_test.go @@ -19,7 +19,7 @@ func TestReorgAboveFinality_FinalityTagDisabled(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - config, err := tc.GetConfig(t.Name(), tc.LogPoller) + config, err := tc.GetConfig([]string{t.Name()}, tc.LogPoller) require.NoError(t, err, "Error getting config") privateNetworkConf, err := actions.EthereumNetworkConfigFromConfig(l, &config) diff --git a/integration-tests/smoke/runlog_test.go b/integration-tests/smoke/runlog_test.go index 862ac911b3..515d9dea33 100644 --- a/integration-tests/smoke/runlog_test.go +++ b/integration-tests/smoke/runlog_test.go @@ -28,7 +28,7 @@ func TestRunLogBasic(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Smoke", tc.RunLog) + config, err := tc.GetConfig([]string{"Smoke"}, tc.RunLog) require.NoError(t, err, "Error getting config") privateNetwork, err := actions.EthereumNetworkConfigFromConfig(l, &config) diff --git a/integration-tests/smoke/vrf_test.go b/integration-tests/smoke/vrf_test.go index 0f706f1576..04e760796d 100644 --- a/integration-tests/smoke/vrf_test.go +++ b/integration-tests/smoke/vrf_test.go @@ -186,7 +186,7 @@ func TestVRFJobReplacement(t *testing.T) { } func prepareVRFtestEnv(t *testing.T, l zerolog.Logger) (*test_env.CLClusterTestEnv, *vrfv1.Contracts, *seth.Client) { - config, err := tc.GetConfig("Smoke", tc.VRF) + config, err := tc.GetChainAndTestTypeSpecificConfig("Smoke", tc.VRF) require.NoError(t, err, "Error getting config") privateNetwork, err := actions.EthereumNetworkConfigFromConfig(l, &config) diff --git a/integration-tests/smoke/vrfv2_test.go b/integration-tests/smoke/vrfv2_test.go index de40322672..8f96fe1767 100644 --- a/integration-tests/smoke/vrfv2_test.go +++ b/integration-tests/smoke/vrfv2_test.go @@ -53,7 +53,7 @@ func TestVRFv2Basic(t *testing.T) { ) l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Smoke", tc.VRFv2) + config, err := tc.GetChainAndTestTypeSpecificConfig("Smoke", tc.VRFv2) require.NoError(t, err, "Error getting config") vrfv2Config := config.VRFv2 chainID := networks.MustGetSelectedNetworkConfig(config.GetNetworkConfig())[0].ChainID @@ -570,7 +570,7 @@ func TestVRFv2MultipleSendingKeys(t *testing.T) { ) l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Smoke", tc.VRFv2) + config, err := tc.GetChainAndTestTypeSpecificConfig("Smoke", tc.VRFv2) if err != nil { t.Fatal(err) } @@ -678,7 +678,7 @@ func TestVRFOwner(t *testing.T) { ) l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Smoke", tc.VRFv2) + config, err := tc.GetChainAndTestTypeSpecificConfig("Smoke", tc.VRFv2) require.NoError(t, err, "Error getting config") chainID := networks.MustGetSelectedNetworkConfig(config.GetNetworkConfig())[0].ChainID vrfv2Config := config.VRFv2 @@ -814,7 +814,7 @@ func TestVRFV2WithBHS(t *testing.T) { ) l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Smoke", tc.VRFv2) + config, err := tc.GetChainAndTestTypeSpecificConfig("Smoke", tc.VRFv2) require.NoError(t, err, "Error getting config") vrfv2Config := config.VRFv2 chainID := networks.MustGetSelectedNetworkConfig(config.GetNetworkConfig())[0].ChainID @@ -1032,7 +1032,7 @@ func TestVRFV2NodeReorg(t *testing.T) { ) l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Smoke", tc.VRFv2) + config, err := tc.GetChainAndTestTypeSpecificConfig("Smoke", tc.VRFv2) require.NoError(t, err, "Error getting config") vrfv2Config := config.VRFv2 network := networks.MustGetSelectedNetworkConfig(config.GetNetworkConfig())[0] @@ -1211,7 +1211,7 @@ func TestVRFv2BatchFulfillmentEnabledDisabled(t *testing.T) { ) l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Smoke", tc.VRFv2) + config, err := tc.GetChainAndTestTypeSpecificConfig("Smoke", tc.VRFv2) require.NoError(t, err, "Error getting config") vrfv2Config := config.VRFv2 network := networks.MustGetSelectedNetworkConfig(config.GetNetworkConfig())[0] diff --git a/integration-tests/smoke/vrfv2plus_test.go b/integration-tests/smoke/vrfv2plus_test.go index b888f56c1a..89a67b6454 100644 --- a/integration-tests/smoke/vrfv2plus_test.go +++ b/integration-tests/smoke/vrfv2plus_test.go @@ -50,8 +50,9 @@ func TestVRFv2Plus(t *testing.T) { ) l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Smoke", tc.VRFv2Plus) + config, err := tc.GetChainAndTestTypeSpecificConfig("Smoke", tc.VRFv2Plus) require.NoError(t, err, "Error getting config") + vrfv2PlusConfig := config.VRFv2Plus chainID := networks.MustGetSelectedNetworkConfig(config.GetNetworkConfig())[0].ChainID @@ -746,7 +747,7 @@ func TestVRFv2PlusMultipleSendingKeys(t *testing.T) { ) l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Smoke", tc.VRFv2Plus) + config, err := tc.GetChainAndTestTypeSpecificConfig("Smoke", tc.VRFv2Plus) require.NoError(t, err, "Error getting config") vrfv2PlusConfig := config.VRFv2Plus chainID := networks.MustGetSelectedNetworkConfig(config.GetNetworkConfig())[0].ChainID @@ -851,7 +852,7 @@ func TestVRFv2PlusMigration(t *testing.T) { ) l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Smoke", tc.VRFv2Plus) + config, err := tc.GetChainAndTestTypeSpecificConfig("Smoke", tc.VRFv2Plus) require.NoError(t, err, "Error getting config") vrfv2PlusConfig := config.VRFv2Plus chainID := networks.MustGetSelectedNetworkConfig(config.GetNetworkConfig())[0].ChainID @@ -1245,7 +1246,7 @@ func TestVRFV2PlusWithBHS(t *testing.T) { ) l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Smoke", tc.VRFv2Plus) + config, err := tc.GetChainAndTestTypeSpecificConfig("Smoke", tc.VRFv2Plus) require.NoError(t, err, "Error getting config") vrfv2PlusConfig := config.VRFv2Plus chainID := networks.MustGetSelectedNetworkConfig(config.GetNetworkConfig())[0].ChainID @@ -1474,7 +1475,7 @@ func TestVRFV2PlusWithBHF(t *testing.T) { ) l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Smoke", tc.VRFv2Plus) + config, err := tc.GetChainAndTestTypeSpecificConfig("Smoke", tc.VRFv2Plus) require.NoError(t, err, "Error getting config") vrfv2PlusConfig := config.VRFv2Plus chainID := networks.MustGetSelectedNetworkConfig(config.GetNetworkConfig())[0].ChainID @@ -1631,7 +1632,7 @@ func TestVRFv2PlusReplayAfterTimeout(t *testing.T) { ) l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Smoke", tc.VRFv2Plus) + config, err := tc.GetChainAndTestTypeSpecificConfig("Smoke", tc.VRFv2Plus) require.NoError(t, err, "Error getting config") vrfv2PlusConfig := config.VRFv2Plus chainID := networks.MustGetSelectedNetworkConfig(config.GetNetworkConfig())[0].ChainID @@ -1800,7 +1801,7 @@ func TestVRFv2PlusPendingBlockSimulationAndZeroConfirmationDelays(t *testing.T) ) l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Smoke", tc.VRFv2Plus) + config, err := tc.GetChainAndTestTypeSpecificConfig("Smoke", tc.VRFv2Plus) require.NoError(t, err, "Error getting config") vrfv2PlusConfig := config.VRFv2Plus chainID := networks.MustGetSelectedNetworkConfig(config.GetNetworkConfig())[0].ChainID @@ -1894,7 +1895,7 @@ func TestVRFv2PlusNodeReorg(t *testing.T) { ) l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Smoke", tc.VRFv2Plus) + config, err := tc.GetChainAndTestTypeSpecificConfig("Smoke", tc.VRFv2Plus) require.NoError(t, err, "Error getting config") vrfv2PlusConfig := config.VRFv2Plus network := networks.MustGetSelectedNetworkConfig(config.GetNetworkConfig())[0] @@ -2070,7 +2071,7 @@ func TestVRFv2PlusBatchFulfillmentEnabledDisabled(t *testing.T) { ) l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Smoke", tc.VRFv2Plus) + config, err := tc.GetChainAndTestTypeSpecificConfig("Smoke", tc.VRFv2Plus) require.NoError(t, err, "Error getting config") vrfv2PlusConfig := config.VRFv2Plus network := networks.MustGetSelectedNetworkConfig(config.GetNetworkConfig())[0] diff --git a/integration-tests/soak/forwarder_ocr_test.go b/integration-tests/soak/forwarder_ocr_test.go index fa9752a9a9..dd7eb10217 100644 --- a/integration-tests/soak/forwarder_ocr_test.go +++ b/integration-tests/soak/forwarder_ocr_test.go @@ -15,7 +15,7 @@ import ( func TestForwarderOCRv1Soak(t *testing.T) { //nolint:revive t.Fatalf("This test is disabled because the implementation is broken") - config, err := tc.GetConfig("Soak", tc.ForwarderOcr) + config, err := tc.GetConfig([]string{"Soak"}, tc.ForwarderOcr) require.NoError(t, err, "Error getting config") executeForwarderOCRSoakTest(t, &config) @@ -24,7 +24,7 @@ func TestForwarderOCRv1Soak(t *testing.T) { func TestForwarderOCRv2Soak(t *testing.T) { //nolint:revive t.Fatalf("This test is disabled because the implementation is broken") - config, err := tc.GetConfig("Soak", tc.ForwarderOcr2) + config, err := tc.GetConfig([]string{"Soak"}, tc.ForwarderOcr2) require.NoError(t, err, "Error getting config") executeForwarderOCRSoakTest(t, &config) diff --git a/integration-tests/soak/ocr_test.go b/integration-tests/soak/ocr_test.go index 0e7b50d15b..1f437e565e 100644 --- a/integration-tests/soak/ocr_test.go +++ b/integration-tests/soak/ocr_test.go @@ -25,7 +25,7 @@ import ( ) func TestOCRv1Soak(t *testing.T) { - config, err := tc.GetConfig("Soak", tc.OCR) + config, err := tc.GetConfig([]string{"Soak"}, tc.OCR) require.NoError(t, err, "Error getting config") ocrSoakTest, err := testsetups.NewOCRSoakTest(t, &config) require.NoError(t, err, "Error creating OCR soak test") @@ -33,7 +33,7 @@ func TestOCRv1Soak(t *testing.T) { } func TestOCRv2Soak(t *testing.T) { - config, err := tc.GetConfig("Soak", tc.OCR2) + config, err := tc.GetConfig([]string{"Soak"}, tc.OCR2) require.NoError(t, err, "Error getting config") ocrSoakTest, err := testsetups.NewOCRSoakTest(t, &config) @@ -42,7 +42,7 @@ func TestOCRv2Soak(t *testing.T) { } func TestOCRSoak_GethReorgBelowFinality_FinalityTagDisabled(t *testing.T) { - config, err := tc.GetConfig(t.Name(), tc.OCR) + config, err := tc.GetConfig([]string{t.Name()}, tc.OCR) require.NoError(t, err, "Error getting config") ocrSoakTest, err := testsetups.NewOCRSoakTest(t, &config) require.NoError(t, err, "Error creating OCR soak test") @@ -50,7 +50,7 @@ func TestOCRSoak_GethReorgBelowFinality_FinalityTagDisabled(t *testing.T) { } func TestOCRSoak_GethReorgBelowFinality_FinalityTagEnabled(t *testing.T) { - config, err := tc.GetConfig(t.Name(), tc.OCR) + config, err := tc.GetConfig([]string{t.Name()}, tc.OCR) require.NoError(t, err, "Error getting config") ocrSoakTest, err := testsetups.NewOCRSoakTest(t, &config) require.NoError(t, err, "Error creating OCR soak test") @@ -58,7 +58,7 @@ func TestOCRSoak_GethReorgBelowFinality_FinalityTagEnabled(t *testing.T) { } func TestOCRSoak_GasSpike(t *testing.T) { - config, err := tc.GetConfig(t.Name(), tc.OCR) + config, err := tc.GetConfig([]string{t.Name()}, tc.OCR) require.NoError(t, err, "Error getting config") ocrSoakTest, err := testsetups.NewOCRSoakTest(t, &config) require.NoError(t, err, "Error creating OCR soak test") @@ -67,7 +67,7 @@ func TestOCRSoak_GasSpike(t *testing.T) { // TestOCRSoak_ChangeBlockGasLimit changes next block gas limit and sets it to percentage of last gasUsed in previous block creating congestion func TestOCRSoak_ChangeBlockGasLimit(t *testing.T) { - config, err := tc.GetConfig(t.Name(), tc.OCR) + config, err := tc.GetConfig([]string{t.Name()}, tc.OCR) require.NoError(t, err, "Error getting config") ocrSoakTest, err := testsetups.NewOCRSoakTest(t, &config) require.NoError(t, err, "Error creating OCR soak test") @@ -76,7 +76,7 @@ func TestOCRSoak_ChangeBlockGasLimit(t *testing.T) { // TestOCRSoak_RPCDownForAllCLNodes simulates a network chaos by bringing down network to RPC node for all Chainlink Nodes func TestOCRSoak_RPCDownForAllCLNodes(t *testing.T) { - config, err := tc.GetConfig(t.Name(), tc.OCR) + config, err := tc.GetConfig([]string{t.Name()}, tc.OCR) require.NoError(t, err, "Error getting config") require.True(t, config.Network.IsSimulatedGethSelected(), "This test requires simulated geth") @@ -108,7 +108,7 @@ func TestOCRSoak_RPCDownForAllCLNodes(t *testing.T) { // TestOCRSoak_RPCDownForAllCLNodes simulates a network chaos by bringing down network to RPC node for 50% of Chainlink Nodes func TestOCRSoak_RPCDownForHalfCLNodes(t *testing.T) { - config, err := tc.GetConfig(t.Name(), tc.OCR) + config, err := tc.GetConfig([]string{t.Name()}, tc.OCR) require.NoError(t, err, "Error getting config") require.True(t, config.Network.IsSimulatedGethSelected(), "This test requires simulated geth") @@ -149,9 +149,6 @@ func executeOCRSoakTest(t *testing.T, test *testsetups.OCRSoakTest, config *tc.T require.NoError(t, err, "Error creating seth client") } - if !test.Interrupted() { - test.DeployEnvironment(config) - } l.Info().Str("test", t.Name()).Msg("Starting OCR soak test") if !test.Interrupted() { test.DeployEnvironment(config) diff --git a/integration-tests/testconfig/configs_embed.go b/integration-tests/testconfig/configs_embed.go index 67e954ff50..0dbd2b29e6 100644 --- a/integration-tests/testconfig/configs_embed.go +++ b/integration-tests/testconfig/configs_embed.go @@ -7,12 +7,17 @@ import "embed" //go:embed default.toml //go:embed automation/automation.toml +//go:embed forwarder_ocr/forwarder_ocr.toml +//go:embed forwarder_ocr2/forwarder_ocr2.toml //go:embed functions/functions.toml //go:embed keeper/keeper.toml //go:embed log_poller/log_poller.toml //go:embed node/node.toml //go:embed ocr/ocr.toml +//go:embed ocr2/ocr2.toml +//go:embed vrf/vrf.toml //go:embed vrfv2/vrfv2.toml +//go:embed ocr2vrf/ocr2vrf.toml //go:embed vrfv2plus/vrfv2plus.toml var embeddedConfigsFs embed.FS diff --git a/integration-tests/testconfig/testconfig.go b/integration-tests/testconfig/testconfig.go index 49fdd70185..c86ab3333f 100644 --- a/integration-tests/testconfig/testconfig.go +++ b/integration-tests/testconfig/testconfig.go @@ -86,7 +86,7 @@ type TestConfig struct { VRFv2 *vrfv2_config.Config `toml:"VRFv2"` VRFv2Plus *vrfv2plus_config.Config `toml:"VRFv2Plus"` - ConfigurationName string `toml:"-"` + ConfigurationNames []string `toml:"-"` } var embeddedConfigs embed.FS @@ -198,8 +198,8 @@ func (c TestConfig) GetOCRConfig() *ocr_config.Config { return c.OCR } -func (c TestConfig) GetConfigurationName() string { - return c.ConfigurationName +func (c TestConfig) GetConfigurationNames() []string { + return c.ConfigurationNames } func (c TestConfig) GetSethConfig() *seth.Config { @@ -273,15 +273,23 @@ func GetConfigurationNameFromEnv() (string, error) { const ( Base64OverrideEnvVarName = k8s_config.EnvBase64ConfigOverride - NoKey = "NO_KEY" ) -func GetConfig(configurationName string, product Product) (TestConfig, error) { +func GetConfig(configurationNames []string, product Product) (TestConfig, error) { logger := logging.GetTestLogger(nil) - configurationName = strings.ReplaceAll(configurationName, "/", "_") - configurationName = strings.ReplaceAll(configurationName, " ", "_") - configurationName = cases.Title(language.English, cases.NoLower).String(configurationName) + for idx, configurationName := range configurationNames { + configurationNames[idx] = strings.ReplaceAll(configurationName, "/", "_") + configurationNames[idx] = strings.ReplaceAll(configurationName, " ", "_") + configurationNames[idx] = cases.Title(language.English, cases.NoLower).String(configurationName) + } + + // add unnamed (default) configuration as the first one to be read + configurationNamesCopy := make([]string, len(configurationNames)) + copy(configurationNamesCopy, configurationNames) + + configurationNames = append([]string{""}, configurationNamesCopy...) + fileNames := []string{ "default.toml", fmt.Sprintf("%s.toml", product), @@ -289,8 +297,9 @@ func GetConfig(configurationName string, product Product) (TestConfig, error) { } testConfig := TestConfig{} - testConfig.ConfigurationName = configurationName - logger.Debug().Msgf("Will apply configuration named '%s' if it is found in any of the configs", configurationName) + testConfig.ConfigurationNames = configurationNames + + logger.Debug().Msgf("Will apply configurations named '%s' if they are found in any of the configs", strings.Join(configurationNames, ",")) // read embedded configs is build tag "embed" is set // this makes our life much easier when using a binary @@ -306,9 +315,11 @@ func GetConfig(configurationName string, product Product) (TestConfig, error) { return TestConfig{}, errors.Wrapf(err, "error reading embedded config") } - err = ctf_config.BytesToAnyTomlStruct(logger, fileName, configurationName, &testConfig, file) - if err != nil { - return TestConfig{}, errors.Wrapf(err, "error unmarshalling embedded config") + for _, configurationName := range configurationNames { + err = ctf_config.BytesToAnyTomlStruct(logger, fileName, configurationName, &testConfig, file) + if err != nil { + return TestConfig{}, errors.Wrapf(err, "error unmarshalling embedded config %s", embeddedFiles) + } } } } else { @@ -330,9 +341,11 @@ func GetConfig(configurationName string, product Product) (TestConfig, error) { return TestConfig{}, errors.Wrapf(err, "error reading file %s", filePath) } - err = ctf_config.BytesToAnyTomlStruct(logger, fileName, configurationName, &testConfig, content) - if err != nil { - return TestConfig{}, errors.Wrapf(err, "error reading file %s", filePath) + for _, configurationName := range configurationNames { + err = ctf_config.BytesToAnyTomlStruct(logger, fileName, configurationName, &testConfig, content) + if err != nil { + return TestConfig{}, errors.Wrapf(err, "error reading file %s", filePath) + } } } } @@ -346,9 +359,11 @@ func GetConfig(configurationName string, product Product) (TestConfig, error) { return TestConfig{}, err } - err = ctf_config.BytesToAnyTomlStruct(logger, Base64OverrideEnvVarName, configurationName, &testConfig, decoded) - if err != nil { - return TestConfig{}, errors.Wrapf(err, "error unmarshaling base64 config") + for _, configurationName := range configurationNames { + err = ctf_config.BytesToAnyTomlStruct(logger, Base64OverrideEnvVarName, configurationName, &testConfig, decoded) + if err != nil { + return TestConfig{}, errors.Wrapf(err, "error unmarshaling base64 config") + } } } else { logger.Debug().Msg("Base64 config override from environment variable not found") diff --git a/integration-tests/testconfig/testconfig_test.go b/integration-tests/testconfig/testconfig_test.go index fd5230dac2..91d2524fcf 100644 --- a/integration-tests/testconfig/testconfig_test.go +++ b/integration-tests/testconfig/testconfig_test.go @@ -76,7 +76,7 @@ func TestBase64ConfigRead(t *testing.T) { testConfigEncoded := base64.StdEncoding.EncodeToString(configMarshalled) os.Setenv(Base64OverrideEnvVarName, testConfigEncoded) - readConfig, err := GetConfig("test", Automation) + readConfig, err := GetConfig([]string{"test"}, Automation) require.NoError(t, err, "Error reading config") require.NotNil(t, readConfig.Automation, "Automation config read from base64 is nil") diff --git a/integration-tests/testconfig/testconfig_utils.go b/integration-tests/testconfig/testconfig_utils.go index d4290689bc..6529771f49 100644 --- a/integration-tests/testconfig/testconfig_utils.go +++ b/integration-tests/testconfig/testconfig_utils.go @@ -68,3 +68,22 @@ selected_networks=` return fmt.Errorf(finalErrStr) } + +func GetChainAndTestTypeSpecificConfig(testType string, product Product) (TestConfig, error) { + config, err := GetConfig([]string{testType}, product) + if err != nil { + return TestConfig{}, fmt.Errorf("error getting config: %w", err) + } + config, err = GetConfig( + []string{ + testType, + config.GetNetworkConfig().SelectedNetworks[0], + fmt.Sprintf("%s-%s", config.GetNetworkConfig().SelectedNetworks[0], testType), + }, + product, + ) + if err != nil { + return TestConfig{}, fmt.Errorf("error getting config: %w", err) + } + return config, err +} diff --git a/integration-tests/testconfig/vrfv2plus/vrfv2plus.toml b/integration-tests/testconfig/vrfv2plus/vrfv2plus.toml index 717a62e997..1d2af76084 100644 --- a/integration-tests/testconfig/vrfv2plus/vrfv2plus.toml +++ b/integration-tests/testconfig/vrfv2plus/vrfv2plus.toml @@ -143,14 +143,6 @@ bhs_test_duration = "10s" bhs_test_rate_limit_unit_duration = "3s" bhs_test_rps = 1 -[Smoke.VRFv2.Performance] -test_duration = "10s" -rate_limit_unit_duration = "3s" -rps = 1 -bhs_test_duration = "10s" -bhs_test_rate_limit_unit_duration = "3s" -bhs_test_rps = 1 - #SOAK TEST CONFIG [Soak.Common] chainlink_node_funding = 0.1 @@ -211,3 +203,650 @@ bhs_test_duration = "1m" bhs_test_rate_limit_unit_duration = "3s" bhs_test_rps = 1 +### POLYGON AMOY Config + +[POLYGON_AMOY.VRFv2Plus.General] +#todo - need to have separate minimum_confirmations config for Coordinator, CL Node and Consumer request +minimum_confirmations = 3 + +# Consumer Request config +subscription_billing_type = "LINK_AND_NATIVE" +callback_gas_limit = 1000000 + +# NEW ENV CONFIG +# CL Node config +cl_node_max_gas_price_gwei = 200 +number_of_sending_keys_to_create = 0 + +# Coordinator config +max_gas_limit_coordinator_config = 2500000 +fallback_wei_per_unit_link = "19823132181656390000" +staleness_seconds = 172_800 +gas_after_payment_calculation = 48_500 +fulfillment_flat_fee_native_ppm = 0 +fulfillment_flat_fee_link_discount_ppm = 0 +native_premium_percentage = 84 +link_premium_percentage = 70 + +# Wrapper config +wrapped_gas_overhead = 13_400 +coordinator_gas_overhead_native = 99_500 +coordinator_gas_overhead_link = 121_500 +coordinator_gas_overhead_per_word = 435 +coordinator_native_premium_percentage = 84 +coordinator_link_premium_percentage = 70 +wrapper_max_number_of_words = 10 + +# VRF Job config +vrf_job_forwarding_allowed = false +vrf_job_estimate_gas_multiplier = 3.0 +vrf_job_batch_fulfillment_enabled = true +vrf_job_batch_fulfillment_gas_multiplier = 1.1 +vrf_job_poll_period = "2s" +vrf_job_request_timeout = "2h0m0s" +vrf_job_simulation_block = "latest" + +# BHS Job config +bhs_job_wait_blocks = 30 +bhs_job_lookback_blocks = 200 +bhs_job_poll_period = "2s" +bhs_job_run_timeout = "30s" +# NEW ENV CONFIG END + +[POLYGON_AMOY.VRFv2Plus.ExistingEnv] +coordinator_address = "0x7541EbaE23f32B4A1A2e7a8Cbf9da9582767A9B4" +consumer_address = "" +sub_id = "" +key_hash = "0xd360445bacd26df47086ccf255c4f932d297ed8d5c7334b51eed32f61c541601" +#key_hash = "0x2328cbee29e32d0b6662d6df82ff0fea7be300bd310561c92f515c9ee19464f1" +#key_hash = "0x25f4e2d0509f42ec77db5380f3433a89fe623fa75f65d5b398d5f498327be4dd" +create_fund_subs_and_add_consumers = true +link_address = "0x0Fd9e8d3aF1aaee056EB9e802c3A762a667b1904" +node_sending_key_funding_min = 10 +node_sending_keys = [ + "0xD96013C241f1741C35a135321969f92Aae02A12F", + "0x0580E61a5523F5CAAC4968E4f8FE63b59596BdD7", + "0xd15FcEa6a6AA17085930Fbd5647A9F7fD2Ff58b8", + "0xB7277cBb6E7028AE65235b8ee9201AcBb14B11d4", + "0x6D36a1dC1eEd25C75961E989c4d01Cd4453bE465", + "0xd299Cd7C0073b71e620bf8A3bfD50F75c0b49af8", + "0x48BE7BAED0b65776D85DF971fA901c637cFC5e87", + # BHS + "0x638372de870eF0F8E675A3f67F18D5bd4A2fd804", + "0xF9eF03816411D037202d5ed4457dC1613e3bd729", + "0xCD66973f8fbaE787211EC20228c6bd90D83562A8", + "0x242ea1F4Bb72EF643B2D8EF22e18a89f00742F40", + "0xaA09B4F9B5710b239fdbf1D0f535dd7f86F91219", + "0xe6b72B647B8B45C5562F7a5259E187889C747d3b", + "0x2c1185C4d3B0B4a577d4079Ee193A4e293164D9d" +] + +#SMOKE TEST CONFIG +[POLYGON_AMOY-Smoke.VRFv2Plus.General] +randomness_request_count_per_request = 1 # amount of randomness requests to make per one TX request +randomness_request_count_per_request_deviation = 0 #NOTE - deviation should be less than randomness_request_count_per_request setting +number_of_sub_to_create = 1 +subscription_funding_amount_link = 5 +subscription_funding_amount_native = 1 +subscription_refunding_amount_link = 5 +subscription_refunding_amount_native = 1 +number_of_words = 1 +random_words_fulfilled_event_timeout = "1m30s" +wait_for_256_blocks_timeout = "15m" + +wrapper_consumer_funding_amount_native_token = 1.0 +wrapper_consumer_funding_amount_link = 5 + + +[POLYGON_AMOY-Smoke.VRFv2Plus.Performance] +test_duration = "2s" +rate_limit_unit_duration = "15s" +rps = 1 + + +#SOAK TEST CONFIG +[POLYGON_AMOY-Soak.VRFv2Plus.General] +randomness_request_count_per_request = 1 # amount of randomness requests to make per one TX request +randomness_request_count_per_request_deviation = 0 #NOTE - deviation should be less than randomness_request_count_per_request setting +number_of_sub_to_create = 1 +subscription_funding_amount_link = 500 +subscription_funding_amount_native = 200 + +[POLYGON_AMOY-Soak.VRFv2Plus.Performance] +test_duration = "5h" +rate_limit_unit_duration = "3s" +rps = 1 + +# LOAD TEST CONFIG +[POLYGON_AMOY-Load.VRFv2Plus.General] +randomness_request_count_per_request = 3 # amount of randomness requests to make per one TX request +randomness_request_count_per_request_deviation = 2 #NOTE - deviation should be less than randomness_request_count_per_request setting +number_of_sub_to_create = 1 +subscription_funding_amount_link = 300 +subscription_funding_amount_native = 300 + +[POLYGON_AMOY-Load.VRFv2Plus.Performance] +test_duration = "2m" +rate_limit_unit_duration = "3s" +rps = 1 + + +# STRESS TEST CONFIG +[POLYGON_AMOY-Stress.VRFv2Plus.General] +randomness_request_count_per_request = 3 # amount of randomness requests to make per one TX request +randomness_request_count_per_request_deviation = 2 #NOTE - deviation should be less than randomness_request_count_per_request setting +number_of_sub_to_create = 1 +subscription_funding_amount_link = 5.0 +subscription_funding_amount_native = 0.1 + +[POLYGON_AMOY-Stress.VRFv2Plus.Performance] +test_duration = "2m" +rate_limit_unit_duration = "3s" +rps = 1 + +### ARBITRUM SEPOLIA Config + +[ARBITRUM_SEPOLIA.VRFv2Plus.General] +#todo - need to have separate minimum_confirmations config for Coordinator, CL Node and Consumer request +minimum_confirmations = 0 +subscription_billing_type = "LINK_AND_NATIVE" + +cl_node_max_gas_price_gwei = 50 +number_of_sending_keys_to_create = 0 + +# Coordinator config +callback_gas_limit = 1000000 +max_gas_limit_coordinator_config = 2500000 +fallback_wei_per_unit_link = "5352799651145251" +staleness_seconds = 172_800 +gas_after_payment_calculation = 56000 +fulfillment_flat_fee_native_ppm=0 +fulfillment_flat_fee_link_discount_ppm=0 +native_premium_percentage=60 +link_premium_percentage=50 + +# Wrapper config +wrapped_gas_overhead = 13_400 +coordinator_gas_overhead_native = 104_500 +coordinator_gas_overhead_link = 126_500 +coordinator_gas_overhead_per_word = 435 +coordinator_native_premium_percentage=60 +coordinator_link_premium_percentage=50 +wrapper_max_number_of_words = 10 + +# VRF Job config +vrf_job_forwarding_allowed = false +vrf_job_estimate_gas_multiplier = 3.0 +vrf_job_batch_fulfillment_enabled = true +vrf_job_batch_fulfillment_gas_multiplier = 1.1 +vrf_job_poll_period = "300ms" +vrf_job_request_timeout = "2h0m0s" +vrf_job_simulation_block="pending" + +# BHS Job config +bhs_job_wait_blocks = 30 +bhs_job_lookback_blocks = 200 +bhs_job_poll_period = "300ms" +bhs_job_run_timeout = "30s" +# NEW ENV CONFIG END + +[ARBITRUM_SEPOLIA.VRFv2Plus.ExistingEnv] +coordinator_address = "0xF7ba1Cf141F9729abC43c146dc2bf86EbcfD8603" +consumer_address = "" +sub_id = "" +key_hash = "0xe13aa26fe94bfcd2ae055911f4d3bf1aed54ca6cf77af34e17f918802fd69ba1" +create_fund_subs_and_add_consumers = true +link_address = "0xb1D4538B4571d411F07960EF2838Ce337FE1E80E" +node_sending_key_funding_min = 20 +node_sending_keys = [ + "0xbE21ae371FcA1aC2d8A152e707D21e68d7d99252", + "0xb13e9BA0aE94FD3b89B13b092e6d41614c805134", + "0x27aa703e585Ee165B7c977EAA652eCFa13b08294", + "0x9324643ACD2ec5b0813488E5EdAb64C3758ae4Ee", + "0x7CBA8c8e86f23f23363051650Fe5AE4DE78c3652", + "0x9A0143a4BAB55A826331A8ef82462557633aA016", + "0xD4259633F8e87949F683433a17e1fFcCE27865AC", + "0x5e47B71d6F95f68cd5538907ec6A9635b1Fe30Fa", + "0xa850a1a257FDF439c8f854ce3b89dd5b6F411827", + "0x7c82D56087c10aF2c3970f9a9Be7786B2850ce91", + "0x9545CB59956347d3debf27f5029754bBE1d398FA", + "0xEb8C69ac19709f27A97FB4A561f51AD2F9b34c5B", + # BHS + "0xf0e8cF7Fbd28Fc4D412B76B744CDA269df671F8e", + "0x317A02A658d12E5Bb4A6270171E7385928dD2d53", + "0x480f1dbcEc118Bd91e4dbb7e45bFa4A73180d21f", + "0x500a2662FaF981EC4669f791349D37Cbf1bE7d85", + "0x2ad350374B904c10B47c64ECdBD9e70BB0833Be2", + "0x0b946F0bF4e63C12b5157137f1c130f0788bC1b1", + # BHF + "0x571BBF4a5b07fc3F47Bd3B65CE2FE73739f86623" +] + +#SMOKE TEST CONFIG +[ARBITRUM_SEPOLIA-Smoke.VRFv2Plus.General] +randomness_request_count_per_request = 1 # amount of randomness requests to make per one TX request +randomness_request_count_per_request_deviation = 0 #NOTE - deviation should be less than randomness_request_count_per_request setting +number_of_sub_to_create = 1 +subscription_funding_amount_link = 5 +subscription_funding_amount_native = 1 +subscription_refunding_amount_link = 20 +subscription_refunding_amount_native = 10 +number_of_words = 1 +random_words_fulfilled_event_timeout = "1m30s" +wait_for_256_blocks_timeout = "100s" + +wrapper_consumer_funding_amount_native_token = 1.0 +wrapper_consumer_funding_amount_link = 5 + +[ARBITRUM_SEPOLIA-Smoke.VRFv2Plus.Performance] +test_duration = "1s" +rate_limit_unit_duration = "10s" +rps = 1 + + +#SOAK TEST CONFIG +[ARBITRUM_SEPOLIA-Soak.VRFv2Plus.General] +randomness_request_count_per_request = 4 # amount of randomness requests to make per one TX request +randomness_request_count_per_request_deviation = 0 #NOTE - deviation should be less than randomness_request_count_per_request setting +number_of_sub_to_create = 1 +subscription_funding_amount_link = 300 +subscription_funding_amount_native = 100 + +[ARBITRUM_SEPOLIA-Soak.VRFv2Plus.Performance] +test_duration = "1h" +rate_limit_unit_duration = "10s" +rps = 1 + +# LOAD TEST CONFIG +[ARBITRUM_SEPOLIA-Load.VRFv2Plus.General] +randomness_request_count_per_request = 1 # amount of randomness requests to make per one TX request +randomness_request_count_per_request_deviation = 0 #NOTE - deviation should be less than randomness_request_count_per_request setting +number_of_sub_to_create = 1 +subscription_funding_amount_link = 100 +subscription_funding_amount_native = 60 + +[ARBITRUM_SEPOLIA-Load.VRFv2Plus.Performance] +test_duration = "30m" +rate_limit_unit_duration = "3s" +rps = 1 + + +# STRESS TEST CONFIG +[ARBITRUM_SEPOLIA-Stress.VRFv2Plus.General] +randomness_request_count_per_request = 3 # amount of randomness requests to make per one TX request +randomness_request_count_per_request_deviation = 2 #NOTE - deviation should be less than randomness_request_count_per_request setting +number_of_sub_to_create = 1 +subscription_funding_amount_link = 5.0 +subscription_funding_amount_native = 0.1 + +[ARBITRUM_SEPOLIA-Stress.VRFv2Plus.Performance] +test_duration = "2m" +rate_limit_unit_duration = "3s" +rps = 1 + + +### AVALANCHE FUJI Config +[AVALANCHE_FUJI.VRFv2Plus.General] +#todo - need to have separate minimum_confirmations config for Coordinator, CL Node and Consumer request +minimum_confirmations = 0 + +# Consumer Request config +subscription_billing_type = "LINK_AND_NATIVE" +callback_gas_limit = 1000000 + +# NEW ENV CONFIG +# CL Node config +cl_node_max_gas_price_gwei = 300 +number_of_sending_keys_to_create = 0 + +# Coordinator config +max_gas_limit_coordinator_config = 2500000 +fallback_wei_per_unit_link = "378709808510249900" +staleness_seconds = 172_800 +gas_after_payment_calculation = 56_000 +fulfillment_flat_fee_native_ppm = 0 +fulfillment_flat_fee_link_discount_ppm = 0 +native_premium_percentage = 60 +link_premium_percentage = 50 + +# Wrapper config +wrapped_gas_overhead = 13_400 +coordinator_gas_overhead_native = 107_000 +coordinator_gas_overhead_link = 129_000 +coordinator_gas_overhead_per_word = 435 +coordinator_native_premium_percentage = 60 +coordinator_link_premium_percentage = 50 +wrapper_max_number_of_words = 10 + +# VRF Job config +vrf_job_forwarding_allowed = false +vrf_job_estimate_gas_multiplier = 3.0 +vrf_job_batch_fulfillment_enabled = true +vrf_job_batch_fulfillment_gas_multiplier = 1.1 +vrf_job_poll_period = "2s" +vrf_job_request_timeout = "2h0m0s" +vrf_job_simulation_block = "pending" + +# BHS Job config +bhs_job_wait_blocks = 30 +bhs_job_lookback_blocks = 200 +bhs_job_poll_period = "2s" +bhs_job_run_timeout = "30s" +# NEW ENV CONFIG END + +[AVALANCHE_FUJI.VRFv2Plus.ExistingEnv] +coordinator_address = "0xE122bf3Badd6545bDec5D4627a6DAd16352A1b36" +consumer_address = "" +sub_id = "" +key_hash = "0x5b03254a80ea3eb72139ff0423cb88be42612780c3dd25f1d95a5ba7708a4be1" +create_fund_subs_and_add_consumers = true +link_address = "0x0b9d5D9136855f6FEc3c0993feE6E9CE8a297846" +node_sending_key_funding_min = 50 +node_sending_keys = [ + "0x3D7Da5D6A23CA2240CE576C8638C1798a023920a", + # BHS + "0x72c8565279430F5179b0090d51ab8BB53Da323B5" +] + +#SMOKE TEST CONFIG +[AVALANCHE_FUJI-Smoke.VRFv2Plus.General] +randomness_request_count_per_request = 1 # amount of randomness requests to make per one TX request +randomness_request_count_per_request_deviation = 0 #NOTE - deviation should be less than randomness_request_count_per_request setting +number_of_sub_to_create = 1 +subscription_funding_amount_link = 20 +subscription_funding_amount_native = 20 +subscription_refunding_amount_link = 20 +subscription_refunding_amount_native = 20 +number_of_words = 1 +random_words_fulfilled_event_timeout = "1m30s" +wait_for_256_blocks_timeout = "10m" + +wrapper_consumer_funding_amount_native_token = 1.0 +wrapper_consumer_funding_amount_link = 5 + +[AVALANCHE_FUJI-Smoke.VRFv2Plus.Performance] +test_duration = "2s" +rate_limit_unit_duration = "10s" +rps = 1 + + +#SOAK TEST CONFIG +[AVALANCHE_FUJI-Soak.VRFv2Plus.General] +randomness_request_count_per_request = 1 # amount of randomness requests to make per one TX request +randomness_request_count_per_request_deviation = 0 #NOTE - deviation should be less than randomness_request_count_per_request setting +number_of_sub_to_create = 1 +subscription_funding_amount_link = 400 +subscription_funding_amount_native = 200 + +[AVALANCHE_FUJI-Soak.VRFv2Plus.Performance] +test_duration = "5h" +rate_limit_unit_duration = "3s" +rps = 1 + +# LOAD TEST CONFIG +[AVALANCHE_FUJI-Load.VRFv2Plus.General] +randomness_request_count_per_request = 3 # amount of randomness requests to make per one TX request +randomness_request_count_per_request_deviation = 2 #NOTE - deviation should be less than randomness_request_count_per_request setting +number_of_sub_to_create = 1 +subscription_funding_amount_link = 300 +subscription_funding_amount_native = 300 + +[AVALANCHE_FUJI-Load.VRFv2Plus.Performance] +test_duration = "2m" +rate_limit_unit_duration = "3s" +rps = 1 + + +# STRESS TEST CONFIG +[AVALANCHE_FUJI-Stress.VRFv2Plus.General] +randomness_request_count_per_request = 3 # amount of randomness requests to make per one TX request +randomness_request_count_per_request_deviation = 2 #NOTE - deviation should be less than randomness_request_count_per_request setting +number_of_sub_to_create = 1 +subscription_funding_amount_link = 5.0 +subscription_funding_amount_native = 0.1 + +[AVALANCHE_FUJI-Stress.VRFv2Plus.Performance] +test_duration = "2m" +rate_limit_unit_duration = "3s" +rps = 1 + +### ETH SEPOLIA Config +[SEPOLIA.VRFv2Plus.General] +#todo - need to have separate minimum_confirmations config for Coordinator, CL Node and Consumer request +minimum_confirmations = 3 + +# Consumer Request config +subscription_billing_type = "LINK_AND_NATIVE" +callback_gas_limit = 1000000 + +# NEW ENV CONFIG +# CL Node config +cl_node_max_gas_price_gwei = 100 +number_of_sending_keys_to_create = 0 + +# Coordinator config +max_gas_limit_coordinator_config = 2500000 +fallback_wei_per_unit_link = "5354747932930759" +staleness_seconds = 172_800 +gas_after_payment_calculation = 38_900 +fulfillment_flat_fee_native_ppm = 0 +fulfillment_flat_fee_link_discount_ppm = 0 +native_premium_percentage = 24 +link_premium_percentage = 20 + +# Wrapper config +wrapped_gas_overhead = 13_400 +coordinator_gas_overhead_native = 90_000 +coordinator_gas_overhead_link = 112_000 +coordinator_gas_overhead_per_word = 435 +coordinator_native_premium_percentage = 24 +coordinator_link_premium_percentage = 20 +wrapper_max_number_of_words = 10 + +# VRF Job config +vrf_job_forwarding_allowed = false +vrf_job_estimate_gas_multiplier = 1.15 +vrf_job_batch_fulfillment_enabled = false +vrf_job_batch_fulfillment_gas_multiplier = 1.1 +vrf_job_poll_period = "5s" +vrf_job_request_timeout = "2h0m0s" +vrf_job_simulation_block = "latest" + +# BHS Job config +bhs_job_wait_blocks = 30 +bhs_job_lookback_blocks = 200 +bhs_job_poll_period = "30s" +bhs_job_run_timeout = "1m0s" +# NEW ENV CONFIG END + +[SEPOLIA.VRFv2Plus.ExistingEnv] +coordinator_address = "0x2F3b892710523Ee9A85c3155a42089fFe99Ca31e" +consumer_address = "" +sub_id = "" +key_hash = "0xf5b4a359df0598eef89872ea2170f2afa844dbf74b417e6d44d4bda9420aceb2" +create_fund_subs_and_add_consumers = true +link_address = "0x779877A7B0D9E8603169DdbD7836e478b4624789" +node_sending_key_funding_min = 50 +node_sending_keys = [ + "0x0c0DC7f33A1256f0247c5ea75861d385fa5FED31", + # BHS + "0xEd8A4b792d16484f6c9B4df1e721e8280925Db80", +] + +#SMOKE TEST CONFIG +[SEPOLIA-Smoke.VRFv2Plus.General] +randomness_request_count_per_request = 1 # amount of randomness requests to make per one TX request +randomness_request_count_per_request_deviation = 0 #NOTE - deviation should be less than randomness_request_count_per_request setting +number_of_sub_to_create = 1 +subscription_funding_amount_link = 5 +subscription_funding_amount_native = 1 +subscription_refunding_amount_link = 5 +subscription_refunding_amount_native = 1 +number_of_words = 1 +random_words_fulfilled_event_timeout = "1m30s" +wait_for_256_blocks_timeout = "70m" + +wrapper_consumer_funding_amount_native_token = 1.0 +wrapper_consumer_funding_amount_link = 5 + +[SEPOLIA-Smoke.VRFv2Plus.Performance] +test_duration = "2s" +rate_limit_unit_duration = "3s" +rps = 1 + +#SOAK TEST CONFIG +[SEPOLIA-Soak.VRFv2Plus.General] +randomness_request_count_per_request = 1 # amount of randomness requests to make per one TX request +randomness_request_count_per_request_deviation = 0 #NOTE - deviation should be less than randomness_request_count_per_request setting +number_of_sub_to_create = 1 +subscription_funding_amount_link = 500 +subscription_funding_amount_native = 200 + +[SEPOLIA-Soak.VRFv2Plus.Performance] +test_duration = "2h" +rate_limit_unit_duration = "3s" +rps = 1 + +# LOAD TEST CONFIG +[SEPOLIA-Load.VRFv2Plus.General] +randomness_request_count_per_request = 3 # amount of randomness requests to make per one TX request +randomness_request_count_per_request_deviation = 2 #NOTE - deviation should be less than randomness_request_count_per_request setting +number_of_sub_to_create = 1 +subscription_funding_amount_link = 5.0 +subscription_funding_amount_native = 30 + +[SEPOLIA-Load.VRFv2Plus.Performance] +test_duration = "2m" +rate_limit_unit_duration = "3s" +rps = 1 + +# STRESS TEST CONFIG +[SEPOLIA-Stress.VRFv2Plus.General] +randomness_request_count_per_request = 3 # amount of randomness requests to make per one TX request +randomness_request_count_per_request_deviation = 2 #NOTE - deviation should be less than randomness_request_count_per_request setting +number_of_sub_to_create = 1 +subscription_funding_amount_link = 5.0 +subscription_funding_amount_native = 0.1 + +[SEPOLIA-Stress.VRFv2Plus.Performance] +test_duration = "2m" +rate_limit_unit_duration = "3s" +rps = 1 + +### BSC SEPOLIA Config +[BSC_TESTNET.VRFv2Plus.General] +use_existing_env = true +#todo - need to have separate minimum_confirmations config for Coordinator, CL Node and Consumer request +minimum_confirmations = 3 + +# Consumer Request config +subscription_billing_type = "LINK_AND_NATIVE" +callback_gas_limit = 1000000 + +# NEW ENV CONFIG +# CL Node config +cl_node_max_gas_price_gwei = 50 +number_of_sending_keys_to_create = 0 + +# Coordinator config +max_gas_limit_coordinator_config = 2500000 +fallback_wei_per_unit_link = "30531029880396850" +staleness_seconds = 172_800 +gas_after_payment_calculation = 48_500 +fulfillment_flat_fee_native_ppm = 0 +fulfillment_flat_fee_link_discount_ppm = 0 +native_premium_percentage = 60 +link_premium_percentage = 50 + +# Wrapper config +wrapped_gas_overhead = 13_400 +coordinator_gas_overhead_native = 99_500 +coordinator_gas_overhead_link = 121_500 +coordinator_gas_overhead_per_word = 435 +coordinator_native_premium_percentage = 60 +coordinator_link_premium_percentage = 50 +wrapper_max_number_of_words = 10 + +# VRF Job config +vrf_job_forwarding_allowed = false +vrf_job_estimate_gas_multiplier = 1.1 +vrf_job_batch_fulfillment_enabled = true +vrf_job_batch_fulfillment_gas_multiplier = 1.1 +vrf_job_poll_period = "3s" +vrf_job_request_timeout = "2h0m0s" +vrf_job_simulation_block = "latest" + +# BHS Job config +bhs_job_wait_blocks = 30 +bhs_job_lookback_blocks = 200 +bhs_job_poll_period = "2s" +bhs_job_run_timeout = "30s" +# NEW ENV CONFIG END + +[BSC_TESTNET.VRFv2Plus.ExistingEnv] +coordinator_address = "0x84A477F6ebF33501eE3ACA86fEcB980b1fC99AC2" +consumer_address = "" +sub_id = "" +key_hash = "0x4d43763d3eff849a89cf578a42787baa32132d7a80032125710e95b3972cd214" +create_fund_subs_and_add_consumers = true +link_address = "0x84b9B910527Ad5C03A9Ca831909E21e236EA7b06" +node_sending_key_funding_min = 150 +node_sending_keys = [ + "0x4EE2Cc6D50E8acb6BaEf673B03559525a6c92fB8", + # BHS + "0xAFB44568f7DAc218EA6e1C71c366692ED4758A07" +] + +#SMOKE TEST CONFIG +[BSC_TESTNET-Smoke.VRFv2Plus.General] +randomness_request_count_per_request = 1 # amount of randomness requests to make per one TX request +randomness_request_count_per_request_deviation = 0 #NOTE - deviation should be less than randomness_request_count_per_request setting +number_of_sub_to_create = 1 +subscription_funding_amount_link = 10 +subscription_funding_amount_native = 10 + +[BSC_TESTNET-Smoke.VRFv2Plus.Performance] +test_duration = "15s" +rate_limit_unit_duration = "3s" +rps = 1 + + +#SOAK TEST CONFIG +[BSC_TESTNET-Soak.VRFv2Plus.General] +randomness_request_count_per_request = 1 # amount of randomness requests to make per one TX request +randomness_request_count_per_request_deviation = 0 #NOTE - deviation should be less than randomness_request_count_per_request setting +number_of_sub_to_create = 1 +subscription_funding_amount_link = 400 +subscription_funding_amount_native = 200 + +[BSC_TESTNET-Soak.VRFv2Plus.Performance] +test_duration = "5h" +rate_limit_unit_duration = "3s" +rps = 1 + +# LOAD TEST CONFIG +[BSC_TESTNET-Load.VRFv2Plus.General] +randomness_request_count_per_request = 3 # amount of randomness requests to make per one TX request +randomness_request_count_per_request_deviation = 2 #NOTE - deviation should be less than randomness_request_count_per_request setting +number_of_sub_to_create = 1 +subscription_funding_amount_link = 300 +subscription_funding_amount_native = 300 + +[BSC_TESTNET-Load.VRFv2Plus.Performance] +test_duration = "2m" +rate_limit_unit_duration = "3s" +rps = 1 + + +# STRESS TEST CONFIG +[BSC_TESTNET-Stress.VRFv2Plus.General] +randomness_request_count_per_request = 3 # amount of randomness requests to make per one TX request +randomness_request_count_per_request_deviation = 2 #NOTE - deviation should be less than randomness_request_count_per_request setting +number_of_sub_to_create = 1 +subscription_funding_amount_link = 5.0 +subscription_funding_amount_native = 0.1 + +[BSC_TESTNET-Stress.VRFv2Plus.Performance] +test_duration = "2m" +rate_limit_unit_duration = "3s" +rps = 1 diff --git a/integration-tests/testsetups/keeper_benchmark.go b/integration-tests/testsetups/keeper_benchmark.go index 81f2ce0edf..d50355f39b 100644 --- a/integration-tests/testsetups/keeper_benchmark.go +++ b/integration-tests/testsetups/keeper_benchmark.go @@ -801,7 +801,7 @@ func (k *KeeperBenchmarkTest) DeployBenchmarkKeeperContracts(index int) { err = actions.DeployMultiCallAndFundDeploymentAddresses(k.chainClient, k.linkToken, upkeep.NumberOfUpkeeps, linkFunds) require.NoError(k.t, err, "Sending link funds to deployment addresses shouldn't fail") - upkeepIds := actions.RegisterUpkeepContractsWithCheckData(k.t, k.chainClient, k.linkToken, linkFunds, uint32(upkeep.UpkeepGasLimit), registry, registrar, upkeep.NumberOfUpkeeps, upkeepAddresses, checkData, false, false) + upkeepIds := actions.RegisterUpkeepContractsWithCheckData(k.t, k.chainClient, k.linkToken, linkFunds, uint32(upkeep.UpkeepGasLimit), registry, registrar, upkeep.NumberOfUpkeeps, upkeepAddresses, checkData, false, false, false, nil) k.keeperRegistries[index] = registry k.keeperRegistrars[index] = registrar diff --git a/integration-tests/testsetups/ocr.go b/integration-tests/testsetups/ocr.go index fe4d15fc96..45c334bf69 100644 --- a/integration-tests/testsetups/ocr.go +++ b/integration-tests/testsetups/ocr.go @@ -21,7 +21,6 @@ import ( geth "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" - "github.com/google/uuid" "github.com/pelletier/go-toml/v2" "github.com/rs/zerolog" "github.com/smartcontractkit/seth" @@ -152,22 +151,17 @@ func NewOCRSoakTest(t *testing.T, config *tc.TestConfig, opts ...OCRSoakTestOpti func (o *OCRSoakTest) DeployEnvironment(ocrTestConfig tt.OcrTestConfig) { nodeNetwork := networks.MustGetSelectedNetworkConfig(ocrTestConfig.GetNetworkConfig())[0] // Environment currently being used to soak test on - // Define namespace if default not set - if o.namespace == "" { - nsPre := fmt.Sprintf("soak-ocr-v%s-", o.OCRVersion) - if o.OperatorForwarderFlow { - nsPre = fmt.Sprintf("%sforwarder-", nsPre) - } - - nsPre = fmt.Sprintf("%s%s", nsPre, strings.ReplaceAll(strings.ToLower(nodeNetwork.Name), " ", "-")) - nsPre = strings.ReplaceAll(nsPre, "_", "-") - - o.namespace = fmt.Sprintf("%s-%s", nsPre, uuid.NewString()[0:5]) + nsPre := fmt.Sprintf("soak-ocr-v%s-", o.OCRVersion) + if o.OperatorForwarderFlow { + nsPre = fmt.Sprintf("%sforwarder-", nsPre) } + nsPre = fmt.Sprintf("%s%s", nsPre, strings.ReplaceAll(strings.ToLower(nodeNetwork.Name), " ", "-")) + nsPre = strings.ReplaceAll(nsPre, "_", "-") + baseEnvironmentConfig := &environment.Config{ TTL: time.Hour * 720, // 30 days, - Namespace: o.namespace, + NamespacePrefix: nsPre, Test: o.t, PreventPodEviction: true, } @@ -378,7 +372,7 @@ func (o *OCRSoakTest) Setup(ocrTestConfig tt.OcrTestConfig) { // Run starts the OCR soak test func (o *OCRSoakTest) Run() { - config, err := tc.GetConfig("soak", tc.OCR) + config, err := tc.GetConfig([]string{"soak"}, tc.OCR) require.NoError(o.t, err, "Error getting config") ctx, cancel := context.WithTimeout(testcontext.Get(o.t), time.Second*5) diff --git a/integration-tests/types/testconfigs.go b/integration-tests/types/testconfigs.go index 58eb1a7c8c..ee8a614bae 100644 --- a/integration-tests/types/testconfigs.go +++ b/integration-tests/types/testconfigs.go @@ -35,7 +35,7 @@ type KeeperBenchmarkTestConfig interface { ctf_config.GlobalTestConfig tc.CommonTestConfig tc.KeeperTestConfig - ctf_config.NamedConfiguration + ctf_config.NamedConfigurations testreporters.GrafanaURLProvider } diff --git a/integration-tests/universal/log_poller/helpers.go b/integration-tests/universal/log_poller/helpers.go index 7bedef393d..ee4060f7ab 100644 --- a/integration-tests/universal/log_poller/helpers.go +++ b/integration-tests/universal/log_poller/helpers.go @@ -13,8 +13,6 @@ import ( "testing" "time" - seth_utils "github.com/smartcontractkit/chainlink-testing-framework/utils/seth" - geth "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" @@ -28,9 +26,11 @@ import ( "github.com/smartcontractkit/wasp" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + ctf_concurrency "github.com/smartcontractkit/chainlink-testing-framework/concurrency" ctf_test_env "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/networks" + seth_utils "github.com/smartcontractkit/chainlink-testing-framework/utils/seth" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/client" @@ -204,11 +204,6 @@ func randomWait(minMilliseconds, maxMilliseconds int) { time.Sleep(time.Duration(randomMilliseconds) * time.Millisecond) } -type LogEmitterChannel struct { - logsEmitted int - err error -} - // getIntSlice returns a slice of ints of the provided length func getIntSlice(length int) []int { result := make([]int, length) @@ -229,95 +224,6 @@ func getStringSlice(length int) []string { return result } -// emitEvents emits events from the provided log emitter concurrently according to the provided config -func emitEvents(ctx context.Context, l zerolog.Logger, client *seth.Client, logEmitter *contracts.LogEmitter, cfg *lp_config.Config, wg *sync.WaitGroup, results chan LogEmitterChannel) { - address := (*logEmitter).Address().String() - defer wg.Done() - - var executionGroup sync.WaitGroup - - // Atomic counter is used to keep track of the number of logs emitted - var atomicCounter = atomic.Int32{} - - for i := 0; i < *cfg.LoopedConfig.ExecutionCount; i++ { - executionGroup.Add(1) - } - - var emitAllEvents = func() { - defer executionGroup.Done() - current := atomicCounter.Add(1) - - for _, event := range cfg.General.EventsToEmit { - select { - case <-ctx.Done(): - l.Warn().Str("Emitter address", address).Msg("Context cancelled, not emitting events") - return - default: - l.Debug().Str("Emitter address", address).Str("Event type", event.Name).Str("index", fmt.Sprintf("%d/%d", current, *cfg.LoopedConfig.ExecutionCount)).Msg("Emitting log from emitter") - var err error - switch event.Name { - case "Log1": - _, err = client.Decode((*logEmitter).EmitLogIntsFromKey(getIntSlice(*cfg.General.EventsPerTx), client.AnySyncedKey())) - case "Log2": - _, err = client.Decode((*logEmitter).EmitLogIntsIndexedFromKey(getIntSlice(*cfg.General.EventsPerTx), client.AnySyncedKey())) - case "Log3": - _, err = client.Decode((*logEmitter).EmitLogStringsFromKey(getStringSlice(*cfg.General.EventsPerTx), client.AnySyncedKey())) - case "Log4": - _, err = client.Decode((*logEmitter).EmitLogIntMultiIndexedFromKey(1, 1, *cfg.General.EventsPerTx, client.AnySyncedKey())) - default: - err = fmt.Errorf("unknown event name: %s", event.Name) - } - - if err != nil { - results <- LogEmitterChannel{ - err: err, - } - return - } - randomWait(*cfg.LoopedConfig.MinEmitWaitTimeMs, *cfg.LoopedConfig.MaxEmitWaitTimeMs) - } - - if (current)%10 == 0 { - l.Info().Str("Emitter address", address).Str("Index", fmt.Sprintf("%d/%d", current, *cfg.LoopedConfig.ExecutionCount)).Msgf("Emitted all %d events", len(cfg.General.EventsToEmit)) - } - } - } - - clientNumber := int(*client.Cfg.EphemeralAddrs) - emissionsPerClient := *cfg.LoopedConfig.ExecutionCount / clientNumber - extraEmissions := *cfg.LoopedConfig.ExecutionCount % clientNumber - - l.Debug().Str("Emitter address", address). - Int("Total logs to emit", *cfg.LoopedConfig.ExecutionCount*len(cfg.General.EventsToEmit)*(*cfg.General.EventsPerTx)). - Int("Total clients", clientNumber). - Int("Emissions per client", emissionsPerClient). - Int("Extra emissions", extraEmissions). - Msg("Starting to emit events") - - for i := 0; i < clientNumber; i++ { - go func(key int) { - numTasks := emissionsPerClient - if key < extraEmissions { - numTasks++ - } - - for idx := 0; idx < numTasks; idx++ { - emitAllEvents() - } - }(i) - } - - executionGroup.Wait() - - localCounter := int(atomicCounter.Load()) * *cfg.General.EventsPerTx * len(cfg.General.EventsToEmit) - l.Info().Str("Emitter address", address).Int("Total logs emitted", localCounter).Msg("Finished emitting events") - - results <- LogEmitterChannel{ - logsEmitted: localCounter, - err: nil, - } -} - // LogPollerHasFinalisedEndBlock returns true if all CL nodes have finalised processing the provided end block func LogPollerHasFinalisedEndBlock(endBlock int64, chainID *big.Int, l zerolog.Logger, coreLogger core_logger.SugaredLogger, nodes *test_env.ClCluster) (bool, error) { wg := &sync.WaitGroup{} @@ -699,7 +605,7 @@ func GetMissingLogs(startBlock, endBlock int64, logEmitters []*contracts.LogEmit if int64(len(allLogsInEVMNode)) != expectedTotalLogsEmitted { l.Warn(). Str("Actual/Expected", fmt.Sprintf("%d/%d", expectedTotalLogsEmitted, len(allLogsInEVMNode))). - Msg("Some of the test logs were not found in EVM node. This is a bug in the test") + Msg("Actual number of logs found on EVM nodes differs from expected ones. Most probably this is a bug in the test") } return missingLogs, nil @@ -854,58 +760,89 @@ func runWaspGenerator(t *testing.T, cfg *lp_config.Config, logEmitters []*contra return counter.value, nil } +type logEmissionTask struct { + emitter *contracts.LogEmitter + eventsToEmit []abi.Event + eventsPerTx int +} + +type emittedLogsData struct { + count int +} + +func (d emittedLogsData) GetResult() emittedLogsData { + return d +} + // runLoopedGenerator runs the looped generator and returns the total number of logs emitted func runLoopedGenerator(t *testing.T, cfg *lp_config.Config, client *seth.Client, logEmitters []*contracts.LogEmitter) (int, error) { l := logging.GetTestLogger(t) - // Start emitting events in parallel, each contract is emitting events in a separate goroutine - // We will stop as soon as we encounter an error - wg := &sync.WaitGroup{} - emitterCh := make(chan LogEmitterChannel, len(logEmitters)) + tasks := make([]logEmissionTask, 0) + for i := 0; i < *cfg.LoopedConfig.ExecutionCount; i++ { + for _, logEmitter := range logEmitters { + tasks = append(tasks, logEmissionTask{ + emitter: logEmitter, + eventsToEmit: cfg.General.EventsToEmit, + eventsPerTx: *cfg.General.EventsPerTx, + }) + } + } - ctx, cancelFn := context.WithCancel(context.Background()) - defer cancelFn() + l.Info().Int("Total tasks", len(tasks)).Msg("Starting to emit events") - for i := 0; i < len(logEmitters); i++ { - wg.Add(1) - go func(idx int) { - emitEvents(ctx, l, client, logEmitters[idx], cfg, wg, emitterCh) - }(i) - } + var atomicCounter = atomic.Int32{} - var emitErr error - total := 0 + var emitAllEventsFn = func(resultCh chan emittedLogsData, errorCh chan error, _ int, task logEmissionTask) { + current := atomicCounter.Add(1) - aggrChan := make(chan int, len(logEmitters)) + address := (*task.emitter).Address().String() - go func() { - for { - select { - case <-ctx.Done(): + for _, event := range cfg.General.EventsToEmit { + l.Debug().Str("Emitter address", address).Str("Event type", event.Name).Str("index", fmt.Sprintf("%d/%d", current, *cfg.LoopedConfig.ExecutionCount)).Msg("Emitting log from emitter") + var err error + switch event.Name { + case "Log1": + _, err = client.Decode((*task.emitter).EmitLogIntsFromKey(getIntSlice(*cfg.General.EventsPerTx), client.AnySyncedKey())) + case "Log2": + _, err = client.Decode((*task.emitter).EmitLogIntsIndexedFromKey(getIntSlice(*cfg.General.EventsPerTx), client.AnySyncedKey())) + case "Log3": + _, err = client.Decode((*task.emitter).EmitLogStringsFromKey(getStringSlice(*cfg.General.EventsPerTx), client.AnySyncedKey())) + case "Log4": + _, err = client.Decode((*task.emitter).EmitLogIntMultiIndexedFromKey(1, 1, *cfg.General.EventsPerTx, client.AnySyncedKey())) + default: + err = fmt.Errorf("unknown event name: %s", event.Name) + } + + if err != nil { + errorCh <- err return - case emitter := <-emitterCh: - if emitter.err != nil { - emitErr = emitter.err - cancelFn() - return - } - aggrChan <- emitter.logsEmitted + } + randomWait(*cfg.LoopedConfig.MinEmitWaitTimeMs, *cfg.LoopedConfig.MaxEmitWaitTimeMs) + + if (current)%10 == 0 { + l.Info().Str("Emitter address", address).Str("Index", fmt.Sprintf("%d/%d", current, *cfg.LoopedConfig.ExecutionCount)).Msgf("Emitted all %d events", len(cfg.General.EventsToEmit)) } } - }() - wg.Wait() - close(emitterCh) + resultCh <- emittedLogsData{ + *cfg.General.EventsPerTx * len(cfg.General.EventsToEmit), + } + } + + executor := ctf_concurrency.NewConcurrentExecutor[emittedLogsData, emittedLogsData, logEmissionTask](l) + r, err := executor.Execute(int(*client.Cfg.EphemeralAddrs), tasks, emitAllEventsFn) - if emitErr != nil { - return 0, emitErr + if err != nil { + return 0, err } - for i := 0; i < len(logEmitters); i++ { - total += <-aggrChan + var total int + for _, result := range r { + total += result.count } - return int(total), nil + return total, nil } // GetExpectedLogCount returns the expected number of logs to be emitted based on the provided config diff --git a/operator_ui/TAG b/operator_ui/TAG index 57ed352173..bd6882b0f5 100644 --- a/operator_ui/TAG +++ b/operator_ui/TAG @@ -1 +1 @@ -v0.8.0-7e6a14f +v0.8.0-094d250 diff --git a/package.json b/package.json index ea42db5bae..b49ae5f206 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ccip", - "version": "2.13.0-ccip1.4.13", + "version": "2.14.0-ccip1.4.13", "description": "node of the decentralized oracle network, bridging on and off-chain computation", "main": "index.js", "scripts": { diff --git a/testdata/scripts/help-all/help-all.txtar b/testdata/scripts/help-all/help-all.txtar index e111295abb..93b24580e3 100644 --- a/testdata/scripts/help-all/help-all.txtar +++ b/testdata/scripts/help-all/help-all.txtar @@ -55,6 +55,12 @@ jobs list # List all jobs jobs run # Trigger a job run jobs show # Show a job keys # Commands for managing various types of keys used by the Chainlink node +keys aptos # Remote commands for administering the node's Aptos keys +keys aptos create # Create a Aptos key +keys aptos delete # Delete Aptos key if present +keys aptos export # Export Aptos key to keyfile +keys aptos import # Import Aptos key from keyfile +keys aptos list # List the Aptos keys keys cosmos # Remote commands for administering the node's Cosmos keys keys cosmos create # Create a Cosmos key keys cosmos delete # Delete Cosmos key if present diff --git a/testdata/scripts/keys/aptos/help.txtar b/testdata/scripts/keys/aptos/help.txtar new file mode 100644 index 0000000000..94f0c7db8c --- /dev/null +++ b/testdata/scripts/keys/aptos/help.txtar @@ -0,0 +1,20 @@ +exec chainlink keys aptos --help +cmp stdout out.txt + +-- out.txt -- +NAME: + chainlink keys aptos - Remote commands for administering the node's Aptos keys + +USAGE: + chainlink keys aptos command [command options] [arguments...] + +COMMANDS: + create Create a Aptos key + import Import Aptos key from keyfile + export Export Aptos key to keyfile + delete Delete Aptos key if present + list List the Aptos keys + +OPTIONS: + --help, -h show help + diff --git a/testdata/scripts/keys/help.txtar b/testdata/scripts/keys/help.txtar index a54002e272..5c144017c6 100644 --- a/testdata/scripts/keys/help.txtar +++ b/testdata/scripts/keys/help.txtar @@ -17,6 +17,7 @@ COMMANDS: cosmos Remote commands for administering the node's Cosmos keys 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 dkgsign Remote commands for administering the node's DKGSign keys dkgencrypt Remote commands for administering the node's DKGEncrypt keys vrf Remote commands for administering the node's vrf keys diff --git a/testdata/scripts/node/validate/disk-based-logging-disabled.txtar b/testdata/scripts/node/validate/disk-based-logging-disabled.txtar index 571eb3a5ba..295ffdb183 100644 --- a/testdata/scripts/node/validate/disk-based-logging-disabled.txtar +++ b/testdata/scripts/node/validate/disk-based-logging-disabled.txtar @@ -331,6 +331,7 @@ NoNewHeadsThreshold = '3m0s' OperatorFactoryAddress = '0x3E64Cd889482443324F91bFA9c84fE72A511f48A' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [EVM.Transactions] ForwardersEnabled = false @@ -385,6 +386,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [EVM.OCR] ContractConfirmations = 4 diff --git a/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar b/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar index 2dc24dd4eb..4fce8e5a68 100644 --- a/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar +++ b/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar @@ -331,6 +331,7 @@ NoNewHeadsThreshold = '3m0s' OperatorFactoryAddress = '0x3E64Cd889482443324F91bFA9c84fE72A511f48A' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [EVM.Transactions] ForwardersEnabled = false @@ -385,6 +386,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [EVM.OCR] ContractConfirmations = 4 diff --git a/testdata/scripts/node/validate/disk-based-logging.txtar b/testdata/scripts/node/validate/disk-based-logging.txtar index 6769552771..e25573c88c 100644 --- a/testdata/scripts/node/validate/disk-based-logging.txtar +++ b/testdata/scripts/node/validate/disk-based-logging.txtar @@ -331,6 +331,7 @@ NoNewHeadsThreshold = '3m0s' OperatorFactoryAddress = '0x3E64Cd889482443324F91bFA9c84fE72A511f48A' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [EVM.Transactions] ForwardersEnabled = false @@ -385,6 +386,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [EVM.OCR] ContractConfirmations = 4 diff --git a/testdata/scripts/node/validate/invalid.txtar b/testdata/scripts/node/validate/invalid.txtar index 3f2dc1d7d2..641d81a115 100644 --- a/testdata/scripts/node/validate/invalid.txtar +++ b/testdata/scripts/node/validate/invalid.txtar @@ -321,6 +321,7 @@ NoNewHeadsThreshold = '3m0s' OperatorFactoryAddress = '0x3E64Cd889482443324F91bFA9c84fE72A511f48A' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [EVM.Transactions] ForwardersEnabled = false @@ -375,6 +376,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [EVM.OCR] ContractConfirmations = 4 diff --git a/testdata/scripts/node/validate/valid.txtar b/testdata/scripts/node/validate/valid.txtar index 6afc5cd82a..5d8b3446e4 100644 --- a/testdata/scripts/node/validate/valid.txtar +++ b/testdata/scripts/node/validate/valid.txtar @@ -328,6 +328,7 @@ NoNewHeadsThreshold = '3m0s' OperatorFactoryAddress = '0x3E64Cd889482443324F91bFA9c84fE72A511f48A' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [EVM.Transactions] ForwardersEnabled = false @@ -382,6 +383,8 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [EVM.OCR] ContractConfirmations = 4